Skip to content

Commit 9ae1598

Browse files
committed
ErrorType instead of throwing in match type "no cases"
Instead of throwing MatchTypeReductionError, return ErrorType(MatchTypeNoCases), which is a proper message as well. This avoids having to catch and ignore it as an exception. But it does require discovering it from type simplification and reporting it then - which replaces its reliance on catching TypeErrors. It also required handling scrutinees that are error types, which previously would always match the first case, due to FlexType semantics.
1 parent d0f9a51 commit 9ae1598

11 files changed

+42
-78
lines changed

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

+1-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ object MatchTypeTrace:
1212

1313
private enum TraceEntry:
1414
case TryReduce(scrut: Type)
15-
case NoMatches(scrut: Type, cases: List[Type])
1615
case Stuck(scrut: Type, stuckCase: Type, otherCases: List[Type])
1716
case NoInstance(scrut: Type, stuckCase: Type, fails: List[(Name, TypeBounds)])
1817
case EmptyScrutinee(scrut: Type)
@@ -51,12 +50,6 @@ object MatchTypeTrace:
5150
case _ =>
5251
case _ =>
5352

54-
/** Record a failure that scrutinee `scrut` does not match any case in `cases`.
55-
* Only the first failure is recorded.
56-
*/
57-
def noMatches(scrut: Type, cases: List[Type])(using Context) =
58-
matchTypeFail(NoMatches(scrut, cases))
59-
6053
/** Record a failure that scrutinee `scrut` does not match `stuckCase` but is
6154
* not disjoint from it either, which means that the remaining cases `otherCases`
6255
* cannot be visited. Only the first failure is recorded.
@@ -99,11 +92,6 @@ object MatchTypeTrace:
9992
private def explainEntry(entry: TraceEntry)(using Context): String = entry match
10093
case TryReduce(scrut: Type) =>
10194
i" trying to reduce $scrut"
102-
case NoMatches(scrut, cases) =>
103-
i""" failed since selector $scrut
104-
| matches none of the cases
105-
|
106-
| ${casesText(cases)}"""
10795
case EmptyScrutinee(scrut) =>
10896
i""" failed since selector $scrut
10997
| is uninhabited (there are no values of that type)."""
@@ -127,6 +115,7 @@ object MatchTypeTrace:
127115
| The computed bounds for the $params are:
128116
| ${fails.map((name, bounds) => i"$name$bounds")}%\n %"""
129117

118+
/** The failure message when the scrutinee `scrut` does not match any case in `cases`. */
130119
def noMatchesText(scrut: Type, cases: List[Type])(using Context): String =
131120
i"""failed since selector $scrut
132121
|matches none of the cases

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -3223,7 +3223,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32233223
tp
32243224
case Nil =>
32253225
val casesText = MatchTypeTrace.noMatchesText(scrut, cases)
3226-
throw MatchTypeReductionError(em"Match type reduction $casesText")
3226+
ErrorType(reporting.MatchTypeNoCases(casesText))
32273227

32283228
inFrozenConstraint {
32293229
// Empty types break the basic assumption that if a scrutinee and a
@@ -3242,6 +3242,16 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32423242
if (provablyEmpty(scrut))
32433243
MatchTypeTrace.emptyScrutinee(scrut)
32443244
NoType
3245+
else if scrut.isError then
3246+
// if the scrutinee is an error type
3247+
// then just return that as the result
3248+
// not doing so will result in the first type case matching
3249+
// because ErrorType (as a FlexType) is <:< any type case
3250+
// this situation can arise from any kind of nesting of match types,
3251+
// e.g. neg/i12049 `Tuple.Concat[Reverse[ts], (t2, t1)]`
3252+
// if Reverse[ts] fails with no matches,
3253+
// the error type should be the reduction of the Concat too
3254+
scrut
32453255
else
32463256
recur(cases)
32473257
}

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

-3
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ object TypeError:
4646
def toMessage(using Context) = msg
4747
end TypeError
4848

49-
class MatchTypeReductionError(msg: Message)(using Context) extends TypeError:
50-
def toMessage(using Context) = msg
51-
5249
class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError:
5350
def toMessage(using Context) = em"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}"
5451

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

+1
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
197197
case UnqualifiedCallToAnyRefMethodID // errorNumber: 181
198198
case NotConstantID // errorNumber: 182
199199
case ClosureCannotHaveInternalParameterDependenciesID // errorNumber: 183
200+
case MatchTypeNoCasesID // errorNumber: 184
200201

201202
def errorNumber = ordinal - 1
202203

compiler/src/dotty/tools/dotc/reporting/messages.scala

+4
Original file line numberDiff line numberDiff line change
@@ -2916,6 +2916,10 @@ class UnusedNonUnitValue(tp: Type)(using Context)
29162916
def msg(using Context) = i"unused value of type $tp"
29172917
def explain(using Context) = ""
29182918

2919+
class MatchTypeNoCases(casesText: String)(using Context) extends TypeMsg(MatchTypeNoCasesID):
2920+
def msg(using Context) = i"Match type reduction $casesText"
2921+
def explain(using Context) = ""
2922+
29192923
class MatchTypeScrutineeCannotBeHigherKinded(tp: Type)(using Context)
29202924
extends TypeMsg(MatchTypeScrutineeCannotBeHigherKindedID) :
29212925
def msg(using Context) = i"the scrutinee of a match type cannot be higher-kinded"

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ trait ImplicitRunInfo:
636636
traverseChildren(t)
637637
case t: MatchType =>
638638
traverseChildren(t)
639-
traverse(try t.normalized catch case _: MatchTypeReductionError => t)
639+
traverse(t.normalized)
640640
case MatchType.InDisguise(mt)
641641
if !t.isInstanceOf[LazyRef] // skip recursive applications (eg. Tuple.Map)
642642
=>

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

+4
Original file line numberDiff line numberDiff line change
@@ -3138,7 +3138,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
31383138
case xtree: untpd.NameTree => typedNamed(xtree, pt)
31393139
case xtree => typedUnnamed(xtree)
31403140

3141+
val unsimplifiedType = result.tpe
31413142
simplify(result, pt, locked)
3143+
result.tpe.stripTypeVar match
3144+
case e: ErrorType if !unsimplifiedType.isErroneous => errorTree(xtree, e.msg, xtree.srcPos)
3145+
case _ => result
31423146
catch case ex: TypeError => errorTree(xtree, ex, xtree.srcPos.focus)
31433147
// use focussed sourcePos since tree might be a large definition
31443148
// and a large error span would hide all errors in interior.

tests/neg-macros/toexproftuple.scala

+4-45
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,8 @@
1-
import scala.quoted._, scala.deriving.* // error
2-
// ^
3-
// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)])
4-
// matches none of the cases
5-
//
6-
// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr]
7-
// case EmptyTuple => EmptyTuple
1+
import scala.quoted._, scala.deriving.*
82

9-
inline def mcr: Any = ${mcrImpl} // error
10-
// ^
11-
// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)])
12-
// matches none of the cases
13-
//
14-
// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr]
15-
// case EmptyTuple => EmptyTuple
3+
inline def mcr: Any = ${mcrImpl}
164

17-
def mcrImpl(using ctx: Quotes): Expr[Any] = { // error // error
18-
//^
19-
// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)])
20-
// matches none of the cases
21-
//
22-
// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr]
23-
// case EmptyTuple => EmptyTuple
24-
25-
// ^
26-
// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)])
27-
// matches none of the cases
28-
//
29-
// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr]
30-
// case EmptyTuple => EmptyTuple
5+
def mcrImpl(using ctx: Quotes): Expr[Any] = {
316

327
val tpl: (Expr[1], Expr[2], Expr[3]) = ('{1}, '{2}, '{3})
338
'{val res: (1, 3, 3) = ${Expr.ofTuple(tpl)}; res} // error
@@ -36,28 +11,12 @@ def mcrImpl(using ctx: Quotes): Expr[Any] = { // error // error
3611
// Required: quoted.Expr[((1 : Int), (3 : Int), (3 : Int))]
3712

3813
val tpl2: (Expr[1], 2, Expr[3]) = ('{1}, 2, '{3})
39-
'{val res = ${Expr.ofTuple(tpl2)}; res} // error // error // error // error
14+
'{val res = ${Expr.ofTuple(tpl2)}; res} // error
4015
// ^
4116
// Cannot prove that (quoted.Expr[(1 : Int)], (2 : Int), quoted.Expr[(3 : Int)]) =:= scala.Tuple.Map[
4217
// scala.Tuple.InverseMap[
4318
// (quoted.Expr[(1 : Int)], (2 : Int), quoted.Expr[(3 : Int)])
4419
// , quoted.Expr]
4520
// , quoted.Expr].
4621

47-
// ^
48-
// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)])
49-
// matches none of the cases
50-
//
51-
// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr]
52-
// case EmptyTuple => EmptyTuple
53-
54-
// ^
55-
// Cyclic reference involving val res
56-
57-
// ^
58-
// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)])
59-
// matches none of the cases
60-
//
61-
// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr]
62-
// case EmptyTuple => EmptyTuple
6322
}

tests/neg/i12049.check

+8-8
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@
1515
| case B => String
1616
|
1717
| longer explanation available when compiling with `-explain`
18-
-- Error: tests/neg/i12049.scala:14:23 ---------------------------------------------------------------------------------
18+
-- [E184] Type Error: tests/neg/i12049.scala:14:23 ---------------------------------------------------------------------
1919
14 |val y3: String = ??? : Last[Int *: Int *: Boolean *: String *: EmptyTuple] // error
20-
| ^
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2121
| Match type reduction failed since selector EmptyTuple.type
2222
| matches none of the cases
2323
|
2424
| case _ *: _ *: t => Last[t]
2525
| case t *: EmptyTuple => t
26-
-- Error: tests/neg/i12049.scala:22:26 ---------------------------------------------------------------------------------
26+
-- [E184] Type Error: tests/neg/i12049.scala:22:26 ---------------------------------------------------------------------
2727
22 |val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error
28-
| ^
28+
| ^^^^^^^^^^^^^^^^^^
2929
| Match type reduction failed since selector A *: EmptyTuple.type
3030
| matches none of the cases
3131
|
@@ -45,17 +45,17 @@
4545
| Therefore, reduction cannot advance to the remaining case
4646
|
4747
| case B => String
48-
-- Error: tests/neg/i12049.scala:25:26 ---------------------------------------------------------------------------------
48+
-- [E184] Type Error: tests/neg/i12049.scala:25:26 ---------------------------------------------------------------------
4949
25 |val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] // error
50-
| ^
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5151
| Match type reduction failed since selector EmptyTuple.type
5252
| matches none of the cases
5353
|
5454
| case _ *: _ *: t => Last[t]
5555
| case t *: EmptyTuple => t
56-
-- Error: tests/neg/i12049.scala:26:29 ---------------------------------------------------------------------------------
56+
-- [E184] Type Error: tests/neg/i12049.scala:26:29 ---------------------------------------------------------------------
5757
26 |val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] // error
58-
| ^
58+
| ^^^^^^^^^^^^^^^^^^
5959
| Match type reduction failed since selector A *: EmptyTuple.type
6060
| matches none of the cases
6161
|
+4-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
object Test:
2-
type AnyKindMatchType1[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded // error
2+
type AnyKindMatchType1[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded
33
case Option[a] => Int
44

55
type AnyKindMatchType2[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded
66
case Option => Int // error: Missing type parameter for Option
77

8-
type AnyKindMatchType3[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded // error
8+
type AnyKindMatchType3[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded
99
case _ => Int
1010

11-
type AnyKindMatchType4[X <: Option] = X match // error // error: the scrutinee of a match type cannot be higher-kinded // error
11+
type AnyKindMatchType4[X <: Option] = X match // error // error: the scrutinee of a match type cannot be higher-kinded
1212
case _ => Int
1313

14-
type AnyKindMatchType5[X[_]] = X match // error: the scrutinee of a match type cannot be higher-kinded // error
14+
type AnyKindMatchType5[X[_]] = X match // error: the scrutinee of a match type cannot be higher-kinded
1515
case _ => Int
1616
end Test

tests/neg/matchtype-seq.check

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
-- Error: tests/neg/matchtype-seq.scala:9:11 ---------------------------------------------------------------------------
1+
-- [E184] Type Error: tests/neg/matchtype-seq.scala:9:11 ---------------------------------------------------------------
22
9 | identity[T1[3]]("") // error
3-
| ^
3+
| ^^^^^
44
| Match type reduction failed since selector (3 : Int)
55
| matches none of the cases
66
|
77
| case (1 : Int) => Int
88
| case (2 : Int) => String
9-
-- Error: tests/neg/matchtype-seq.scala:10:11 --------------------------------------------------------------------------
9+
-- [E184] Type Error: tests/neg/matchtype-seq.scala:10:11 --------------------------------------------------------------
1010
10 | identity[T1[3]](1) // error
11-
| ^
11+
| ^^^^^
1212
| Match type reduction failed since selector (3 : Int)
1313
| matches none of the cases
1414
|

0 commit comments

Comments
 (0)