@@ -18,7 +18,7 @@ import util.{SimpleIdentitySet, EqHashMap, EqHashSet, SrcPos, Property}
18
18
import transform .{Recheck , PreRecheck , CapturedVars }
19
19
import Recheck .*
20
20
import scala .collection .mutable
21
- import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap , CompareResult , VarState }
21
+ import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap , CompareResult }
22
22
import CCState .*
23
23
import StdNames .nme
24
24
import NameKinds .{DefaultGetterName , WildcardParamName , UniqueNameKind }
@@ -253,6 +253,10 @@ object CheckCaptures:
253
253
* the type of the formal paremeter corresponding to the argument.
254
254
*/
255
255
def formalType : Type
256
+
257
+ /** The "use set", i.e. the capture set marked as free at this node. */
258
+ def markedFree : CaptureSet
259
+
256
260
end CheckerAPI
257
261
258
262
class CheckCaptures extends Recheck , SymTransformer :
@@ -298,9 +302,12 @@ class CheckCaptures extends Recheck, SymTransformer:
298
302
*/
299
303
private val sepCheckFormals = util.EqHashMap [Tree , Type ]()
300
304
305
+ private val usedSet = util.EqHashMap [Tree , CaptureSet ]()
306
+
301
307
extension [T <: Tree ](tree : T )
302
308
def needsSepCheck : Boolean = sepCheckFormals.contains(tree)
303
309
def formalType : Type = sepCheckFormals.getOrElse(tree, NoType )
310
+ def markedFree = usedSet.getOrElse(tree, CaptureSet .empty)
304
311
305
312
/** Instantiate capture set variables appearing contra-variantly to their
306
313
* upper approximation.
@@ -404,17 +411,17 @@ class CheckCaptures extends Recheck, SymTransformer:
404
411
/** Include `sym` in the capture sets of all enclosing environments nested in the
405
412
* the environment in which `sym` is defined.
406
413
*/
407
- def markFree (sym : Symbol , pos : SrcPos )(using Context ): Unit =
408
- markFree(sym, sym.termRef, pos )
414
+ def markFree (sym : Symbol , tree : Tree )(using Context ): Unit =
415
+ markFree(sym, sym.termRef, tree )
409
416
410
- def markFree (sym : Symbol , ref : CaptureRef , pos : SrcPos )(using Context ): Unit =
411
- if sym.exists && ref.isTracked then markFree(ref.captureSet, pos )
417
+ def markFree (sym : Symbol , ref : CaptureRef , tree : Tree )(using Context ): Unit =
418
+ if sym.exists && ref.isTracked then markFree(ref.captureSet, tree )
412
419
413
420
/** Make sure the (projected) `cs` is a subset of the capture sets of all enclosing
414
421
* environments. At each stage, only include references from `cs` that are outside
415
422
* the environment's owner
416
423
*/
417
- def markFree (cs : CaptureSet , pos : SrcPos )(using Context ): Unit =
424
+ def markFree (cs : CaptureSet , tree : Tree )(using Context ): Unit =
418
425
// A captured reference with the symbol `sym` is visible from the environment
419
426
// if `sym` is not defined inside the owner of the environment.
420
427
inline def isVisibleFromEnv (sym : Symbol , env : Env ) =
@@ -436,7 +443,7 @@ class CheckCaptures extends Recheck, SymTransformer:
436
443
val what = if ref.isType then " Capture set parameter" else " Local reach capability"
437
444
report.error(
438
445
em """ $what $c leaks into capture scope of ${env.ownerString}.
439
- |To allow this, the ${ref.symbol} should be declared with a @use annotation """ , pos )
446
+ |To allow this, the ${ref.symbol} should be declared with a @use annotation """ , tree.srcPos )
440
447
case _ =>
441
448
442
449
/** Avoid locally defined capability by charging the underlying type
@@ -456,7 +463,7 @@ class CheckCaptures extends Recheck, SymTransformer:
456
463
CaptureSet .ofType(c.widen, followResult = false )
457
464
capt.println(i " Widen reach $c to $underlying in ${env.owner}" )
458
465
underlying.disallowRootCapability: () =>
459
- report.error(em " Local capability $c in ${env.ownerString} cannot have `cap` as underlying capture set " , pos )
466
+ report.error(em " Local capability $c in ${env.ownerString} cannot have `cap` as underlying capture set " , tree.srcPos )
460
467
recur(underlying, env, lastEnv)
461
468
462
469
/** Avoid locally defined capability if it is a reach capability or capture set
@@ -479,7 +486,7 @@ class CheckCaptures extends Recheck, SymTransformer:
479
486
val underlying = CaptureSet .ofTypeDeeply(c1.widen)
480
487
capt.println(i " Widen reach $c to $underlying in ${env.owner}" )
481
488
underlying.disallowRootCapability: () =>
482
- report.error(em " Local reach capability $c leaks into capture scope of ${env.ownerString}" , pos )
489
+ report.error(em " Local reach capability $c leaks into capture scope of ${env.ownerString}" , tree.srcPos )
483
490
recur(underlying, env, null )
484
491
case c : TypeRef if c.isParamPath =>
485
492
checkUseDeclared(c, env, null )
@@ -496,22 +503,23 @@ class CheckCaptures extends Recheck, SymTransformer:
496
503
then avoidLocalCapability(c, env, lastEnv)
497
504
else avoidLocalReachCapability(c, env)
498
505
isVisible
499
- checkSubset(included, env.captured, pos , provenance(env))
506
+ checkSubset(included, env.captured, tree.srcPos , provenance(env))
500
507
capt.println(i " Include call or box capture $included from $cs in ${env.owner} --> ${env.captured}" )
501
508
if ! isOfNestedMethod(env) then
502
509
recur(included, nextEnvToCharge(env, ! _.owner.isStaticOwner), env)
503
510
// Don't propagate out of methods inside terms. The use set of these methods
504
511
// will be charged when that method is called.
505
512
506
513
recur(cs, curEnv, null )
514
+ usedSet(tree) = tree.markedFree ++ cs
507
515
end markFree
508
516
509
517
/** Include references captured by the called method in the current environment stack */
510
- def includeCallCaptures (sym : Symbol , resType : Type , pos : SrcPos )(using Context ): Unit = resType match
518
+ def includeCallCaptures (sym : Symbol , resType : Type , tree : Tree )(using Context ): Unit = resType match
511
519
case _ : MethodOrPoly => // wait until method is fully applied
512
520
case _ =>
513
521
if sym.exists then
514
- if curEnv.isOpen then markFree(capturedVars(sym), pos )
522
+ if curEnv.isOpen then markFree(capturedVars(sym), tree )
515
523
516
524
/** Under the sealed policy, disallow the root capability in type arguments.
517
525
* Type arguments come either from a TypeApply node or from an AppliedType
@@ -535,23 +543,23 @@ class CheckCaptures extends Recheck, SymTransformer:
535
543
536
544
for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
537
545
def where = if sym.exists then i " in an argument of $sym" else " "
538
- val (addendum, pos ) =
546
+ val (addendum, errTree ) =
539
547
if arg.isInferred
540
- then (" \n This is often caused by a local capability$where\n leaking as part of its result." , fn.srcPos )
541
- else if arg.span.exists then (" " , arg.srcPos )
542
- else (" " , fn.srcPos )
548
+ then (" \n This is often caused by a local capability$where\n leaking as part of its result." , fn)
549
+ else if arg.span.exists then (" " , arg)
550
+ else (" " , fn)
543
551
disallowRootCapabilitiesIn(arg.nuType, NoSymbol ,
544
- i " Type variable $pname of $sym" , " be instantiated to" , addendum, pos )
552
+ i " Type variable $pname of $sym" , " be instantiated to" , addendum, errTree.srcPos )
545
553
546
554
val param = fn.symbol.paramNamed(pname)
547
- if param.isUseParam then markFree(arg.nuType.deepCaptureSet, pos )
555
+ if param.isUseParam then markFree(arg.nuType.deepCaptureSet, errTree )
548
556
end disallowCapInTypeArgs
549
557
550
558
override def recheckIdent (tree : Ident , pt : Type )(using Context ): Type =
551
559
val sym = tree.symbol
552
560
if sym.is(Method ) then
553
561
// If ident refers to a parameterless method, charge its cv to the environment
554
- includeCallCaptures(sym, sym.info, tree.srcPos )
562
+ includeCallCaptures(sym, sym.info, tree)
555
563
else if ! sym.isStatic then
556
564
// Otherwise charge its symbol, but add all selections implied by the e
557
565
// expected type `pt`.
@@ -569,7 +577,7 @@ class CheckCaptures extends Recheck, SymTransformer:
569
577
var pathRef : CaptureRef = addSelects(sym.termRef, pt)
570
578
if pathRef.derivesFrom(defn.Caps_Mutable ) && pt.isValueType && ! pt.isMutableType then
571
579
pathRef = pathRef.readOnly
572
- markFree(sym, pathRef, tree.srcPos )
580
+ markFree(sym, pathRef, tree)
573
581
super .recheckIdent(tree, pt)
574
582
575
583
/** The expected type for the qualifier of a selection. If the selection
@@ -668,7 +676,7 @@ class CheckCaptures extends Recheck, SymTransformer:
668
676
super .recheckFinish(argType, tree, pt)
669
677
else
670
678
val res = super .recheckApply(tree, pt)
671
- includeCallCaptures(meth, res, tree.srcPos )
679
+ includeCallCaptures(meth, res, tree)
672
680
res
673
681
674
682
/** Recheck argument, and, if formal parameter carries a `@use`,
@@ -681,7 +689,7 @@ class CheckCaptures extends Recheck, SymTransformer:
681
689
if formal.hasUseAnnot then
682
690
// The @use annotation is added to `formal` by `prepareFunction`
683
691
capt.println(i " charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}" )
684
- markFree(argType.deepCaptureSet, arg.srcPos )
692
+ markFree(argType.deepCaptureSet, arg)
685
693
if formal.containsCap then
686
694
sepCheckFormals(arg) = freshenedFormal
687
695
argType
@@ -815,7 +823,7 @@ class CheckCaptures extends Recheck, SymTransformer:
815
823
case fun => fun.symbol
816
824
disallowCapInTypeArgs(tree.fun, meth, tree.args)
817
825
val res = Existential .toCap(super .recheckTypeApply(tree, pt))
818
- includeCallCaptures(tree.symbol, res, tree.srcPos )
826
+ includeCallCaptures(tree.symbol, res, tree)
819
827
checkContains(tree)
820
828
res
821
829
end recheckTypeApply
@@ -1092,7 +1100,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1092
1100
case AnnotatedType (_, annot) if annot.symbol == defn.RequiresCapabilityAnnot =>
1093
1101
annot.tree match
1094
1102
case Apply (_, cap :: Nil ) =>
1095
- markFree(cap.symbol, tree.srcPos )
1103
+ markFree(cap.symbol, tree)
1096
1104
case _ =>
1097
1105
case _ =>
1098
1106
super .recheckTyped(tree)
@@ -1147,7 +1155,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1147
1155
super .recheck(tree, pt)
1148
1156
finally curEnv = saved
1149
1157
if tree.isTerm && ! pt.isBoxedCapturing && pt != LhsProto then
1150
- markFree(res.boxedCaptureSet, tree.srcPos )
1158
+ markFree(res.boxedCaptureSet, tree)
1151
1159
res
1152
1160
end recheck
1153
1161
@@ -1214,7 +1222,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1214
1222
override def checkConformsExpr (actual : Type , expected : Type , tree : Tree , addenda : Addenda )(using Context ): Type =
1215
1223
var expected1 = alignDependentFunction(expected, actual.stripCapturing)
1216
1224
val boxErrors = new mutable.ListBuffer [Message ]
1217
- val actualBoxed = adapt(actual, expected1, tree.srcPos , boxErrors)
1225
+ val actualBoxed = adapt(actual, expected1, tree, boxErrors)
1218
1226
// println(i"check conforms $actualBoxed <<< $expected1")
1219
1227
1220
1228
if actualBoxed eq actual then
@@ -1334,7 +1342,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1334
1342
*
1335
1343
* @param alwaysConst always make capture set variables constant after adaptation
1336
1344
*/
1337
- def adaptBoxed (actual : Type , expected : Type , pos : SrcPos , covariant : Boolean , alwaysConst : Boolean , boxErrors : BoxErrors )(using Context ): Type =
1345
+ def adaptBoxed (actual : Type , expected : Type , tree : Tree , covariant : Boolean , alwaysConst : Boolean , boxErrors : BoxErrors )(using Context ): Type =
1338
1346
1339
1347
def recur (actual : Type , expected : Type , covariant : Boolean ): Type =
1340
1348
@@ -1401,7 +1409,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1401
1409
if ! leaked.subCaptures(cs).isOK then
1402
1410
report.error(
1403
1411
em """ $expected cannot be box-converted to ${actual.capturing(leaked)}
1404
- |since the additional capture set $leaked resulted from box conversion is not allowed in $actual""" , pos )
1412
+ |since the additional capture set $leaked resulted from box conversion is not allowed in $actual""" , tree.srcPos )
1405
1413
cs
1406
1414
1407
1415
def adaptedType (resultBoxed : Boolean ) =
@@ -1433,11 +1441,11 @@ class CheckCaptures extends Recheck, SymTransformer:
1433
1441
return actual
1434
1442
// Disallow future addition of `cap` to `criticalSet`.
1435
1443
criticalSet.disallowRootCapability: () =>
1436
- report.error(msg, pos )
1444
+ report.error(msg, tree.srcPos )
1437
1445
1438
1446
if ! insertBox then // we are unboxing
1439
1447
// debugShowEnvs()
1440
- markFree(criticalSet, pos )
1448
+ markFree(criticalSet, tree )
1441
1449
end if
1442
1450
1443
1451
// Compute the adapted type.
@@ -1497,14 +1505,14 @@ class CheckCaptures extends Recheck, SymTransformer:
1497
1505
* - narrow nested captures of `x`'s underlying type to `{x*}`
1498
1506
* - do box adaptation
1499
1507
*/
1500
- def adapt (actual : Type , expected : Type , pos : SrcPos , boxErrors : BoxErrors )(using Context ): Type =
1508
+ def adapt (actual : Type , expected : Type , tree : Tree , boxErrors : BoxErrors )(using Context ): Type =
1501
1509
if expected == LhsProto || expected.isSingleton && actual.isSingleton then
1502
1510
actual
1503
1511
else
1504
1512
val improvedVAR = improveCaptures(actual.widen.dealiasKeepAnnots, actual)
1505
1513
val improvedRO = improveReadOnly(improvedVAR, expected)
1506
1514
val adapted = adaptBoxed(
1507
- improvedRO.withReachCaptures(actual), expected, pos ,
1515
+ improvedRO.withReachCaptures(actual), expected, tree ,
1508
1516
covariant = true , alwaysConst = false , boxErrors)
1509
1517
if adapted eq improvedVAR // no .rd improvement, no box-adaptation
1510
1518
then actual // might as well use actual instead of improved widened
@@ -1519,19 +1527,19 @@ class CheckCaptures extends Recheck, SymTransformer:
1519
1527
* But maybe we can then elide the check during the RefChecks phase under captureChecking?
1520
1528
*/
1521
1529
def checkOverrides = new TreeTraverser :
1522
- class OverridingPairsCheckerCC (clazz : ClassSymbol , self : Type , srcPos : SrcPos )(using Context ) extends OverridingPairsChecker (clazz, self):
1530
+ class OverridingPairsCheckerCC (clazz : ClassSymbol , self : Type , tree : Tree )(using Context ) extends OverridingPairsChecker (clazz, self):
1523
1531
/** Check subtype with box adaptation.
1524
1532
* This function is passed to RefChecks to check the compatibility of overriding pairs.
1525
1533
* @param sym symbol of the field definition that is being checked
1526
1534
*/
1527
1535
override def checkSubType (actual : Type , expected : Type )(using Context ): Boolean =
1528
- val expected1 = alignDependentFunction(addOuterRefs(expected, actual, srcPos), actual.stripCapturing)
1536
+ val expected1 = alignDependentFunction(addOuterRefs(expected, actual, tree. srcPos), actual.stripCapturing)
1529
1537
val actual1 =
1530
1538
val saved = curEnv
1531
1539
try
1532
1540
curEnv = Env (clazz, EnvKind .NestedInOwner , capturedVars(clazz), outer0 = curEnv)
1533
1541
val adapted =
1534
- adaptBoxed(actual, expected1, srcPos , covariant = true , alwaysConst = true , null )
1542
+ adaptBoxed(actual, expected1, tree , covariant = true , alwaysConst = true , null )
1535
1543
actual match
1536
1544
case _ : MethodType =>
1537
1545
// We remove the capture set resulted from box adaptation for method types,
0 commit comments