From 15eedb9d1914b1bc403332f612ab3b7e89398324 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 7 Jun 2023 14:02:21 +0100 Subject: [PATCH] Change MT reduction to simplify with Mode.Type Transcribing and paraphrasing from smarter's comment in https://github.com/lampepfl/dotty/issues/16408#issuecomment-1533347659 : Type erasure assumes method signatures aren't simplified, since simplification logic is implementation-defined. For instance, some intersection types can be simplified down, but intersection types and their simplification can erase to different types - prefering classes over traits, for instance (for Java interop, as it matches Java's erasure). Also note, simplify doesn't simplify intersections and unions in Type mode. But Match Types will cache their reduction without considering the type mode as a cache input, thus the simplified reduction leaks even when called in Type mode. So we call simplified in Mode.Type, in both cases (another desire), so only that result is cached instead. Using normalise doesn't work because, for example, that doesn't normalise match types that are applied type args (e.g. args of Pair). And not caching the result of those reductions means that they'll get repeat over and over. --- .../dotty/tools/dotc/core/TypeComparer.scala | 4 +- tests/pos/i16408.min1.scala | 26 +++++++++ tests/pos/i16408.min2.scala | 22 +++++++ tests/pos/i16408.scala | 57 +++++++++++++++++++ tests/pos/i17257.min.scala | 10 ++++ 5 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i16408.min1.scala create mode 100644 tests/pos/i16408.min2.scala create mode 100644 tests/pos/i16408.scala create mode 100644 tests/pos/i17257.min.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index a0922c1f0574..61928f1e4f53 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3192,7 +3192,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { } } case redux => - MatchResult.Reduced(redux.simplified) + MatchResult.Reduced(redux) case _ => MatchResult.Reduced(body) @@ -3220,7 +3220,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { MatchTypeTrace.noInstance(scrut, cas, fails) NoType case MatchResult.Reduced(tp) => - tp + withMode(Mode.Type)(tp.simplified) case Nil => val casesText = MatchTypeTrace.noMatchesText(scrut, cases) throw MatchTypeReductionError(em"Match type reduction $casesText") diff --git a/tests/pos/i16408.min1.scala b/tests/pos/i16408.min1.scala new file mode 100644 index 000000000000..8c45fbaa9783 --- /dev/null +++ b/tests/pos/i16408.min1.scala @@ -0,0 +1,26 @@ +object Helpers: + type NodeFun[R] = Matchable // compiles without [R] parameter + + type URIFun[R] = R match + case GetURI[u] => u & NodeFun[R] + + private type GetURI[U] = RDF { type URI = U } +end Helpers + +trait RDF: + type URI + +trait ROps[R <: RDF]: + def auth(uri: Helpers.URIFun[R]): String + +object TraitRDF extends RDF: + override type URI = TraitTypes.UriImpl + + val rops = new ROps[TraitRDF.type] { + override def auth(uri: Helpers.URIFun[TraitRDF.type]): String = ??? + } +end TraitRDF + +object TraitTypes: + trait UriImpl // doesn't compile + // class UriImpl // compiles diff --git a/tests/pos/i16408.min2.scala b/tests/pos/i16408.min2.scala new file mode 100644 index 000000000000..3eb9d395ac4b --- /dev/null +++ b/tests/pos/i16408.min2.scala @@ -0,0 +1,22 @@ +object Helpers: + type NodeFun[R] = Matchable // compiles without [R] parameter + + type URIFun[R] = R match + case RDF[u] => u & NodeFun[R] +end Helpers + +trait RDF[URIParam] + +trait ROps[R <: RDF[?]]: + def auth(uri: Helpers.URIFun[R]): String + +object TraitRDF extends RDF[TraitTypes.UriImpl]: + + val rops = new ROps[TraitRDF.type] { + override def auth(uri: Helpers.URIFun[TraitRDF.type]): String = ??? + } +end TraitRDF + +object TraitTypes: + trait UriImpl // doesn't compile + // class UriImpl // compiles diff --git a/tests/pos/i16408.scala b/tests/pos/i16408.scala new file mode 100644 index 000000000000..10e8b16bab72 --- /dev/null +++ b/tests/pos/i16408.scala @@ -0,0 +1,57 @@ +import scala.util.Try + +trait RDF: + rdf => + + type R = rdf.type + type Node <: Matchable + type URI <: Node + + given rops: ROps[R] +end RDF + +object RDF: + type Node[R <: RDF] = R match + case GetNode[n] => Matchable //n & rNode[R] + + type URI[R <: RDF] <: Node[R] = R match + case GetURI[u] => u & Node[R] + + private type GetNode[N] = RDF { type Node = N } + private type GetURI[U] = RDF { type URI = U } +end RDF + +trait ROps[R <: RDF]: + def mkUri(str: String): Try[RDF.URI[R]] + def auth(uri: RDF.URI[R]): Try[String] + +object TraitTypes: + trait Node: + def value: String + + trait Uri extends Node + + def mkUri(u: String): Uri = + new Uri { def value = u } + +object TraitRDF extends RDF: + import TraitTypes as tz + + override opaque type Node <: Matchable = tz.Node + override opaque type URI <: Node = tz.Uri + + given rops: ROps[R] with + override def mkUri(str: String): Try[RDF.URI[R]] = Try(tz.mkUri(str)) + override def auth(uri: RDF.URI[R]): Try[String] = + Try(java.net.URI.create(uri.value).getAuthority()) + +end TraitRDF + +class Test[R <: RDF](using rops: ROps[R]): + import rops.given + lazy val uriT: Try[RDF.URI[R]] = rops.mkUri("https://bblfish.net/#i") + lazy val x: String = "uri authority=" + uriT.map(u => rops.auth(u)) + +@main def run = + val test = Test[TraitRDF.type] + println(test.x) diff --git a/tests/pos/i17257.min.scala b/tests/pos/i17257.min.scala new file mode 100644 index 000000000000..3225625fd220 --- /dev/null +++ b/tests/pos/i17257.min.scala @@ -0,0 +1,10 @@ +// Minimisation of tests/run-macros/i17257 +// to understand how changes to match type reduction +// impacted this use of Tuple.IsMappedBy. +class C[+A] +def foo[T <: Tuple : Tuple.IsMappedBy[C]] = ??? +def bar[X] = foo[( + C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], + C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], + C[X], C[X], C[X], +)]