Skip to content

Commit

Permalink
Use experimental language import to enable the new behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Dec 18, 2023
1 parent bc36acb commit fab7147
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 23 deletions.
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ object Feature:
val pureFunctions = experimental("pureFunctions")
val captureChecking = experimental("captureChecking")
val into = experimental("into")
val avoidLoopingGivens = experimental("avoidLoopingGivens")

val globalOnlyImports: Set[TermName] = Set(pureFunctions, captureChecking)

Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ object Scala3:
type SemanticSymbol = Symbol | FakeSymbol

given SemanticSymbolOps : AnyRef with
import SymbolOps.*
import StringOps.*

extension (sym: SemanticSymbol)
def name(using Context): Name = sym match
case s: Symbol => s.name
Expand Down
47 changes: 34 additions & 13 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1589,19 +1589,40 @@ trait Implicits:

val eligible = if contextual then preEligible.filterNot(comesTooLate) else preEligible

def checkResolutionChange(result: SearchResult) = result match
case result: SearchSuccess
if (eligible ne preEligible) && !sourceVersion.isAtLeast(SourceVersion.`future`) =>
searchImplicit(preEligible.diff(eligible), contextual) match
case prevResult: SearchSuccess =>
report.error(
em"""Warning: result of implicit search for $pt will change.
|current result: ${prevResult.ref.symbol.showLocated}
|result with -source future: ${result.ref.symbol.showLocated}""",
srcPos
)
case _ =>
case _ =>
def checkResolutionChange(result: SearchResult) =
if (eligible ne preEligible)
&& !Feature.enabled(Feature.avoidLoopingGivens)
then searchImplicit(preEligible.diff(eligible), contextual) match
case prevResult: SearchSuccess =>
def remedy = pt match
case _: SelectionProto =>
"conversion,\n - use an import to get extension method into scope"
case _: ViewProto =>
"conversion"
case _ =>
"argument"

def showResult(r: SearchResult) = r match
case r: SearchSuccess => ctx.printer.toTextRef(r.ref).show
case r => r.show

result match
case result: SearchSuccess if prevResult.ref frozen_=:= result.ref =>
// OK
case _ =>
report.error(
em"""Warning: result of implicit search for $pt will change.
|Current result ${showResult(prevResult)} will be no longer eligible
| because it is not defined before the search position.
|Result with new rules: ${showResult(result)}.
|To opt into the new rules, use the `avoidLoopingGivens` language import,
|
|To fix the problem you could try one of the following:
| - rearrange definitions,
| - use an explicit $remedy.""",
srcPos)
case _ =>
end checkResolutionChange

searchImplicit(eligible, contextual) match
case result: SearchSuccess =>
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
end TypeRepr

given TypeReprMethods: TypeReprMethods with
import SymbolMethods.*

extension (self: TypeRepr)

def show(using printer: Printer[TypeRepr]): String = printer.show(self)
Expand Down Expand Up @@ -2608,6 +2610,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
end Symbol

given SymbolMethods: SymbolMethods with
import FlagsMethods.*

extension (self: Symbol)
def owner: Symbol = self.denot.owner
def maybeOwner: Symbol = self.denot.maybeOwner
Expand Down
9 changes: 9 additions & 0 deletions library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ object language:
@compileTimeOnly("`into` can only be used at compile time in import statements")
object into

/** Experimental support for new given resolution rules that avoid looping
* givens. By the new rules, a given may not implicitly use itself or givens
* defined after it.
*
* @see [[https://dotty.epfl.ch/docs/reference/experimental/avoid-looping-givens]]
*/
@compileTimeOnly("`avoidLoopingGivens` can only be used at compile time in import statements")
object avoidLoopingGivens

/** Was needed to add support for relaxed imports of extension methods.
* The language import is no longer needed as this is now a standard feature since SIP was accepted.
* @see [[http://dotty.epfl.ch/docs/reference/contextual/extension-methods]]
Expand Down
12 changes: 9 additions & 3 deletions tests/neg/i15474.check
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
-- Error: tests/neg/i15474.scala:16:56 ---------------------------------------------------------------------------------
16 | given Ordering[Price] = summon[Ordering[BigDecimal]] // error
| ^
| Warning: result of implicit search for Ordering[BigDecimal] will change.
| current result: given instance given_Ordering_Price in object Price
| result with -source future: object BigDecimal in object Ordering
| Warning: result of implicit search for Ordering[BigDecimal] will change.
| Current result Prices.Price.given_Ordering_Price will be no longer eligible
| because it is not defined before the search position.
| Result with new rules: scala.math.Ordering.BigDecimal.
| To opt into the new rules, use the `avoidLoopingGivens` language import,
|
| To fix the problem you could try one of the following:
| - rearrange definitions,
| - use an explicit argument.
10 changes: 8 additions & 2 deletions tests/neg/i6716.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
12 | given Monad[Bar] = summon[Monad[Foo]] // error
| ^
| Warning: result of implicit search for Monad[Foo] will change.
| current result: given instance given_Monad_Bar in object Bar
| result with -source future: object given_Monad_Foo in object Foo
| Current result Bar.given_Monad_Bar will be no longer eligible
| because it is not defined before the search position.
| Result with new rules: Foo.given_Monad_Foo.
| To opt into the new rules, use the `avoidLoopingGivens` language import,
|
| To fix the problem you could try one of the following:
| - rearrange definitions,
| - use an explicit argument.
23 changes: 23 additions & 0 deletions tests/neg/i7294-a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- Error: tests/neg/i7294-a.scala:6:10 ---------------------------------------------------------------------------------
6 | case x: T => x.g(10) // error // error
| ^
| Warning: result of implicit search for scala.reflect.TypeTest[Nothing, T] will change.
| Current result foo.i7294-a$package.f will be no longer eligible
| because it is not defined before the search position.
| Result with new rules: No Matching Implicit.
| To opt into the new rules, use the `avoidLoopingGivens` language import,
|
| To fix the problem you could try one of the following:
| - rearrange definitions,
| - use an explicit argument.
|
| where: T is a type in given instance f with bounds <: foo.Foo
-- [E007] Type Mismatch Error: tests/neg/i7294-a.scala:6:15 ------------------------------------------------------------
6 | case x: T => x.g(10) // error // error
| ^
| Found: (x : Nothing)
| Required: ?{ g: ? }
| Note that implicit conversions were not tried because the result of an implicit conversion
| must be more specific than ?{ g: [applied to (10) returning T] }
|
| longer explanation available when compiling with `-explain`
2 changes: 1 addition & 1 deletion tests/neg/i7294-a.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package foo
trait Foo { def g(x: Int): Any }

inline given f[T <: Foo]: T = ??? match {
case x: T => x.g(10) // error
case x: T => x.g(10) // error // error
}

@main def Test = f
2 changes: 1 addition & 1 deletion tests/neg/i7294-b.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package foo
trait Foo { def g(x: Any): Any }

inline given f[T <: Foo]: T = ??? match {
case x: T => x.g(10) // error
case x: T => x.g(10) // error // error
}

@main def Test = f
2 changes: 1 addition & 1 deletion tests/pos/i15474.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//> using options -Xfatal-warnings
import scala.language.implicitConversions
import language.future
import scala.language.experimental.avoidLoopingGivens

object Test1:
given c: Conversion[ String, Int ] with
Expand Down
4 changes: 2 additions & 2 deletions tests/run/i6716.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//> using options -Xfatal-warnings

import language.future
import scala.language.experimental.avoidLoopingGivens

trait Monad[T]:
def id: String
Expand All @@ -11,7 +11,7 @@ object Foo {

opaque type Bar = Foo
object Bar {
given Monad[Bar] = summon[Monad[Foo]] // error
given Monad[Bar] = summon[Monad[Foo]] // was error fixed by avoidLoopingGivens
}

object Test extends App {
Expand Down

0 comments on commit fab7147

Please sign in to comment.