@@ -328,20 +328,22 @@ class CheckCaptures extends Recheck, SymTransformer:
328328        then  CaptureSet .Var (sym.owner, level =  sym.ccLevel)
329329        else  CaptureSet .empty)
330330
331-     /**  For all nested environments up to `limit` or a closed environment perform `op`, 
332-      *  but skip environmenrts directly enclosing environments of kind ClosureResult. 
331+     /**  The next environment enclosing `env` that needs to be charged 
332+      *  with free references. 
333+      *  Skips environments directly enclosing environments of kind ClosureResult. 
334+      *  @param  included  Whether an environment is included in the range of 
335+      *                  environments to charge. Once `included` is false, no 
336+      *                  more environments need to be charged. 
333337     */  
334-     def  forallOuterEnvsUpTo (limit : Symbol )(op : Env  =>  Unit )(using  Context ):  Unit  = 
335-       def  recur (env : Env , skip : Boolean ):  Unit  = 
336-         if  env.isOpen &&  env.owner !=  limit then 
337-           if  ! skip then  op(env)
338-           if  ! env.isOutermost then 
339-             var  nextEnv  =  env.outer
340-             if  env.owner.isConstructor then 
341-               if  nextEnv.owner !=  limit &&  ! nextEnv.isOutermost then 
342-                 nextEnv =  nextEnv.outer
343-             recur(nextEnv, skip =  env.kind ==  EnvKind .ClosureResult )
344-       recur(curEnv, skip =  false )
338+     def  nextEnvToCharge (env : Env , included : Env  =>  Boolean )(using  Context ):  Env  = 
339+       var  nextEnv  =  env.outer
340+       if  env.owner.isConstructor then 
341+         if  included(nextEnv) then  nextEnv =  nextEnv.outer
342+       if  env.kind ==  EnvKind .ClosureResult  then 
343+         //  skip this one
344+         nextEnvToCharge(nextEnv, included)
345+       else 
346+         nextEnv
345347
346348    /**  A description where this environment comes from */  
347349    private  def  provenance (env : Env )(using  Context ):  String  = 
@@ -355,7 +357,6 @@ class CheckCaptures extends Recheck, SymTransformer:
355357      else 
356358        i " \n of the enclosing  ${owner.showLocated}" 
357359
358- 
359360    /**  Include `sym` in the capture sets of all enclosing environments nested in the 
360361     *  the environment in which `sym` is defined. 
361362     */  
@@ -364,9 +365,12 @@ class CheckCaptures extends Recheck, SymTransformer:
364365
365366    def  markFree (sym : Symbol , ref : TermRef , pos : SrcPos )(using  Context ):  Unit  = 
366367      if  sym.exists &&  ref.isTracked then 
367-         forallOuterEnvsUpTo(sym.enclosure):  env => 
368-           capt.println(i " Mark  $sym with cs  ${ref.captureSet} free in  ${env.owner}" )
369-           checkElem(ref, env.captured, pos, provenance(env))
368+         def  recur (env : Env ):  Unit  = 
369+           if  env.isOpen &&  env.owner !=  sym.enclosure then 
370+             capt.println(i " Mark  $sym with cs  ${ref.captureSet} free in  ${env.owner}" )
371+             checkElem(ref, env.captured, pos, provenance(env))
372+             recur(nextEnvToCharge(env, _.owner !=  sym.enclosure))
373+         recur(curEnv)
370374
371375    /**  Make sure (projected) `cs` is a subset of the capture sets of all enclosing 
372376     *  environments. At each stage, only include references from `cs` that are outside 
@@ -381,20 +385,30 @@ class CheckCaptures extends Recheck, SymTransformer:
381385        else 
382386          ! sym.isContainedIn(env.owner)
383387
384-       def  checkSubsetEnv (cs : CaptureSet , env : Env )(using  Context ):  Unit  = 
385-         //  Only captured references that are visible from the environment
386-         //  should be included.
387-         val  included  =  cs.filter:  c => 
388-           c.stripReach.pathRoot match 
389-             case  ref : NamedType  =>  isVisibleFromEnv(ref.symbol.owner, env)
390-             case  ref : ThisType  =>  isVisibleFromEnv(ref.cls, env)
391-             case  _ =>  false 
392-         checkSubset(included, env.captured, pos, provenance(env))
393-         capt.println(i " Include call or box capture  $included from  $cs in  ${env.owner} -->  ${env.captured}" )
394- 
395-       if  ! cs.isAlwaysEmpty then 
396-         forallOuterEnvsUpTo(ctx.owner.topLevelClass):  env => 
397-           checkSubsetEnv(cs, env)
388+       def  recur (cs : CaptureSet , env : Env )(using  Context ):  Unit  = 
389+         if  env.isOpen &&  ! env.owner.isStaticOwner &&  ! cs.isAlwaysEmpty then 
390+           //  Only captured references that are visible from the environment
391+           //  should be included.
392+           val  included  =  cs.filter:  c => 
393+             val  isVisible  =  c.stripReach.pathRoot match 
394+               case  ref : NamedType  =>  isVisibleFromEnv(ref.symbol.owner, env)
395+               case  ref : ThisType  =>  isVisibleFromEnv(ref.cls, env)
396+               case  _ =>  false 
397+             c match 
398+               case  ReachCapability (c1) if  ! isVisible &&  ! c1.isParamPath => 
399+                 //  When a reach capabilty x* where `x` is not a parameter goes out
400+                 //  of scope, we need to continue with `x`'s underlying deep capture set.
401+                 //  The same is not an issue for normal capabilities since in a local
402+                 //  definition `val x = e`, the capabilities of `e` have already been charged.
403+                 val  underlying  =  CaptureSet .ofTypeDeeply(c1.widen)
404+                 capt.println(i " Widen reach  $c to  $underlying in  ${env.owner}" )
405+                 recur(underlying, env)
406+               case  _ => 
407+             isVisible
408+           checkSubset(included, env.captured, pos, provenance(env))
409+           capt.println(i " Include call or box capture  $included from  $cs in  ${env.owner} -->  ${env.captured}" )
410+           recur(included, nextEnvToCharge(env, ! _.owner.isStaticOwner))
411+       recur(cs, curEnv)
398412    end  markFree 
399413
400414    /**  Include references captured by the called method in the current environment stack */  
0 commit comments