Skip to content

Commit a825f9b

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.
1 parent ab05913 commit a825f9b

File tree

2 files changed

+37
-17
lines changed

2 files changed

+37
-17
lines changed

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

Lines changed: 24 additions & 17 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

847847
def traverse(tp: Type) = {
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 !tp.tref.symbol.isStaticOwner && !singletons.contains(tp.tref.symbol) =>
852+
singletons(tp.tref.symbol) = tp
853853
traverseChildren(tp.tref)
854+
case tp: TermRef if tp.symbol.is(Param) =>
855+
singletons(tp.typeSymbol) = tp
856+
traverseChildren(tp)
854857
case tp: TypeRef if 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)
865866
}
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,10 @@ object TypeOps:
913917
}
914918

915919
def instantiate(): Type = {
916-
for tp <- mixins.reverseIterator do protoTp1 <:< tp
920+
var failedMixin = false
921+
for tp <- mixins.reverseIterator do
922+
failedMixin ||= !(protoTp1 <:< tp)
923+
if failedMixin then return NoType
917924
maximizeType(protoTp1, NoSpan)
918925
wildApprox(protoTp1)
919926
}

tests/pos/i19031.ci-reg2.scala

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

0 commit comments

Comments
 (0)