@@ -385,34 +385,8 @@ class CheckCaptures extends Recheck, SymTransformer:
385
385
// Only captured references that are visible from the environment
386
386
// should be included.
387
387
val included = cs.filter: c =>
388
- c.stripReach match
389
- case ref : NamedType =>
390
- val refSym = ref.symbol
391
- val refOwner = refSym.owner
392
- val isVisible = isVisibleFromEnv(refOwner, env)
393
- if isVisible && ! ref.isRootCapability then
394
- ref match
395
- case ref : TermRef if ref.prefix `ne` NoPrefix =>
396
- // If c is a path of a class defined outside the environment,
397
- // we check the capture set of its info.
398
- checkSubsetEnv(ref.captureSetOfInfo, env)
399
- case _ =>
400
- if ! isVisible
401
- && (c.isReach || ref.isType)
402
- && (! ccConfig.useSealed || refSym.is(Param ))
403
- && refOwner == env.owner
404
- then
405
- if refSym.hasAnnotation(defn.UnboxAnnot ) then
406
- capt.println(i " exempt: $ref in $refOwner" )
407
- else
408
- // Reach capabilities that go out of scope have to be approximated
409
- // by their underlying capture set, which cannot be universal.
410
- // Reach capabilities of @unboxed parameters are exempted.
411
- val cs = CaptureSet .ofInfo(c)
412
- cs.disallowRootCapability: () =>
413
- report.error(em " Local reach capability $c leaks into capture scope of ${env.ownerString}" , pos)
414
- checkSubset(cs, env.captured, pos, provenance(env))
415
- isVisible
388
+ c.stripReach.pathRoot match
389
+ case ref : NamedType => isVisibleFromEnv(ref.symbol.owner, env)
416
390
case ref : ThisType => isVisibleFromEnv(ref.cls, env)
417
391
case _ => false
418
392
checkSubset(included, env.captured, pos, provenance(env))
@@ -424,48 +398,14 @@ class CheckCaptures extends Recheck, SymTransformer:
424
398
end markFree
425
399
426
400
/** Include references captured by the called method in the current environment stack */
427
- def includeCallCaptures (sym : Symbol , pos : SrcPos )(using Context ): Unit =
428
- if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos)
429
-
430
- private val prefixCalls = util.EqHashSet [GenericApply ]()
431
- private val unboxedArgs = util.EqHashSet [Tree ]()
432
-
433
- def handleCall (meth : Symbol , call : GenericApply , eval : () => Type )(using Context ): Type =
434
- if prefixCalls.remove(call) then return eval()
435
-
436
- val unboxedParamNames =
437
- meth.rawParamss.flatMap: params =>
438
- params.collect:
439
- case param if param.hasAnnotation(defn.UnboxAnnot ) =>
440
- param.name
441
- .toSet
442
-
443
- def markUnboxedArgs (call : GenericApply ): Unit = call.fun.tpe.widen match
444
- case MethodType (pnames) =>
445
- for (pname, arg) <- pnames.lazyZip(call.args) do
446
- if unboxedParamNames.contains(pname) then
447
- unboxedArgs.add(arg)
448
- case _ =>
449
-
450
- def markPrefixCalls (tree : Tree ): Unit = tree match
451
- case tree : GenericApply =>
452
- prefixCalls.add(tree)
453
- markUnboxedArgs(tree)
454
- markPrefixCalls(tree.fun)
455
- case _ =>
456
-
457
- markUnboxedArgs(call)
458
- markPrefixCalls(call.fun)
459
- val res = eval()
460
- includeCallCaptures(meth, call.srcPos)
461
- res
462
- end handleCall
401
+ def includeCallCaptures (sym : Symbol , resType : Type , pos : SrcPos )(using Context ): Unit = resType match
402
+ case _ : MethodOrPoly => // wait until method is fully applied
403
+ case _ =>
404
+ if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos)
463
405
464
406
override def recheckIdent (tree : Ident , pt : Type )(using Context ): Type =
465
407
if tree.symbol.is(Method ) then
466
- if tree.symbol.info.isParameterless then
467
- // there won't be an apply; need to include call captures now
468
- includeCallCaptures(tree.symbol, tree.srcPos)
408
+ includeCallCaptures(tree.symbol, tree.symbol.info, tree.srcPos)
469
409
else if ! tree.symbol.isStatic then
470
410
// debugShowEnvs()
471
411
def addSelects (ref : TermRef , pt : Type ): TermRef = pt match
@@ -570,15 +510,16 @@ class CheckCaptures extends Recheck, SymTransformer:
570
510
tp.derivedCapturingType(forceBox(parent), refs)
571
511
mapArgUsing(forceBox)
572
512
else
573
- handleCall(meth, tree, () => super .recheckApply(tree, pt))
513
+ val res = super .recheckApply(tree, pt)
514
+ includeCallCaptures(meth, res, tree.srcPos)
515
+ res
574
516
end recheckApply
575
517
576
518
protected override
577
519
def recheckArg (arg : Tree , formal : Type )(using Context ): Type =
578
520
val argType = recheck(arg, formal)
579
- if unboxedArgs.contains(arg) then
580
- capt.println(i " charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}" )
581
- markFree(argType.deepCaptureSet, arg.srcPos)
521
+ capt.println(i " charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}" )
522
+ markFree(argType.deepCaptureSet, arg.srcPos)
582
523
argType
583
524
584
525
/** A specialized implementation of the apply rule.
@@ -589,27 +530,18 @@ class CheckCaptures extends Recheck, SymTransformer:
589
530
* ---------------------
590
531
* E |- f(a): Tr^C
591
532
*
592
- * If the function `f` does not have an `@unboxed` parameter, then
593
- * any unboxing it does would be charged to the environment of the function
594
- * so they have to appear in Cq. Since any capabilities of the result of the
595
- * application must already be present in the application, an upper
596
- * approximation of the result capture set is Cq \union Ca, where `Ca`
597
- * is the capture set of the argument.
598
- * If the function `f` does have an `@unboxed` parameter, then it could in addition
599
- * unbox reach capabilities over its formal parameter. Therefore, the approximation
600
- * would be `Cq \union dcs(Ca)` instead.
533
+ * If the type of the function `f` does not mention any formal parameters
534
+ * any capabilities of the result of the application must already be present in
535
+ * the application. So an upper approximation of the result capture set is Cq \union Ca,
536
+ * where `Ca` is the deep capture set of the argument.
601
537
* If the approximation is known to subcapture the declared result Cr, we pick it for C
602
- * otherwise we pick Cr.
538
+ * otherwise we pick Cr. ???
603
539
*/
604
540
protected override
605
541
def recheckApplication (tree : Apply , qualType : Type , funType : MethodType , argTypes : List [Type ])(using Context ): Type =
606
542
val appType = Existential .toCap(super .recheckApplication(tree, qualType, funType, argTypes))
607
543
val qualCaptures = qualType.captureSet
608
- val argCaptures =
609
- for (arg, argType) <- tree.args.lazyZip(argTypes) yield
610
- if unboxedArgs.remove(arg) // need to ensure the remove happens, that's why argCaptures is computed even if not needed.
611
- then argType.deepCaptureSet
612
- else argType.captureSet
544
+ val argCaptures = argTypes.map(_.deepCaptureSet)
613
545
appType match
614
546
case appType @ CapturingType (appType1, refs)
615
547
if qualType.exists
@@ -704,8 +636,10 @@ class CheckCaptures extends Recheck, SymTransformer:
704
636
i " Sealed type variable $pname" , " be instantiated to" ,
705
637
i " This is often caused by a local capability $where\n leaking as part of its result. " ,
706
638
tree.srcPos)
707
- try handleCall(meth, tree, () => Existential .toCap(super .recheckTypeApply(tree, pt)))
708
- finally checkContains(tree)
639
+ val res = Existential .toCap(super .recheckTypeApply(tree, pt))
640
+ includeCallCaptures(meth, res, tree.srcPos)
641
+ checkContains(tree)
642
+ res
709
643
end recheckTypeApply
710
644
711
645
/** Faced with a tree of form `caps.contansImpl[CS, r.type]`, check that `R` is a tracked
@@ -1156,12 +1090,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1156
1090
(erefs /: erefs.elems): (erefs, eref) =>
1157
1091
eref match
1158
1092
case eref : ThisType if isPureContext(ctx.owner, eref.cls) =>
1159
-
1160
- def pathRoot (aref : Type ): Type = aref match
1161
- case aref : NamedType if aref.symbol.owner.isClass => pathRoot(aref.prefix)
1162
- case _ => aref
1163
-
1164
- def isOuterRef (aref : Type ): Boolean = pathRoot(aref) match
1093
+ def isOuterRef (aref : Type ): Boolean = aref.pathRoot match
1165
1094
case aref : NamedType => eref.cls.isProperlyContainedIn(aref.symbol.owner)
1166
1095
case aref : ThisType => eref.cls.isProperlyContainedIn(aref.cls)
1167
1096
case _ => false
@@ -1171,7 +1100,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1171
1100
// Include implicitly added outer references in the capture set of the class of `eref`.
1172
1101
for outerRef <- outerRefs.elems do
1173
1102
if ! erefs.elems.contains(outerRef)
1174
- && ! pathRoot( outerRef) .isInstanceOf [ThisType ]
1103
+ && ! outerRef.pathRoot .isInstanceOf [ThisType ]
1175
1104
// we don't need to add outer ThisTypes as these are anyway added as path
1176
1105
// prefixes at the use site. And this exemption is required since capture sets
1177
1106
// of non-local classes are always empty, so we can't add an outer this to them.
@@ -1328,6 +1257,12 @@ class CheckCaptures extends Recheck, SymTransformer:
1328
1257
1329
1258
/** If actual is a tracked CaptureRef `a` and widened is a capturing type T^C,
1330
1259
* improve `T^C` to `T^{a}`, following the VAR rule of CC.
1260
+ * TODO: We probably should do this also for other top-level occurrences of captures
1261
+ * E.g.
1262
+ * class Foo { def a: C^{io}; val def: C^{async} }
1263
+ * val foo: Foo^{io, async}
1264
+ * Then
1265
+ * foo: Foo { def a: C^{foo}; def b: C^{foo} }^{foo}
1331
1266
*/
1332
1267
private def improveCaptures (widened : Type , actual : Type )(using Context ): Type = actual match
1333
1268
case ref : CaptureRef if ref.isTracked =>
@@ -1388,21 +1323,6 @@ class CheckCaptures extends Recheck, SymTransformer:
1388
1323
! setup.isPreCC(overriding) && ! setup.isPreCC(overridden)
1389
1324
1390
1325
override def checkInheritedTraitParameters : Boolean = false
1391
-
1392
- /** Check that overrides don't change the @unbox status of their parameters */
1393
- override def additionalChecks (member : Symbol , other : Symbol )(using Context ): Unit =
1394
- for
1395
- (params1, params2) <- member.rawParamss.lazyZip(other.rawParamss)
1396
- (param1, param2) <- params1.lazyZip(params2)
1397
- do
1398
- if param1.hasAnnotation(defn.UnboxAnnot ) != param2.hasAnnotation(defn.UnboxAnnot ) then
1399
- report.error(
1400
- OverrideError (
1401
- i " has a parameter ${param1.name} with different @unbox status than the corresponding parameter in the overridden definition " ,
1402
- self, member, other, self.memberInfo(member), self.memberInfo(other)
1403
- ),
1404
- if member.owner == clazz then member.srcPos else clazz.srcPos
1405
- )
1406
1326
end OverridingPairsCheckerCC
1407
1327
1408
1328
def traverse (t : Tree )(using Context ) =
0 commit comments