@@ -424,61 +424,28 @@ class CheckCaptures extends Recheck, SymTransformer:
424
424
end markFree
425
425
426
426
/** 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
427
+ def includeCallCaptures (sym : Symbol , resType : Type , pos : SrcPos )(using Context ): Unit = resType match
428
+ case _ : MethodOrPoly => // wait until method is fully applied
429
+ case _ =>
430
+ if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos)
463
431
464
432
override def recheckIdent (tree : Ident , pt : Type )(using Context ): Type =
465
- 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)
469
- else if ! tree.symbol.isStatic then
433
+ val sym = tree.symbol
434
+ if sym.is(Method ) then
435
+ includeCallCaptures(sym, sym.info, tree.srcPos)
436
+ else if ! sym.isStatic then
470
437
// debugShowEnvs()
471
438
def addSelects (ref : TermRef , pt : Type ): TermRef = pt match
472
439
case pt : PathSelectionProto if ref.isTracked =>
473
440
// if `ref` is not tracked then the selection could not give anything new
474
441
// class SerializationProxy in stdlib-cc/../LazyListIterable.scala has an example where this matters.
475
442
addSelects(ref.select(pt.sym).asInstanceOf [TermRef ], pt.pt)
476
443
case _ => ref
477
- val ref = tree.symbol .termRef
444
+ val ref = sym .termRef
478
445
val pathRef = addSelects(ref, pt)
479
446
// if pathRef ne ref then
480
447
// println(i"add selects $ref --> $pathRef")
481
- markFree(tree.symbol , if false then ref else pathRef, tree.srcPos)
448
+ markFree(sym , if false then ref else pathRef, tree.srcPos)
482
449
super .recheckIdent(tree, pt)
483
450
484
451
override def selectionProto (tree : Select , pt : Type )(using Context ): Type =
@@ -536,6 +503,16 @@ class CheckCaptures extends Recheck, SymTransformer:
536
503
selType
537
504
}// .showing(i"recheck sel $tree, $qualType = $result")
538
505
506
+ /** Copy all @use annotations on method parameter symbols to the corresponding paramInfo types.
507
+ */
508
+ override def prepareFunction (funtpe : MethodType , meth : Symbol )(using Context ): MethodType =
509
+ val paramInfosWithUses = funtpe.paramInfos.zipWithConserve(funtpe.paramNames): (formal, pname) =>
510
+ val paramOpt = meth.rawParamss.nestedFind(_.name == pname)
511
+ paramOpt.flatMap(_.getAnnotation(defn.UnboxAnnot )) match
512
+ case Some (ann) => AnnotatedType (formal, ann)
513
+ case _ => formal
514
+ funtpe.derivedLambdaType(paramInfos = paramInfosWithUses)
515
+
539
516
override def recheckApply (tree : Apply , pt : Type )(using Context ): Type =
540
517
val meth = tree.fun.symbol
541
518
@@ -570,15 +547,19 @@ class CheckCaptures extends Recheck, SymTransformer:
570
547
tp.derivedCapturingType(forceBox(parent), refs)
571
548
mapArgUsing(forceBox)
572
549
else
573
- handleCall(meth, tree, () => super .recheckApply(tree, pt))
550
+ val res = super .recheckApply(tree, pt)
551
+ includeCallCaptures(meth, res, tree.srcPos)
552
+ res
574
553
end recheckApply
575
554
576
555
protected override
577
556
def recheckArg (arg : Tree , formal : Type )(using Context ): Type =
578
557
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)
558
+ formal match
559
+ case AnnotatedType (formal1, ann) if ann.symbol == defn.UnboxAnnot =>
560
+ capt.println(i " charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}" )
561
+ markFree(argType.deepCaptureSet, arg.srcPos)
562
+ case _ =>
582
563
argType
583
564
584
565
/** A specialized implementation of the apply rule.
@@ -606,10 +587,10 @@ class CheckCaptures extends Recheck, SymTransformer:
606
587
val appType = Existential .toCap(super .recheckApplication(tree, qualType, funType, argTypes))
607
588
val qualCaptures = qualType.captureSet
608
589
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
590
+ for (argType, formal ) <- argTypes. lazyZip(funType.paramInfos ) yield
591
+ formal match
592
+ case AnnotatedType (_, ann) if ann.symbol == defn. UnboxAnnot => argType.deepCaptureSet
593
+ case _ => argType.captureSet
613
594
appType match
614
595
case appType @ CapturingType (appType1, refs)
615
596
if qualType.exists
@@ -704,8 +685,10 @@ class CheckCaptures extends Recheck, SymTransformer:
704
685
i " Sealed type variable $pname" , " be instantiated to" ,
705
686
i " This is often caused by a local capability $where\n leaking as part of its result. " ,
706
687
tree.srcPos)
707
- try handleCall(meth, tree, () => Existential .toCap(super .recheckTypeApply(tree, pt)))
708
- finally checkContains(tree)
688
+ val res = Existential .toCap(super .recheckTypeApply(tree, pt))
689
+ includeCallCaptures(meth, res, tree.srcPos)
690
+ checkContains(tree)
691
+ res
709
692
end recheckTypeApply
710
693
711
694
/** Faced with a tree of form `caps.contansImpl[CS, r.type]`, check that `R` is a tracked
@@ -1156,12 +1139,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1156
1139
(erefs /: erefs.elems): (erefs, eref) =>
1157
1140
eref match
1158
1141
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
1142
+ def isOuterRef (aref : Type ): Boolean = aref.pathRoot match
1165
1143
case aref : NamedType => eref.cls.isProperlyContainedIn(aref.symbol.owner)
1166
1144
case aref : ThisType => eref.cls.isProperlyContainedIn(aref.cls)
1167
1145
case _ => false
@@ -1171,7 +1149,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1171
1149
// Include implicitly added outer references in the capture set of the class of `eref`.
1172
1150
for outerRef <- outerRefs.elems do
1173
1151
if ! erefs.elems.contains(outerRef)
1174
- && ! pathRoot( outerRef) .isInstanceOf [ThisType ]
1152
+ && ! outerRef.pathRoot .isInstanceOf [ThisType ]
1175
1153
// we don't need to add outer ThisTypes as these are anyway added as path
1176
1154
// prefixes at the use site. And this exemption is required since capture sets
1177
1155
// of non-local classes are always empty, so we can't add an outer this to them.
@@ -1328,6 +1306,12 @@ class CheckCaptures extends Recheck, SymTransformer:
1328
1306
1329
1307
/** If actual is a tracked CaptureRef `a` and widened is a capturing type T^C,
1330
1308
* improve `T^C` to `T^{a}`, following the VAR rule of CC.
1309
+ * TODO: We probably should do this also for other top-level occurrences of captures
1310
+ * E.g.
1311
+ * class Foo { def a: C^{io}; val def: C^{async} }
1312
+ * val foo: Foo^{io, async}
1313
+ * Then
1314
+ * foo: Foo { def a: C^{foo}; def b: C^{foo} }^{foo}
1331
1315
*/
1332
1316
private def improveCaptures (widened : Type , actual : Type )(using Context ): Type = actual match
1333
1317
case ref : CaptureRef if ref.isTracked =>
0 commit comments