@@ -16,7 +16,7 @@ import typer.ErrorReporting.{Addenda, err}
1616import typer .ProtoTypes .{AnySelectionProto , LhsProto }
1717import 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 }
@@ -149,15 +149,25 @@ object CheckCaptures:
149149
150150 private val seen = new EqHashSet [TypeRef ]
151151
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+ */
152160 extension (tparam : Symbol ) def isParametricIn (carrier : Symbol ): Boolean =
153- val encl = carrier.maybeOwner.enclosingMethodOrClass
154- if encl.isClass then tparam.isParametricIn(encl)
155- else
156- def recur (encl : Symbol ): Boolean =
157- if tparam.owner == encl then true
158- else if encl.isStatic || ! encl.exists then false
159- else recur(encl.owner.enclosingMethodOrClass)
160- 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+ }
161171
162172 def traverse (t : Type ) =
163173 t.dealiasKeepAnnots match
@@ -168,9 +178,12 @@ object CheckCaptures:
168178 t.info match
169179 case TypeBounds (_, hi) if ! t.isSealed && ! t.symbol.isParametricIn(carrier) =>
170180 if hi.isAny then
181+ val detailStr =
182+ if t eq tp then " variable"
183+ else i " refers to the type variable $t, which "
171184 report.error(
172185 em """ $what cannot $have $tp since
173- |that type refers to the type variable $t , which is not sealed.
186+ |that type $detailStr is not sealed.
174187 | $addendum""" ,
175188 pos)
176189 else
@@ -549,7 +562,7 @@ class CheckCaptures extends Recheck, SymTransformer:
549562 for case (arg : TypeTree , formal, pname) <- args.lazyZip(polyType.paramRefs).lazyZip((polyType.paramNames)) do
550563 if formal.isSealed then
551564 def where = if fn.symbol.exists then i " in an argument of ${fn.symbol}" else " "
552- disallowRootCapabilitiesIn(arg.knownType, fn.symbol ,
565+ disallowRootCapabilitiesIn(arg.knownType, NoSymbol ,
553566 i " Sealed type variable $pname" , " be instantiated to" ,
554567 i " This is often caused by a local capability $where\n leaking as part of its result. " ,
555568 tree.srcPos)
@@ -590,13 +603,58 @@ class CheckCaptures extends Recheck, SymTransformer:
590603 openClosures = openClosures.tail
591604 end recheckClosureBlock
592605
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+
593639 override def recheckValDef (tree : ValDef , sym : Symbol )(using Context ): Type =
594640 try
595641 if sym.is(Module ) then sym.info // Modules are checked by checking the module class
596642 else
597643 if sym.is(Mutable ) && ! sym.hasAnnotation(defn.UncheckedCapturesAnnot ) then
598- disallowRootCapabilitiesIn(tree.tpt.knownType, sym,
599- 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)
600658 checkInferredResult(super .recheckValDef(tree, sym), tree)
601659 finally
602660 if ! sym.is(Param ) then
@@ -1168,11 +1226,12 @@ class CheckCaptures extends Recheck, SymTransformer:
11681226 private val setup : SetupAPI = thisPhase.prev.asInstanceOf [Setup ]
11691227
11701228 override def checkUnit (unit : CompilationUnit )(using Context ): Unit =
1171- setup.setupUnit(ctx.compilationUnit.tpdTree, completeDef)
1229+ setup.setupUnit(unit.tpdTree, completeDef)
1230+ collectCapturedMutVars.traverse(unit.tpdTree)
11721231
11731232 if ctx.settings.YccPrintSetup .value then
11741233 val echoHeader = " [[syntax tree at end of cc setup]]"
1175- val treeString = show(ctx.compilationUnit .tpdTree)
1234+ val treeString = show(unit .tpdTree)
11761235 report.echo(s " $echoHeader\n $treeString\n " )
11771236
11781237 withCaptureSetsExplained :
0 commit comments