Skip to content

Commit 90c3fbd

Browse files
Normalize types before collecting parts determining implicit scope
This is necessary to ensure the implicit scope is consistent when involving match types, since they may or may not have been reduced before implicit search. We can for example get different results when loading from tasty than when in the same run. Fixes #20071
1 parent 9a5b9b4 commit 90c3fbd

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ trait ImplicitRunInfo:
636636
else if implicitScopeCache.contains(t) then parts += t
637637
else
638638
partSeen += t
639-
t.dealias match
639+
t.dealias.normalized match
640640
case t: TypeRef =>
641641
if isAnchor(t.symbol) then
642642
parts += t
@@ -663,7 +663,6 @@ trait ImplicitRunInfo:
663663
traverseChildren(t)
664664
case t =>
665665
traverseChildren(t)
666-
traverse(t.normalized)
667666
catch case ex: Throwable => handleRecursive("collectParts of", t.show, ex)
668667

669668
def apply(tp: Type): collection.Set[Type] =
@@ -775,6 +774,7 @@ trait ImplicitRunInfo:
775774
* if `T` is of the form `(P#x).type`, the anchors of `P`.
776775
* - If `T` is the this-type of a static object, the anchors of a term reference to that object.
777776
* - If `T` is some other this-type `P.this.type`, the anchors of `P`.
777+
* - If `T` is match type or an applied match alias, the anchors of the normalization of `T`.
778778
* - If `T` is some other type, the union of the anchors of each constituent type of `T`.
779779
*
780780
* The _implicit scope_ of a type `tp` is the smallest set S of term references (i.e. TermRefs)
@@ -787,7 +787,7 @@ trait ImplicitRunInfo:
787787
* - If `T` is a reference to an opaque type alias named `A`, S includes
788788
* a reference to an object `A` defined in the same scope as the type, if it exists,
789789
* as well as the implicit scope of `T`'s underlying type or bounds.
790-
* - If `T` is a reference to an an abstract type or match type alias named `A`,
790+
* - If `T` is a reference to an an abstract type or unreducible match type alias named `A`,
791791
* S includes a reference to an object `A` defined in the same scope as the type,
792792
* if it exists, as well as the implicit scopes of `T`'s lower and upper bound,
793793
* if present.

tests/neg/i20071.scala

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
trait Scope
3+
object Scope:
4+
given i: Int = ???
5+
6+
type ReferencesScope[S] >: Int <: Int
7+
8+
type ScopeToInt[Why] = Why match
9+
case Scope => Int
10+
11+
def foo[T](using d: ReferencesScope[T]): Any = ???
12+
13+
def bar[T](using d: ScopeToInt[T]): Any = ???
14+
15+
def test: Unit =
16+
foo[Scope] // ok
17+
bar[Scope] // error
18+
19+
import Scope.i
20+
bar[Scope] // ok
21+
22+
/*
23+
Before the changes:
24+
`ScopeToInt[Scope]` may or may not be reduced before implicit search,
25+
thereby impacting the scope considered for the search. `Scope.i` is included
26+
iff `Scope` still appears in the type, which is the case only before reduction.
27+
In contrast, `ReferencesScope[Scope]` is ok since it will never lose the anchor.
28+
*/

tests/pos/i15183/test_2.scala

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
// Fails in each cases below
2+
import Decoder.{derived as _, given}
3+
// NOTE Decoder.derived is already in the implicit scope
4+
// but the others require an import as they depend on match type reduction
5+
26
enum Env derives Decoder:
37
case Local,Sit,Prod
48

0 commit comments

Comments
 (0)