Skip to content

Commit c1d18cd

Browse files
committed
Reduce projections of type aliases with class type prefixes
Projections P # X are types that we would like to avoid. If X is a class type, there's nothing we can do. If X is an abstract type, we use skolemization and rewrite to (x?: P).X. If X is an alias type we should simply dealias but this was not done before. This caused an exonential blowup in #19892, where we costructed types of the form ZPartialServerEndpoint[R, A, B, I, E, O, -C] # EndpointType[A, I, E, T, R] ... # EndpointType[A, I, E, T, R] When the were 5 or more such selections, sompile times blew up (33s for 5, timeout after 5 minutes for 6). I am still not qute sure where the blowup happened. Looking at stacktraces of random interrups it seemed to be in a deep recursion of memberDenot and asSeenFrom calls.I believe it would still be interesting to find out more about this, in case there are other similar situations where combinations of deep projections with wide applications cannot be avoided. But for this precise problem, eagerly dealising fixes it.
1 parent a9bb881 commit c1d18cd

File tree

3 files changed

+42
-4
lines changed

3 files changed

+42
-4
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/Types.scala

+12-4
Original file line numberDiff line numberDiff line change
@@ -2708,13 +2708,21 @@ object Types extends TypeUtils {
27082708
case _ => true
27092709
}
27102710

2711-
/** Reduce a type-ref `T { X = U; ... } # X` to `U`
2712-
* provided `U` does not refer with a RecThis to the
2713-
* refinement type `T { X = U; ... }`
2711+
/** Reduce a type ref P # X, where X is a type alias and P is a refined type or
2712+
* a class type. If P is a refined type `T { X = U; ... }`, reduce P to U,
2713+
* provided U does not refer with a RecThis to the same refined type. If P is a
2714+
* class type, reduce it to the dealiasd version of P # X. This means that at typer
2715+
* we create projections only for inner classes with class prefixes, since projections
2716+
* of P # X where X is an abstract type are handled by skolemization. At later phases
2717+
* these projections might arise, though.
27142718
*/
27152719
def reduceProjection(using Context): Type =
27162720
val reduced = prefix.lookupRefined(name)
2717-
if reduced.exists then reduced else this
2721+
if reduced.exists then reduced
2722+
else prefix.stripTypeVar match
2723+
case pre: (AppliedType | TypeRef)
2724+
if prefix.typeSymbol.isClass && this.symbol.isAliasType => dealias
2725+
case _ => this
27182726

27192727
/** Guard against cycles that can arise if given `op`
27202728
* follows info. The problematic cases are a type alias to itself or

Diff for: compiler/test/dotc/pos-test-pickling.blacklist

+4
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,7 @@ java-inherited-type1
114114

115115
# recursion limit exceeded
116116
i7445b.scala
117+
118+
# more aggresive reduce projection makes a difference
119+
i15525.scala
120+

Diff for: tests/pos/i19892.scala

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
abstract class ZPartialServerEndpoint[R, A, B, I, E, O, -C]
2+
extends EndpointOps[A, I, E, O, C]{
3+
override type ThisType[-_R] = ZPartialServerEndpoint[R, A, B, I, E, O, _R]
4+
override type EndpointType[_A, _I, _E, _O, -_R] =ZPartialServerEndpoint[R, _A, B, _I, _E, _O, _R]
5+
}
6+
7+
trait EndpointOps[A, I, E, O, -R] {
8+
type EndpointType[_A, _I, _E, _O, -_R]
9+
type ThisType[-_R]
10+
def out[T]: EndpointType[A, I, E, T, R]
11+
def description(d: String): ThisType[R]
12+
}
13+
14+
object Test {
15+
def basicEndpoint[R](): ZPartialServerEndpoint[R, Any, Any, Unit, Any, Unit, Any] = ???
16+
17+
// commonts next to `.out[Any]` contain information about compilation time when chaining up to N `out` functions
18+
val case1 =
19+
basicEndpoint() // 1.5s
20+
.out[Any] // 1.6s
21+
.out[Any] // 1.7s
22+
.out[Any] // 2s
23+
.out[Any] // 4s
24+
.out[Any] // 33s
25+
.out[Any] // aborted after 5 min
26+
}

0 commit comments

Comments
 (0)