@@ -17,10 +17,10 @@ import transform.SymUtils.*
17
17
import transform .{Recheck , PreRecheck }
18
18
import Recheck .*
19
19
import scala .collection .mutable
20
- import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap }
20
+ import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap , LevelException }
21
21
import StdNames .nme
22
22
import NameKinds .DefaultGetterName
23
- import reporting .{ trace , ClosureParameterMismatch }
23
+ import reporting .*
24
24
25
25
/** The capture checker */
26
26
object CheckCaptures :
@@ -157,6 +157,10 @@ object CheckCaptures:
157
157
/** Attachment key for bodies of closures, provided they are values */
158
158
val ClosureBodyValue = Property .Key [Unit ]
159
159
160
+ /** Report level violation */
161
+ def levelError [T ](ex : LevelException , pos : SrcPos )(using Context ): Unit =
162
+ report.error(em " local reference ${ex.ref} escapes into outer capture set of ${ex.owner}" , pos)
163
+
160
164
class CheckCaptures extends Recheck , SymTransformer :
161
165
thisPhase =>
162
166
@@ -203,6 +207,15 @@ class CheckCaptures extends Recheck, SymTransformer:
203
207
case _ =>
204
208
traverseChildren(t)
205
209
210
+ /** Map all occurrences of `cap` in a type to the currently valid local capture root. */
211
+ private def mapRootsIn (using Context ) = new IdempotentCaptRefMap :
212
+ def apply (t : Type ) = t.dealiasKeepAnnots match
213
+ case CapturingType (parent, refs) if refs.isUniversal =>
214
+ val parent1 = apply(parent)
215
+ CapturingType (parent1, CaptureSet (setup.localRoot(setup.currentScopeOwner)))
216
+ case _ =>
217
+ mapOver(t)
218
+
206
219
/** If `tpt` is an inferred type, interpolate capture set variables appearing contra-
207
220
* variantly in it.
208
221
*/
@@ -504,11 +517,16 @@ class CheckCaptures extends Recheck, SymTransformer:
504
517
mdef.rhs.putAttachment(ClosureBodyValue , ())
505
518
case _ =>
506
519
520
+ val mapRoots = mapRootsIn(using ctx.withOwner(mdef.symbol))
521
+
522
+ def checkCompatible (lower : Type , upper : Type , tree : Tree , msgFn : (Type , Type ) => TypeMismatchMsg ): Unit =
523
+ withMode(Mode .CCIgnoreBoxing ):
524
+ if ! isCompatible(lower, upper, tree) then
525
+ report.error(msgFn(lower, upper), tree.srcPos)
526
+
507
527
def constrainParams (ptformals : List [Type ], params : ParamClause ) =
508
- for (param, ptformal) <- params.lazyZip(ptformals) do
509
- withMode(Mode .CCIgnoreBoxing ):
510
- if ! isCompatible(ptformal, param.symbol.info, param) then
511
- report.error(ClosureParameterMismatch (ptformal, param.symbol.info), param.srcPos)
528
+ for (param, ptformal) <- params.lazyZip(ptformals.mapConserve(mapRoots)) do
529
+ checkCompatible(ptformal, param.symbol.info, param, ClosureParameterMismatch (_, _))
512
530
513
531
def constrain (pt : Type , paramss : List [ParamClause ]): Unit = (pt : @ unchecked) match
514
532
case RefinedType (_, nme.apply, rinfo) =>
@@ -521,10 +539,11 @@ class CheckCaptures extends Recheck, SymTransformer:
521
539
constrainParams(ptformals, paramss.head)
522
540
case _ =>
523
541
524
- // println(i"recheck closure $mdef with $pt")
525
542
constrain(pt.dealias.stripCapturing, mdef.paramss)
526
543
recheckDef(mdef, mdef.symbol)
527
- recheckClosure(expr, pt)
544
+ val clt = recheckClosure(expr, pt)
545
+ inContext(ctx.withOwner(mdef.symbol)):
546
+ mapRootsIn(clt)
528
547
end recheckClosureBlock
529
548
530
549
override def recheckValDef (tree : ValDef , sym : Symbol )(using Context ): Unit =
@@ -621,19 +640,23 @@ class CheckCaptures extends Recheck, SymTransformer:
621
640
* adding all references in the boxed capture set to the current environment.
622
641
*/
623
642
override def recheck (tree : Tree , pt : Type = WildcardType )(using Context ): Type =
624
- val saved = curEnv
625
- tree match
626
- case _ : RefTree | closureDef(_) if pt.isBoxedCapturing =>
627
- curEnv = Env (curEnv.owner, EnvKind .Boxed , CaptureSet .Var (curEnv.owner), curEnv)
628
- case _ if tree.hasAttachment(ClosureBodyValue ) =>
629
- curEnv = Env (curEnv.owner, EnvKind .ClosureResult , CaptureSet .Var (curEnv.owner), curEnv)
630
- case _ =>
631
- val res =
632
- try super .recheck(tree, pt)
633
- finally curEnv = saved
634
- if tree.isTerm && ! pt.isBoxedCapturing then
635
- markFree(res.boxedCaptureSet, tree.srcPos)
636
- res
643
+ try
644
+ val saved = curEnv
645
+ tree match
646
+ case _ : RefTree | closureDef(_) if pt.isBoxedCapturing =>
647
+ curEnv = Env (curEnv.owner, EnvKind .Boxed , CaptureSet .Var (curEnv.owner), curEnv)
648
+ case _ if tree.hasAttachment(ClosureBodyValue ) =>
649
+ curEnv = Env (curEnv.owner, EnvKind .ClosureResult , CaptureSet .Var (curEnv.owner), curEnv)
650
+ case _ =>
651
+ val res =
652
+ try super .recheck(tree, pt)
653
+ finally curEnv = saved
654
+ if tree.isTerm && ! pt.isBoxedCapturing then
655
+ markFree(res.boxedCaptureSet, tree.srcPos)
656
+ res
657
+ catch case ex : LevelException =>
658
+ levelError(ex, tree.srcPos)
659
+ UnspecifiedErrorType
637
660
638
661
/** If `tree` is a reference or an application where the result type refers
639
662
* to an enclosing class or method parameter of the reference, check that the result type
@@ -941,7 +964,8 @@ class CheckCaptures extends Recheck, SymTransformer:
941
964
def traverse (t : Tree )(using Context ) =
942
965
t match
943
966
case t : Template =>
944
- checkAllOverrides(ctx.owner.asClass, OverridingPairsCheckerCC (_, _, t))
967
+ try checkAllOverrides(ctx.owner.asClass, OverridingPairsCheckerCC (_, _, t))
968
+ catch case ex : LevelException => levelError(ex, t.srcPos)
945
969
case _ =>
946
970
traverseChildren(t)
947
971
@@ -1014,7 +1038,7 @@ class CheckCaptures extends Recheck, SymTransformer:
1014
1038
capt.println(i " checked $root with $selfType" )
1015
1039
end checkSelfTypes
1016
1040
1017
- /** Heal ill-formed capture sets in the type parameter.
1041
+ /** Heal ill-formed capture sets in the type parameter of function `meth`
1018
1042
*
1019
1043
* We can push parameter refs into a capture set in type parameters
1020
1044
* that this type parameter can't see.
@@ -1032,28 +1056,32 @@ class CheckCaptures extends Recheck, SymTransformer:
1032
1056
* compensate this by pushing the widened capture set of `f` into ?1.
1033
1057
* This solves the soundness issue caused by the ill-formness of ?1.
1034
1058
*/
1035
- private def healTypeParam (tree : Tree )(using Context ): Unit =
1059
+ private def healTypeParam (meth : Symbol , tree : Tree )(using Context ): Unit =
1036
1060
val checker = new TypeTraverser :
1037
1061
private var allowed : SimpleIdentitySet [TermParamRef ] = SimpleIdentitySet .empty
1038
1062
1039
1063
private def isAllowed (ref : CaptureRef ): Boolean = ref match
1040
1064
case ref : TermParamRef => allowed.contains(ref)
1065
+ case ref : TermRef => ! (ref.isLocalRootCapability && ref.symbol.owner == meth)
1041
1066
case _ => true
1042
1067
1043
1068
private def healCaptureSet (cs : CaptureSet ): Unit =
1044
1069
cs.ensureWellformed: elems =>
1045
1070
ctx ?=>
1046
1071
var seen = new util.HashSet [CaptureRef ]
1047
- def recur (elems : List [CaptureRef ]): Unit =
1072
+ def recur (elems : List [CaptureRef ], prev : Option [ CaptureRef ] ): Unit =
1048
1073
for ref <- elems do
1049
1074
if ! isAllowed(ref) && ! seen.contains(ref) then
1075
+ if ref.isLocalRootCapability
1076
+ && ref.termSymbol.nestingLevel > cs.owner.nestingLevel then
1077
+ throw LevelException (cs.owner, prev.getOrElse(ref))
1050
1078
seen += ref
1051
1079
val widened = ref.captureSetOfInfo
1052
1080
val added = widened.filter(isAllowed(_))
1053
1081
capt.println(i " heal $ref in $cs by widening to $added" )
1054
1082
checkSubset(added, cs, tree.srcPos)
1055
- recur(widened.elems.toList)
1056
- recur(elems)
1083
+ recur(widened.elems.toList, Some (ref) )
1084
+ recur(elems, None )
1057
1085
1058
1086
def traverse (tp : Type ) =
1059
1087
tp match
@@ -1144,7 +1172,9 @@ class CheckCaptures extends Recheck, SymTransformer:
1144
1172
checkBounds(normArgs, tl)
1145
1173
case _ =>
1146
1174
1147
- args.foreach(healTypeParam(_))
1175
+ args.foreach: arg =>
1176
+ try healTypeParam(fun.symbol, arg)
1177
+ catch case ex : LevelException => levelError(ex, arg.srcPos)
1148
1178
case _ =>
1149
1179
end check
1150
1180
end checker
0 commit comments