@@ -62,6 +62,9 @@ object CheckCaptures:
62
62
val res = cur
63
63
cur = cur.outer
64
64
res
65
+
66
+ def ownerString (using Context ): String =
67
+ if owner.isAnonymousFunction then " enclosing function" else owner.show
65
68
end Env
66
69
67
70
/** Similar normal substParams, but this is an approximating type map that
@@ -386,21 +389,68 @@ class CheckCaptures extends Recheck, SymTransformer:
386
389
val included = cs.filter: c =>
387
390
c.stripReach match
388
391
case ref : TermRef =>
389
- val isVisible = isVisibleFromEnv(ref.symbol.owner)
390
- if ! isVisible && c.isReach then
392
+ // if c.isReach then println(i"REACH $c in ${env.owner}")
393
+ // assert(!env.owner.isAnonymousFunction)
394
+ val refSym = ref.symbol
395
+ val refOwner = refSym.owner
396
+ val isVisible = isVisibleFromEnv(refOwner)
397
+ if ! isVisible && c.isReach && refSym.is(Param ) && refOwner == env.owner then
398
+ if refSym.hasAnnotation(defn.UnboxedAnnot ) then
399
+ capt.println(i " exempt: $ref in $refOwner" )
400
+ else
391
401
// Reach capabilities that go out of scope have to be approximated
392
- // by their underlyiong capture set. See i20503.scala.
393
- checkSubset(CaptureSet .ofInfo(c), env.captured, pos, provenance(env))
402
+ // by their underlying capture set, which cannot be universal.
403
+ // Reach capabilities of @unboxed parameters are exempted.
404
+ val cs = CaptureSet .ofInfo(c)
405
+ if ccConfig.useUnboxedParams then
406
+ cs.disallowRootCapability: () =>
407
+ report.error(em " Local reach capability $c leaks into capture scope of ${env.ownerString}" , pos)
408
+ checkSubset(cs, env.captured, pos, provenance(env))
394
409
isVisible
395
410
case ref : ThisType => isVisibleFromEnv(ref.cls)
396
411
case _ => false
397
- capt.println(i " Include call or box capture $included from $cs in ${env.owner}" )
398
412
checkSubset(included, env.captured, pos, provenance(env))
413
+ capt.println(i " Include call or box capture $included from $cs in ${env.owner} --> ${env.captured}" )
414
+ end markFree
399
415
400
416
/** Include references captured by the called method in the current environment stack */
401
417
def includeCallCaptures (sym : Symbol , pos : SrcPos )(using Context ): Unit =
402
418
if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos)
403
419
420
+ private val prefixCalls = util.EqHashSet [GenericApply ]()
421
+ private val unboxedArgs = util.EqHashSet [Tree ]()
422
+
423
+ def handleCall (meth : Symbol , call : GenericApply , eval : () => Type )(using Context ): Type =
424
+ if prefixCalls.remove(call) then return eval()
425
+
426
+ val unboxedParamNames =
427
+ meth.rawParamss.flatMap: params =>
428
+ params.collect:
429
+ case param if param.hasAnnotation(defn.UnboxedAnnot ) =>
430
+ param.name
431
+ .toSet
432
+
433
+ def markUnboxedArgs (call : GenericApply ): Unit = call.fun.tpe.widen match
434
+ case MethodType (pnames) =>
435
+ for (pname, arg) <- pnames.lazyZip(call.args) do
436
+ if unboxedParamNames.contains(pname) then
437
+ unboxedArgs.add(arg)
438
+ case _ =>
439
+
440
+ def markPrefixCalls (tree : Tree ): Unit = tree match
441
+ case tree : GenericApply =>
442
+ prefixCalls.add(tree)
443
+ markUnboxedArgs(tree)
444
+ markPrefixCalls(tree.fun)
445
+ case _ =>
446
+
447
+ markUnboxedArgs(call)
448
+ markPrefixCalls(call.fun)
449
+ val res = eval()
450
+ includeCallCaptures(meth, call.srcPos)
451
+ res
452
+ end handleCall
453
+
404
454
override def recheckIdent (tree : Ident , pt : Type )(using Context ): Type =
405
455
if tree.symbol.is(Method ) then
406
456
if tree.symbol.info.isParameterless then
@@ -470,7 +520,6 @@ class CheckCaptures extends Recheck, SymTransformer:
470
520
*/
471
521
override def recheckApply (tree : Apply , pt : Type )(using Context ): Type =
472
522
val meth = tree.fun.symbol
473
- includeCallCaptures(meth, tree.srcPos)
474
523
475
524
// Unsafe box/unbox handlng, only for versions < 3.3
476
525
def mapArgUsing (f : Type => Type ) =
@@ -503,7 +552,7 @@ class CheckCaptures extends Recheck, SymTransformer:
503
552
tp.derivedCapturingType(forceBox(parent), refs)
504
553
mapArgUsing(forceBox)
505
554
else
506
- Existential .toCap(super .recheckApply(tree, pt)) match
555
+ handleCall(meth, tree, () => Existential .toCap(super .recheckApply(tree, pt) )) match
507
556
case appType @ CapturingType (appType1, refs) =>
508
557
tree.fun match
509
558
case Select (qual, _)
@@ -521,6 +570,13 @@ class CheckCaptures extends Recheck, SymTransformer:
521
570
case appType => appType
522
571
end recheckApply
523
572
573
+ override def recheckArg (arg : Tree , formal : Type )(using Context ): Type =
574
+ val argType = recheck(arg, formal)
575
+ if unboxedArgs.remove(arg) && ccConfig.useUnboxedParams then
576
+ capt.println(i " charging deep capture set of $arg: ${argType} = ${CaptureSet .deepCaptureSet(argType)}" )
577
+ markFree(CaptureSet .deepCaptureSet(argType), arg.srcPos)
578
+ argType
579
+
524
580
private def isDistinct (xs : List [Type ]): Boolean = xs match
525
581
case x :: xs1 => xs1.isEmpty || ! xs1.contains(x) && isDistinct(xs1)
526
582
case Nil => true
@@ -589,20 +645,21 @@ class CheckCaptures extends Recheck, SymTransformer:
589
645
end instantiate
590
646
591
647
override def recheckTypeApply (tree : TypeApply , pt : Type )(using Context ): Type =
648
+ val meth = tree.symbol
592
649
if ccConfig.useSealed then
593
650
val TypeApply (fn, args) = tree
594
651
val polyType = atPhase(thisPhase.prev):
595
652
fn.tpe.widen.asInstanceOf [TypeLambda ]
596
653
def isExempt (sym : Symbol ) =
597
654
sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
598
655
for case (arg : TypeTree , formal, pname) <- args.lazyZip(polyType.paramRefs).lazyZip((polyType.paramNames)) do
599
- if ! isExempt(tree.symbol ) then
600
- def where = if fn.symbol. exists then i " in an argument of ${fn.symbol} " else " "
656
+ if ! isExempt(meth ) then
657
+ def where = if meth. exists then i " in an argument of $meth " else " "
601
658
disallowRootCapabilitiesIn(arg.knownType, NoSymbol ,
602
659
i " Sealed type variable $pname" , " be instantiated to" ,
603
660
i " This is often caused by a local capability $where\n leaking as part of its result. " ,
604
661
tree.srcPos)
605
- Existential .toCap(super .recheckTypeApply(tree, pt))
662
+ handleCall(meth, tree, () => Existential .toCap(super .recheckTypeApply(tree, pt) ))
606
663
607
664
override def recheckBlock (tree : Block , pt : Type )(using Context ): Type =
608
665
inNestedLevel(super .recheckBlock(tree, pt))
0 commit comments