@@ -18,7 +18,7 @@ import util.{SimpleIdentitySet, EqHashMap, EqHashSet, SrcPos, Property}
1818import transform .{Recheck , PreRecheck , CapturedVars }
1919import Recheck .*
2020import scala .collection .mutable
21- import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap , CompareResult , VarState }
21+ import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap , CompareResult }
2222import CCState .*
2323import StdNames .nme
2424import NameKinds .{DefaultGetterName , WildcardParamName , UniqueNameKind }
@@ -253,6 +253,10 @@ object CheckCaptures:
253253 * the type of the formal paremeter corresponding to the argument.
254254 */
255255 def formalType : Type
256+
257+ /** The "use set", i.e. the capture set marked as free at this node. */
258+ def markedFree : CaptureSet
259+
256260 end CheckerAPI
257261
258262class CheckCaptures extends Recheck , SymTransformer :
@@ -298,9 +302,12 @@ class CheckCaptures extends Recheck, SymTransformer:
298302 */
299303 private val sepCheckFormals = util.EqHashMap [Tree , Type ]()
300304
305+ private val usedSet = util.EqHashMap [Tree , CaptureSet ]()
306+
301307 extension [T <: Tree ](tree : T )
302308 def needsSepCheck : Boolean = sepCheckFormals.contains(tree)
303309 def formalType : Type = sepCheckFormals.getOrElse(tree, NoType )
310+ def markedFree = usedSet.getOrElse(tree, CaptureSet .empty)
304311
305312 /** Instantiate capture set variables appearing contra-variantly to their
306313 * upper approximation.
@@ -404,17 +411,17 @@ class CheckCaptures extends Recheck, SymTransformer:
404411 /** Include `sym` in the capture sets of all enclosing environments nested in the
405412 * the environment in which `sym` is defined.
406413 */
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 )
409416
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 )
412419
413420 /** Make sure the (projected) `cs` is a subset of the capture sets of all enclosing
414421 * environments. At each stage, only include references from `cs` that are outside
415422 * the environment's owner
416423 */
417- def markFree (cs : CaptureSet , pos : SrcPos )(using Context ): Unit =
424+ def markFree (cs : CaptureSet , tree : Tree )(using Context ): Unit =
418425 // A captured reference with the symbol `sym` is visible from the environment
419426 // if `sym` is not defined inside the owner of the environment.
420427 inline def isVisibleFromEnv (sym : Symbol , env : Env ) =
@@ -436,7 +443,7 @@ class CheckCaptures extends Recheck, SymTransformer:
436443 val what = if ref.isType then " Capture set parameter" else " Local reach capability"
437444 report.error(
438445 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 )
440447 case _ =>
441448
442449 /** Avoid locally defined capability by charging the underlying type
@@ -456,7 +463,7 @@ class CheckCaptures extends Recheck, SymTransformer:
456463 CaptureSet .ofType(c.widen, followResult = false )
457464 capt.println(i " Widen reach $c to $underlying in ${env.owner}" )
458465 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 )
460467 recur(underlying, env, lastEnv)
461468
462469 /** Avoid locally defined capability if it is a reach capability or capture set
@@ -479,7 +486,7 @@ class CheckCaptures extends Recheck, SymTransformer:
479486 val underlying = CaptureSet .ofTypeDeeply(c1.widen)
480487 capt.println(i " Widen reach $c to $underlying in ${env.owner}" )
481488 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 )
483490 recur(underlying, env, null )
484491 case c : TypeRef if c.isParamPath =>
485492 checkUseDeclared(c, env, null )
@@ -496,22 +503,23 @@ class CheckCaptures extends Recheck, SymTransformer:
496503 then avoidLocalCapability(c, env, lastEnv)
497504 else avoidLocalReachCapability(c, env)
498505 isVisible
499- checkSubset(included, env.captured, pos , provenance(env))
506+ checkSubset(included, env.captured, tree.srcPos , provenance(env))
500507 capt.println(i " Include call or box capture $included from $cs in ${env.owner} --> ${env.captured}" )
501508 if ! isOfNestedMethod(env) then
502509 recur(included, nextEnvToCharge(env, ! _.owner.isStaticOwner), env)
503510 // Don't propagate out of methods inside terms. The use set of these methods
504511 // will be charged when that method is called.
505512
506513 recur(cs, curEnv, null )
514+ usedSet(tree) = tree.markedFree ++ cs
507515 end markFree
508516
509517 /** 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
511519 case _ : MethodOrPoly => // wait until method is fully applied
512520 case _ =>
513521 if sym.exists then
514- if curEnv.isOpen then markFree(capturedVars(sym), pos )
522+ if curEnv.isOpen then markFree(capturedVars(sym), tree )
515523
516524 /** Under the sealed policy, disallow the root capability in type arguments.
517525 * Type arguments come either from a TypeApply node or from an AppliedType
@@ -535,23 +543,23 @@ class CheckCaptures extends Recheck, SymTransformer:
535543
536544 for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
537545 def where = if sym.exists then i " in an argument of $sym" else " "
538- val (addendum, pos ) =
546+ val (addendum, errTree ) =
539547 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)
543551 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 )
545553
546554 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 )
548556 end disallowCapInTypeArgs
549557
550558 override def recheckIdent (tree : Ident , pt : Type )(using Context ): Type =
551559 val sym = tree.symbol
552560 if sym.is(Method ) then
553561 // 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)
555563 else if ! sym.isStatic then
556564 // Otherwise charge its symbol, but add all selections implied by the e
557565 // expected type `pt`.
@@ -569,7 +577,7 @@ class CheckCaptures extends Recheck, SymTransformer:
569577 var pathRef : CaptureRef = addSelects(sym.termRef, pt)
570578 if pathRef.derivesFrom(defn.Caps_Mutable ) && pt.isValueType && ! pt.isMutableType then
571579 pathRef = pathRef.readOnly
572- markFree(sym, pathRef, tree.srcPos )
580+ markFree(sym, pathRef, tree)
573581 super .recheckIdent(tree, pt)
574582
575583 /** The expected type for the qualifier of a selection. If the selection
@@ -668,7 +676,7 @@ class CheckCaptures extends Recheck, SymTransformer:
668676 super .recheckFinish(argType, tree, pt)
669677 else
670678 val res = super .recheckApply(tree, pt)
671- includeCallCaptures(meth, res, tree.srcPos )
679+ includeCallCaptures(meth, res, tree)
672680 res
673681
674682 /** Recheck argument, and, if formal parameter carries a `@use`,
@@ -681,7 +689,7 @@ class CheckCaptures extends Recheck, SymTransformer:
681689 if formal.hasUseAnnot then
682690 // The @use annotation is added to `formal` by `prepareFunction`
683691 capt.println(i " charging deep capture set of $arg: ${argType} = ${argType.deepCaptureSet}" )
684- markFree(argType.deepCaptureSet, arg.srcPos )
692+ markFree(argType.deepCaptureSet, arg)
685693 if formal.containsCap then
686694 sepCheckFormals(arg) = freshenedFormal
687695 argType
@@ -815,7 +823,7 @@ class CheckCaptures extends Recheck, SymTransformer:
815823 case fun => fun.symbol
816824 disallowCapInTypeArgs(tree.fun, meth, tree.args)
817825 val res = Existential .toCap(super .recheckTypeApply(tree, pt))
818- includeCallCaptures(tree.symbol, res, tree.srcPos )
826+ includeCallCaptures(tree.symbol, res, tree)
819827 checkContains(tree)
820828 res
821829 end recheckTypeApply
@@ -1092,7 +1100,7 @@ class CheckCaptures extends Recheck, SymTransformer:
10921100 case AnnotatedType (_, annot) if annot.symbol == defn.RequiresCapabilityAnnot =>
10931101 annot.tree match
10941102 case Apply (_, cap :: Nil ) =>
1095- markFree(cap.symbol, tree.srcPos )
1103+ markFree(cap.symbol, tree)
10961104 case _ =>
10971105 case _ =>
10981106 super .recheckTyped(tree)
@@ -1147,7 +1155,7 @@ class CheckCaptures extends Recheck, SymTransformer:
11471155 super .recheck(tree, pt)
11481156 finally curEnv = saved
11491157 if tree.isTerm && ! pt.isBoxedCapturing && pt != LhsProto then
1150- markFree(res.boxedCaptureSet, tree.srcPos )
1158+ markFree(res.boxedCaptureSet, tree)
11511159 res
11521160 end recheck
11531161
@@ -1214,7 +1222,7 @@ class CheckCaptures extends Recheck, SymTransformer:
12141222 override def checkConformsExpr (actual : Type , expected : Type , tree : Tree , addenda : Addenda )(using Context ): Type =
12151223 var expected1 = alignDependentFunction(expected, actual.stripCapturing)
12161224 val boxErrors = new mutable.ListBuffer [Message ]
1217- val actualBoxed = adapt(actual, expected1, tree.srcPos , boxErrors)
1225+ val actualBoxed = adapt(actual, expected1, tree, boxErrors)
12181226 // println(i"check conforms $actualBoxed <<< $expected1")
12191227
12201228 if actualBoxed eq actual then
@@ -1334,7 +1342,7 @@ class CheckCaptures extends Recheck, SymTransformer:
13341342 *
13351343 * @param alwaysConst always make capture set variables constant after adaptation
13361344 */
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 =
13381346
13391347 def recur (actual : Type , expected : Type , covariant : Boolean ): Type =
13401348
@@ -1401,7 +1409,7 @@ class CheckCaptures extends Recheck, SymTransformer:
14011409 if ! leaked.subCaptures(cs).isOK then
14021410 report.error(
14031411 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 )
14051413 cs
14061414
14071415 def adaptedType (resultBoxed : Boolean ) =
@@ -1433,11 +1441,11 @@ class CheckCaptures extends Recheck, SymTransformer:
14331441 return actual
14341442 // Disallow future addition of `cap` to `criticalSet`.
14351443 criticalSet.disallowRootCapability: () =>
1436- report.error(msg, pos )
1444+ report.error(msg, tree.srcPos )
14371445
14381446 if ! insertBox then // we are unboxing
14391447 // debugShowEnvs()
1440- markFree(criticalSet, pos )
1448+ markFree(criticalSet, tree )
14411449 end if
14421450
14431451 // Compute the adapted type.
@@ -1497,14 +1505,14 @@ class CheckCaptures extends Recheck, SymTransformer:
14971505 * - narrow nested captures of `x`'s underlying type to `{x*}`
14981506 * - do box adaptation
14991507 */
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 =
15011509 if expected == LhsProto || expected.isSingleton && actual.isSingleton then
15021510 actual
15031511 else
15041512 val improvedVAR = improveCaptures(actual.widen.dealiasKeepAnnots, actual)
15051513 val improvedRO = improveReadOnly(improvedVAR, expected)
15061514 val adapted = adaptBoxed(
1507- improvedRO.withReachCaptures(actual), expected, pos ,
1515+ improvedRO.withReachCaptures(actual), expected, tree ,
15081516 covariant = true , alwaysConst = false , boxErrors)
15091517 if adapted eq improvedVAR // no .rd improvement, no box-adaptation
15101518 then actual // might as well use actual instead of improved widened
@@ -1519,19 +1527,19 @@ class CheckCaptures extends Recheck, SymTransformer:
15191527 * But maybe we can then elide the check during the RefChecks phase under captureChecking?
15201528 */
15211529 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):
15231531 /** Check subtype with box adaptation.
15241532 * This function is passed to RefChecks to check the compatibility of overriding pairs.
15251533 * @param sym symbol of the field definition that is being checked
15261534 */
15271535 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)
15291537 val actual1 =
15301538 val saved = curEnv
15311539 try
15321540 curEnv = Env (clazz, EnvKind .NestedInOwner , capturedVars(clazz), outer0 = curEnv)
15331541 val adapted =
1534- adaptBoxed(actual, expected1, srcPos , covariant = true , alwaysConst = true , null )
1542+ adaptBoxed(actual, expected1, tree , covariant = true , alwaysConst = true , null )
15351543 actual match
15361544 case _ : MethodType =>
15371545 // We remove the capture set resulted from box adaptation for method types,
0 commit comments