Skip to content

Remove unnecessary and recursive Space decomposition #19216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
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.
  • Loading branch information
dwijnand committed Dec 13, 2023
commit 0931c43a29d7d4ca5ac63449c749d6c4a25b5da1
45 changes: 25 additions & 20 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -839,33 +839,34 @@ object TypeOps:
}
}

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

def traverse(tp: Type) = {
def traverse(tp: Type) = try
val tpd = tp.dealias
if tpd ne tp then traverse(tpd)
else tp match
case tp: ThisType if !tp.tref.symbol.isStaticOwner && !thisTypes.contains(tp) =>
thisTypes += tp
case tp: ThisType if !singletons.contains(tp.tref.symbol) && !tp.tref.symbol.isStaticOwner =>
singletons(tp.tref.symbol) = tp
traverseChildren(tp.tref)
case tp: TypeRef if tp.symbol.isAbstractOrParamType =>
case tp: TermRef if tp.symbol.is(Param) =>
singletons(tp.typeSymbol) = tp
traverseChildren(tp)
case tp: TypeRef if !gadtSyms.contains(tp.symbol) && tp.symbol.isAbstractOrParamType =>
gadtSyms += tp.symbol
traverseChildren(tp)
val owners = Iterator.iterate(tp.symbol)(_.maybeOwner).takeWhile(_.exists)
for sym <- owners do
// add ThisType's for the classes symbols in the ownership of `tp`
// for example, i16451.CanForward.scala, add `Namer.this`, as one of the owners of the type parameter `A1`
if sym.isClass && !sym.isAnonymousClass && !sym.isStaticOwner then
traverse(sym.thisType)
// traverse abstract type infos, to add any singletons
// for example, i16451.CanForward.scala, add `Namer.this`, from the info of the type parameter `A1`
// also, i19031.ci-reg2.scala, add `out`, from the info of the type parameter `A1` (from synthetic applyOrElse)
traverseChildren(tp.info)
case _ =>
traverseChildren(tp)
}
catch case ex: Throwable => handleRecursive("traverseTp2", tp.show, ex)
TraverseTp2.traverse(tp2)
val thisTypes = TraverseTp2.thisTypes
val gadtSyms = TraverseTp2.gadtSyms.toList
val singletons = TraverseTp2.singletons
val gadtSyms = TraverseTp2.gadtSyms.toList

// Prefix inference, given `p.C.this.Child`:
// 1. return it as is, if `C.this` is found in `tp`, i.e. the scrutinee; or
Expand All @@ -875,10 +876,13 @@ object TypeOps:
class InferPrefixMap extends TypeMap {
var prefixTVar: Type | Null = null
def apply(tp: Type): Type = tp match {
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner =>
case tp: TermRef if singletons.contains(tp.symbol) =>
prefixTVar = singletons(tp.symbol) // e.g. tests/pos/i19031.ci-reg2.scala, keep out
prefixTVar.uncheckedNN
case ThisType(tref) if !tref.symbol.isStaticOwner =>
val symbol = tref.symbol
if thisTypes.contains(tp) then
prefixTVar = tp // e.g. tests/pos/i16785.scala, keep Outer.this
if singletons.contains(symbol) then
prefixTVar = singletons(symbol) // e.g. tests/pos/i16785.scala, keep Outer.this
prefixTVar.uncheckedNN
else if symbol.is(Module) then
TermRef(this(tref.prefix), symbol.sourceModule)
Expand Down Expand Up @@ -913,7 +917,8 @@ object TypeOps:
}

def instantiate(): Type = {
for tp <- mixins.reverseIterator do protoTp1 <:< tp
for tp <- mixins.reverseIterator do
protoTp1 <:< tp
maximizeType(protoTp1, NoSpan)
wildApprox(protoTp1)
}
Expand Down
4 changes: 4 additions & 0 deletions tests/pos/i19031.ci-reg1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ class Test:
def t1(foo: Foo[Int]): Unit = foo match
case _: Mark[t] =>
case _ =>

def t2[F <: Foo[Int]](foo: F): Unit = foo match
case _: Mark[t] =>
case _ =>
15 changes: 15 additions & 0 deletions tests/pos/i19031.ci-reg2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//> using options -Werror

trait Outer:
sealed trait Foo
case class Bar1() extends Foo
case class Bar2() extends Foo
case object Bar3 extends Foo

def foos: List[Foo]

class Test:
def t1(out: Outer) = out.foos.collect:
case out.Bar1() => 1
case out.Bar2() => 2
case out.Bar3 => 3