diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index fa262a5880ff..e8ca30ecb243 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala b/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala index f49b00089712..fdb9251951e5 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index bb35306f696c..7cdc3d4a2508 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -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 => diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 51f133c972b4..1203e309c484 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -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) @@ -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 diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index c2a12cec2ecc..9fa8bff120af 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -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]] diff --git a/tests/neg/i15474.check b/tests/neg/i15474.check index 267a02a80786..4bf344dc5a71 100644 --- a/tests/neg/i15474.check +++ b/tests/neg/i15474.check @@ -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. diff --git a/tests/neg/i6716.check b/tests/neg/i6716.check index 1e1359442bec..3746eaafad50 100644 --- a/tests/neg/i6716.check +++ b/tests/neg/i6716.check @@ -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. diff --git a/tests/neg/i7294-a.check b/tests/neg/i7294-a.check new file mode 100644 index 000000000000..9541f7979a7a --- /dev/null +++ b/tests/neg/i7294-a.check @@ -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` diff --git a/tests/neg/i7294-a.scala b/tests/neg/i7294-a.scala index 13981fa4d375..538dc3159fb8 100644 --- a/tests/neg/i7294-a.scala +++ b/tests/neg/i7294-a.scala @@ -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 diff --git a/tests/neg/i7294-b.scala b/tests/neg/i7294-b.scala index 423d5037db96..b06d814444e8 100644 --- a/tests/neg/i7294-b.scala +++ b/tests/neg/i7294-b.scala @@ -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 diff --git a/tests/pos/i15474.scala b/tests/pos/i15474.scala index e40e11d84581..8adc5ad7233d 100644 --- a/tests/pos/i15474.scala +++ b/tests/pos/i15474.scala @@ -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 diff --git a/tests/run/i6716.scala b/tests/run/i6716.scala index 7c4e7fe394d8..6208a52190fe 100644 --- a/tests/run/i6716.scala +++ b/tests/run/i6716.scala @@ -1,6 +1,6 @@ //> using options -Xfatal-warnings -import language.future +import scala.language.experimental.avoidLoopingGivens trait Monad[T]: def id: String @@ -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 {