Skip to content

Commit da2ca2e

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. [Cherry-picked cd04d00][modified]
1 parent f4381a9 commit da2ca2e

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
@@ -2589,14 +2589,22 @@ object Types extends TypeUtils {
25892589
case _ => true
25902590
}
25912591

2592-
/** Reduce a type-ref `T { X = U; ... } # X` to `U`
2593-
* provided `U` does not refer with a RecThis to the
2594-
* refinement type `T { X = U; ... }`
2592+
/** Reduce a type ref P # X, where X is a type alias and P is a refined type or
2593+
* a class type. If P is a refined type `T { X = U; ... }`, reduce P to U,
2594+
* provided U does not refer with a RecThis to the same refined type. If P is a
2595+
* class type, reduce it to the dealiasd version of P # X. This means that at typer
2596+
* we create projections only for inner classes with class prefixes, since projections
2597+
* of P # X where X is an abstract type are handled by skolemization. At later phases
2598+
* these projections might arise, though.
25952599
*/
25962600
def reduceProjection(using Context): Type =
25972601
if (isType) {
25982602
val reduced = prefix.lookupRefined(name)
2599-
if (reduced.exists) reduced else this
2603+
if reduced.exists then reduced
2604+
else prefix.stripTypeVar match
2605+
case pre: (AppliedType | TypeRef)
2606+
if prefix.typeSymbol.isClass && this.symbol.isAliasType => dealias
2607+
case _ => this
26002608
}
26012609
else this
26022610

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

+4
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,7 @@ java-inherited-type1
105105

106106
# recursion limit exceeded
107107
i7445b.scala
108+
109+
# more aggresive reduce projection makes a difference
110+
i15525.scala
111+

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)