diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index cee1ec7fffa8..0890d5889fa7 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2902,6 +2902,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling tp case tp: ConstantType => tp + case tp: AppliedType if tp.tryCompiletimeConstantFold.exists => + tp.tryCompiletimeConstantFold case tp: HKTypeLambda => tp case tp: ParamRef => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 3dcbf2536509..af8a6e8eb004 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -468,12 +468,10 @@ object Types extends TypeUtils { /** Does this application expand to a match type? */ def isMatchAlias(using Context): Boolean = underlyingMatchType.exists - def underlyingMatchType(using Context): Type = stripped match { + def underlyingMatchType(using Context): Type = stripped match case tp: MatchType => tp - case tp: HKTypeLambda => tp.resType.underlyingMatchType case tp: AppliedType => tp.underlyingMatchType case _ => NoType - } /** Is this a higher-kinded type lambda with given parameter variances? * These lambdas are used as the RHS of higher-kinded abstract types or @@ -4647,6 +4645,7 @@ object Types extends TypeUtils { /** Exists if the tycon is a TypeRef of an alias with an underlying match type. * Anything else should have already been reduced in `appliedTo` by the TypeAssigner. + * May reduce several HKTypeLambda applications before the underlying MatchType is reached. */ override def underlyingMatchType(using Context): Type = if ctx.period != validUnderlyingMatch then @@ -4654,28 +4653,15 @@ object Types extends TypeUtils { cachedUnderlyingMatch = superType.underlyingMatchType cachedUnderlyingMatch - override def tryNormalize(using Context): Type = tycon.stripTypeVar match { - case tycon: TypeRef => - def tryMatchAlias = tycon.info match - case AliasingBounds(alias) if isMatchAlias => - trace(i"normalize $this", typr, show = true) { - MatchTypeTrace.recurseWith(this) { - alias.applyIfParameterized(args.map(_.normalized)).tryNormalize - /* `applyIfParameterized` may reduce several HKTypeLambda applications - * before the underlying MatchType is reached. - * Even if they do not involve any match type normalizations yet, - * we still want to record these reductions in the MatchTypeTrace. - * They should however only be attempted if they eventually expand - * to a match type, which is ensured by the `isMatchAlias` guard. - */ - } - } - case _ => - NoType - tryCompiletimeConstantFold.orElse(tryMatchAlias) - case _ => - NoType - } + override def tryNormalize(using Context): Type = + def tryMatchAlias = + if isMatchAlias then trace(i"normalize $this", typr, show = true): + if MatchTypeTrace.isRecording then + MatchTypeTrace.recurseWith(this)(superType.tryNormalize) + else + underlyingMatchType.tryNormalize + else NoType + tryCompiletimeConstantFold.orElse(tryMatchAlias) /** Is this an unreducible application to wildcard arguments? * This is the case if tycon is higher-kinded. This means diff --git a/tests/neg/i12049d.check b/tests/neg/i12049d.check new file mode 100644 index 000000000000..fdb13aae4e43 --- /dev/null +++ b/tests/neg/i12049d.check @@ -0,0 +1,14 @@ +-- [E007] Type Mismatch Error: tests/neg/i12049d.scala:14:52 ----------------------------------------------------------- +14 |val x: M[NotRelevant[Nothing], Relevant[Nothing]] = 2 // error + | ^ + | Found: (2 : Int) + | Required: M[NotRelevant[Nothing], Relevant[Nothing]] + | + | Note: a match type could not be fully reduced: + | + | trying to reduce M[NotRelevant[Nothing], Relevant[Nothing]] + | trying to reduce Relevant[Nothing] + | failed since selector Nothing + | is uninhabited (there are no values of that type). + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i12049d.scala b/tests/neg/i12049d.scala new file mode 100644 index 000000000000..0011ec1f00b1 --- /dev/null +++ b/tests/neg/i12049d.scala @@ -0,0 +1,14 @@ + +trait A +trait B + +type M[X, Y] = Y match + case A => Int + case B => String + +type Relevant[Z] = Z match + case A => B +type NotRelevant[Z] = Z match + case B => A + +val x: M[NotRelevant[Nothing], Relevant[Nothing]] = 2 // error diff --git a/tests/pos/i20166.scala b/tests/pos/i20166.scala new file mode 100644 index 000000000000..38b7556a82f4 --- /dev/null +++ b/tests/pos/i20166.scala @@ -0,0 +1,24 @@ +import scala.compiletime.ops.int.* + +// NOTE ops.int.S is documented as equivalent to MyS + +type MyS[X] = X match + case 0 => 1 + case 1 => 2 + case 2 => 3 + +type M[I <: Int] = 4 match + case 1 - 1 => "0" + case MyS[I] => "2" + case S[I] => "2" // Not provablyDisjoint before changes + case 2 + I => "3" + case I + 3 => "4" + +val _: M[1] = "4" + + +type M2[I <: Int, P] = I match + case P => "b" + case _ => "c" + +val _: M2[5, 2 + 3] = "b" diff --git a/tests/pos/matchtype-unusedArg/A_1.scala b/tests/pos/matchtype-unusedArg/A_1.scala new file mode 100644 index 000000000000..4364a812f12c --- /dev/null +++ b/tests/pos/matchtype-unusedArg/A_1.scala @@ -0,0 +1,8 @@ + +type Rec[X] = X match + case Int => Rec[X] + +type M[Unused, Y] = Y match + case String => Double + +def foo[X](d: M[Rec[X], "hi"]) = ??? diff --git a/tests/pos/matchtype-unusedArg/B_2.scala b/tests/pos/matchtype-unusedArg/B_2.scala new file mode 100644 index 000000000000..437e53a1691d --- /dev/null +++ b/tests/pos/matchtype-unusedArg/B_2.scala @@ -0,0 +1,2 @@ + +def Test = foo[Int](3d) // crash before changes