Skip to content

Commit 6313db6

Browse files
authored
Remove unnecessary and recursive Space decomposition (#19216)
2 parents 10f2c10 + 0931c43 commit 6313db6

File tree

8 files changed

+80
-34
lines changed

8 files changed

+80
-34
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,10 +2848,12 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
28482848
case (tp1: TypeRef, tp2: TypeRef) if tp1.symbol.isClass && tp2.symbol.isClass =>
28492849
val cls1 = tp1.classSymbol
28502850
val cls2 = tp2.classSymbol
2851-
def isDecomposable(tp: Symbol): Boolean =
2852-
tp.is(Sealed) && !tp.hasAnonymousChild
2851+
val sameKind = tp1.hasSameKindAs(tp2)
2852+
def isDecomposable(sym: Symbol): Boolean =
2853+
sameKind && sym.is(Sealed) && !sym.hasAnonymousChild
28532854
def decompose(sym: Symbol, tp: Type): List[Type] =
2854-
sym.children.map(x => refineUsingParent(tp, x)).filter(_.exists)
2855+
val tpSimple = tp.applyIfParameterized(tp.typeParams.map(_ => WildcardType))
2856+
sym.children.map(x => refineUsingParent(tpSimple, x)).filter(_.exists)
28552857
if (cls1.derivesFrom(cls2) || cls2.derivesFrom(cls1))
28562858
false
28572859
else

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
}

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ class PatternMatcher extends MiniPhase {
5555

5656
if !inInlinedCode then
5757
// check exhaustivity and unreachability
58-
SpaceEngine.checkExhaustivity(tree)
59-
SpaceEngine.checkRedundancy(tree)
58+
SpaceEngine.checkMatch(tree)
6059

6160
translated.ensureConforms(matchType)
6261
}

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -210,17 +210,13 @@ object SpaceEngine {
210210
case (a @ Typ(tp1, _), b @ Typ(tp2, _)) =>
211211
if isSubType(tp1, tp2) then a
212212
else if isSubType(tp2, tp1) then b
213-
else if canDecompose(a) then intersect(Or(decompose(a)), b)
214-
else if canDecompose(b) then intersect(a, Or(decompose(b)))
215213
else intersectUnrelatedAtomicTypes(tp1, tp2)(a)
216214
case (a @ Typ(tp1, _), Prod(tp2, fun, ss)) =>
217215
if isSubType(tp2, tp1) then b
218-
else if canDecompose(a) then intersect(Or(decompose(a)), b)
219216
else if isSubType(tp1, tp2) then a // problematic corner case: inheriting a case class
220217
else intersectUnrelatedAtomicTypes(tp1, tp2)(b)
221218
case (Prod(tp1, fun, ss), b @ Typ(tp2, _)) =>
222219
if isSubType(tp1, tp2) then a
223-
else if canDecompose(b) then intersect(a, Or(decompose(b)))
224220
else if isSubType(tp2, tp1) then a // problematic corner case: inheriting a case class
225221
else intersectUnrelatedAtomicTypes(tp1, tp2)(a)
226222
case (a @ Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) =>
@@ -880,7 +876,7 @@ object SpaceEngine {
880876
case _ => tp
881877
})
882878

883-
def checkExhaustivity(m: Match)(using Context): Unit = if exhaustivityCheckable(m.selector) then trace(i"checkExhaustivity($m)", debug) {
879+
def checkExhaustivity(m: Match)(using Context): Unit = trace(i"checkExhaustivity($m)", debug) {
884880
val selTyp = toUnderlying(m.selector.tpe).dealias
885881
debug.println(i"selTyp = $selTyp")
886882

@@ -903,7 +899,7 @@ object SpaceEngine {
903899
report.warning(PatternMatchExhaustivity(showSpaces(deduped), m), m.selector)
904900
}
905901

906-
private def redundancyCheckable(sel: Tree)(using Context): Boolean =
902+
private def reachabilityCheckable(sel: Tree)(using Context): Boolean =
907903
// Ignore Expr[T] and Type[T] for unreachability as a special case.
908904
// Quote patterns produce repeated calls to the same unapply method, but with different implicit parameters.
909905
// Since we assume that repeated calls to the same unapply method overlap
@@ -913,7 +909,7 @@ object SpaceEngine {
913909
&& !sel.tpe.widen.isRef(defn.QuotedExprClass)
914910
&& !sel.tpe.widen.isRef(defn.QuotedTypeClass)
915911

916-
def checkRedundancy(m: Match)(using Context): Unit = if redundancyCheckable(m.selector) then trace(i"checkRedundancy($m)", debug) {
912+
def checkReachability(m: Match)(using Context): Unit = trace(i"checkReachability($m)", debug) {
917913
val cases = m.cases.toIndexedSeq
918914

919915
val selTyp = toUnderlying(m.selector.tpe).dealias
@@ -965,4 +961,8 @@ object SpaceEngine {
965961
i += 1
966962
}
967963
}
964+
965+
def checkMatch(m: Match)(using Context): Unit =
966+
if exhaustivityCheckable(m.selector) then checkExhaustivity(m)
967+
if reachabilityCheckable(m.selector) then checkReachability(m)
968968
}

scaladoc-js/common/src/utils/html.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ object HTML {
2626
case ("id", id) => elem.id = id
2727
case ("class", value) => value.split("\\s+").foreach(cls => elem.classList.add(cls))
2828
case (attr, value) => elem.setAttribute(attr, value)
29-
case s: Seq[AppliedAttr] => unpackAttributes(s*)
29+
case s: Seq[AppliedAttr @unchecked] => unpackAttributes(s*)
3030
}
3131

3232
unpackTags(tags*)
@@ -118,4 +118,4 @@ object HTML {
118118
val titleAttr =Attr("title")
119119
val onkeyup = Attr("onkeyup")
120120

121-
}
121+
}

tests/pos/i19031.ci-reg1.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//> using options -Werror
2+
3+
sealed trait Mark[T]
4+
5+
trait Foo[T]
6+
class Bar1[T] extends Foo[T]
7+
class Bar2[T] extends Foo[T] with Mark[T]
8+
9+
class Test:
10+
def t1(foo: Foo[Int]): Unit = foo match
11+
case _: Mark[t] =>
12+
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

tests/pos/i19031.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//> using options -Werror
2+
3+
sealed trait A:
4+
class B extends A
5+
6+
class Test:
7+
def t1(a: A): Boolean =
8+
a match
9+
case b: A#B => true

0 commit comments

Comments
 (0)