@@ -120,11 +120,11 @@ object CheckCaptures:
120
120
report.error(em " $elem: $tpe is not a legal element of a capture set " , ann.srcPos)
121
121
ann.retainedSet.retainedElementsRaw.foreach(check)
122
122
123
- /** Under the sealed policy, report an error if some part of `tp` contains the
124
- * root capability in its capture set or if it refers to a type parameter that
125
- * could possibly be instantiated with cap in a way that's visible at the type .
123
+ /** Disallow bad roots anywhere in type `tp``.
124
+ * @param upto controls up to which owner local fresh capabilities should be disallowed.
125
+ * See disallowBadRoots for details .
126
126
*/
127
- private def disallowRootCapabilitiesIn (tp : Type , upto : Symbol , what : String , have : String , addendum : String , pos : SrcPos )(using Context ) =
127
+ private def disallowBadRootsIn (tp : Type , upto : Symbol , what : => String , have : => String , addendum : => String , pos : SrcPos )(using Context ) =
128
128
val check = new TypeTraverser :
129
129
130
130
private val seen = new EqHashSet [TypeRef ]
@@ -151,7 +151,7 @@ object CheckCaptures:
151
151
case CapturingType (parent, refs) =>
152
152
if variance >= 0 then
153
153
val openScopes = openExistentialScopes
154
- refs.disallowRootCapability (upto): () =>
154
+ refs.disallowBadRoots (upto): () =>
155
155
def part =
156
156
if t eq tp then " "
157
157
else
@@ -179,7 +179,10 @@ object CheckCaptures:
179
179
case t =>
180
180
traverseChildren(t)
181
181
check.traverse(tp)
182
- end disallowRootCapabilitiesIn
182
+ end disallowBadRootsIn
183
+
184
+ private def ownerStr (owner : Symbol )(using Context ): String =
185
+ if owner.isAnonymousFunction then " enclosing function" else owner.show
183
186
184
187
trait CheckerAPI :
185
188
/** Complete symbol info of a val or a def */
@@ -490,7 +493,7 @@ class CheckCaptures extends Recheck, SymTransformer:
490
493
case c1 : CoreCapability =>
491
494
CaptureSet .ofType(c1.widen, followResult = true )
492
495
capt.println(i " Widen reach $c to $underlying in ${env.owner}" )
493
- underlying.disallowRootCapability (NoSymbol ): () =>
496
+ underlying.disallowBadRoots (NoSymbol ): () =>
494
497
report.error(em " Local capability $c${env.owner.qualString(" in" )} cannot have `cap` as underlying capture set " , tree.srcPos)
495
498
recur(underlying, env, lastEnv)
496
499
@@ -534,20 +537,26 @@ class CheckCaptures extends Recheck, SymTransformer:
534
537
// Under deferredReaches, don't propagate out of methods inside terms.
535
538
// The use set of these methods will be charged when that method is called.
536
539
537
- recur(cs, curEnv, null )
538
- useInfos += ((tree, cs, curEnv))
540
+ if ! cs.isAlwaysEmpty then
541
+ recur(cs, curEnv, null )
542
+ useInfos += ((tree, cs, curEnv))
539
543
end markFree
540
544
541
- /** If capability `c` refers to a parameter that is not @use declared, report an error.
545
+ /** If capability `c` refers to a parameter that is not implicitly or explicitly
546
+ * @use declared, report an error.
542
547
*/
543
548
def checkUseDeclared (c : Capability , pos : SrcPos )(using Context ): Unit =
544
549
c.paramPathRoot match
545
550
case ref : NamedType if ! ref.symbol.isUseParam =>
546
551
val what = if ref.isType then " Capture set parameter" else " Local reach capability"
547
- val owner = ref.symbol.owner
552
+ def mitigation =
553
+ if ccConfig.allowUse
554
+ then i " \n To allow this, the ${ref.symbol} should be declared with a @use annotation. "
555
+ else if ! ref.isType then i " \n You could try to abstract the capabilities referred to by $c in a capset variable. "
556
+ else " "
548
557
report.error(
549
- em """ $what $c leaks into capture scope ${owner.qualString(" of" )}.
550
- |To allow this, the ${ref.symbol} should be declared with a @use annotation """ , pos)
558
+ em " $what $c leaks into capture scope ${ref.symbol. owner.qualString(" of" )}. $mitigation " ,
559
+ pos)
551
560
case _ =>
552
561
553
562
/** Include references captured by the called method in the current environment stack */
@@ -570,16 +579,16 @@ class CheckCaptures extends Recheck, SymTransformer:
570
579
case _ =>
571
580
tp
572
581
573
- /** Under the sealed policy, disallow the root capability in type arguments.
574
- * Type arguments come either from a TypeApply node or from an AppliedType
582
+ /** Type arguments come either from a TypeApply node or from an AppliedType
575
583
* which represents a trait parent in a template.
576
- * Also, if a corresponding formal type parameter is declared or implied @use,
577
- * charge the deep capture set of the argument to the environent.
584
+ * - Disallow global cap and result caps in such arguments.
585
+ * - If a corresponding formal type parameter is declared or implied @use,
586
+ * charge the deep capture set of the argument to the environent.
578
587
* @param fn the type application, of type TypeApply or TypeTree
579
588
* @param sym the constructor symbol (could be a method or a val or a class)
580
589
* @param args the type arguments
581
590
*/
582
- def disallowCapInTypeArgs (fn : Tree , sym : Symbol , args : List [Tree ])(using Context ): Unit =
591
+ def markFreeTypeArgs (fn : Tree , sym : Symbol , args : List [Tree ])(using Context ): Unit =
583
592
def isExempt = sym.isTypeTestOrCast || defn.capsErasedValueMethods.contains(sym)
584
593
if ! isExempt then
585
594
val paramNames = atPhase(thisPhase.prev):
@@ -592,17 +601,17 @@ class CheckCaptures extends Recheck, SymTransformer:
592
601
593
602
for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
594
603
def where = if sym.exists then i " in an argument of $sym" else " "
595
- val ( addendum, errTree) =
604
+ def addendum =
596
605
if arg.isInferred
597
- then ( i " \n This is often caused by a local capability $where\n leaking as part of its result. " , fn)
598
- else if arg.span.exists then ( " " , arg)
599
- else ( " " , fn)
600
- disallowRootCapabilitiesIn (arg.nuType, NoSymbol ,
606
+ then i " \n This is often caused by a local capability $where\n leaking as part of its result. "
607
+ else " "
608
+ def errTree = if ! arg.isInferred && arg.span.exists then arg else fn
609
+ disallowBadRootsIn (arg.nuType, NoSymbol ,
601
610
i " Type variable $pname of $sym" , " be instantiated to" , addendum, errTree.srcPos)
602
611
603
612
val param = fn.symbol.paramNamed(pname)
604
613
if param.isUseParam then markFree(arg.nuType.deepCaptureSet, errTree)
605
- end disallowCapInTypeArgs
614
+ end markFreeTypeArgs
606
615
607
616
/** Rechecking idents involves:
608
617
* - adding call captures for idents referring to methods
@@ -870,7 +879,7 @@ class CheckCaptures extends Recheck, SymTransformer:
870
879
case fun @ Select (qual, nme.apply) => qual.symbol.orElse(fun.symbol)
871
880
case fun => fun.symbol
872
881
def methDescr = if meth.exists then i " $meth's type " else " "
873
- disallowCapInTypeArgs (tree.fun, meth, tree.args)
882
+ markFreeTypeArgs (tree.fun, meth, tree.args)
874
883
val funType = super .recheckTypeApply(tree, pt)
875
884
val res = resultToFresh(funType, Origin .ResultInstance (funType, meth))
876
885
includeCallCaptures(tree.symbol, res, tree)
@@ -919,7 +928,7 @@ class CheckCaptures extends Recheck, SymTransformer:
919
928
assert(params.hasSameLengthAs(argTypes), i " $mdef vs $pt, ${params}" )
920
929
for (argType, param) <- argTypes.lazyZip(params) do
921
930
val paramTpt = param.asInstanceOf [ValDef ].tpt
922
- val paramType = freshToCap(paramTpt.nuType)
931
+ val paramType = freshToCap(param.symbol, paramTpt.nuType)
923
932
checkConformsExpr(argType, paramType, param)
924
933
.showing(i " compared expected closure formal $argType against $param with ${paramTpt.nuType}" , capt)
925
934
if ! pt.isInstanceOf [RefinedType ]
@@ -986,7 +995,7 @@ class CheckCaptures extends Recheck, SymTransformer:
986
995
i " \n\n Note that $sym does not count as local since it is captured by $enclStr"
987
996
case _ =>
988
997
" "
989
- disallowRootCapabilitiesIn (
998
+ disallowBadRootsIn (
990
999
tree.tpt.nuType, NoSymbol , i " Mutable $sym" , " have type" , addendum, sym.srcPos)
991
1000
checkInferredResult(super .recheckValDef(tree, sym), tree)
992
1001
finally
@@ -1169,7 +1178,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1169
1178
for case tpt : TypeTree <- impl.parents do
1170
1179
tpt.tpe match
1171
1180
case AppliedType (fn, args) =>
1172
- disallowCapInTypeArgs (tpt, fn.typeSymbol, args.map(TypeTree (_)))
1181
+ markFreeTypeArgs (tpt, fn.typeSymbol, args.map(TypeTree (_)))
1173
1182
case _ =>
1174
1183
ccState.inNestedLevelUnless(cls.is(Module )):
1175
1184
super .recheckClassDef(tree, impl, cls)
@@ -1202,7 +1211,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1202
1211
recheck(tree.expr, pt)
1203
1212
val tp = recheckTryRest(bodyType, tree.cases, tree.finalizer, pt)
1204
1213
if Feature .enabled(Feature .saferExceptions) then
1205
- disallowRootCapabilitiesIn (tp, ctx.owner,
1214
+ disallowBadRootsIn (tp, ctx.owner,
1206
1215
" The result of `try`" , " have type" ,
1207
1216
" \n This is often caused by a locally generated exception capability leaking as part of its result." ,
1208
1217
tree.srcPos)
@@ -1747,7 +1756,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1747
1756
1748
1757
override def checkInheritedTraitParameters : Boolean = false
1749
1758
1750
- /** Check that overrides don't change the @use or @consume status of their parameters */
1759
+ /** Check that overrides don't change the @use, @consume, or @reserve status of their parameters */
1751
1760
override def additionalChecks (member : Symbol , other : Symbol )(using Context ): Unit =
1752
1761
for
1753
1762
(params1, params2) <- member.rawParamss.lazyZip(other.rawParamss)
@@ -1764,6 +1773,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1764
1773
1765
1774
checkAnnot(defn.UseAnnot )
1766
1775
checkAnnot(defn.ConsumeAnnot )
1776
+ checkAnnot(defn.ReserveAnnot )
1767
1777
end OverridingPairsCheckerCC
1768
1778
1769
1779
def traverse (t : Tree )(using Context ) =
@@ -1946,7 +1956,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1946
1956
if ! (pos.span.isSynthetic && ctx.reporter.errorsReported)
1947
1957
&& ! arg.typeSymbol.name.is(WildcardParamName )
1948
1958
then
1949
- CheckCaptures .disallowRootCapabilitiesIn (arg, NoSymbol ,
1959
+ CheckCaptures .disallowBadRootsIn (arg, NoSymbol ,
1950
1960
" Array" , " have element type" , " " ,
1951
1961
pos)
1952
1962
traverseChildren(t)
@@ -1991,7 +2001,13 @@ class CheckCaptures extends Recheck, SymTransformer:
1991
2001
case c : DerivedCapability =>
1992
2002
checkElem(c.underlying)
1993
2003
case c : FreshCap =>
1994
- check(c.hiddenSet)
2004
+ c.origin match
2005
+ case Origin .Parameter (param) =>
2006
+ report.error(
2007
+ em " Local $c created in type of $param leaks into capture scope ${param.owner.qualString(" of" )}" ,
2008
+ tree.srcPos)
2009
+ case _ =>
2010
+ check(c.hiddenSet)
1995
2011
case _ =>
1996
2012
1997
2013
check(uses)
0 commit comments