Skip to content

Commit 736630d

Browse files
Make match types with no matching cases not an error
Modify the MatchReducer to return NoType in the case no matches, rather than throwing an MatchTypeReductionError. This makes it consistent with the other match type reduction failures, where being stuck does not result in an error, but simply in an unreduced match type. We still get the explanations of the underlying error in the MatchTypeTrace, but in positions which need the reduction for conformance, rather than at application site of the match type. The diff in neg/10349.scala is quite interesting. With a few intermediate values: ```scala type First[X] = X match case Map[_, v] => First[Option[v]] def first[X](x: X): First[X] = x match case x: Map[k, v] => val hdOpt: Option[v] = x.values.headOption first(hdOpt): First[Option[v]] // error only before changes ``` This now type-checks but will fail at runtime because of the in-exhaustivity of the match expression. Perhaps we should add some additional condition in `isMatchTypeShaped` to account for this, or at least emmit a warning ?
1 parent bb3f891 commit 736630d

File tree

9 files changed

+105
-47
lines changed

9 files changed

+105
-47
lines changed

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -3594,8 +3594,12 @@ class MatchReducer(initctx: Context) extends TypeComparer(initctx) {
35943594
MatchTypeTrace.emptyScrutinee(scrut)
35953595
NoType
35963596
case Nil =>
3597-
val casesText = MatchTypeTrace.noMatchesText(scrut, cases)
3598-
throw MatchTypeReductionError(em"Match type reduction $casesText")
3597+
/* TODO warn ? then re-enable warn/12974.scala:26
3598+
val noCasesText = MatchTypeTrace.noMatchesText(scrut, cases)
3599+
report.warning(reporting.MatchTypeNoCases(noCasesText), pos = ???)
3600+
*/
3601+
MatchTypeTrace.noMatches(scrut, cases)
3602+
NoType
35993603

36003604
inFrozenConstraint(recur(cases))
36013605
}

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

-3
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ object TypeError:
5353
def toMessage(using Context) = msg
5454
end TypeError
5555

56-
class MatchTypeReductionError(msg: Message)(using Context) extends TypeError:
57-
def toMessage(using Context) = msg
58-
5956
class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError:
6057
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(", ")}"
6158

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ trait ImplicitRunInfo:
663663
traverseChildren(t)
664664
case t =>
665665
traverseChildren(t)
666-
traverse(try t.normalized catch case _: MatchTypeReductionError => t)
666+
traverse(t.normalized)
667667
catch case ex: Throwable => handleRecursive("collectParts of", t.show, ex)
668668

669669
def apply(tp: Type): collection.Set[Type] =

tests/neg/10349.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ object Firsts:
44
case Map[_, v] => First[Option[v]]
55

66
def first[X](x: X): First[X] = x match
7-
case x: Map[_, _] => first(x.values.headOption) // error
7+
case x: Map[_, _] => first(x.values.headOption)
88

99
@main
1010
def runFirsts2(): Unit =

tests/neg/10747.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ type Foo[A] = A match {
22
case Int => String
33
}
44

5-
type B = Foo[Boolean] // error
5+
type B = Foo[Boolean]
6+
val _: B = "hello" // error

tests/neg/i12049.check

+64-24
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,45 @@
1515
| case B => String
1616
|
1717
| longer explanation available when compiling with `-explain`
18-
-- Error: tests/neg/i12049.scala:14:23 ---------------------------------------------------------------------------------
18+
-- [E007] Type Mismatch Error: tests/neg/i12049.scala:14:17 ------------------------------------------------------------
1919
14 |val y3: String = ??? : Last[Int *: Int *: Boolean *: String *: EmptyTuple] // error
20-
| ^
21-
| Match type reduction failed since selector EmptyTuple
22-
| matches none of the cases
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21+
| Found: Last[EmptyTuple]
22+
| Required: String
2323
|
24-
| case _ *: _ *: t => Last[t]
25-
| case t *: EmptyTuple => t
26-
-- Error: tests/neg/i12049.scala:22:26 ---------------------------------------------------------------------------------
24+
| Note: a match type could not be fully reduced:
25+
|
26+
| trying to reduce Last[EmptyTuple]
27+
| failed since selector EmptyTuple
28+
| matches none of the cases
29+
|
30+
| case _ *: _ *: t => Last[t]
31+
| case t *: EmptyTuple => t
32+
|
33+
| longer explanation available when compiling with `-explain`
34+
-- [E007] Type Mismatch Error: tests/neg/i12049.scala:22:20 ------------------------------------------------------------
2735
22 |val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error
28-
| ^
29-
| Match type reduction failed since selector A *: EmptyTuple.type
30-
| matches none of the cases
36+
| ^^^^^^^^^^^^^^^^^^^^^^^^
37+
| Found: Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)]
38+
| Required: (A, B, A)
39+
|
40+
| Note: a match type could not be fully reduced:
41+
|
42+
| trying to reduce Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)]
43+
| trying to reduce Reverse[A *: EmptyTuple.type]
44+
| failed since selector A *: EmptyTuple.type
45+
| matches none of the cases
46+
|
47+
| case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)]
48+
| case EmptyTuple => EmptyTuple
49+
| trying to reduce Reverse[A *: EmptyTuple.type]
50+
| failed since selector A *: EmptyTuple.type
51+
| matches none of the cases
3152
|
32-
| case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)]
33-
| case EmptyTuple => EmptyTuple
53+
| case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)]
54+
| case EmptyTuple => EmptyTuple
55+
|
56+
| longer explanation available when compiling with `-explain`
3457
-- [E172] Type Error: tests/neg/i12049.scala:24:20 ---------------------------------------------------------------------
3558
24 |val _ = summon[M[B]] // error
3659
| ^
@@ -45,22 +68,39 @@
4568
| Therefore, reduction cannot advance to the remaining case
4669
|
4770
| case B => String
48-
-- Error: tests/neg/i12049.scala:25:26 ---------------------------------------------------------------------------------
71+
-- [E172] Type Error: tests/neg/i12049.scala:25:78 ---------------------------------------------------------------------
4972
25 |val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] // error
50-
| ^
51-
| Match type reduction failed since selector EmptyTuple
52-
| matches none of the cases
73+
| ^
74+
| Cannot prove that String =:= Last[EmptyTuple].
75+
|
76+
| Note: a match type could not be fully reduced:
77+
|
78+
| trying to reduce Last[EmptyTuple]
79+
| failed since selector EmptyTuple
80+
| matches none of the cases
5381
|
54-
| case _ *: _ *: t => Last[t]
55-
| case t *: EmptyTuple => t
56-
-- Error: tests/neg/i12049.scala:26:29 ---------------------------------------------------------------------------------
82+
| case _ *: _ *: t => Last[t]
83+
| case t *: EmptyTuple => t
84+
-- [E172] Type Error: tests/neg/i12049.scala:26:48 ---------------------------------------------------------------------
5785
26 |val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] // error
58-
| ^
59-
| Match type reduction failed since selector A *: EmptyTuple.type
60-
| matches none of the cases
86+
| ^
87+
| Cannot prove that (A, B, A) =:= Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)].
88+
|
89+
| Note: a match type could not be fully reduced:
90+
|
91+
| trying to reduce Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)]
92+
| trying to reduce Reverse[A *: EmptyTuple.type]
93+
| failed since selector A *: EmptyTuple.type
94+
| matches none of the cases
95+
|
96+
| case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)]
97+
| case EmptyTuple => EmptyTuple
98+
| trying to reduce Reverse[A *: EmptyTuple.type]
99+
| failed since selector A *: EmptyTuple.type
100+
| matches none of the cases
61101
|
62-
| case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)]
63-
| case EmptyTuple => EmptyTuple
102+
| case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)]
103+
| case EmptyTuple => EmptyTuple
64104
-- [E008] Not Found Error: tests/neg/i12049.scala:28:21 ----------------------------------------------------------------
65105
28 |val _ = (??? : M[B]).length // error
66106
| ^^^^^^^^^^^^^^^^^^^

tests/neg/i13757-match-type-anykind.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ object Test:
88
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

+28-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,35 @@
1-
-- Error: tests/neg/matchtype-seq.scala:9:11 ---------------------------------------------------------------------------
1+
-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:9:18 ------------------------------------------------------
22
9 | identity[T1[3]]("") // error
3-
| ^
4-
| Match type reduction failed since selector (3 : Int)
5-
| matches none of the cases
3+
| ^^
4+
| Found: ("" : String)
5+
| Required: Test.T1[(3 : Int)]
66
|
7-
| case (1 : Int) => Int
8-
| case (2 : Int) => String
9-
-- Error: tests/neg/matchtype-seq.scala:10:11 --------------------------------------------------------------------------
7+
| Note: a match type could not be fully reduced:
8+
|
9+
| trying to reduce Test.T1[(3 : Int)]
10+
| failed since selector (3 : Int)
11+
| matches none of the cases
12+
|
13+
| case (1 : Int) => Int
14+
| case (2 : Int) => String
15+
|
16+
| longer explanation available when compiling with `-explain`
17+
-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:10:18 -----------------------------------------------------
1018
10 | identity[T1[3]](1) // error
11-
| ^
12-
| Match type reduction failed since selector (3 : Int)
13-
| matches none of the cases
19+
| ^
20+
| Found: (1 : Int)
21+
| Required: Test.T1[(3 : Int)]
1422
|
15-
| case (1 : Int) => Int
16-
| case (2 : Int) => String
23+
| Note: a match type could not be fully reduced:
24+
|
25+
| trying to reduce Test.T1[(3 : Int)]
26+
| failed since selector (3 : Int)
27+
| matches none of the cases
28+
|
29+
| case (1 : Int) => Int
30+
| case (2 : Int) => String
31+
|
32+
| longer explanation available when compiling with `-explain`
1733
-- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:11:20 -----------------------------------------------------
1834
11 | identity[T1[Int]]("") // error
1935
| ^^

tests/neg/12974.scala renamed to tests/warn/12974.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ object RecMap {
2323
def main(args: Array[String]) =
2424
import Record._
2525

26-
val foo: Any = Rec.empty.fetch("foo") // error
26+
val foo: Any = Rec.empty.fetch("foo") // TODO
2727
// ^
2828
// Match type reduction failed since selector EmptyTuple.type
2929
// matches none of the cases

0 commit comments

Comments
 (0)