Skip to content

Commit 8c29c1f

Browse files
dwijnandWojciechMazur
authored andcommitted
Fix instantiating subtypes with outer references
Minimising from the `case test.Generic =>` in ParallelTesting, the anonymous pattern match is expanded, wrapping the match with `applyOrElse`, which has a type parameter A1 as the scrutinee type, with an upper bound of the original element type (out.Foo for us). During reachability analysis the pattern type, e.g. out.Bar3.type, is intersected with the scrutinee type, A1 - giving out.Bar3.type & A1. Then that we attempt to decompose that type. Previously the abstract A1 in that type lead to 3 WildcardTypes, for the 3 subclasses, which are a subtype of previous cases. The fix that by generalising how we recognise the singleton types in the scrutinee type, so instead of the ownership chain we use the parameter type info, and we also match term parameters. For extra correctness we consider the failure to be a subtype of a mixin as a failure for instantiating. Also, make sure to handle and avoid recursion in traverseTp2. [Cherry-picked 0931c43]
1 parent f87a634 commit 8c29c1f

File tree

3 files changed

+44
-20
lines changed

3 files changed

+44
-20
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/TypeOps.scala

+25-20
Original file line numberDiff line numberDiff line change
@@ -838,33 +838,34 @@ object TypeOps:
838838
}
839839
}
840840

841-
/** Gather GADT symbols and `ThisType`s found in `tp2`, ie. the scrutinee. */
841+
/** Gather GADT symbols and singletons found in `tp2`, ie. the scrutinee. */
842842
object TraverseTp2 extends TypeTraverser:
843-
val thisTypes = util.HashSet[ThisType]()
844-
val gadtSyms = new mutable.ListBuffer[Symbol]
843+
val singletons = util.HashMap[Symbol, SingletonType]()
844+
val gadtSyms = new mutable.ListBuffer[Symbol]
845845

846-
def traverse(tp: Type) = {
846+
def traverse(tp: Type) = try
847847
val tpd = tp.dealias
848848
if tpd ne tp then traverse(tpd)
849849
else tp match
850-
case tp: ThisType if !tp.tref.symbol.isStaticOwner && !thisTypes.contains(tp) =>
851-
thisTypes += tp
850+
case tp: ThisType if !singletons.contains(tp.tref.symbol) && !tp.tref.symbol.isStaticOwner =>
851+
singletons(tp.tref.symbol) = tp
852852
traverseChildren(tp.tref)
853-
case tp: TypeRef if tp.symbol.isAbstractOrParamType =>
853+
case tp: TermRef if tp.symbol.is(Param) =>
854+
singletons(tp.typeSymbol) = tp
855+
traverseChildren(tp)
856+
case tp: TypeRef if !gadtSyms.contains(tp.symbol) && tp.symbol.isAbstractOrParamType =>
854857
gadtSyms += tp.symbol
855858
traverseChildren(tp)
856-
val owners = Iterator.iterate(tp.symbol)(_.maybeOwner).takeWhile(_.exists)
857-
for sym <- owners do
858-
// add ThisType's for the classes symbols in the ownership of `tp`
859-
// for example, i16451.CanForward.scala, add `Namer.this`, as one of the owners of the type parameter `A1`
860-
if sym.isClass && !sym.isAnonymousClass && !sym.isStaticOwner then
861-
traverse(sym.thisType)
859+
// traverse abstract type infos, to add any singletons
860+
// for example, i16451.CanForward.scala, add `Namer.this`, from the info of the type parameter `A1`
861+
// also, i19031.ci-reg2.scala, add `out`, from the info of the type parameter `A1` (from synthetic applyOrElse)
862+
traverseChildren(tp.info)
862863
case _ =>
863864
traverseChildren(tp)
864-
}
865+
catch case ex: Throwable => handleRecursive("traverseTp2", tp.show, ex)
865866
TraverseTp2.traverse(tp2)
866-
val thisTypes = TraverseTp2.thisTypes
867-
val gadtSyms = TraverseTp2.gadtSyms.toList
867+
val singletons = TraverseTp2.singletons
868+
val gadtSyms = TraverseTp2.gadtSyms.toList
868869

869870
// Prefix inference, given `p.C.this.Child`:
870871
// 1. return it as is, if `C.this` is found in `tp`, i.e. the scrutinee; or
@@ -874,10 +875,13 @@ object TypeOps:
874875
class InferPrefixMap extends TypeMap {
875876
var prefixTVar: Type | Null = null
876877
def apply(tp: Type): Type = tp match {
877-
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner =>
878+
case tp: TermRef if singletons.contains(tp.symbol) =>
879+
prefixTVar = singletons(tp.symbol) // e.g. tests/pos/i19031.ci-reg2.scala, keep out
880+
prefixTVar.uncheckedNN
881+
case ThisType(tref) if !tref.symbol.isStaticOwner =>
878882
val symbol = tref.symbol
879-
if thisTypes.contains(tp) then
880-
prefixTVar = tp // e.g. tests/pos/i16785.scala, keep Outer.this
883+
if singletons.contains(symbol) then
884+
prefixTVar = singletons(symbol) // e.g. tests/pos/i16785.scala, keep Outer.this
881885
prefixTVar.uncheckedNN
882886
else if symbol.is(Module) then
883887
TermRef(this(tref.prefix), symbol.sourceModule)
@@ -912,7 +916,8 @@ object TypeOps:
912916
}
913917

914918
def instantiate(): Type = {
915-
for tp <- mixins.reverseIterator do protoTp1 <:< tp
919+
for tp <- mixins.reverseIterator do
920+
protoTp1 <:< tp
916921
maximizeType(protoTp1, NoSpan)
917922
wildApprox(protoTp1)
918923
}

Diff for: tests/pos/i19031.ci-reg1.scala

+4
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ class Test:
1010
def t1(foo: Foo[Int]): Unit = foo match
1111
case _: Mark[t] =>
1212
case _ =>
13+
14+
def t2[F <: Foo[Int]](foo: F): Unit = foo match
15+
case _: Mark[t] =>
16+
case _ =>

Diff for: tests/pos/i19031.ci-reg2.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//> using options -Werror
2+
3+
trait Outer:
4+
sealed trait Foo
5+
case class Bar1() extends Foo
6+
case class Bar2() extends Foo
7+
case object Bar3 extends Foo
8+
9+
def foos: List[Foo]
10+
11+
class Test:
12+
def t1(out: Outer) = out.foos.collect:
13+
case out.Bar1() => 1
14+
case out.Bar2() => 2
15+
case out.Bar3 => 3

0 commit comments

Comments
 (0)