Skip to content

Commit 15eedb9

Browse files
committed
Change MT reduction to simplify with Mode.Type
Transcribing and paraphrasing from smarter's comment in #16408 (comment) : 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.
1 parent 5d2812a commit 15eedb9

File tree

5 files changed

+117
-2
lines changed

5 files changed

+117
-2
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -3192,7 +3192,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
31923192
}
31933193
}
31943194
case redux =>
3195-
MatchResult.Reduced(redux.simplified)
3195+
MatchResult.Reduced(redux)
31963196
case _ =>
31973197
MatchResult.Reduced(body)
31983198

@@ -3220,7 +3220,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
32203220
MatchTypeTrace.noInstance(scrut, cas, fails)
32213221
NoType
32223222
case MatchResult.Reduced(tp) =>
3223-
tp
3223+
withMode(Mode.Type)(tp.simplified)
32243224
case Nil =>
32253225
val casesText = MatchTypeTrace.noMatchesText(scrut, cases)
32263226
throw MatchTypeReductionError(em"Match type reduction $casesText")

tests/pos/i16408.min1.scala

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
object Helpers:
2+
type NodeFun[R] = Matchable // compiles without [R] parameter
3+
4+
type URIFun[R] = R match
5+
case GetURI[u] => u & NodeFun[R]
6+
7+
private type GetURI[U] = RDF { type URI = U }
8+
end Helpers
9+
10+
trait RDF:
11+
type URI
12+
13+
trait ROps[R <: RDF]:
14+
def auth(uri: Helpers.URIFun[R]): String
15+
16+
object TraitRDF extends RDF:
17+
override type URI = TraitTypes.UriImpl
18+
19+
val rops = new ROps[TraitRDF.type] {
20+
override def auth(uri: Helpers.URIFun[TraitRDF.type]): String = ???
21+
}
22+
end TraitRDF
23+
24+
object TraitTypes:
25+
trait UriImpl // doesn't compile
26+
// class UriImpl // compiles

tests/pos/i16408.min2.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
object Helpers:
2+
type NodeFun[R] = Matchable // compiles without [R] parameter
3+
4+
type URIFun[R] = R match
5+
case RDF[u] => u & NodeFun[R]
6+
end Helpers
7+
8+
trait RDF[URIParam]
9+
10+
trait ROps[R <: RDF[?]]:
11+
def auth(uri: Helpers.URIFun[R]): String
12+
13+
object TraitRDF extends RDF[TraitTypes.UriImpl]:
14+
15+
val rops = new ROps[TraitRDF.type] {
16+
override def auth(uri: Helpers.URIFun[TraitRDF.type]): String = ???
17+
}
18+
end TraitRDF
19+
20+
object TraitTypes:
21+
trait UriImpl // doesn't compile
22+
// class UriImpl // compiles

tests/pos/i16408.scala

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import scala.util.Try
2+
3+
trait RDF:
4+
rdf =>
5+
6+
type R = rdf.type
7+
type Node <: Matchable
8+
type URI <: Node
9+
10+
given rops: ROps[R]
11+
end RDF
12+
13+
object RDF:
14+
type Node[R <: RDF] = R match
15+
case GetNode[n] => Matchable //n & rNode[R]
16+
17+
type URI[R <: RDF] <: Node[R] = R match
18+
case GetURI[u] => u & Node[R]
19+
20+
private type GetNode[N] = RDF { type Node = N }
21+
private type GetURI[U] = RDF { type URI = U }
22+
end RDF
23+
24+
trait ROps[R <: RDF]:
25+
def mkUri(str: String): Try[RDF.URI[R]]
26+
def auth(uri: RDF.URI[R]): Try[String]
27+
28+
object TraitTypes:
29+
trait Node:
30+
def value: String
31+
32+
trait Uri extends Node
33+
34+
def mkUri(u: String): Uri =
35+
new Uri { def value = u }
36+
37+
object TraitRDF extends RDF:
38+
import TraitTypes as tz
39+
40+
override opaque type Node <: Matchable = tz.Node
41+
override opaque type URI <: Node = tz.Uri
42+
43+
given rops: ROps[R] with
44+
override def mkUri(str: String): Try[RDF.URI[R]] = Try(tz.mkUri(str))
45+
override def auth(uri: RDF.URI[R]): Try[String] =
46+
Try(java.net.URI.create(uri.value).getAuthority())
47+
48+
end TraitRDF
49+
50+
class Test[R <: RDF](using rops: ROps[R]):
51+
import rops.given
52+
lazy val uriT: Try[RDF.URI[R]] = rops.mkUri("https://bblfish.net/#i")
53+
lazy val x: String = "uri authority=" + uriT.map(u => rops.auth(u))
54+
55+
@main def run =
56+
val test = Test[TraitRDF.type]
57+
println(test.x)

tests/pos/i17257.min.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Minimisation of tests/run-macros/i17257
2+
// to understand how changes to match type reduction
3+
// impacted this use of Tuple.IsMappedBy.
4+
class C[+A]
5+
def foo[T <: Tuple : Tuple.IsMappedBy[C]] = ???
6+
def bar[X] = foo[(
7+
C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X],
8+
C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X], C[X],
9+
C[X], C[X], C[X],
10+
)]

0 commit comments

Comments
 (0)