Skip to content

Commit 64315b7

Browse files
authored
Merge pull request #9950 from dotty-staging/fix-#9928
Fix #9928: Don't treat import contexts as properly nested
2 parents b27e044 + ff9de66 commit 64315b7

File tree

6 files changed

+97
-17
lines changed

6 files changed

+97
-17
lines changed

compiler/src/dotty/tools/dotc/core/Contexts.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ object Contexts {
247247
else
248248
outer.implicits
249249
if (implicitRefs.isEmpty) outerImplicits
250-
else new ContextualImplicits(implicitRefs, outerImplicits)(this)
250+
else new ContextualImplicits(implicitRefs, outerImplicits, isImportContext)(this)
251251
}
252252
implicitsCache
253253
}
@@ -777,7 +777,7 @@ object Contexts {
777777

778778
@sharable object NoContext extends Context(null) {
779779
source = NoSource
780-
override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null)(this)
780+
override val implicits: ContextualImplicits = new ContextualImplicits(Nil, null, false)(this)
781781
}
782782

783783
/** A context base defines state and associated methods that exist once per

compiler/src/dotty/tools/dotc/typer/Implicits.scala

+11-5
Original file line numberDiff line numberDiff line change
@@ -295,21 +295,27 @@ object Implicits:
295295
* name, b, whereas the name of the symbol is the original name, a.
296296
* @param outerCtx the next outer context that makes visible further implicits
297297
*/
298-
class ContextualImplicits(val refs: List[ImplicitRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) {
298+
class ContextualImplicits(
299+
val refs: List[ImplicitRef],
300+
val outerImplicits: ContextualImplicits,
301+
isImport: Boolean)(initctx: Context) extends ImplicitRefs(initctx) {
299302
private val eligibleCache = EqHashMap[Type, List[Candidate]]()
300303

301304
/** The level increases if current context has a different owner or scope than
302305
* the context of the next-outer ImplicitRefs. This is however disabled under
303306
* Scala2 mode, since we do not want to change the implicit disambiguation then.
304307
*/
305308
override val level: Int =
309+
def isSameOwner = irefCtx.owner eq outerImplicits.irefCtx.owner
310+
def isSameScope = irefCtx.scope eq outerImplicits.irefCtx.scope
311+
def isLazyImplicit = refs.head.implicitName.is(LazyImplicitName)
312+
306313
if outerImplicits == null then 1
307314
else if migrateTo3(using irefCtx)
308-
|| (irefCtx.owner eq outerImplicits.irefCtx.owner)
309-
&& (irefCtx.scope eq outerImplicits.irefCtx.scope)
310-
&& !refs.head.implicitName.is(LazyImplicitName)
315+
|| isSameOwner && (isImport || isSameScope && !isLazyImplicit)
311316
then outerImplicits.level
312317
else outerImplicits.level + 1
318+
end level
313319

314320
/** Is this the outermost implicits? This is the case if it either the implicits
315321
* of NoContext, or the last one before it.
@@ -370,7 +376,7 @@ object Implicits:
370376
val outerExcluded = outerImplicits exclude root
371377
if (irefCtx.importInfo.site.termSymbol == root) outerExcluded
372378
else if (outerExcluded eq outerImplicits) this
373-
else new ContextualImplicits(refs, outerExcluded)(irefCtx)
379+
else new ContextualImplicits(refs, outerExcluded, isImport)(irefCtx)
374380
}
375381
}
376382

compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala

+12-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import ast.{untpd, tpd}
1313
import Implicits.{hasExtMethod, Candidate}
1414
import java.util.{Timer, TimerTask}
1515
import collection.mutable
16+
import scala.util.control.NonFatal
1617

1718
/** This trait defines the method `importSuggestionAddendum` that adds an addendum
1819
* to error messages suggesting additional imports.
@@ -58,10 +59,12 @@ trait ImportSuggestions:
5859
val seen = mutable.Set[TermRef]()
5960

6061
def lookInside(root: Symbol)(using Context): Boolean =
61-
if root.is(Package) then root.isTerm && root.isCompleted
62-
else !root.name.is(FlatName)
63-
&& !root.name.lastPart.contains('$')
64-
&& root.is(ModuleVal, butNot = JavaDefined)
62+
explore {
63+
if root.is(Package) then root.isTerm && root.isCompleted
64+
else !root.name.is(FlatName)
65+
&& !root.name.lastPart.contains('$')
66+
&& root.is(ModuleVal, butNot = JavaDefined)
67+
}
6568

6669
def nestedRoots(site: Type)(using Context): List[Symbol] =
6770
val seenNames = mutable.Set[Name]()
@@ -245,12 +248,11 @@ trait ImportSuggestions:
245248
match
246249
case (Nil, partials) => (extensionImports, partials)
247250
case givenImports => givenImports
248-
catch
249-
case ex: Throwable =>
250-
if ctx.settings.Ydebug.value then
251-
println("caught exception when searching for suggestions")
252-
ex.printStackTrace()
253-
(Nil, Nil)
251+
catch case NonFatal(ex) =>
252+
if ctx.settings.Ydebug.value then
253+
println("caught exception when searching for suggestions")
254+
ex.printStackTrace()
255+
(Nil, Nil)
254256
finally
255257
timer.cancel()
256258
reduceTimeBudget(((System.currentTimeMillis() - start) min Int.MaxValue).toInt)

tests/neg/i9928.scala

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
trait Magic[F]:
2+
extension (x: Int) def read: F
3+
4+
object Magic:
5+
given Magic[String]:
6+
extension(x: Int) def read: String =
7+
println("In string")
8+
s"$x"
9+
10+
opaque type Foo = String
11+
object Foo:
12+
import Magic.{given _}
13+
def apply(s: String): Foo = s
14+
15+
given Magic[Foo]:
16+
extension (x: Int) def read: Foo =
17+
println("In foo")
18+
Foo(s"$x")
19+
20+
def test: Unit =
21+
(3.read: Foo) // error: ambiguous
22+
23+
24+
@main def Test = {
25+
Foo.test
26+
}

tests/run/i9928.check

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
In foo
2+
In foo

tests/run/i9928.scala

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
trait Magic[F]:
2+
extension (x: Int) def read: F
3+
4+
trait LowPrio:
5+
given Magic[String]:
6+
extension(x: Int) def read: String =
7+
println("In string")
8+
s"$x"
9+
10+
object test1:
11+
object Magic extends LowPrio
12+
13+
opaque type Foo = String
14+
object Foo extends LowPrio:
15+
import Magic.{given _}
16+
def apply(s: String): Foo = s
17+
18+
given Magic[Foo]:
19+
extension (x: Int) def read: Foo =
20+
println("In foo")
21+
Foo(s"$x")
22+
23+
def test: Unit =
24+
(3.read: Foo)
25+
26+
object test2:
27+
object Magic extends LowPrio:
28+
given Magic[Foo]:
29+
extension (x: Int) def read: Foo =
30+
println("In foo")
31+
Foo(s"$x")
32+
33+
opaque type Foo = String
34+
object Foo extends LowPrio:
35+
import Magic.{given _}
36+
def apply(s: String): Foo = s
37+
38+
def test: Unit =
39+
(3.read: Foo)
40+
41+
42+
@main def Test =
43+
test1.Foo.test
44+
test2.Foo.test

0 commit comments

Comments
 (0)