@@ -27,32 +27,25 @@ object ccConfig:
2727 */
2828 inline val allowUnsoundMaps = false
2929
30- /** If true, when computing the memberinfo of a refined type created
31- * by addCaptureRefinements take the refineInfo directly without intersecting
32- * with the parent info.
33- */
34- inline val optimizedRefinements = false
35-
3630 /** If enabled, use a special path in recheckClosure for closures
37- * that are eta expansions. This can improve some error messages but
38- * currently leads to unsoundess for handling reach capabilities.
39- * TODO: The unsoundness needs followin up.
31+ * that are eta expansions. This can improve some error messages.
4032 */
41- inline val handleEtaExpansionsSpecially = false
33+ inline val handleEtaExpansionsSpecially = true
4234
43- /** If true, use existential capture set variables */
44- def useExistentials (using Context ) =
45- Feature .sourceVersion.stable.isAtLeast(SourceVersion .`3.5`)
35+ /** Don't require @use for reach capabilities that are accessed
36+ * only in a nested closure. This is unsound without additional
37+ * mitigation measures, as shown by unsound-reach-5.scala.
38+ */
39+ inline val deferredReaches = false
4640
4741 /** If true, use "sealed" as encapsulation mechanism, meaning that we
4842 * check that type variable instantiations don't have `cap` in any of
4943 * their capture sets. This is an alternative of the original restriction
50- * that `cap` can't be boxed or unboxed. It is used in 3.3 and 3.4 but
51- * dropped again in 3.5 .
44+ * that `cap` can't be boxed or unboxed. It is dropped in 3.5 but used
45+ * again in 3.6 .
5246 */
5347 def useSealed (using Context ) =
54- Feature .sourceVersion.stable == SourceVersion .`3.3`
55- || Feature .sourceVersion.stable == SourceVersion .`3.4`
48+ Feature .sourceVersion.stable != SourceVersion .`3.5`
5649end ccConfig
5750
5851
@@ -134,10 +127,6 @@ end CCState
134127def ccState (using Context ) =
135128 Phases .checkCapturesPhase.asInstanceOf [CheckCaptures ].ccState1
136129
137- class NoCommonRoot (rs : Symbol * )(using Context ) extends Exception (
138- i " No common capture root nested in ${rs.mkString(" and " )}"
139- )
140-
141130extension (tree : Tree )
142131
143132 /** Map tree with CaptureRef type to its type,
@@ -221,19 +210,25 @@ extension (tp: Type)
221210 case tp : SingletonCaptureRef => tp.captureSetOfInfo
222211 case _ => CaptureSet .ofType(tp, followResult = false )
223212
224- /** The deep capture set of a type.
225- * For singleton capabilities `x` and reach capabilities `x*`, this is `{x*}`, provided
226- * the underlying capture set resulting from traversing the type is non-empty.
227- * For other types this is the union of all covariant capture sets embedded
228- * in the type, as computed by `CaptureSet.ofTypeDeeply `.
213+ /** The deep capture set of a type. This is by default the union of all
214+ * covariant capture sets embedded in the widened type, as computed by
215+ * `CaptureSet.ofTypeDeeply`. If that set is nonempty, and the type is
216+ * a singleton capability `x` or a reach capability `x*`, the deep capture
217+ * set can be narrowed to`{x*} `.
229218 */
230- def deepCaptureSet (using Context ): CaptureSet =
231- val dcs = CaptureSet .ofTypeDeeply(tp)
232- if dcs.isAlwaysEmpty then dcs
219+ def deepCaptureSet (includeTypevars : Boolean )( using Context ): CaptureSet =
220+ val dcs = CaptureSet .ofTypeDeeply(tp.widen.stripCapturing, includeTypevars )
221+ if dcs.isAlwaysEmpty then tp.captureSet
233222 else tp match
234- case tp @ ReachCapability (_) => tp.singletonCaptureSet
235- case tp : SingletonCaptureRef => tp.reach.singletonCaptureSet
236- case _ => dcs
223+ case tp @ ReachCapability (_) =>
224+ tp.singletonCaptureSet
225+ case tp : SingletonCaptureRef if tp.isTrackableRef =>
226+ tp.reach.singletonCaptureSet
227+ case _ =>
228+ tp.captureSet ++ dcs
229+
230+ def deepCaptureSet (using Context ): CaptureSet =
231+ deepCaptureSet(includeTypevars = false )
237232
238233 /** A type capturing `ref` */
239234 def capturing (ref : CaptureRef )(using Context ): Type =
@@ -273,6 +268,29 @@ extension (tp: Type)
273268 case _ =>
274269 tp
275270
271+ /** The first element of this path type */
272+ final def pathRoot (using Context ): Type = tp.dealias match
273+ case tp1 : NamedType if tp1.symbol.owner.isClass => tp1.prefix.pathRoot
274+ case tp1 => tp1
275+
276+ /** If this part starts with `C.this`, the class `C`.
277+ * Otherwise, if it starts with a reference `r`, `r`'s owner.
278+ * Otherwise NoSymbol.
279+ */
280+ final def pathOwner (using Context ): Symbol = pathRoot match
281+ case tp1 : NamedType => tp1.symbol.owner
282+ case tp1 : ThisType => tp1.cls
283+ case _ => NoSymbol
284+
285+ final def isParamPath (using Context ): Boolean = tp.dealias match
286+ case tp1 : NamedType =>
287+ tp1.prefix match
288+ case _ : ThisType | NoPrefix =>
289+ tp1.symbol.is(Param ) || tp1.symbol.is(ParamAccessor )
290+ case prefix => prefix.isParamPath
291+ case _ : ParamRef => true
292+ case _ => false
293+
276294 /** If this is a unboxed capturing type with nonempty capture set, its boxed version.
277295 * Or, if type is a TypeBounds of capturing types, the version where the bounds are boxed.
278296 * The identity for all other types.
@@ -456,53 +474,35 @@ extension (tp: Type)
456474 * occurrences of cap are allowed in instance types of type variables.
457475 */
458476 def withReachCaptures (ref : Type )(using Context ): Type =
459- class CheckContraCaps extends TypeTraverser :
460- var ok = true
461- def traverse (t : Type ): Unit =
462- if ok then
463- t.dealias match
464- case CapturingType (_, cs) if cs.isUniversal && variance <= 0 =>
465- ok = false
466- case _ =>
467- traverseChildren(t)
468- end CheckContraCaps
469-
470477 object narrowCaps extends TypeMap :
471- /** Has the variance been flipped at this point? */
472- private var isFlipped : Boolean = false
473-
478+ var change = false
474479 def apply (t : Type ) =
475- val saved = isFlipped
476- try
477- if variance <= 0 then isFlipped = true
478- t.dealias match
479- case t1 @ CapturingType (p, cs) if cs.isUniversal && ! isFlipped =>
480- t1.derivedCapturingType(apply(p), ref.reach.singletonCaptureSet)
481- case t1 @ FunctionOrMethod (args, res @ Existential (_, _))
482- if args.forall(_.isAlwaysPure) =>
483- // Also map existentials in results to reach capabilities if all
484- // preceding arguments are known to be always pure
485- apply(t1.derivedFunctionOrMethod(args, Existential .toCap(res)))
486- case Existential (_, _) =>
487- t
488- case _ => t match
489- case t @ CapturingType (p, cs) =>
490- t.derivedCapturingType(apply(p), cs) // don't map capture set variables
491- case t =>
492- mapOver(t)
493- finally isFlipped = saved
480+ if variance <= 0 then t
481+ else t.dealiasKeepAnnots match
482+ case t @ CapturingType (p, cs) if cs.isUniversal =>
483+ change = true
484+ t.derivedCapturingType(apply(p), ref.reach.singletonCaptureSet)
485+ case t @ AnnotatedType (parent, ann) =>
486+ // Don't map annotations, which includes capture sets
487+ t.derivedAnnotatedType(this (parent), ann)
488+ case t @ FunctionOrMethod (args, res @ Existential (_, _))
489+ if args.forall(_.isAlwaysPure) =>
490+ // Also map existentials in results to reach capabilities if all
491+ // preceding arguments are known to be always pure
492+ apply(t.derivedFunctionOrMethod(args, Existential .toCap(res)))
493+ case Existential (_, _) =>
494+ t
495+ case _ =>
496+ mapOver(t)
494497 end narrowCaps
495498
496499 ref match
497500 case ref : CaptureRef if ref.isTrackableRef =>
498- val checker = new CheckContraCaps
499- if ! ccConfig.useExistentials then checker.traverse(tp)
500- if checker.ok then
501- val tp1 = narrowCaps(tp)
502- if tp1 ne tp then capt.println(i " narrow $tp of $ref to $tp1" )
501+ val tp1 = narrowCaps(tp)
502+ if narrowCaps.change then
503+ capt.println(i " narrow $tp of $ref to $tp1" )
503504 tp1
504505 else
505- capt.println(i " cannot narrow $tp of $ref" )
506506 tp
507507 case _ =>
508508 tp
@@ -588,24 +588,33 @@ extension (sym: Symbol)
588588 case _ => false
589589 containsEnclTypeParam(sym.info.finalResultType)
590590 && ! sym.allowsRootCapture
591- && sym != defn.Caps_unsafeBox
592- && sym != defn.Caps_unsafeUnbox
593591 && ! defn.isPolymorphicAfterErasure(sym)
594592 && ! defn.isTypeTestOrCast(sym)
595593
594+ /** It's a parameter accessor that is not annotated @constructorOnly or @uncheckedCaptures */
596595 def isRefiningParamAccessor (using Context ): Boolean =
597596 sym.is(ParamAccessor )
598597 && {
599- val param = sym.owner.primaryConstructor.paramSymss
600- .nestedFind(_.name == sym.name)
601- .getOrElse(NoSymbol )
598+ val param = sym.owner.primaryConstructor.paramNamed(sym.name)
602599 ! param.hasAnnotation(defn.ConstructorOnlyAnnot )
603600 && ! param.hasAnnotation(defn.UntrackedCapturesAnnot )
604601 }
605602
606603 def hasTrackedParts (using Context ): Boolean =
607604 ! CaptureSet .ofTypeDeeply(sym.info).isAlwaysEmpty
608605
606+ /** `sym` is annotated @use or it is a type parameter with a matching
607+ * @use-annotated term parameter that contains `sym` in its deep capture set.
608+ */
609+ def isUseParam (using Context ): Boolean =
610+ sym.hasAnnotation(defn.UseAnnot )
611+ || sym.is(TypeParam )
612+ && sym.owner.rawParamss.nestedExists: param =>
613+ param.is(TermParam ) && param.hasAnnotation(defn.UseAnnot )
614+ && param.info.deepCaptureSet.elems.exists:
615+ case c : TypeRef => c.symbol == sym
616+ case _ => false
617+
609618extension (tp : AnnotatedType )
610619 /** Is this a boxed capturing type? */
611620 def isBoxed (using Context ): Boolean = tp.annot match
@@ -639,8 +648,8 @@ object CapsOfApply:
639648class AnnotatedCapability (annot : Context ?=> ClassSymbol ):
640649 def apply (tp : Type )(using Context ) =
641650 AnnotatedType (tp, Annotation (annot, util.Spans .NoSpan ))
642- def unapply (tree : AnnotatedType )(using Context ): Option [SingletonCaptureRef ] = tree match
643- case AnnotatedType (parent : SingletonCaptureRef , ann) if ann.symbol == annot => Some (parent)
651+ def unapply (tree : AnnotatedType )(using Context ): Option [CaptureRef ] = tree match
652+ case AnnotatedType (parent : CaptureRef , ann) if ann.symbol == annot => Some (parent)
644653 case _ => None
645654
646655/** An extractor for `ref @annotation.internal.reachCapability`, which is used to express
0 commit comments