@@ -122,10 +122,6 @@ object CheckCaptures:
122122 * This check is performed at Typer.
123123 */
124124 def checkWellformed (parent : Tree , ann : Tree )(using Context ): Unit =
125- parent.tpe match
126- case _ : SingletonType =>
127- report.error(em " Singleton type $parent cannot have capture set " , parent.srcPos)
128- case _ =>
129125 def check (elem : Tree , pos : SrcPos ): Unit = elem.tpe match
130126 case ref : CaptureRef =>
131127 if ! ref.isTrackableRef then
@@ -373,45 +369,54 @@ class CheckCaptures extends Recheck, SymTransformer:
373369 * the environment's owner
374370 */
375371 def markFree (cs : CaptureSet , pos : SrcPos )(using Context ): Unit =
372+ // A captured reference with the symbol `sym` is visible from the environment
373+ // if `sym` is not defined inside the owner of the environment.
374+ inline def isVisibleFromEnv (sym : Symbol , env : Env ) =
375+ if env.kind == EnvKind .NestedInOwner then
376+ ! sym.isProperlyContainedIn(env.owner)
377+ else
378+ ! sym.isContainedIn(env.owner)
379+
380+ def checkSubsetEnv (cs : CaptureSet , env : Env )(using Context ): Unit =
381+ // Only captured references that are visible from the environment
382+ // should be included.
383+ val included = cs.filter: c =>
384+ c.stripReach match
385+ case ref : NamedType =>
386+ val refSym = ref.symbol
387+ val refOwner = refSym.owner
388+ val isVisible = isVisibleFromEnv(refOwner, env)
389+ if isVisible && ! ref.isRootCapability then
390+ ref match
391+ case ref : TermRef if ref.prefix `ne` NoPrefix =>
392+ // If c is a path of a class defined outside the environment,
393+ // we check the capture set of its info.
394+ checkSubsetEnv(ref.captureSetOfInfo, env)
395+ case _ =>
396+ if ! isVisible
397+ && (c.isReach || ref.isType)
398+ && (! ccConfig.useSealed || refSym.is(Param ))
399+ && refOwner == env.owner
400+ then
401+ if refSym.hasAnnotation(defn.UnboxAnnot ) then
402+ capt.println(i " exempt: $ref in $refOwner" )
403+ else
404+ // Reach capabilities that go out of scope have to be approximated
405+ // by their underlying capture set, which cannot be universal.
406+ // Reach capabilities of @unboxed parameters are exempted.
407+ val cs = CaptureSet .ofInfo(c)
408+ cs.disallowRootCapability: () =>
409+ report.error(em " Local reach capability $c leaks into capture scope of ${env.ownerString}" , pos)
410+ checkSubset(cs, env.captured, pos, provenance(env))
411+ isVisible
412+ case ref : ThisType => isVisibleFromEnv(ref.cls, env)
413+ case _ => false
414+ checkSubset(included, env.captured, pos, provenance(env))
415+ capt.println(i " Include call or box capture $included from $cs in ${env.owner} --> ${env.captured}" )
416+
376417 if ! cs.isAlwaysEmpty then
377418 forallOuterEnvsUpTo(ctx.owner.topLevelClass): env =>
378- // Whether a symbol is defined inside the owner of the environment?
379- inline def isContainedInEnv (sym : Symbol ) =
380- if env.kind == EnvKind .NestedInOwner then
381- sym.isProperlyContainedIn(env.owner)
382- else
383- sym.isContainedIn(env.owner)
384- // A captured reference with the symbol `sym` is visible from the environment
385- // if `sym` is not defined inside the owner of the environment
386- inline def isVisibleFromEnv (sym : Symbol ) = ! isContainedInEnv(sym)
387- // Only captured references that are visible from the environment
388- // should be included.
389- val included = cs.filter: c =>
390- c.stripReach match
391- case ref : NamedType =>
392- val refSym = ref.symbol
393- val refOwner = refSym.owner
394- val isVisible = isVisibleFromEnv(refOwner)
395- if ! isVisible
396- && (c.isReach || ref.isType)
397- && (! ccConfig.useSealed || refSym.is(Param ))
398- && refOwner == env.owner
399- then
400- if refSym.hasAnnotation(defn.UnboxAnnot ) then
401- capt.println(i " exempt: $ref in $refOwner" )
402- else
403- // Reach capabilities that go out of scope have to be approximated
404- // by their underlying capture set, which cannot be universal.
405- // Reach capabilities of @unboxed parameters are exempted.
406- val cs = CaptureSet .ofInfo(c)
407- cs.disallowRootCapability: () =>
408- report.error(em " Local reach capability $c leaks into capture scope of ${env.ownerString}" , pos)
409- checkSubset(cs, env.captured, pos, provenance(env))
410- isVisible
411- case ref : ThisType => isVisibleFromEnv(ref.cls)
412- case _ => false
413- checkSubset(included, env.captured, pos, provenance(env))
414- capt.println(i " Include call or box capture $included from $cs in ${env.owner} --> ${env.captured}" )
419+ checkSubsetEnv(cs, env)
415420 end markFree
416421
417422 /** Include references captured by the called method in the current environment stack */
@@ -488,21 +493,28 @@ class CheckCaptures extends Recheck, SymTransformer:
488493 case _ => denot
489494
490495 val selType = recheckSelection(tree, qualType, name, disambiguate)
491- val selCs = selType.widen.captureSet
492- if selCs.isAlwaysEmpty
493- || selType.widen.isBoxedCapturing
496+ val selWiden = selType.widen
497+ def isStableSel = selType match
498+ case selType : NamedType => selType.symbol.isStableMember
499+ case _ => false
500+
501+ if pt == LhsProto
494502 || qualType.isBoxedCapturing
495- || pt == LhsProto
503+ || selType.isTrackableRef
504+ || selWiden.isBoxedCapturing
505+ || selWiden.captureSet.isAlwaysEmpty
496506 then
497507 selType
498508 else
499509 val qualCs = qualType.captureSet
500- capt.println(i " pick one of $qualType, ${selType.widen}, $qualCs, $selCs in $tree" )
510+ val selCs = selType.captureSet
511+ capt.println(i " pick one of $qualType, ${selType.widen}, $qualCs, $selCs ${selWiden.captureSet} in $tree" )
512+
501513 if qualCs.mightSubcapture(selCs)
502514 && ! selCs.mightSubcapture(qualCs)
503515 && ! pt.stripCapturing.isInstanceOf [SingletonType ]
504516 then
505- selType.widen .stripCapturing.capturing(qualCs)
517+ selWiden .stripCapturing.capturing(qualCs)
506518 .showing(i " alternate type for select $tree: $selType --> $result, $qualCs / $selCs" , capt)
507519 else
508520 selType
0 commit comments