Skip to content

Commit 36c7d26

Browse files
committed
Avoid blowup of compute times for ill-formed retains
- Completely drop all retains-like annotations if cc is not enabled somewhere. This is the same as in the reverted commit but now done in Annotation.mapWith - Strip nonsensical parts of retains-like annotations to avoid blowup.
1 parent e7f4e54 commit 36c7d26

File tree

3 files changed

+108
-18
lines changed

3 files changed

+108
-18
lines changed

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

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import ast.tpd, tpd.*
77
import util.Spans.Span
88
import printing.{Showable, Printer}
99
import printing.Texts.Text
10+
import cc.isRetainsLike
11+
import config.Feature
1012

1113
import scala.annotation.internal.sharable
1214

@@ -53,23 +55,49 @@ object Annotations {
5355
* be overridden. Returns EmptyAnnotation if type type map produces a range
5456
* type, since ranges cannot be types of trees.
5557
*/
56-
def mapWith(tm: TypeMap)(using Context) =
57-
val args = tpd.allArguments(tree)
58-
if args.isEmpty then this
59-
else
60-
// Checks if `tm` would result in any change by applying it to types
61-
// inside the annotations' arguments and checking if the resulting types
62-
// are different.
63-
val findDiff = new TreeAccumulator[Type]:
64-
def apply(x: Type, tree: Tree)(using Context): Type =
65-
if tm.isRange(x) then x
66-
else
67-
val tp1 = tm(tree.tpe)
68-
foldOver(if !tp1.exists || tp1.eql(tree.tpe) then x else tp1, tree)
69-
val diff = findDiff(NoType, args)
70-
if tm.isRange(diff) then EmptyAnnotation
71-
else if diff.exists then derivedAnnotation(tm.mapOver(tree))
72-
else this
58+
def mapWith(tm: TypeMap)(using Context): Annotation =
59+
tpd.allArguments(tree) match
60+
case Nil => this
61+
62+
case arg :: Nil if symbol.isRetainsLike =>
63+
64+
// Map all skolems (?n: T) to (?n: Any), and drop all annotations.
65+
// Skolems and annotated types are not allowed in a retains annotation anyway,
66+
// so the underlying type does not matter. This simplification prevents
67+
// exponential blowup in some cases. See i24556.scala and i24556a.scala.
68+
def sanitize(tp: Type): Type = tp match
69+
case SkolemType(_) => SkolemType(defn.AnyType)
70+
case AnnotatedType(parent, _) => parent
71+
case tp @ OrType(tp1, tp2) => tp.derivedOrType(sanitize(tp1), sanitize(tp2))
72+
case _ => tp
73+
74+
def rebuild(tree: Tree, mappedType: Type): Tree = tree match
75+
case Apply(fn, Nil) => cpy.Apply(tree)(rebuild(fn, mappedType), Nil)
76+
case TypeApply(fn, arg :: Nil) => cpy.TypeApply(tree)(fn, TypeTree(mappedType) :: Nil)
77+
case Block(Nil, expr) => rebuild(expr, mappedType)
78+
79+
if !Feature.ccEnabledSomewhere then
80+
EmptyAnnotation // strip retains-like annotations unless capture checking is enabled
81+
else
82+
val mappedType = sanitize(tm(arg.tpe))
83+
if mappedType eq arg.tpe then this
84+
else derivedAnnotation(rebuild(tree, mappedType))
85+
86+
case args =>
87+
// Checks if `tm` would result in any change by applying it to types
88+
// inside the annotations' arguments and checking if the resulting types
89+
// are different.
90+
val findDiff = new TreeAccumulator[Type]:
91+
def apply(x: Type, tree: Tree)(using Context): Type =
92+
if tm.isRange(x) then x
93+
else
94+
val tp1 = tm(tree.tpe)
95+
foldOver(if !tp1.exists || tp1.eql(tree.tpe) then x else tp1, tree)
96+
val diff = findDiff(NoType, args)
97+
if tm.isRange(diff) then EmptyAnnotation
98+
else if diff.exists then derivedAnnotation(tm.mapOver(tree))
99+
else this
100+
end mapWith
73101

74102
/** Does this annotation refer to a parameter of `tl`? */
75103
def refersToParamOf(tl: TermLambda)(using Context): Boolean =
@@ -303,7 +331,7 @@ object Annotations {
303331
case annot @ ExperimentalAnnotation(msg) => ExperimentalAnnotation(msg, annot.tree.span)
304332
}
305333
}
306-
334+
307335
object PreviewAnnotation {
308336
/** Matches and extracts the message from an instance of `@preview(msg)`
309337
* Returns `Some("")` for `@preview` with no message.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import language.experimental.captureChecking
2+
3+
trait Item
4+
5+
trait ItOps[+T, +CC[_], +C]:
6+
def ++[B >: T](other: It[B]^): CC[B]^{this, other}
7+
8+
trait It[+T] extends ItOps[T, It, It[T]]
9+
10+
trait Sq[+T] extends It[T] with ItOps[T, Seq, Seq[T]]
11+
12+
def items: Sq[Item] = ???
13+
14+
@main def main(): It[Item] =
15+
items
16+
++ items
17+
++ items
18+
++ items
19+
++ items
20+
++ items
21+
++ items
22+
++ items
23+
++ items
24+
++ items
25+
++ items
26+
++ items
27+
++ items
28+
++ items
29+
++ items
30+
++ items
31+
++ items
32+
++ items
33+
++ items
34+
++ items
35+
++ items
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import language.experimental.captureChecking
2+
3+
trait Item
4+
def items : Seq[Item] = ???
5+
6+
@main def main() =
7+
items
8+
++ items
9+
++ items
10+
++ items
11+
++ items
12+
++ items
13+
++ items
14+
++ items
15+
++ items
16+
++ items
17+
++ items
18+
++ items
19+
++ items
20+
++ items
21+
++ items
22+
++ items
23+
++ items
24+
++ items
25+
++ items
26+
++ items
27+
++ items

0 commit comments

Comments
 (0)