diff --git a/community-build/community-projects/scalatest b/community-build/community-projects/scalatest index e447cc132f61..70c08e1b6312 160000 --- a/community-build/community-projects/scalatest +++ b/community-build/community-projects/scalatest @@ -1 +1 @@ -Subproject commit e447cc132f61444ec97e50d9937a85b10c0d76e2 +Subproject commit 70c08e1b63125b149ea5e6f5a34d8a0bb3b78b9f diff --git a/community-build/community-projects/scodec b/community-build/community-projects/scodec index e04ba7c17f84..5de8baee3794 160000 --- a/community-build/community-projects/scodec +++ b/community-build/community-projects/scodec @@ -1 +1 @@ -Subproject commit e04ba7c17f848a57425a7e0342c00c0314c9559d +Subproject commit 5de8baee379463996e9837e39a462ca8e1e33c45 diff --git a/community-build/community-projects/shapeless b/community-build/community-projects/shapeless index 9f6cb180bdab..b4c2d61ac785 160000 --- a/community-build/community-projects/shapeless +++ b/community-build/community-projects/shapeless @@ -1 +1 @@ -Subproject commit 9f6cb180bdabc09886c2665ed17ed8bc1ea77afd +Subproject commit b4c2d61ac785720c8ea33eda51104af15fb007d8 diff --git a/community-build/community-projects/xml-interpolator b/community-build/community-projects/xml-interpolator index b744dff4553b..5b7836580ed0 160000 --- a/community-build/community-projects/xml-interpolator +++ b/community-build/community-projects/xml-interpolator @@ -1 +1 @@ -Subproject commit b744dff4553b7f7d9855bb9943a868fafc4db101 +Subproject commit 5b7836580ed0473bf6a91360a76e306a3e7b0b96 diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index b2258dee613d..f4cb12b9d270 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -247,7 +247,7 @@ object Contexts { else outer.implicits if (implicitRefs.isEmpty) outerImplicits - else new ContextualImplicits(implicitRefs, outerImplicits)(this) + else new ContextualImplicits(implicitRefs, outerImplicits, isImportContext)(this) } implicitsCache } @@ -777,7 +777,7 @@ object Contexts { @sharable object NoContext extends Context(null) { source = NoSource - override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null)(this) + override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null, false)(this) } /** A context base defines state and associated methods that exist once per diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 14ece3c6c594..52db5ac6ee53 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -295,7 +295,10 @@ object Implicits: * name, b, whereas the name of the symbol is the original name, a. * @param outerCtx the next outer context that makes visible further implicits */ - class ContextualImplicits(val refs: List[ImplicitRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) { + class ContextualImplicits( + val refs: List[ImplicitRef], + val outerImplicits: ContextualImplicits, + isImport: Boolean)(initctx: Context) extends ImplicitRefs(initctx) { private val eligibleCache = EqHashMap[Type, List[Candidate]]() /** The level increases if current context has a different owner or scope than @@ -303,13 +306,16 @@ object Implicits: * Scala2 mode, since we do not want to change the implicit disambiguation then. */ override val level: Int = + def isSameOwner = irefCtx.owner eq outerImplicits.irefCtx.owner + def isSameScope = irefCtx.scope eq outerImplicits.irefCtx.scope + def isLazyImplicit = refs.head.implicitName.is(LazyImplicitName) + if outerImplicits == null then 1 else if migrateTo3(using irefCtx) - || (irefCtx.owner eq outerImplicits.irefCtx.owner) - && (irefCtx.scope eq outerImplicits.irefCtx.scope) - && !refs.head.implicitName.is(LazyImplicitName) + || isSameOwner && (isImport || isSameScope && !isLazyImplicit) then outerImplicits.level else outerImplicits.level + 1 + end level /** Is this the outermost implicits? This is the case if it either the implicits * of NoContext, or the last one before it. @@ -370,7 +376,7 @@ object Implicits: val outerExcluded = outerImplicits exclude root if (irefCtx.importInfo.site.termSymbol == root) outerExcluded else if (outerExcluded eq outerImplicits) this - else new ContextualImplicits(refs, outerExcluded)(irefCtx) + else new ContextualImplicits(refs, outerExcluded, isImport)(irefCtx) } } diff --git a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala index 4ef01317789f..0cf4dff29009 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala @@ -13,6 +13,7 @@ import ast.{untpd, tpd} import Implicits.{hasExtMethod, Candidate} import java.util.{Timer, TimerTask} import collection.mutable +import scala.util.control.NonFatal /** This trait defines the method `importSuggestionAddendum` that adds an addendum * to error messages suggesting additional imports. @@ -58,10 +59,12 @@ trait ImportSuggestions: val seen = mutable.Set[TermRef]() def lookInside(root: Symbol)(using Context): Boolean = - if root.is(Package) then root.isTerm && root.isCompleted - else !root.name.is(FlatName) - && !root.name.lastPart.contains('$') - && root.is(ModuleVal, butNot = JavaDefined) + explore { + if root.is(Package) then root.isTerm && root.isCompleted + else !root.name.is(FlatName) + && !root.name.lastPart.contains('$') + && root.is(ModuleVal, butNot = JavaDefined) + } def nestedRoots(site: Type)(using Context): List[Symbol] = val seenNames = mutable.Set[Name]() @@ -245,12 +248,11 @@ trait ImportSuggestions: match case (Nil, partials) => (extensionImports, partials) case givenImports => givenImports - catch - case ex: Throwable => - if ctx.settings.Ydebug.value then - println("caught exception when searching for suggestions") - ex.printStackTrace() - (Nil, Nil) + catch case NonFatal(ex) => + if ctx.settings.Ydebug.value then + println("caught exception when searching for suggestions") + ex.printStackTrace() + (Nil, Nil) finally timer.cancel() reduceTimeBudget(((System.currentTimeMillis() - start) min Int.MaxValue).toInt) diff --git a/tests/neg/i9928.scala b/tests/neg/i9928.scala new file mode 100644 index 000000000000..a7c9e77dfb23 --- /dev/null +++ b/tests/neg/i9928.scala @@ -0,0 +1,26 @@ +trait Magic[F]: + extension (x: Int) def read: F + +object Magic: + given Magic[String]: + extension(x: Int) def read: String = + println("In string") + s"$x" + +opaque type Foo = String +object Foo: + import Magic.{given _} + def apply(s: String): Foo = s + + given Magic[Foo]: + extension (x: Int) def read: Foo = + println("In foo") + Foo(s"$x") + + def test: Unit = + (3.read: Foo) // error: ambiguous + + +@main def Test = { + Foo.test +} \ No newline at end of file diff --git a/tests/run/i9928.check b/tests/run/i9928.check new file mode 100644 index 000000000000..d0f61f692cac --- /dev/null +++ b/tests/run/i9928.check @@ -0,0 +1,2 @@ +In foo +In foo diff --git a/tests/run/i9928.scala b/tests/run/i9928.scala new file mode 100644 index 000000000000..e66fa064cb74 --- /dev/null +++ b/tests/run/i9928.scala @@ -0,0 +1,44 @@ +trait Magic[F]: + extension (x: Int) def read: F + +trait LowPrio: + given Magic[String]: + extension(x: Int) def read: String = + println("In string") + s"$x" + +object test1: + object Magic extends LowPrio + + opaque type Foo = String + object Foo extends LowPrio: + import Magic.{given _} + def apply(s: String): Foo = s + + given Magic[Foo]: + extension (x: Int) def read: Foo = + println("In foo") + Foo(s"$x") + + def test: Unit = + (3.read: Foo) + +object test2: + object Magic extends LowPrio: + given Magic[Foo]: + extension (x: Int) def read: Foo = + println("In foo") + Foo(s"$x") + + opaque type Foo = String + object Foo extends LowPrio: + import Magic.{given _} + def apply(s: String): Foo = s + + def test: Unit = + (3.read: Foo) + + +@main def Test = + test1.Foo.test + test2.Foo.test