@@ -118,6 +118,9 @@ object CheckCaptures:
118118 def inverse = thisMap
119119 end SubstParamsBiMap
120120
121+ /** A prototype that indicates selection with an immutable value */
122+ class PathSelectionProto (val sym : Symbol , val pt : Type )(using Context ) extends WildcardSelectionProto
123+
121124 /** Check that a @retains annotation only mentions references that can be tracked.
122125 * This check is performed at Typer.
123126 */
@@ -141,9 +144,9 @@ object CheckCaptures:
141144 case ReachCapabilityApply (arg) => check(arg, elem.srcPos)
142145 case _ => check(elem, elem.srcPos)
143146
144- /** Report an error if some part of `tp` contains the root capability in its capture set
145- * or if it refers to an unsealed type parameter that could possibly be instantiated with
146- * cap in a way that's visible at the type.
147+ /** Under the sealed policy, report an error if some part of `tp` contains the
148+ * root capability in its capture set or if it refers to a type parameter that
149+ * could possibly be instantiated with cap in a way that's visible at the type.
147150 */
148151 private def disallowRootCapabilitiesIn (tp : Type , carrier : Symbol , what : String , have : String , addendum : String , pos : SrcPos )(using Context ) =
149152 val check = new TypeTraverser :
@@ -179,8 +182,66 @@ object CheckCaptures:
179182 if ccConfig.useSealed then check.traverse(tp)
180183 end disallowRootCapabilitiesIn
181184
182- /** A prototype that indicates selection with an immutable value */
183- class PathSelectionProto (val sym : Symbol , val pt : Type )(using Context ) extends WildcardSelectionProto
185+ /** Under the sealed policy, disallow the root capability in type arguments.
186+ * Type arguments come either from a TypeApply node or from an AppliedType
187+ * which represents a trait parent in a template.
188+ * @param fn the type application, of type TypeApply or TypeTree
189+ * @param sym the constructor symbol (could be a method or a val or a class)
190+ * @param args the type arguments
191+ */
192+ private def disallowCapInTypeArgs (fn : Tree , sym : Symbol , args : List [Tree ], thisPhase : Phase )(using Context ): Unit =
193+ def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
194+ if ccConfig.useSealed && ! isExempt then
195+ val paramNames = atPhase(thisPhase.prev):
196+ fn.tpe.widenDealias match
197+ case tl : TypeLambda => tl.paramNames
198+ case ref : AppliedType if ref.typeSymbol.isClass => ref.typeSymbol.typeParams.map(_.name)
199+ case t =>
200+ println(i " parent type: $t" )
201+ args.map(_ => EmptyTypeName )
202+ for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
203+ def where = if sym.exists then i " in an argument of $sym" else " "
204+ val (addendum, pos) =
205+ if arg.isInferred
206+ then (" \n This is often caused by a local capability$where\n leaking as part of its result." , fn.srcPos)
207+ else if arg.span.exists then (" " , arg.srcPos)
208+ else (" " , fn.srcPos)
209+ disallowRootCapabilitiesIn(arg.knownType, NoSymbol ,
210+ i " Type variable $pname of $sym" , " be instantiated to" , addendum, pos)
211+ end disallowCapInTypeArgs
212+
213+ /** If we are not under the sealed policy, and a tree is an application that unboxes
214+ * its result or is a try, check that the tree's type does not have covariant universal
215+ * capabilities.
216+ */
217+ private def checkNotUniversalInUnboxedResult (tpe : Type , tree : Tree )(using Context ): Unit =
218+ def needsUniversalCheck = tree match
219+ case _ : RefTree | _ : Apply | _ : TypeApply => tree.symbol.unboxesResult
220+ case _ : Try => true
221+ case _ => false
222+
223+ object checkNotUniversal extends TypeTraverser :
224+ def traverse (tp : Type ) =
225+ tp.dealias match
226+ case wtp @ CapturingType (parent, refs) =>
227+ if variance > 0 then
228+ refs.disallowRootCapability: () =>
229+ def part = if wtp eq tpe.widen then " " else i " in its part $wtp"
230+ report.error(
231+ em """ The expression's type ${tpe.widen} is not allowed to capture the root capability `cap` $part.
232+ |This usually means that a capability persists longer than its allowed lifetime. """ ,
233+ tree.srcPos)
234+ if ! wtp.isBoxed then traverse(parent)
235+ case tp =>
236+ traverseChildren(tp)
237+
238+ if ! ccConfig.useSealed
239+ && ! tpe.hasAnnotation(defn.UncheckedCapturesAnnot )
240+ && needsUniversalCheck
241+ && tpe.widen.isValueType
242+ then
243+ checkNotUniversal.traverse(tpe.widen)
244+ end checkNotUniversalInUnboxedResult
184245
185246class CheckCaptures extends Recheck , SymTransformer :
186247 thisPhase =>
@@ -264,41 +325,6 @@ class CheckCaptures extends Recheck, SymTransformer:
264325 def showRef (ref : CaptureRef )(using Context ): String =
265326 ctx.printer.toTextCaptureRef(ref).show
266327
267- // Uses 4-space indent as a trial
268- private def checkReachCapsIsolated (tpe : Type , pos : SrcPos )(using Context ): Unit =
269-
270- object checker extends TypeTraverser :
271- var refVariances : Map [Boolean , Int ] = Map .empty
272- var seenReach : CaptureRef | Null = null
273- def traverse (tp : Type ) =
274- tp.dealias match
275- case CapturingType (parent, refs) =>
276- traverse(parent)
277- for ref <- refs.elems do
278- if ref.isReach && ! ref.stripReach.isInstanceOf [TermParamRef ]
279- || ref.isRootCapability
280- then
281- val isReach = ref.isReach
282- def register () =
283- refVariances = refVariances.updated(isReach, variance)
284- seenReach = ref
285- refVariances.get(isReach) match
286- case None => register()
287- case Some (v) => if v != 0 && variance == 0 then register()
288- case _ =>
289- traverseChildren(tp)
290-
291- checker.traverse(tpe)
292- if checker.refVariances.size == 2
293- && checker.refVariances(true ) >= 0
294- && checker.refVariances(false ) <= 0
295- then
296- report.error(
297- em """ Reach capability ${showRef(checker.seenReach.nn)} and universal capability cap cannot both
298- |appear in the type $tpe of this expression """ ,
299- pos)
300- end checkReachCapsIsolated
301-
302328 /** The current environment */
303329 private val rootEnv : Env = inContext(ictx):
304330 Env (defn.RootClass , EnvKind .Regular , CaptureSet .empty, null )
@@ -680,32 +706,11 @@ class CheckCaptures extends Recheck, SymTransformer:
680706 else ownType
681707 end instantiate
682708
683- def disallowCapInTypeArgs (fn : Tree , sym : Symbol , args : List [Tree ])(using Context ): Unit =
684- def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
685- if ccConfig.useSealed && ! isExempt then
686- val paramNames = atPhase(thisPhase.prev):
687- fn.tpe.widenDealias match
688- case tl : TypeLambda => tl.paramNames
689- case ref : AppliedType if ref.typeSymbol.isClass => ref.typeSymbol.typeParams.map(_.name)
690- case t =>
691- println(i " parent type: $t" )
692- args.map(_ => EmptyTypeName )
693- for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
694- def where = if sym.exists then i " in an argument of $sym" else " "
695- val (addendum, pos) =
696- if arg.isInferred
697- then (" \n This is often caused by a local capability$where\n leaking as part of its result." , fn.srcPos)
698- else if arg.span.exists then (" " , arg.srcPos)
699- else (" " , fn.srcPos)
700- disallowRootCapabilitiesIn(arg.knownType, NoSymbol ,
701- i " Type variable $pname of $sym" , " be instantiated to" , addendum, pos)
702- end disallowCapInTypeArgs
703-
704709 override def recheckTypeApply (tree : TypeApply , pt : Type )(using Context ): Type =
705710 val meth = tree.fun match
706711 case fun @ Select (qual, nme.apply) => qual.symbol.orElse(fun.symbol)
707712 case fun => fun.symbol
708- disallowCapInTypeArgs(tree.fun, meth, tree.args)
713+ disallowCapInTypeArgs(tree.fun, meth, tree.args, thisPhase )
709714 val res = Existential .toCap(super .recheckTypeApply(tree, pt))
710715 includeCallCaptures(tree.symbol, res, tree.srcPos)
711716 checkContains(tree)
@@ -935,7 +940,7 @@ class CheckCaptures extends Recheck, SymTransformer:
935940 for case tpt : TypeTree <- impl.parents do
936941 tpt.tpe match
937942 case AppliedType (fn, args) =>
938- disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree (_)))
943+ disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree (_)), thisPhase )
939944 case _ =>
940945 inNestedLevelUnless(cls.is(Module )):
941946 super .recheckClassDef(tree, impl, cls)
@@ -1003,40 +1008,12 @@ class CheckCaptures extends Recheck, SymTransformer:
10031008 report.error(ex.getMessage.nn)
10041009 tree.tpe
10051010 finally curEnv = saved
1006- if tree.isTerm then
1007- if ! ccConfig.useExistentials then
1008- checkReachCapsIsolated(res.widen, tree.srcPos)
1009- if ! pt.isBoxedCapturing && pt != LhsProto then
1010- markFree(res.boxedCaptureSet, tree.srcPos)
1011+ if tree.isTerm && ! pt.isBoxedCapturing && pt != LhsProto then
1012+ markFree(res.boxedCaptureSet, tree.srcPos)
10111013 res
10121014
10131015 override def recheckFinish (tpe : Type , tree : Tree , pt : Type )(using Context ): Type =
1014- def needsUniversalCheck = tree match
1015- case _ : RefTree | _ : Apply | _ : TypeApply => tree.symbol.unboxesResult
1016- case _ : Try => true
1017- case _ => false
1018-
1019- object checkNotUniversal extends TypeTraverser :
1020- def traverse (tp : Type ) =
1021- tp.dealias match
1022- case wtp @ CapturingType (parent, refs) =>
1023- if variance > 0 then
1024- refs.disallowRootCapability: () =>
1025- def part = if wtp eq tpe.widen then " " else i " in its part $wtp"
1026- report.error(
1027- em """ The expression's type ${tpe.widen} is not allowed to capture the root capability `cap` $part.
1028- |This usually means that a capability persists longer than its allowed lifetime. """ ,
1029- tree.srcPos)
1030- if ! wtp.isBoxed then traverse(parent)
1031- case tp =>
1032- traverseChildren(tp)
1033-
1034- if ! ccConfig.useSealed
1035- && ! tpe.hasAnnotation(defn.UncheckedCapturesAnnot )
1036- && needsUniversalCheck
1037- && tpe.widen.isValueType
1038- then
1039- checkNotUniversal.traverse(tpe.widen)
1016+ checkNotUniversalInUnboxedResult(tpe, tree)
10401017 super .recheckFinish(tpe, tree, pt)
10411018 end recheckFinish
10421019
0 commit comments