@@ -121,6 +121,9 @@ object CheckCaptures:
121121 def inverse = thisMap
122122 end SubstParamsBiMap
123123
124+ /** A prototype that indicates selection with an immutable value */
125+ class PathSelectionProto (val sym : Symbol , val pt : Type )(using Context ) extends WildcardSelectionProto
126+
124127 /** Check that a @retains annotation only mentions references that can be tracked.
125128 * This check is performed at Typer.
126129 */
@@ -144,9 +147,9 @@ object CheckCaptures:
144147 case ReachCapabilityApply (arg) => check(arg, elem.srcPos)
145148 case _ => check(elem, elem.srcPos)
146149
147- /** Report an error if some part of `tp` contains the root capability in its capture set
148- * or if it refers to an unsealed type parameter that could possibly be instantiated with
149- * cap in a way that's visible at the type.
150+ /** Under the sealed policy, report an error if some part of `tp` contains the
151+ * root capability in its capture set or if it refers to a type parameter that
152+ * could possibly be instantiated with cap in a way that's visible at the type.
150153 */
151154 private def disallowRootCapabilitiesIn (tp : Type , carrier : Symbol , what : String , have : String , addendum : String , pos : SrcPos )(using Context ) =
152155 val check = new TypeTraverser :
@@ -182,8 +185,66 @@ object CheckCaptures:
182185 if ccConfig.useSealed then check.traverse(tp)
183186 end disallowRootCapabilitiesIn
184187
185- /** A prototype that indicates selection with an immutable value */
186- class PathSelectionProto (val sym : Symbol , val pt : Type )(using Context ) extends WildcardSelectionProto
188+ /** Under the sealed policy, disallow the root capability in type arguments.
189+ * Type arguments come either from a TypeApply node or from an AppliedType
190+ * which represents a trait parent in a template.
191+ * @param fn the type application, of type TypeApply or TypeTree
192+ * @param sym the constructor symbol (could be a method or a val or a class)
193+ * @param args the type arguments
194+ */
195+ private def disallowCapInTypeArgs (fn : Tree , sym : Symbol , args : List [Tree ], thisPhase : Phase )(using Context ): Unit =
196+ def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
197+ if ccConfig.useSealed && ! isExempt then
198+ val paramNames = atPhase(thisPhase.prev):
199+ fn.tpe.widenDealias match
200+ case tl : TypeLambda => tl.paramNames
201+ case ref : AppliedType if ref.typeSymbol.isClass => ref.typeSymbol.typeParams.map(_.name)
202+ case t =>
203+ println(i " parent type: $t" )
204+ args.map(_ => EmptyTypeName )
205+ for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
206+ def where = if sym.exists then i " in an argument of $sym" else " "
207+ val (addendum, pos) =
208+ if arg.isInferred
209+ then (" \n This is often caused by a local capability$where\n leaking as part of its result." , fn.srcPos)
210+ else if arg.span.exists then (" " , arg.srcPos)
211+ else (" " , fn.srcPos)
212+ disallowRootCapabilitiesIn(arg.knownType, NoSymbol ,
213+ i " Type variable $pname of $sym" , " be instantiated to" , addendum, pos)
214+ end disallowCapInTypeArgs
215+
216+ /** If we are not under the sealed policy, and a tree is an application that unboxes
217+ * its result or is a try, check that the tree's type does not have covariant universal
218+ * capabilities.
219+ */
220+ private def checkNotUniversalInUnboxedResult (tpe : Type , tree : Tree )(using Context ): Unit =
221+ def needsUniversalCheck = tree match
222+ case _ : RefTree | _ : Apply | _ : TypeApply => tree.symbol.unboxesResult
223+ case _ : Try => true
224+ case _ => false
225+
226+ object checkNotUniversal extends TypeTraverser :
227+ def traverse (tp : Type ) =
228+ tp.dealias match
229+ case wtp @ CapturingType (parent, refs) =>
230+ if variance > 0 then
231+ refs.disallowRootCapability: () =>
232+ def part = if wtp eq tpe.widen then " " else i " in its part $wtp"
233+ report.error(
234+ em """ The expression's type ${tpe.widen} is not allowed to capture the root capability `cap` $part.
235+ |This usually means that a capability persists longer than its allowed lifetime. """ ,
236+ tree.srcPos)
237+ if ! wtp.isBoxed then traverse(parent)
238+ case tp =>
239+ traverseChildren(tp)
240+
241+ if ! ccConfig.useSealed
242+ && ! tpe.hasAnnotation(defn.UncheckedCapturesAnnot )
243+ && needsUniversalCheck
244+ && tpe.widen.isValueType
245+ then
246+ checkNotUniversal.traverse(tpe.widen)
247+ end checkNotUniversalInUnboxedResult
187248
188249class CheckCaptures extends Recheck , SymTransformer :
189250 thisPhase =>
@@ -269,41 +330,6 @@ class CheckCaptures extends Recheck, SymTransformer:
269330 def showRef (ref : CaptureRef )(using Context ): String =
270331 ctx.printer.toTextCaptureRef(ref).show
271332
272- // Uses 4-space indent as a trial
273- private def checkReachCapsIsolated (tpe : Type , pos : SrcPos )(using Context ): Unit =
274-
275- object checker extends TypeTraverser :
276- var refVariances : Map [Boolean , Int ] = Map .empty
277- var seenReach : CaptureRef | Null = null
278- def traverse (tp : Type ) =
279- tp.dealias match
280- case CapturingType (parent, refs) =>
281- traverse(parent)
282- for ref <- refs.elems do
283- if ref.isReach && ! ref.stripReach.isInstanceOf [TermParamRef ]
284- || ref.isRootCapability
285- then
286- val isReach = ref.isReach
287- def register () =
288- refVariances = refVariances.updated(isReach, variance)
289- seenReach = ref
290- refVariances.get(isReach) match
291- case None => register()
292- case Some (v) => if v != 0 && variance == 0 then register()
293- case _ =>
294- traverseChildren(tp)
295-
296- checker.traverse(tpe)
297- if checker.refVariances.size == 2
298- && checker.refVariances(true ) >= 0
299- && checker.refVariances(false ) <= 0
300- then
301- report.error(
302- em """ Reach capability ${showRef(checker.seenReach.nn)} and universal capability cap cannot both
303- |appear in the type $tpe of this expression """ ,
304- pos)
305- end checkReachCapsIsolated
306-
307333 /** The current environment */
308334 private val rootEnv : Env = inContext(ictx):
309335 Env (defn.RootClass , EnvKind .Regular , CaptureSet .empty, null )
@@ -685,32 +711,11 @@ class CheckCaptures extends Recheck, SymTransformer:
685711 else ownType
686712 end instantiate
687713
688- def disallowCapInTypeArgs (fn : Tree , sym : Symbol , args : List [Tree ])(using Context ): Unit =
689- def isExempt = sym.isTypeTestOrCast || sym == defn.Compiletime_erasedValue
690- if ccConfig.useSealed && ! isExempt then
691- val paramNames = atPhase(thisPhase.prev):
692- fn.tpe.widenDealias match
693- case tl : TypeLambda => tl.paramNames
694- case ref : AppliedType if ref.typeSymbol.isClass => ref.typeSymbol.typeParams.map(_.name)
695- case t =>
696- println(i " parent type: $t" )
697- args.map(_ => EmptyTypeName )
698- for case (arg : TypeTree , pname) <- args.lazyZip(paramNames) do
699- def where = if sym.exists then i " in an argument of $sym" else " "
700- val (addendum, pos) =
701- if arg.isInferred
702- then (" \n This is often caused by a local capability$where\n leaking as part of its result." , fn.srcPos)
703- else if arg.span.exists then (" " , arg.srcPos)
704- else (" " , fn.srcPos)
705- disallowRootCapabilitiesIn(arg.knownType, NoSymbol ,
706- i " Type variable $pname of $sym" , " be instantiated to" , addendum, pos)
707- end disallowCapInTypeArgs
708-
709714 override def recheckTypeApply (tree : TypeApply , pt : Type )(using Context ): Type =
710715 val meth = tree.fun match
711716 case fun @ Select (qual, nme.apply) => qual.symbol.orElse(fun.symbol)
712717 case fun => fun.symbol
713- disallowCapInTypeArgs(tree.fun, meth, tree.args)
718+ disallowCapInTypeArgs(tree.fun, meth, tree.args, thisPhase )
714719 val res = Existential .toCap(super .recheckTypeApply(tree, pt))
715720 includeCallCaptures(tree.symbol, res, tree.srcPos)
716721 checkContains(tree)
@@ -940,7 +945,7 @@ class CheckCaptures extends Recheck, SymTransformer:
940945 for case tpt : TypeTree <- impl.parents do
941946 tpt.tpe match
942947 case AppliedType (fn, args) =>
943- disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree (_)))
948+ disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree (_)), thisPhase )
944949 case _ =>
945950 inNestedLevelUnless(cls.is(Module )):
946951 super .recheckClassDef(tree, impl, cls)
@@ -1008,40 +1013,12 @@ class CheckCaptures extends Recheck, SymTransformer:
10081013 report.error(ex.getMessage.nn)
10091014 tree.tpe
10101015 finally curEnv = saved
1011- if tree.isTerm then
1012- if ! ccConfig.useExistentials then
1013- checkReachCapsIsolated(res.widen, tree.srcPos)
1014- if ! pt.isBoxedCapturing && pt != LhsProto then
1015- markFree(res.boxedCaptureSet, tree.srcPos)
1016+ if tree.isTerm && ! pt.isBoxedCapturing && pt != LhsProto then
1017+ markFree(res.boxedCaptureSet, tree.srcPos)
10161018 res
10171019
10181020 override def recheckFinish (tpe : Type , tree : Tree , pt : Type )(using Context ): Type =
1019- def needsUniversalCheck = tree match
1020- case _ : RefTree | _ : Apply | _ : TypeApply => tree.symbol.unboxesResult
1021- case _ : Try => true
1022- case _ => false
1023-
1024- object checkNotUniversal extends TypeTraverser :
1025- def traverse (tp : Type ) =
1026- tp.dealias match
1027- case wtp @ CapturingType (parent, refs) =>
1028- if variance > 0 then
1029- refs.disallowRootCapability: () =>
1030- def part = if wtp eq tpe.widen then " " else i " in its part $wtp"
1031- report.error(
1032- em """ The expression's type ${tpe.widen} is not allowed to capture the root capability `cap` $part.
1033- |This usually means that a capability persists longer than its allowed lifetime. """ ,
1034- tree.srcPos)
1035- if ! wtp.isBoxed then traverse(parent)
1036- case tp =>
1037- traverseChildren(tp)
1038-
1039- if ! ccConfig.useSealed
1040- && ! tpe.hasAnnotation(defn.UncheckedCapturesAnnot )
1041- && needsUniversalCheck
1042- && tpe.widen.isValueType
1043- then
1044- checkNotUniversal.traverse(tpe.widen)
1021+ checkNotUniversalInUnboxedResult(tpe, tree)
10451022 super .recheckFinish(tpe, tree, pt)
10461023 end recheckFinish
10471024
0 commit comments