@@ -33,9 +33,16 @@ object Annotations {
3333 def derivedAnnotation (tree : Tree )(using Context ): Annotation =
3434 if (tree eq this .tree) this else Annotation (tree)
3535
36+ def derivedClassAnnotation (cls : ClassSymbol )(using Context ) =
37+ Annotation (cls, tree.span)
38+
3639 /** All term arguments of this annotation in a single flat list */
3740 def arguments (using Context ): List [Tree ] = tpd.allTermArguments(tree)
3841
42+ /** All type arguments of this annotation in a single flat list */
43+ def argumentTypes (using Context ): List [Type ] =
44+ tpd.allArguments(tree).filterConserve(_.isType).tpes
45+
3946 def argument (i : Int )(using Context ): Option [Tree ] = {
4047 val args = arguments
4148 if (i < args.length) Some (args(i)) else None
@@ -66,23 +73,8 @@ object Annotations {
6673 // the original tree. TODO Try to use this scheme for other annotations that
6774 // take only type arguments as well. We should wait until after 3.9 LTS to
6875 // do this, though.
69- // 2. Map all skolems (?n: T) to (?n: Any), and map all recursive captures of
70- // that are not on CapSet to `^`. Skolems and capturing types on types
71- // other than CapSet are not allowed in a retains annotation anyway,
72- // so the underlying type does not matter. This simplification prevents
73- // exponential blowup in some cases. See i24556.scala and i24556a.scala.
74- // 3. Drop the annotation entirely if CC is not enabled somehwere.
75-
76- def sanitize (tp : Type ): Type = tp match
77- case SkolemType (_) =>
78- SkolemType (defn.AnyType )
79- case tp @ AnnotatedType (parent, ann)
80- if ann.symbol.isRetainsLike && parent.typeSymbol != defn.Caps_CapSet =>
81- tp.derivedAnnotatedType(parent, Annotation (defn.RetainsCapAnnot , ann.tree.span))
82- case tp @ OrType (tp1, tp2) =>
83- tp.derivedOrType(sanitize(tp1), sanitize(tp2))
84- case _ =>
85- tp
76+ // 2. Sanitize the arguments to prevent compilation time blowup.
77+ // 3. Drop the annotation entirely if CC is not enabled somewhere.
8678
8779 def rebuild (tree : Tree , mappedType : Type ): Tree = tree match
8880 case Apply (fn, Nil ) => cpy.Apply (tree)(rebuild(fn, mappedType), Nil )
@@ -93,8 +85,12 @@ object Annotations {
9385 EmptyAnnotation // strip retains-like annotations unless capture checking is enabled
9486 else
9587 val mappedType = sanitize(tm(arg.tpe))
96- if mappedType `eql` arg.tpe then this
97- else derivedAnnotation(rebuild(tree, mappedType))
88+ if mappedType `eql` arg.tpe then
89+ this
90+ else if cc.ccConfig.newScheme then
91+ CompactAnnotation (symbol.typeRef.appliedTo(mappedType))
92+ else
93+ derivedAnnotation(rebuild(tree, mappedType))
9894
9995 case args =>
10096 // Checks if `tm` would result in any change by applying it to types
@@ -114,17 +110,12 @@ object Annotations {
114110
115111 /** Does this annotation refer to a parameter of `tl`? */
116112 def refersToParamOf (tl : TermLambda )(using Context ): Boolean =
117- def isLambdaParam (t : Type ) = t match
118- case TermParamRef (tl1, _) => tl eq tl1
119- case _ => false
120-
121113 val acc = new TreeAccumulator [Boolean ]:
122114 def apply (x : Boolean , t : Tree )(using Context ) =
123115 if x then true
124- else if t.isType then
125- t.tpe.existsPart(isLambdaParam, stopAt = StopAt .Static )
116+ else if t.isType then refersToLambdaParam(t.tpe, tl)
126117 else t match
127- case id : (Ident | This ) => isLambdaParam(id.tpe.stripped)
118+ case id : (Ident | This ) => isLambdaParam(id.tpe.stripped, tl )
128119 case _ => foldOver(x, t)
129120
130121 tpd.allArguments(tree).exists(acc(false , _))
@@ -163,6 +154,82 @@ object Annotations {
163154 case class ConcreteAnnotation (t : Tree ) extends Annotation :
164155 def tree (using Context ): Tree = t
165156
157+ case class CompactAnnotation (tp : Type ) extends Annotation :
158+ assert(tp.isInstanceOf [AppliedType | TypeRef ], tp)
159+
160+ def tree (using Context ) = TypeTree (tp)
161+
162+ override def symbol (using Context ) = tp.typeSymbol
163+
164+ override def derivedAnnotation (tree : Tree )(using Context ): Annotation =
165+ derivedAnnotation(tree.tpe)
166+
167+ override def derivedClassAnnotation (cls : ClassSymbol )(using Context ) =
168+ derivedAnnotation(cls.typeRef)
169+
170+ def derivedAnnotation (tp : Type )(using Context ): Annotation =
171+ if tp eq this .tp then this else CompactAnnotation (tp)
172+
173+ override def arguments (using Context ): List [Tree ] =
174+ argumentTypes.map(TypeTree (_))
175+
176+ override def argumentTypes (using Context ): List [Type ] = tp.argTypes
177+
178+ def argumentType (i : Int )(using Context ): Type =
179+ val args = argumentTypes
180+ if i < args.length then args(i) else NoType
181+
182+ override def argumentConstant (i : Int )(using Context ): Option [Constant ] =
183+ argumentType(i).normalized match
184+ case ConstantType (c) => Some (c)
185+ case _ => None
186+
187+ override def mapWith (tm : TypeMap )(using Context ): Annotation =
188+ def derived (tp : Type ) =
189+ if tm.isRange(tp) then EmptyAnnotation else derivedAnnotation(tp)
190+ def sanitizeArg (tp : Type ) = tp match
191+ case tp @ AppliedType (tycon, args) =>
192+ tp.derivedAppliedType(tycon, args.mapConserve(sanitize))
193+ case _ =>
194+ tp
195+ if ! symbol.isRetainsLike then derived(tm(tp))
196+ else if Feature .ccEnabledSomewhere then derived(sanitizeArg(tm(tp)))
197+ else EmptyAnnotation // strip retains-like annotations unless capture checking is enabled
198+
199+ override def refersToParamOf (tl : TermLambda )(using Context ): Boolean =
200+ refersToLambdaParam(tp, tl)
201+
202+ override def hash : Int = tp.hash
203+ override def eql (that : Annotation ) = that match
204+ case that : CompactAnnotation => this .tp `eql` that.tp
205+ case _ => false
206+ end CompactAnnotation
207+
208+ /** Sanitize @retains arguments to approximate illegal types that could cause a compilation
209+ * time blowup before they are dropped ot detected. This means mapping all all skolems
210+ * (?n: T) to (?n: Any), and mapping all recursive captures that are not on CapSet to `^`.
211+ * Skolems and capturing types on types other than CapSet are not allowed in a
212+ * @retains annotation anyway, so the underlying type does not matter as long as it is also
213+ * illegal. See i24556.scala and i24556a.scala.
214+ */
215+ private def sanitize (tp : Type )(using Context ): Type = tp match
216+ case SkolemType (_) =>
217+ SkolemType (defn.AnyType )
218+ case tp @ AnnotatedType (parent, ann)
219+ if ann.symbol.isRetainsLike && parent.typeSymbol != defn.Caps_CapSet =>
220+ tp.derivedAnnotatedType(parent, ann.derivedClassAnnotation(defn.RetainsCapAnnot ))
221+ case tp @ OrType (tp1, tp2) =>
222+ tp.derivedOrType(sanitize(tp1), sanitize(tp2))
223+ case _ =>
224+ tp
225+
226+ private def isLambdaParam (t : Type , tl : TermLambda ): Boolean = t match
227+ case TermParamRef (tl1, _) => tl eq tl1
228+ case _ => false
229+
230+ private def refersToLambdaParam (tp : Type , tl : TermLambda )(using Context ): Boolean =
231+ tp.existsPart(isLambdaParam(_, tl), stopAt = StopAt .Static )
232+
166233 abstract class LazyAnnotation extends Annotation {
167234 protected var mySym : Symbol | (Context ?=> Symbol ) | Null
168235 override def symbol (using parentCtx : Context ): Symbol =
@@ -244,7 +311,9 @@ object Annotations {
244311
245312 object Annotation {
246313
247- def apply (tree : Tree ): ConcreteAnnotation = ConcreteAnnotation (tree)
314+ def apply (tree : Tree ): Annotation = tree match
315+ case tree : TypeTree => CompactAnnotation (tree.tpe)
316+ case _ => ConcreteAnnotation (tree)
248317
249318 def apply (cls : ClassSymbol , span : Span )(using Context ): Annotation =
250319 apply(cls, Nil , span)
0 commit comments