@@ -14,14 +14,14 @@ import typer.RefChecks.{checkAllOverrides, checkSelfAgainstParents, OverridingPa
1414import typer .Checking .{checkBounds , checkAppliedTypesIn }
1515import typer .ErrorReporting .{Addenda , err }
1616import typer .ProtoTypes .{AnySelectionProto , LhsProto }
17- import util .{SimpleIdentitySet , EqHashMap , SrcPos , Property }
17+ import util .{SimpleIdentitySet , EqHashMap , EqHashSet , SrcPos , Property }
1818import transform .SymUtils .*
19- import transform .{Recheck , PreRecheck }
19+ import transform .{Recheck , PreRecheck , CapturedVars }
2020import Recheck .*
2121import scala .collection .mutable
2222import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap , CompareResult }
2323import StdNames .nme
24- import NameKinds .DefaultGetterName
24+ import NameKinds .{ DefaultGetterName , WildcardParamName }
2525import reporting .trace
2626
2727/** The capture checker */
@@ -147,33 +147,49 @@ object CheckCaptures:
147147 private def disallowRootCapabilitiesIn (tp : Type , carrier : Symbol , what : String , have : String , addendum : String , pos : SrcPos )(using Context ) =
148148 val check = new TypeTraverser :
149149
150+ private val seen = new EqHashSet [TypeRef ]
151+
152+ /** Check that there is at least one method containing carrier and defined
153+ * in the scope of tparam. E.g. this is OK:
154+ * def f[T] = { ... var x: T ... }
155+ * So is this:
156+ * class C[T] { def f() = { class D { var x: T }}}
157+ * But this is not OK:
158+ * class C[T] { object o { var x: T }}
159+ */
150160 extension (tparam : Symbol ) def isParametricIn (carrier : Symbol ): Boolean =
151- val encl = carrier.owner.enclosingMethodOrClass
152- if encl.isClass then tparam.isParametricIn(encl)
153- else
154- def recur (encl : Symbol ): Boolean =
155- if tparam.owner == encl then true
156- else if encl.isStatic || ! encl.exists then false
157- else recur(encl.owner.enclosingMethodOrClass)
158- recur(encl)
161+ carrier.exists && {
162+ val encl = carrier.owner.enclosingMethodOrClass
163+ if encl.isClass then tparam.isParametricIn(encl)
164+ else
165+ def recur (encl : Symbol ): Boolean =
166+ if tparam.owner == encl then true
167+ else if encl.isStatic || ! encl.exists then false
168+ else recur(encl.owner.enclosingMethodOrClass)
169+ recur(encl)
170+ }
159171
160172 def traverse (t : Type ) =
161173 t.dealiasKeepAnnots match
162174 case t : TypeRef =>
163- capt.println(i " disallow $t, $tp, $what, ${t.symbol.is(Sealed )}" )
164- t.info match
165- case TypeBounds (_, hi)
166- if ! t.symbol.is(Sealed ) && ! t.symbol.isParametricIn(carrier) =>
167- if hi.isAny then
168- report.error(
169- em """ $what cannot $have $tp since
170- |that type refers to the type variable $t, which is not sealed.
171- | $addendum""" ,
172- pos)
173- else
174- traverse(hi)
175- case _ =>
176- traverseChildren(t)
175+ if ! seen.contains(t) then
176+ capt.println(i " disallow $t, $tp, $what, ${t.isSealed}" )
177+ seen += t
178+ t.info match
179+ case TypeBounds (_, hi) if ! t.isSealed && ! t.symbol.isParametricIn(carrier) =>
180+ if hi.isAny then
181+ val detailStr =
182+ if t eq tp then " variable"
183+ else i " refers to the type variable $t, which "
184+ report.error(
185+ em """ $what cannot $have $tp since
186+ |that type $detailStr is not sealed.
187+ | $addendum""" ,
188+ pos)
189+ else
190+ traverse(hi)
191+ case _ =>
192+ traverseChildren(t)
177193 case AnnotatedType (_, ann) if ann.symbol == defn.UncheckedCapturesAnnot =>
178194 ()
179195 case t =>
@@ -260,11 +276,12 @@ class CheckCaptures extends Recheck, SymTransformer:
260276 pos, provenance)
261277
262278 /** Check subcapturing `cs1 <: cs2`, report error on failure */
263- def checkSubset (cs1 : CaptureSet , cs2 : CaptureSet , pos : SrcPos , provenance : => String = " " )(using Context ) =
279+ def checkSubset (cs1 : CaptureSet , cs2 : CaptureSet , pos : SrcPos ,
280+ provenance : => String = " " , cs1description : String = " " )(using Context ) =
264281 checkOK(
265282 cs1.subCaptures(cs2, frozen = false ),
266- if cs1.elems.size == 1 then i " reference ${cs1.elems.toList.head} is not "
267- else i " references $cs1 are not all " ,
283+ if cs1.elems.size == 1 then i " reference ${cs1.elems.toList.head}$cs1description is not "
284+ else i " references $cs1$cs1description are not all " ,
268285 pos, provenance)
269286
270287 /** The current environment */
@@ -542,10 +559,10 @@ class CheckCaptures extends Recheck, SymTransformer:
542559 val TypeApply (fn, args) = tree
543560 val polyType = atPhase(thisPhase.prev):
544561 fn.tpe.widen.asInstanceOf [TypeLambda ]
545- for case (arg : TypeTree , pinfo , pname) <- args.lazyZip(polyType.paramInfos ).lazyZip((polyType.paramNames)) do
546- if pinfo.bounds.hi.hasAnnotation(defn. Caps_SealedAnnot ) then
562+ for case (arg : TypeTree , formal , pname) <- args.lazyZip(polyType.paramRefs ).lazyZip((polyType.paramNames)) do
563+ if formal.isSealed then
547564 def where = if fn.symbol.exists then i " in an argument of ${fn.symbol}" else " "
548- disallowRootCapabilitiesIn(arg.knownType, fn.symbol ,
565+ disallowRootCapabilitiesIn(arg.knownType, NoSymbol ,
549566 i " Sealed type variable $pname" , " be instantiated to" ,
550567 i " This is often caused by a local capability $where\n leaking as part of its result. " ,
551568 tree.srcPos)
@@ -586,13 +603,58 @@ class CheckCaptures extends Recheck, SymTransformer:
586603 openClosures = openClosures.tail
587604 end recheckClosureBlock
588605
606+ /** Maps mutable variables to the symbols that capture them (in the
607+ * CheckCaptures sense, i.e. symbol is referred to from a different method
608+ * than the one it is defined in).
609+ */
610+ private val capturedBy = util.HashMap [Symbol , Symbol ]()
611+
612+ /** Maps anonymous functions appearing as function arguments to
613+ * the function that is called.
614+ */
615+ private val anonFunCallee = util.HashMap [Symbol , Symbol ]()
616+
617+ /** Populates `capturedBy` and `anonFunCallee`. Called by `checkUnit`.
618+ */
619+ private def collectCapturedMutVars (using Context ) = new TreeTraverser :
620+ def traverse (tree : Tree )(using Context ) = tree match
621+ case id : Ident =>
622+ val sym = id.symbol
623+ if sym.is(Mutable , butNot = Method ) && sym.owner.isTerm then
624+ val enclMeth = ctx.owner.enclosingMethod
625+ if sym.enclosingMethod != enclMeth then
626+ capturedBy(sym) = enclMeth
627+ case Apply (fn, args) =>
628+ for case closureDef(mdef) <- args do
629+ anonFunCallee(mdef.symbol) = fn.symbol
630+ traverseChildren(tree)
631+ case Inlined (_, bindings, expansion) =>
632+ traverse(bindings)
633+ traverse(expansion)
634+ case mdef : DefDef =>
635+ if ! mdef.symbol.isInlineMethod then traverseChildren(tree)
636+ case _ =>
637+ traverseChildren(tree)
638+
589639 override def recheckValDef (tree : ValDef , sym : Symbol )(using Context ): Type =
590640 try
591641 if sym.is(Module ) then sym.info // Modules are checked by checking the module class
592642 else
593643 if sym.is(Mutable ) && ! sym.hasAnnotation(defn.UncheckedCapturesAnnot ) then
594- disallowRootCapabilitiesIn(tree.tpt.knownType, sym,
595- i " mutable $sym" , " have type" , " " , sym.srcPos)
644+ val (carrier, addendum) = capturedBy.get(sym) match
645+ case Some (encl) =>
646+ val enclStr =
647+ if encl.isAnonymousFunction then
648+ val location = anonFunCallee.get(encl) match
649+ case Some (meth) if meth.exists => i " argument in a call to $meth"
650+ case _ => " "
651+ s " an anonymous function $location"
652+ else encl.show
653+ (NoSymbol , i " \n Note that $sym does not count as local since it is captured by $enclStr" )
654+ case _ =>
655+ (sym, " " )
656+ disallowRootCapabilitiesIn(
657+ tree.tpt.knownType, carrier, i " Mutable $sym" , " have type" , addendum, sym.srcPos)
596658 checkInferredResult(super .recheckValDef(tree, sym), tree)
597659 finally
598660 if ! sym.is(Param ) then
@@ -680,9 +742,15 @@ class CheckCaptures extends Recheck, SymTransformer:
680742 if ! param.hasAnnotation(defn.ConstructorOnlyAnnot ) then
681743 checkSubset(param.termRef.captureSet, thisSet, param.srcPos) // (3)
682744 for pureBase <- cls.pureBaseClass do // (4)
745+ def selfType = impl.body
746+ .collect:
747+ case TypeDef (tpnme.SELF , rhs) => rhs
748+ .headOption
749+ .getOrElse(tree)
750+ .orElse(tree)
683751 checkSubset(thisSet,
684752 CaptureSet .empty.withDescription(i " of pure base class $pureBase" ),
685- tree .srcPos)
753+ selfType .srcPos, cs1description = " captured by this self type " )
686754 super .recheckClassDef(tree, impl, cls)
687755 finally
688756 curEnv = saved
@@ -1122,6 +1190,8 @@ class CheckCaptures extends Recheck, SymTransformer:
11221190
11231191 override def needsCheck (overriding : Symbol , overridden : Symbol )(using Context ): Boolean =
11241192 ! setup.isPreCC(overriding) && ! setup.isPreCC(overridden)
1193+
1194+ override def checkInheritedTraitParameters : Boolean = false
11251195 end OverridingPairsCheckerCC
11261196
11271197 def traverse (t : Tree )(using Context ) =
@@ -1158,11 +1228,12 @@ class CheckCaptures extends Recheck, SymTransformer:
11581228 private val setup : SetupAPI = thisPhase.prev.asInstanceOf [Setup ]
11591229
11601230 override def checkUnit (unit : CompilationUnit )(using Context ): Unit =
1161- setup.setupUnit(ctx.compilationUnit.tpdTree, completeDef)
1231+ setup.setupUnit(unit.tpdTree, completeDef)
1232+ collectCapturedMutVars.traverse(unit.tpdTree)
11621233
11631234 if ctx.settings.YccPrintSetup .value then
11641235 val echoHeader = " [[syntax tree at end of cc setup]]"
1165- val treeString = show(ctx.compilationUnit .tpdTree)
1236+ val treeString = show(unit .tpdTree)
11661237 report.echo(s " $echoHeader\n $treeString\n " )
11671238
11681239 withCaptureSetsExplained :
@@ -1298,6 +1369,39 @@ class CheckCaptures extends Recheck, SymTransformer:
12981369 checker.traverse(tree.knownType)
12991370 end healTypeParam
13001371
1372+ def checkNoLocalRootIn (sym : Symbol , info : Type , pos : SrcPos )(using Context ): Unit =
1373+ val check = new TypeTraverser :
1374+ def traverse (tp : Type ) = tp match
1375+ case tp : TermRef if tp.isLocalRootCapability =>
1376+ if tp.localRootOwner == sym then
1377+ report.error(i " local root $tp cannot appear in type of $sym" , pos)
1378+ case tp : ClassInfo =>
1379+ traverseChildren(tp)
1380+ for mbr <- tp.decls do
1381+ if ! mbr.is(Private ) then checkNoLocalRootIn(sym, mbr.info, mbr.srcPos)
1382+ case _ =>
1383+ traverseChildren(tp)
1384+ check.traverse(info)
1385+
1386+ def checkArraysAreSealedIn (tp : Type , pos : SrcPos )(using Context ): Unit =
1387+ val check = new TypeTraverser :
1388+ def traverse (t : Type ): Unit =
1389+ t match
1390+ case AppliedType (tycon, arg :: Nil ) if tycon.typeSymbol == defn.ArrayClass =>
1391+ if ! (pos.span.isSynthetic && ctx.reporter.errorsReported)
1392+ && ! arg.typeSymbol.name.is(WildcardParamName )
1393+ then
1394+ CheckCaptures .disallowRootCapabilitiesIn(arg, NoSymbol ,
1395+ " Array" , " have element type" ,
1396+ " Since arrays are mutable, they have to be treated like variables,\n so their element type must be sealed." ,
1397+ pos)
1398+ traverseChildren(t)
1399+ case defn.RefinedFunctionOf (rinfo : MethodType ) =>
1400+ traverse(rinfo)
1401+ case _ =>
1402+ traverseChildren(t)
1403+ check.traverse(tp)
1404+
13011405 /** Perform the following kinds of checks
13021406 * - Check all explicitly written capturing types for well-formedness using `checkWellFormedPost`.
13031407 * - Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
@@ -1309,10 +1413,11 @@ class CheckCaptures extends Recheck, SymTransformer:
13091413 val lctx = tree match
13101414 case _ : DefTree | _ : TypeDef if tree.symbol.exists => ctx.withOwner(tree.symbol)
13111415 case _ => ctx
1312- traverseChildren(tree)(using lctx)
1313- check(tree)
1416+ trace(i " post check $tree" ):
1417+ traverseChildren(tree)(using lctx)
1418+ check(tree)
13141419 def check (tree : Tree )(using Context ) = tree match
1315- case t @ TypeApply (fun, args) =>
1420+ case TypeApply (fun, args) =>
13161421 fun.knownType.widen match
13171422 case tl : PolyType =>
13181423 val normArgs = args.lazyZip(tl.paramInfos).map: (arg, bounds) =>
@@ -1321,6 +1426,10 @@ class CheckCaptures extends Recheck, SymTransformer:
13211426 checkBounds(normArgs, tl)
13221427 args.lazyZip(tl.paramNames).foreach(healTypeParam(_, _, fun.symbol))
13231428 case _ =>
1429+ case _ : ValOrDefDef | _ : TypeDef =>
1430+ checkNoLocalRootIn(tree.symbol, tree.symbol.info, tree.symbol.srcPos)
1431+ case tree : TypeTree =>
1432+ checkArraysAreSealedIn(tree.tpe, tree.srcPos)
13241433 case _ =>
13251434 end check
13261435 end checker
0 commit comments