@@ -7,6 +7,8 @@ import ast.tpd, tpd.*
77import util .Spans .Span
88import printing .{Showable , Printer }
99import printing .Texts .Text
10+ import cc .isRetainsLike
11+ import config .Feature
1012
1113import 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.
0 commit comments