diff --git a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala index 60ebc95e7bed..2109a5839380 100644 --- a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala +++ b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala @@ -12,7 +12,6 @@ object MatchTypeTrace: private enum TraceEntry: case TryReduce(scrut: Type) - case NoMatches(scrut: Type, cases: List[Type]) case Stuck(scrut: Type, stuckCase: Type, otherCases: List[Type]) case NoInstance(scrut: Type, stuckCase: Type, fails: List[(Name, TypeBounds)]) case EmptyScrutinee(scrut: Type) @@ -51,12 +50,6 @@ object MatchTypeTrace: case _ => case _ => - /** Record a failure that scrutinee `scrut` does not match any case in `cases`. - * Only the first failure is recorded. - */ - def noMatches(scrut: Type, cases: List[Type])(using Context) = - matchTypeFail(NoMatches(scrut, cases)) - /** Record a failure that scrutinee `scrut` does not match `stuckCase` but is * not disjoint from it either, which means that the remaining cases `otherCases` * cannot be visited. Only the first failure is recorded. @@ -99,11 +92,6 @@ object MatchTypeTrace: private def explainEntry(entry: TraceEntry)(using Context): String = entry match case TryReduce(scrut: Type) => i" trying to reduce $scrut" - case NoMatches(scrut, cases) => - i""" failed since selector $scrut - | matches none of the cases - | - | ${casesText(cases)}""" case EmptyScrutinee(scrut) => i""" failed since selector $scrut | is uninhabited (there are no values of that type).""" @@ -127,6 +115,7 @@ object MatchTypeTrace: | The computed bounds for the $params are: | ${fails.map((name, bounds) => i"$name$bounds")}%\n %""" + /** The failure message when the scrutinee `scrut` does not match any case in `cases`. */ def noMatchesText(scrut: Type, cases: List[Type])(using Context): String = i"""failed since selector $scrut |matches none of the cases diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index a0922c1f0574..8328906aa0b8 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3223,7 +3223,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { tp case Nil => val casesText = MatchTypeTrace.noMatchesText(scrut, cases) - throw MatchTypeReductionError(em"Match type reduction $casesText") + ErrorType(reporting.MatchTypeNoCases(casesText)) inFrozenConstraint { // Empty types break the basic assumption that if a scrutinee and a @@ -3242,6 +3242,16 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { if (provablyEmpty(scrut)) MatchTypeTrace.emptyScrutinee(scrut) NoType + else if scrut.isError then + // if the scrutinee is an error type + // then just return that as the result + // not doing so will result in the first type case matching + // because ErrorType (as a FlexType) is <:< any type case + // this situation can arise from any kind of nesting of match types, + // e.g. neg/i12049 `Tuple.Concat[Reverse[ts], (t2, t1)]` + // if Reverse[ts] fails with no matches, + // the error type should be the reduction of the Concat too + scrut else recur(cases) } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index f59bd08da779..24a207da6836 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -46,9 +46,6 @@ object TypeError: def toMessage(using Context) = msg end TypeError -class MatchTypeReductionError(msg: Message)(using Context) extends TypeError: - def toMessage(using Context) = msg - class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError: 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(", ")}" diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index ad56f29287fc..5809a66cdc0f 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -196,6 +196,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case AmbiguousExtensionMethodID // errorNumber 180 case UnqualifiedCallToAnyRefMethodID // errorNumber: 181 case NotConstantID // errorNumber: 182 + case MatchTypeNoCasesID // errorNumber: 183 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 0218d8a9c915..19265333c69d 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2909,6 +2909,10 @@ class UnusedNonUnitValue(tp: Type)(using Context) def msg(using Context) = i"unused value of type $tp" def explain(using Context) = "" +class MatchTypeNoCases(casesText: String)(using Context) extends TypeMsg(MatchTypeNoCasesID): + def msg(using Context) = i"Match type reduction $casesText" + def explain(using Context) = "" + class MatchTypeScrutineeCannotBeHigherKinded(tp: Type)(using Context) extends TypeMsg(MatchTypeScrutineeCannotBeHigherKindedID) : def msg(using Context) = i"the scrutinee of a match type cannot be higher-kinded" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0d40f818fd86..91c90923f323 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -636,7 +636,7 @@ trait ImplicitRunInfo: traverseChildren(t) case t: MatchType => traverseChildren(t) - traverse(try t.normalized catch case _: MatchTypeReductionError => t) + traverse(t.normalized) case MatchType.InDisguise(mt) if !t.isInstanceOf[LazyRef] // skip recursive applications (eg. Tuple.Map) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 458e60ddfa38..fe3e4265c2d1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3115,7 +3115,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case xtree: untpd.NameTree => typedNamed(xtree, pt) case xtree => typedUnnamed(xtree) + val unsimplifiedType = result.tpe simplify(result, pt, locked) + result.tpe.stripTypeVar match + case e: ErrorType if !unsimplifiedType.isErroneous => errorTree(xtree, e.msg, xtree.srcPos) + case _ => result catch case ex: TypeError => errorTree(xtree, ex, xtree.srcPos.focus) // use focussed sourcePos since tree might be a large definition // and a large error span would hide all errors in interior. diff --git a/tests/neg-macros/toexproftuple.scala b/tests/neg-macros/toexproftuple.scala index 20ae2f08ff8d..7b69c578be70 100644 --- a/tests/neg-macros/toexproftuple.scala +++ b/tests/neg-macros/toexproftuple.scala @@ -1,33 +1,8 @@ -import scala.quoted._, scala.deriving.* // error -// ^ -// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) -// matches none of the cases -// -// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] -// case EmptyTuple => EmptyTuple +import scala.quoted._, scala.deriving.* -inline def mcr: Any = ${mcrImpl} // error -// ^ -// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) -// matches none of the cases -// -// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] -// case EmptyTuple => EmptyTuple +inline def mcr: Any = ${mcrImpl} -def mcrImpl(using ctx: Quotes): Expr[Any] = { // error // error - //^ - // Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) - // matches none of the cases - // - // case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] - // case EmptyTuple => EmptyTuple - - // ^ - // Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) - // matches none of the cases - // - // case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] - // case EmptyTuple => EmptyTuple +def mcrImpl(using ctx: Quotes): Expr[Any] = { val tpl: (Expr[1], Expr[2], Expr[3]) = ('{1}, '{2}, '{3}) '{val res: (1, 3, 3) = ${Expr.ofTuple(tpl)}; res} // error @@ -36,7 +11,7 @@ def mcrImpl(using ctx: Quotes): Expr[Any] = { // error // error // Required: quoted.Expr[((1 : Int), (3 : Int), (3 : Int))] val tpl2: (Expr[1], 2, Expr[3]) = ('{1}, 2, '{3}) - '{val res = ${Expr.ofTuple(tpl2)}; res} // error // error // error // error + '{val res = ${Expr.ofTuple(tpl2)}; res} // error // ^ // Cannot prove that (quoted.Expr[(1 : Int)], (2 : Int), quoted.Expr[(3 : Int)]) =:= scala.Tuple.Map[ // scala.Tuple.InverseMap[ @@ -44,20 +19,4 @@ def mcrImpl(using ctx: Quotes): Expr[Any] = { // error // error // , quoted.Expr] // , quoted.Expr]. - // ^ - // Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) - // matches none of the cases - // - // case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] - // case EmptyTuple => EmptyTuple - - // ^ - // Cyclic reference involving val res - - // ^ - // Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) - // matches none of the cases - // - // case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] - // case EmptyTuple => EmptyTuple } diff --git a/tests/neg/i12049.check b/tests/neg/i12049.check index 11c648e35a57..526395940d4e 100644 --- a/tests/neg/i12049.check +++ b/tests/neg/i12049.check @@ -15,17 +15,17 @@ | case B => String | | longer explanation available when compiling with `-explain` --- Error: tests/neg/i12049.scala:14:23 --------------------------------------------------------------------------------- +-- [E183] Type Error: tests/neg/i12049.scala:14:23 --------------------------------------------------------------------- 14 |val y3: String = ??? : Last[Int *: Int *: Boolean *: String *: EmptyTuple] // error - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Match type reduction failed since selector EmptyTuple.type | matches none of the cases | | case _ *: _ *: t => Last[t] | case t *: EmptyTuple => t --- Error: tests/neg/i12049.scala:22:26 --------------------------------------------------------------------------------- +-- [E183] Type Error: tests/neg/i12049.scala:22:26 --------------------------------------------------------------------- 22 |val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error - | ^ + | ^^^^^^^^^^^^^^^^^^ | Match type reduction failed since selector A *: EmptyTuple.type | matches none of the cases | @@ -45,17 +45,17 @@ | Therefore, reduction cannot advance to the remaining case | | case B => String --- Error: tests/neg/i12049.scala:25:26 --------------------------------------------------------------------------------- +-- [E183] Type Error: tests/neg/i12049.scala:25:26 --------------------------------------------------------------------- 25 |val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] // error - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Match type reduction failed since selector EmptyTuple.type | matches none of the cases | | case _ *: _ *: t => Last[t] | case t *: EmptyTuple => t --- Error: tests/neg/i12049.scala:26:29 --------------------------------------------------------------------------------- +-- [E183] Type Error: tests/neg/i12049.scala:26:29 --------------------------------------------------------------------- 26 |val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] // error - | ^ + | ^^^^^^^^^^^^^^^^^^ | Match type reduction failed since selector A *: EmptyTuple.type | matches none of the cases | diff --git a/tests/neg/i13757-match-type-anykind.scala b/tests/neg/i13757-match-type-anykind.scala index d8273e546dab..a80e8b2b289b 100644 --- a/tests/neg/i13757-match-type-anykind.scala +++ b/tests/neg/i13757-match-type-anykind.scala @@ -1,16 +1,16 @@ object Test: - type AnyKindMatchType1[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded // error + type AnyKindMatchType1[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded case Option[a] => Int type AnyKindMatchType2[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded case Option => Int // error: Missing type parameter for Option - type AnyKindMatchType3[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded // error + type AnyKindMatchType3[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded case _ => Int - type AnyKindMatchType4[X <: Option] = X match // error // error: the scrutinee of a match type cannot be higher-kinded // error + type AnyKindMatchType4[X <: Option] = X match // error // error: the scrutinee of a match type cannot be higher-kinded case _ => Int - type AnyKindMatchType5[X[_]] = X match // error: the scrutinee of a match type cannot be higher-kinded // error + type AnyKindMatchType5[X[_]] = X match // error: the scrutinee of a match type cannot be higher-kinded case _ => Int end Test diff --git a/tests/neg/matchtype-seq.check b/tests/neg/matchtype-seq.check index 9c37fc08c4df..af69dc65cc33 100644 --- a/tests/neg/matchtype-seq.check +++ b/tests/neg/matchtype-seq.check @@ -1,14 +1,14 @@ --- Error: tests/neg/matchtype-seq.scala:9:11 --------------------------------------------------------------------------- +-- [E183] Type Error: tests/neg/matchtype-seq.scala:9:11 --------------------------------------------------------------- 9 | identity[T1[3]]("") // error - | ^ + | ^^^^^ | Match type reduction failed since selector (3 : Int) | matches none of the cases | | case (1 : Int) => Int | case (2 : Int) => String --- Error: tests/neg/matchtype-seq.scala:10:11 -------------------------------------------------------------------------- +-- [E183] Type Error: tests/neg/matchtype-seq.scala:10:11 -------------------------------------------------------------- 10 | identity[T1[3]](1) // error - | ^ + | ^^^^^ | Match type reduction failed since selector (3 : Int) | matches none of the cases |