Skip to content

Commit 413cdf8

Browse files
Fix #9171: Eliminate difference _ and Any in MT
`case _ =>` use to be typed as a `HKTypeLambda`, despite not binding anything. As a result, result of match type reduction going through `case _` would get further reduce that their `case Any` counterpart. This commit eliminates this distinction with the following changes: - Eliminate this distinction in typing (type `case _ =>` *as* `case Any =>`) - Simplify the body of match types in non-binding cases - Change the match type/expression unification to treat the `case _ =>` in a pattern like `case _: Any =>` Unfortunately this change introduces a regression in `matchtype-loop.scala` where the loop suddenly turns into an infinite loop that doesn't stack overflow. I don't see any other way to nicely fail than to introduce a new fuel-like counter to keep track of match type reductions.
1 parent eb34c6c commit 413cdf8

File tree

6 files changed

+60
-13
lines changed

6 files changed

+60
-13
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2588,7 +2588,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
25882588
val instances = paramInstances(new Array(caseLambda.paramNames.length), pat)
25892589
instantiateParams(instances)(body)
25902590
case _ =>
2591-
body
2591+
body.simplified
25922592
}
25932593
}
25942594
else if (isSubType(widenAbstractTypes(scrut), widenAbstractTypes(pat)))

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -410,15 +410,20 @@ trait TypeAssigner {
410410
def assignType(tree: untpd.CaseDef, pat: Tree, body: Tree)(using Context): CaseDef = {
411411
val ownType =
412412
if (body.isType) {
413-
val params = new TreeAccumulator[mutable.ListBuffer[TypeSymbol]] {
414-
def apply(ps: mutable.ListBuffer[TypeSymbol], t: Tree)(using Context) = t match {
415-
case t: Bind if t.symbol.isType => foldOver(ps += t.symbol.asType, t)
416-
case _ => foldOver(ps, t)
417-
}
413+
pat match {
414+
case Bind(name, _) if name == nme.WILDCARD.toTypeName =>
415+
defn.MatchCase(defn.AnyType, body.tpe)
416+
case pat =>
417+
val params = new TreeAccumulator[mutable.ListBuffer[TypeSymbol]] {
418+
def apply(ps: mutable.ListBuffer[TypeSymbol], t: Tree)(using Context) = t match {
419+
case t: Bind if t.symbol.isType => foldOver(ps += t.symbol.asType, t)
420+
case _ => foldOver(ps, t)
421+
}
422+
}
423+
HKTypeLambda.fromParams(
424+
params(new mutable.ListBuffer[TypeSymbol](), pat).toList,
425+
defn.MatchCase(pat.tpe, body.tpe))
418426
}
419-
HKTypeLambda.fromParams(
420-
params(new mutable.ListBuffer[TypeSymbol](), pat).toList,
421-
defn.MatchCase(pat.tpe, body.tpe))
422427
}
423428
else body.tpe
424429
tree.withType(ownType)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,11 @@ class Typer extends Namer
12951295
case defn.MatchCase(patternTp, _) => tpt.tpe frozen_=:= patternTp
12961296
case _ => false
12971297
}
1298+
case (id @ Ident(nme.WILDCARD), pt) =>
1299+
pt match {
1300+
case defn.MatchCase(patternTp, _) => defn.AnyType frozen_=:= patternTp
1301+
case _ => false
1302+
}
12981303
case _ => false
12991304
}
13001305

@@ -1417,7 +1422,7 @@ class Typer extends Namer
14171422
assignType(cpy.Labeled(tree)(bind1, expr1))
14181423
}
14191424

1420-
/** Type a case of a type match */
1425+
/** Type a case of a match type */
14211426
def typedTypeCase(cdef: untpd.CaseDef, selType: Type, pt: Type)(using Context): CaseDef = {
14221427
def caseRest(using Context) = {
14231428
val pat1 = checkSimpleKinded(typedType(cdef.pat)(using ctx.addMode(Mode.Pattern)))

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ object Test {
66
case Int => LL[LL[X]]
77
}
88
def a: L[Boolean] = ???
9-
def b: L[Int] = ???
9+
// def b: L[Int] = ??? // times out
1010
def g[X]: L[X] = ???
11-
val x: Int = g[Int] // error: found: L[Int], required: Int
11+
// val x: Int = g[Int] // times out
1212

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

tests/pos/i8449.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import scala.compiletime.ops.int._
32

43
object Test {
@@ -15,3 +14,18 @@ object Test {
1514
val fib5: Fib[5] = 5
1615
val fib6: Fib[6] = 8
1716
}
17+
18+
object Test2 {
19+
type Fib[N <: Int] <: Int = N match {
20+
case 0 => 0
21+
case 1 => 1
22+
case Int => Fib[N - 1] + Fib[N - 2]
23+
}
24+
val fib0: Fib[0] = 0
25+
val fib1: Fib[1] = 1
26+
val fib2: Fib[2] = 1
27+
val fib3: Fib[3] = 2
28+
val fib4: Fib[4] = 3
29+
val fib5: Fib[5] = 5
30+
val fib6: Fib[6] = 8
31+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// `case _ => expr` in a match expression should be equivalant to
2+
// `case _: Any => expr`. Likewise, in a match type, `case _ => T`
3+
// should be equivalant to `case Any => T`.
4+
5+
object Test0 {
6+
type M[X] = X match { case String => Int case Any => String }
7+
def m[X](x: X): M[X] = x match { case _: String => 1 case _: Any => "s" }
8+
}
9+
10+
object Test1 {
11+
type M[X] = X match { case String => Int case Any => String }
12+
def m[X](x: X): M[X] = x match { case _: String => 1 case _ => "s" }
13+
}
14+
15+
object Test2 {
16+
type M[X] = X match { case String => Int case _ => String }
17+
def m[X](x: X): M[X] = x match { case _: String => 1 case _: Any => "s" }
18+
}
19+
20+
object Test3 {
21+
type M[X] = X match { case String => Int case _ => String }
22+
def m[X](x: X): M[X] = x match { case _: String => 1 case _ => "s" }
23+
}

0 commit comments

Comments
 (0)