@@ -121,6 +121,9 @@ object CheckCaptures:
121
121
def inverse = thisMap
122
122
end SubstParamsBiMap
123
123
124
+ /** A prototype that indicates selection with an immutable value */
125
+ class PathSelectionProto (val sym : Symbol , val pt : Type )(using Context ) extends WildcardSelectionProto
126
+
124
127
/** Check that a @retains annotation only mentions references that can be tracked.
125
128
* This check is performed at Typer.
126
129
*/
@@ -144,9 +147,9 @@ object CheckCaptures:
144
147
case ReachCapabilityApply (arg) => check(arg, elem.srcPos)
145
148
case _ => check(elem, elem.srcPos)
146
149
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.
150
153
*/
151
154
private def disallowRootCapabilitiesIn (tp : Type , carrier : Symbol , what : String , have : String , addendum : String , pos : SrcPos )(using Context ) =
152
155
val check = new TypeTraverser :
@@ -182,8 +185,66 @@ object CheckCaptures:
182
185
if ccConfig.useSealed then check.traverse(tp)
183
186
end disallowRootCapabilitiesIn
184
187
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
187
248
188
249
class CheckCaptures extends Recheck , SymTransformer :
189
250
thisPhase =>
@@ -269,41 +330,6 @@ class CheckCaptures extends Recheck, SymTransformer:
269
330
def showRef (ref : CaptureRef )(using Context ): String =
270
331
ctx.printer.toTextCaptureRef(ref).show
271
332
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
-
307
333
/** The current environment */
308
334
private val rootEnv : Env = inContext(ictx):
309
335
Env (defn.RootClass , EnvKind .Regular , CaptureSet .empty, null )
@@ -685,32 +711,11 @@ class CheckCaptures extends Recheck, SymTransformer:
685
711
else ownType
686
712
end instantiate
687
713
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
-
709
714
override def recheckTypeApply (tree : TypeApply , pt : Type )(using Context ): Type =
710
715
val meth = tree.fun match
711
716
case fun @ Select (qual, nme.apply) => qual.symbol.orElse(fun.symbol)
712
717
case fun => fun.symbol
713
- disallowCapInTypeArgs(tree.fun, meth, tree.args)
718
+ disallowCapInTypeArgs(tree.fun, meth, tree.args, thisPhase )
714
719
val res = Existential .toCap(super .recheckTypeApply(tree, pt))
715
720
includeCallCaptures(tree.symbol, res, tree.srcPos)
716
721
checkContains(tree)
@@ -940,7 +945,7 @@ class CheckCaptures extends Recheck, SymTransformer:
940
945
for case tpt : TypeTree <- impl.parents do
941
946
tpt.tpe match
942
947
case AppliedType (fn, args) =>
943
- disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree (_)))
948
+ disallowCapInTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree (_)), thisPhase )
944
949
case _ =>
945
950
inNestedLevelUnless(cls.is(Module )):
946
951
super .recheckClassDef(tree, impl, cls)
@@ -1008,40 +1013,12 @@ class CheckCaptures extends Recheck, SymTransformer:
1008
1013
report.error(ex.getMessage.nn)
1009
1014
tree.tpe
1010
1015
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)
1016
1018
res
1017
1019
1018
1020
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)
1045
1022
super .recheckFinish(tpe, tree, pt)
1046
1023
end recheckFinish
1047
1024
0 commit comments