Skip to content

Commit c51a2c0

Browse files
committed
Normalize when simplifying
We cannot normalize match types and S types on creation since that ignores constraints that might change. So we normalize instead as part of `simplify`. The new scheme causes deeper subtype recursions than the old, so the Tuple23 part in tuples1 had to me moved to pos-deep-subtype.
1 parent 00de2f8 commit c51a2c0

File tree

8 files changed

+109
-72
lines changed

8 files changed

+109
-72
lines changed

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -410,15 +410,8 @@ class TypeApplications(val self: Type) extends AnyVal {
410410
LazyRef(c => dealiased.ref(c).appliedTo(args))
411411
case dealiased: WildcardType =>
412412
WildcardType(dealiased.optBounds.appliedTo(args).bounds)
413-
case dealiased: TypeRef =>
414-
val sym = dealiased.symbol
415-
if (sym == defn.NothingClass) return dealiased
416-
if (defn.isTypelevel_S(sym) && args.length == 1)
417-
args.head.safeDealias match {
418-
case ConstantType(Constant(n: Int)) => return ConstantType(Constant(n + 1))
419-
case none =>
420-
}
421-
AppliedType(self, args)
413+
case dealiased: TypeRef if dealiased.symbol == defn.NothingClass =>
414+
dealiased
422415
case dealiased =>
423416
AppliedType(self, args)
424417
}

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

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -80,35 +80,41 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
8080
pre.isStable || !ctx.phase.isTyper
8181

8282
/** Implementation of Types#simplified */
83-
final def simplify(tp: Type, theMap: SimplifyMap): Type = tp match {
84-
case tp: NamedType =>
85-
if (tp.symbol.isStatic || (tp.prefix `eq` NoPrefix)) tp
86-
else tp.derivedSelect(simplify(tp.prefix, theMap)) match {
87-
case tp1: NamedType if tp1.denotationIsCurrent =>
88-
val tp2 = tp1.reduceProjection
89-
//if (tp2 ne tp1) println(i"simplified $tp1 -> $tp2")
90-
tp2
91-
case tp1 => tp1
92-
}
93-
case tp: TypeParamRef =>
94-
if (tp.paramName.is(DepParamName)) {
95-
val bounds = ctx.typeComparer.bounds(tp)
96-
if (bounds.lo.isRef(defn.NothingClass)) bounds.hi else bounds.lo
97-
}
98-
else {
99-
val tvar = typerState.constraint.typeVarOfParam(tp)
100-
if (tvar.exists) tvar else tp
101-
}
102-
case _: ThisType | _: BoundType =>
103-
tp
104-
case tp: AliasingBounds =>
105-
tp.derivedAlias(simplify(tp.alias, theMap))
106-
case AndType(l, r) if !ctx.mode.is(Mode.Type) =>
107-
simplify(l, theMap) & simplify(r, theMap)
108-
case OrType(l, r) if !ctx.mode.is(Mode.Type) =>
109-
simplify(l, theMap) | simplify(r, theMap)
110-
case _ =>
111-
(if (theMap != null) theMap else new SimplifyMap).mapOver(tp)
83+
final def simplify(tp: Type, theMap: SimplifyMap): Type = {
84+
def mapOver = (if (theMap != null) theMap else new SimplifyMap).mapOver(tp)
85+
tp match {
86+
case tp: NamedType =>
87+
if (tp.symbol.isStatic || (tp.prefix `eq` NoPrefix)) tp
88+
else tp.derivedSelect(simplify(tp.prefix, theMap)) match {
89+
case tp1: NamedType if tp1.denotationIsCurrent =>
90+
val tp2 = tp1.reduceProjection
91+
//if (tp2 ne tp1) println(i"simplified $tp1 -> $tp2")
92+
tp2
93+
case tp1 => tp1
94+
}
95+
case tp: TypeParamRef =>
96+
if (tp.paramName.is(DepParamName)) {
97+
val bounds = ctx.typeComparer.bounds(tp)
98+
if (bounds.lo.isRef(defn.NothingClass)) bounds.hi else bounds.lo
99+
}
100+
else {
101+
val tvar = typerState.constraint.typeVarOfParam(tp)
102+
if (tvar.exists) tvar else tp
103+
}
104+
case _: ThisType | _: BoundType =>
105+
tp
106+
case tp: AliasingBounds =>
107+
tp.derivedAlias(simplify(tp.alias, theMap))
108+
case AndType(l, r) if !ctx.mode.is(Mode.Type) =>
109+
simplify(l, theMap) & simplify(r, theMap)
110+
case OrType(l, r) if !ctx.mode.is(Mode.Type) =>
111+
simplify(l, theMap) | simplify(r, theMap)
112+
case _: AppliedType | _: MatchType =>
113+
val normed = tp.tryNormalize
114+
if (normed.exists) normed else mapOver
115+
case _ =>
116+
mapOver
117+
}
112118
}
113119

114120
class SimplifyMap extends TypeMap {

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

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import annotation.tailrec
3434
import language.implicitConversions
3535
import scala.util.hashing.{ MurmurHash3 => hashing }
3636
import config.Printers.{core, typr}
37+
import reporting.trace
3738
import java.lang.ref.WeakReference
3839

3940
import scala.annotation.internal.sharable
@@ -1074,6 +1075,20 @@ object Types {
10741075
/** Like `dealiasKeepAnnots`, but keeps only refining annotations */
10751076
final def dealiasKeepRefiningAnnots(implicit ctx: Context): Type = dealias1(keepIfRefining)
10761077

1078+
/** The result of normalization using `tryNormalize`, or the type itself if
1079+
* tryNormlize yields NoType
1080+
*/
1081+
final def normalized(implicit ctx: Context) = {
1082+
val normed = tryNormalize
1083+
if (normed.exists) normed else this
1084+
}
1085+
1086+
/** If this type can be normalized at the top-level by rewriting match types
1087+
* of S[n] types, the result after applying all toplevel normalizations,
1088+
* otherwise NoType
1089+
*/
1090+
def tryNormalize(implicit ctx: Context): Type = NoType
1091+
10771092
private def widenDealias1(keep: AnnotatedType => Context => Boolean)(implicit ctx: Context): Type = {
10781093
val res = this.widen.dealias1(keep)
10791094
if (res eq this) res else res.widenDealias1(keep)
@@ -3264,6 +3279,29 @@ object Types {
32643279
cachedSuper
32653280
}
32663281

3282+
override def tryNormalize(implicit ctx: Context): Type = tycon match {
3283+
case tycon: TypeRef =>
3284+
def tryMatchAlias = tycon.info match {
3285+
case MatchAlias(alias) =>
3286+
trace("normalize $this", show = true) {
3287+
alias.applyIfParameterized(args).tryNormalize
3288+
}
3289+
case _ =>
3290+
NoType
3291+
}
3292+
if (defn.isTypelevel_S(tycon.symbol) && args.length == 1) {
3293+
trace("normalize S $this", show = true) {
3294+
args.head.normalized match {
3295+
case ConstantType(Constant(n: Int)) => ConstantType(Constant(n + 1))
3296+
case none => tryMatchAlias
3297+
}
3298+
}
3299+
}
3300+
else tryMatchAlias
3301+
case _ =>
3302+
NoType
3303+
}
3304+
32673305
def lowerBound(implicit ctx: Context) = tycon.stripTypeVar match {
32683306
case tycon: TypeRef =>
32693307
tycon.info match {
@@ -3574,6 +3612,8 @@ object Types {
35743612
private[this] var myReduced: Type = null
35753613
private[this] var reductionContext: mutable.Map[Type, TypeBounds] = null
35763614

3615+
override def tryNormalize(implicit ctx: Context): Type = reduced.normalized
3616+
35773617
def reduced(implicit ctx: Context): Type = {
35783618
val trackingCtx = ctx.fresh.setTypeComparerFn(new TrackingTypeComparer(_))
35793619
val cmp = trackingCtx.typeComparer.asInstanceOf[TrackingTypeComparer]
@@ -3617,7 +3657,8 @@ object Types {
36173657
if (!Config.cacheMatchReduced || myReduced == null || !upToDate) {
36183658
record("MatchType.reduce computed")
36193659
if (myReduced != null) record("MatchType.reduce cache miss")
3620-
myReduced = recur(cases)(trackingCtx)
3660+
myReduced =
3661+
trace(i"reduce match type $this", show = true) { recur(cases)(trackingCtx) }
36213662
updateReductionContext()
36223663
}
36233664
myReduced
@@ -3635,12 +3676,8 @@ object Types {
36353676
class CachedMatchType(bound: Type, scrutinee: Type, cases: List[Type]) extends MatchType(bound, scrutinee, cases)
36363677

36373678
object MatchType {
3638-
def apply(bound: Type, scrutinee: Type, cases: List[Type])(implicit ctx: Context) = {
3679+
def apply(bound: Type, scrutinee: Type, cases: List[Type])(implicit ctx: Context) =
36393680
unique(new CachedMatchType(bound, scrutinee, cases))
3640-
// TODO: maybe we should try to reduce match types immediately, but this risks creating illegal
3641-
// cycles. So we can do this only if we can prove that the redux is in some sense simpler than
3642-
// the original type.
3643-
}
36443681
}
36453682

36463683
// ------ ClassInfo, Type Bounds --------------------------------------------------

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class CompilationTests extends ParallelTesting {
161161
compileFile("tests/neg-custom-args/i1754.scala", allowDeepSubtypes) +
162162
compileFilesInDir("tests/neg-custom-args/isInstanceOf", allowDeepSubtypes and "-Xfatal-warnings") +
163163
compileFile("tests/neg-custom-args/i3627.scala", allowDeepSubtypes) +
164+
compileFile("tests/neg-custom-args/matchtype-loop.scala", allowDeepSubtypes) +
164165
compileFile("tests/neg-custom-args/completeFromSource/nested/Test1.scala", defaultOptions.and("-sourcepath", "tests/neg-custom-args", "-scansource"))
165166
}.checkExpectedErrors()
166167

tests/neg/matchtype-loop.scala renamed to tests/neg-custom-args/matchtype-loop.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ object Test {
88
def a: L[Boolean] = ???
99
def b: L[Int] = ???
1010
def g[X]: L[X] = ???
11-
def g[X]: L[X] = ??? // error: found: L[Int], required: Int
11+
val x: Int = g[Int] // error: found: L[Int], required: Int
1212

1313
def aa: LL[Boolean] = ???
1414
def bb: LL[Int] = ??? // error: recursion limit exceeded with subtype LazyRef(Test.LL[Int]) <:< Int

tests/pos-deep-subtype/tuples23.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
object Test extends App {
2+
val x23 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
3+
type T23 = (Int, Int, Int, Int, Int,
4+
Int, Int, Int, Int, Int,
5+
Int, Int, Int, Int, Int,
6+
Int, Int, Int, Int, Int,
7+
Int, Int, Int)
8+
val x23c: T23 = x23
9+
println(x23)
10+
assert(x23(0) == 1)
11+
assert(x23(22) == 23)
12+
13+
x23 match {
14+
case (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23) =>
15+
println(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + x22 + x23)
16+
}
17+
rewrite def decompose3 = rewrite x23 match { case x *: y *: xs => (x, y, xs) }
18+
19+
{ val (x, y, xs) = decompose3
20+
val xc: Int = x
21+
val yc: Int = y
22+
val xsc: Unit = xs
23+
println(s"$x23 -> $x, $y, $xs")
24+
}
25+
26+
val x23s: 23 = x23.size
27+
}

tests/run/tuples1.check

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,5 @@ c2_1 = (A,1,1)
3232
c2_2 = (A,1,A,1)
3333
c2_3 = (A,1,2,A,1)
3434
c3_3 = (2,A,1,2,A,1)
35-
(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23)
36-
276
3735
(A,1) -> A, (1)
3836
(A,1) -> A, 1, ()
39-
(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) -> 1, 2, (3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23)

tests/run/tuples1.scala

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,8 @@ object Test extends App {
3434
val c2_3 = x2 ++ x3; val c2_3c: (String, Int, Int, String, Int) = c2_3; println(s"c2_3 = $c2_3")
3535
val c3_3 = x3 ++ x3; val c3_3c: (Int, String, Int, Int, String, Int) = c3_3; println(s"c3_3 = $c3_3")
3636

37-
val x23 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)
38-
type T23 = (Int, Int, Int, Int, Int,
39-
Int, Int, Int, Int, Int,
40-
Int, Int, Int, Int, Int,
41-
Int, Int, Int, Int, Int,
42-
Int, Int, Int)
43-
val x23c: T23 = x23
44-
println(x23)
45-
assert(x23(0) == 1)
46-
assert(x23(22) == 23)
47-
48-
x23 match {
49-
case (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23) =>
50-
println(x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + x22 + x23)
51-
}
5237
rewrite def decompose1 = rewrite x2 match { case x *: xs => (x, xs) }
5338
rewrite def decompose2 = rewrite x2 match { case x *: y *: xs => (x, y, xs) }
54-
rewrite def decompose3 = rewrite x23 match { case x *: y *: xs => (x, y, xs) }
5539

5640
{ val (x, xs) = decompose1
5741
val xc: String = x
@@ -66,16 +50,8 @@ object Test extends App {
6650
println(s"$x2 -> $x, $y, $xs")
6751
}
6852

69-
{ val (x, y, xs) = decompose3
70-
val xc: Int = x
71-
val yc: Int = y
72-
val xsc: Unit = xs
73-
println(s"$x23 -> $x, $y, $xs")
74-
}
75-
7653
val x3s: 3 = x3.size
7754
val us: 0 = ().size
78-
val x23s: 23 = x23.size
7955

8056
// dynamic operations
8157

0 commit comments

Comments
 (0)