Skip to content

Commit 0931c43

Browse files
committed
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.
1 parent 7e47294 commit 0931c43

File tree

3 files changed

+44
-20
lines changed

3 files changed

+44
-20
lines changed

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

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -839,33 +839,34 @@ object TypeOps:
839839
}
840840
}
841841

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

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

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

915919
def instantiate(): Type = {
916-
for tp <- mixins.reverseIterator do protoTp1 <:< tp
920+
for tp <- mixins.reverseIterator do
921+
protoTp1 <:< tp
917922
maximizeType(protoTp1, NoSpan)
918923
wildApprox(protoTp1)
919924
}

tests/pos/i19031.ci-reg1.scala

Lines changed: 4 additions & 0 deletions
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 _ =>

tests/pos/i19031.ci-reg2.scala

Lines changed: 15 additions & 0 deletions
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)