@@ -385,34 +385,8 @@ class CheckCaptures extends Recheck, SymTransformer:
385385        //  Only captured references that are visible from the environment
386386        //  should be included.
387387        val  included  =  cs.filter:  c => 
388-           c.stripReach match 
389-             case  ref : NamedType  => 
390-               val  refSym  =  ref.symbol
391-               val  refOwner  =  refSym.owner
392-               val  isVisible  =  isVisibleFromEnv(refOwner, env)
393-               if  isVisible &&  ! ref.isRootCapability then 
394-                 ref match 
395-                   case  ref : TermRef  if  ref.prefix `ne` NoPrefix  => 
396-                     //  If c is a path of a class defined outside the environment,
397-                     //  we check the capture set of its info.
398-                     checkSubsetEnv(ref.captureSetOfInfo, env)
399-                   case  _ => 
400-               if  ! isVisible
401-                   &&  (c.isReach ||  ref.isType)
402-                   &&  (! ccConfig.useSealed ||  refSym.is(Param ))
403-                   &&  refOwner ==  env.owner
404-               then 
405-                 if  refSym.hasAnnotation(defn.UnboxAnnot ) then 
406-                   capt.println(i " exempt:  $ref in  $refOwner" )
407-                 else 
408-                   //  Reach capabilities that go out of scope have to be approximated
409-                   //  by their underlying capture set, which cannot be universal.
410-                   //  Reach capabilities of @unboxed parameters are exempted.
411-                   val  cs  =  CaptureSet .ofInfo(c)
412-                   cs.disallowRootCapability:  () => 
413-                     report.error(em " Local reach capability  $c leaks into capture scope of  ${env.ownerString}" , pos)
414-                   checkSubset(cs, env.captured, pos, provenance(env))
415-               isVisible
388+           c.stripReach.pathRoot match 
389+             case  ref : NamedType  =>  isVisibleFromEnv(ref.symbol.owner, env)
416390            case  ref : ThisType  =>  isVisibleFromEnv(ref.cls, env)
417391            case  _ =>  false 
418392        checkSubset(included, env.captured, pos, provenance(env))
@@ -424,48 +398,14 @@ class CheckCaptures extends Recheck, SymTransformer:
424398    end  markFree 
425399
426400    /**  Include references captured by the called method in the current environment stack */  
427-     def  includeCallCaptures (sym : Symbol , pos : SrcPos )(using  Context ):  Unit  = 
428-       if  sym.exists &&  curEnv.isOpen then  markFree(capturedVars(sym), pos)
429- 
430-     private  val  prefixCalls  =  util.EqHashSet [GenericApply ]()
431-     private  val  unboxedArgs  =  util.EqHashSet [Tree ]()
432- 
433-     def  handleCall (meth : Symbol , call : GenericApply , eval : () =>  Type )(using  Context ):  Type  = 
434-       if  prefixCalls.remove(call) then  return  eval()
435- 
436-       val  unboxedParamNames  = 
437-         meth.rawParamss.flatMap:  params => 
438-           params.collect: 
439-             case  param if  param.hasAnnotation(defn.UnboxAnnot ) => 
440-               param.name
441-         .toSet
442- 
443-       def  markUnboxedArgs (call : GenericApply ):  Unit  =  call.fun.tpe.widen match 
444-         case  MethodType (pnames) => 
445-           for  (pname, arg) <-  pnames.lazyZip(call.args) do 
446-             if  unboxedParamNames.contains(pname) then 
447-               unboxedArgs.add(arg)
448-         case  _ => 
449- 
450-       def  markPrefixCalls (tree : Tree ):  Unit  =  tree match 
451-         case  tree : GenericApply  => 
452-           prefixCalls.add(tree)
453-           markUnboxedArgs(tree)
454-           markPrefixCalls(tree.fun)
455-         case  _ => 
456- 
457-       markUnboxedArgs(call)
458-       markPrefixCalls(call.fun)
459-       val  res  =  eval()
460-       includeCallCaptures(meth, call.srcPos)
461-       res
462-     end  handleCall 
401+     def  includeCallCaptures (sym : Symbol , resType : Type , pos : SrcPos )(using  Context ):  Unit  =  resType match 
402+       case  _ : MethodOrPoly  =>  //  wait until method is fully applied
403+       case  _ => 
404+         if  sym.exists &&  curEnv.isOpen then  markFree(capturedVars(sym), pos)
463405
464406    override  def  recheckIdent (tree : Ident , pt : Type )(using  Context ):  Type  = 
465407      if  tree.symbol.is(Method ) then 
466-         if  tree.symbol.info.isParameterless then 
467-           //  there won't be an apply; need to include call captures now
468-           includeCallCaptures(tree.symbol, tree.srcPos)
408+         includeCallCaptures(tree.symbol, tree.symbol.info, tree.srcPos)
469409      else  if  ! tree.symbol.isStatic then 
470410        // debugShowEnvs()
471411        def  addSelects (ref : TermRef , pt : Type ):  TermRef  =  pt match 
@@ -570,15 +510,16 @@ class CheckCaptures extends Recheck, SymTransformer:
570510            tp.derivedCapturingType(forceBox(parent), refs)
571511        mapArgUsing(forceBox)
572512      else 
573-         handleCall(meth, tree, () =>  super .recheckApply(tree, pt))
513+         val  res  =  super .recheckApply(tree, pt)
514+         includeCallCaptures(meth, res, tree.srcPos)
515+         res
574516    end  recheckApply 
575517
576518    protected  override 
577519    def  recheckArg (arg : Tree , formal : Type )(using  Context ):  Type  = 
578520      val  argType  =  recheck(arg, formal)
579-       if  unboxedArgs.contains(arg) then 
580-         capt.println(i " charging deep capture set of  $arg:  ${argType} =  ${argType.deepCaptureSet}" )
581-         markFree(argType.deepCaptureSet, arg.srcPos)
521+       capt.println(i " charging deep capture set of  $arg:  ${argType} =  ${argType.deepCaptureSet}" )
522+       markFree(argType.deepCaptureSet, arg.srcPos)
582523      argType
583524
584525    /**  A specialized implementation of the apply rule. 
@@ -589,27 +530,18 @@ class CheckCaptures extends Recheck, SymTransformer:
589530     *  --------------------- 
590531     *  E |- f(a): Tr^C 
591532     * 
592-      *  If the function `f` does not have an `@unboxed` parameter, then 
593-      *  any unboxing it does would be charged to the environment of the function 
594-      *  so they have to appear in Cq. Since any capabilities of the result of the 
595-      *  application must already be present in the application, an upper 
596-      *  approximation of the result capture set is Cq \union Ca, where `Ca` 
597-      *  is the capture set of the argument. 
598-      *  If the function `f` does have an `@unboxed` parameter, then it could in addition 
599-      *  unbox reach capabilities over its formal parameter. Therefore, the approximation 
600-      *  would be `Cq \union dcs(Ca)` instead. 
533+      *  If the type of the function `f` does not mention any formal parameters 
534+      *  any capabilities of the result of the application must already be present in 
535+      *  the application. So an upper approximation of the result capture set is Cq \union Ca, 
536+      *  where `Ca` is the deep capture set of the argument. 
601537     *  If the approximation is known to subcapture the declared result Cr, we pick it for C 
602-      *  otherwise we pick Cr. 
538+      *  otherwise we pick Cr. ???  
603539     */  
604540    protected  override 
605541    def  recheckApplication (tree : Apply , qualType : Type , funType : MethodType , argTypes : List [Type ])(using  Context ):  Type  = 
606542      val  appType  =  Existential .toCap(super .recheckApplication(tree, qualType, funType, argTypes))
607543      val  qualCaptures  =  qualType.captureSet
608-       val  argCaptures  = 
609-         for  (arg, argType) <-  tree.args.lazyZip(argTypes) yield 
610-           if  unboxedArgs.remove(arg) //  need to ensure the remove happens, that's why argCaptures is computed even if not needed.
611-           then  argType.deepCaptureSet
612-           else  argType.captureSet
544+       val  argCaptures  =  argTypes.map(_.deepCaptureSet)
613545      appType match 
614546        case  appType @  CapturingType (appType1, refs)
615547        if  qualType.exists
@@ -704,8 +636,10 @@ class CheckCaptures extends Recheck, SymTransformer:
704636              i " Sealed type variable  $pname" , " be instantiated to" 
705637              i " This is often caused by a local capability $where\n leaking as part of its result. " ,
706638              tree.srcPos)
707-       try  handleCall(meth, tree, () =>  Existential .toCap(super .recheckTypeApply(tree, pt)))
708-       finally  checkContains(tree)
639+       val  res  =  Existential .toCap(super .recheckTypeApply(tree, pt))
640+       includeCallCaptures(meth, res, tree.srcPos)
641+       checkContains(tree)
642+       res
709643    end  recheckTypeApply 
710644
711645    /**  Faced with a tree of form `caps.contansImpl[CS, r.type]`, check that `R` is a tracked 
@@ -1156,12 +1090,7 @@ class CheckCaptures extends Recheck, SymTransformer:
11561090        (erefs /:  erefs.elems):  (erefs, eref) => 
11571091          eref match 
11581092            case  eref : ThisType  if  isPureContext(ctx.owner, eref.cls) => 
1159- 
1160-               def  pathRoot (aref : Type ):  Type  =  aref match 
1161-                 case  aref : NamedType  if  aref.symbol.owner.isClass =>  pathRoot(aref.prefix)
1162-                 case  _ =>  aref
1163- 
1164-               def  isOuterRef (aref : Type ):  Boolean  =  pathRoot(aref) match 
1093+               def  isOuterRef (aref : Type ):  Boolean  =  aref.pathRoot match 
11651094                case  aref : NamedType  =>  eref.cls.isProperlyContainedIn(aref.symbol.owner)
11661095                case  aref : ThisType  =>  eref.cls.isProperlyContainedIn(aref.cls)
11671096                case  _ =>  false 
@@ -1171,7 +1100,7 @@ class CheckCaptures extends Recheck, SymTransformer:
11711100              //  Include implicitly added outer references in the capture set of the class of `eref`.
11721101              for  outerRef <-  outerRefs.elems do 
11731102                if  ! erefs.elems.contains(outerRef)
1174-                     &&  ! pathRoot( outerRef) .isInstanceOf [ThisType ]
1103+                     &&  ! outerRef.pathRoot .isInstanceOf [ThisType ]
11751104                    //  we don't need to add outer ThisTypes as these are anyway added as path
11761105                    //  prefixes at the use site. And this exemption is required since capture sets
11771106                    //  of non-local classes are always empty, so we can't add an outer this to them.
@@ -1328,6 +1257,12 @@ class CheckCaptures extends Recheck, SymTransformer:
13281257
13291258    /**  If actual is a tracked CaptureRef `a` and widened is a capturing type T^C, 
13301259     *  improve `T^C` to `T^{a}`, following the VAR rule of CC. 
1260+      *  TODO: We probably should do this also for other top-level occurrences of captures 
1261+      *  E.g. 
1262+      *    class Foo { def a: C^{io}; val def: C^{async} } 
1263+      *    val foo: Foo^{io, async} 
1264+      *  Then 
1265+      *    foo: Foo { def a: C^{foo}; def b: C^{foo} }^{foo} 
13311266     */  
13321267    private  def  improveCaptures (widened : Type , actual : Type )(using  Context ):  Type  =  actual match 
13331268      case  ref : CaptureRef  if  ref.isTracked => 
@@ -1388,21 +1323,6 @@ class CheckCaptures extends Recheck, SymTransformer:
13881323          ! setup.isPreCC(overriding) &&  ! setup.isPreCC(overridden)
13891324
13901325        override  def  checkInheritedTraitParameters :  Boolean  =  false 
1391- 
1392-         /**  Check that overrides don't change the @unbox status of their parameters */  
1393-         override  def  additionalChecks (member : Symbol , other : Symbol )(using  Context ):  Unit  = 
1394-           for 
1395-             (params1, params2) <-  member.rawParamss.lazyZip(other.rawParamss)
1396-             (param1, param2) <-  params1.lazyZip(params2)
1397-           do 
1398-             if  param1.hasAnnotation(defn.UnboxAnnot ) !=  param2.hasAnnotation(defn.UnboxAnnot ) then 
1399-               report.error(
1400-                 OverrideError (
1401-                     i " has a parameter  ${param1.name} with different @unbox status than the corresponding parameter in the overridden definition " ,
1402-                     self, member, other, self.memberInfo(member), self.memberInfo(other)
1403-                   ),
1404-                 if  member.owner ==  clazz then  member.srcPos else  clazz.srcPos
1405-               )
14061326      end  OverridingPairsCheckerCC 
14071327
14081328      def  traverse (t : Tree )(using  Context ) = 
0 commit comments