@@ -222,19 +222,27 @@ extension (tp: Type)
222
222
case tp : SingletonCaptureRef => tp.captureSetOfInfo
223
223
case _ => CaptureSet .ofType(tp, followResult = false )
224
224
225
- /** The deep capture set of a type.
226
- * For singleton capabilities `x` and reach capabilities `x*`, this is `{x*}`, provided
227
- * the underlying capture set resulting from traversing the type is non-empty.
228
- * For other types this is the union of all covariant capture sets embedded
229
- * in the type, as computed by `CaptureSet.ofTypeDeeply`.
225
+ /** The deep capture set of a type. This is by default the union of all
226
+ * covariant capture sets embedded in the widened type, as computed by
227
+ * `CaptureSet.ofTypeDeeply`. If that set is nonempty, and the type is
228
+ * a singleton capability `x` or a reach capability `x*`, the deep capture
229
+ * set can be narrowed to`{x*}`. However, A deep capture set should not be
230
+ * narrowed to a reach capability `x*` if there are elements in the underlying
231
+ * set that live longer than `x`. See `delayedRunops.scala` for a test case.
230
232
*/
231
233
def deepCaptureSet (using Context ): CaptureSet =
232
234
val dcs = CaptureSet .ofTypeDeeply(tp.widen.stripCapturing)
235
+ def reachCanSubsumDcs =
236
+ dcs.isUniversal
237
+ || dcs.elems.forall(c => c.pathOwner.isContainedIn(tp.pathOwner))
233
238
if dcs.isAlwaysEmpty then tp.captureSet
234
239
else tp match
235
- case tp @ ReachCapability (_) => tp.singletonCaptureSet
236
- case tp : SingletonCaptureRef if tp.isTrackableRef => tp.reach.singletonCaptureSet
237
- case _ => tp.captureSet ++ dcs
240
+ case tp @ ReachCapability (_) if reachCanSubsumDcs =>
241
+ tp.singletonCaptureSet
242
+ case tp : SingletonCaptureRef if tp.isTrackableRef && reachCanSubsumDcs =>
243
+ tp.reach.singletonCaptureSet
244
+ case _ =>
245
+ tp.captureSet ++ dcs
238
246
239
247
/** A type capturing `ref` */
240
248
def capturing (ref : CaptureRef )(using Context ): Type =
@@ -277,8 +285,18 @@ extension (tp: Type)
277
285
/** The first element of this path type */
278
286
final def pathRoot (using Context ): Type = tp.dealias match
279
287
case tp1 : NamedType if tp1.symbol.owner.isClass => tp1.prefix.pathRoot
288
+ case tp1 @ ReachCapability (tp2) => tp2.pathRoot
280
289
case _ => tp
281
290
291
+ /** If this part starts with `C.this`, the class `C`.
292
+ * Otherwise, if it starts with a reference `r`, `r`'s owner.
293
+ * Otherwise NoSymbol.
294
+ */
295
+ final def pathOwner (using Context ): Symbol = pathRoot match
296
+ case tp1 : NamedType => tp1.symbol.owner
297
+ case tp1 : ThisType => tp1.cls
298
+ case _ => NoSymbol
299
+
282
300
/** If this is a unboxed capturing type with nonempty capture set, its boxed version.
283
301
* Or, if type is a TypeBounds of capturing types, the version where the bounds are boxed.
284
302
* The identity for all other types.
0 commit comments