From 6fd2734e7fc8f473342c526c8e0616e2418f3935 Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Thu, 11 Apr 2024 23:16:17 +0200 Subject: [PATCH 1/3] tryCompiletimeConstantFold in disjointnessBoundary Fixes #20166 --- .../dotty/tools/dotc/core/TypeComparer.scala | 2 ++ tests/pos/i20166.scala | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/pos/i20166.scala 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/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" From ee323eff59bc5f1fd660c9c25c023d1a220d51a3 Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Fri, 12 Apr 2024 00:20:47 +0200 Subject: [PATCH 2/3] Drop normalization of applied match alias arguments Delay their normalization until it is needed. Avoids overflows from infinite match types that did not need to normalize. Also improves MatchTypeTraces as a side effect. It appears to have been added to avoid some separate issue, which seems to have been fixed. It is no longer needed since the previous fix with constant folding in disjointnessBoundary. --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- tests/neg/i12049d.check | 14 ++++++++++++++ tests/neg/i12049d.scala | 14 ++++++++++++++ tests/pos/matchtype-unusedArg/A_1.scala | 8 ++++++++ tests/pos/matchtype-unusedArg/B_2.scala | 2 ++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i12049d.check create mode 100644 tests/neg/i12049d.scala create mode 100644 tests/pos/matchtype-unusedArg/A_1.scala create mode 100644 tests/pos/matchtype-unusedArg/B_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 3dcbf2536509..97bf222381f8 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4660,7 +4660,7 @@ object Types extends TypeUtils { case AliasingBounds(alias) if isMatchAlias => trace(i"normalize $this", typr, show = true) { MatchTypeTrace.recurseWith(this) { - alias.applyIfParameterized(args.map(_.normalized)).tryNormalize + alias.applyIfParameterized(args).tryNormalize /* `applyIfParameterized` may reduce several HKTypeLambda applications * before the underlying MatchType is reached. * Even if they do not involve any match type normalizations yet, 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/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 From a0839bcaa4ccd3d7886c10be18d8f9550e80e90a Mon Sep 17 00:00:00 2001 From: Eugene Flesselle Date: Wed, 24 Apr 2024 17:22:08 +0200 Subject: [PATCH 3/3] Use cached underlyingMatchType when normalizing applied match aliases Also fixes underlyingMatchType to not use the resType of HKTypeLambdas It should only be in `isMatch` used for `AliasingBounds`, not `isMatchAlias` --- .../src/dotty/tools/dotc/core/Types.scala | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 97bf222381f8..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).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