@@ -301,9 +301,9 @@ class CheckCaptures extends Recheck, SymTransformer:
301301
302302 /** A specialized implementation of the selection rule.
303303 *
304- * E |- f: Cf f { m: Cr R }
305- * ------------------------
306- * E |- f.m: C R
304+ * E |- f: f { m: Cr R }^Cf
305+ * -----------------------
306+ * E |- f.m: R^C
307307 *
308308 * The implementation picks as `C` one of `{f}` or `Cr`, depending on the
309309 * outcome of a `mightSubcapture` test. It picks `{f}` if this might subcapture Cr
@@ -343,10 +343,10 @@ class CheckCaptures extends Recheck, SymTransformer:
343343
344344 /** A specialized implementation of the apply rule.
345345 *
346- * E |- f: Cf ( Ra -> Cr Rr)
347- * E |- a: Ca Ra
348- * ------------------------
349- * E |- f a: C Rr
346+ * E |- f: Ra ->Cf Rr^Cr
347+ * E |- a: Ra^Ca
348+ * ---------------------
349+ * E |- f a: Rr^C
350350 *
351351 * The implementation picks as `C` one of `{f, a}` or `Cr`, depending on the
352352 * outcome of a `mightSubcapture` test. It picks `{f, a}` if this might subcapture Cr
@@ -368,8 +368,8 @@ class CheckCaptures extends Recheck, SymTransformer:
368368 mapArgUsing(_.forceBoxStatus(false ))
369369 else if meth == defn.Caps_unsafeBoxFunArg then
370370 mapArgUsing :
371- case defn.FunctionOf (paramtpe :: Nil , restpe, isContectual ) =>
372- defn.FunctionOf (paramtpe.forceBoxStatus(true ) :: Nil , restpe, isContectual )
371+ case defn.FunctionOf (paramtpe :: Nil , restpe, isContextual ) =>
372+ defn.FunctionOf (paramtpe.forceBoxStatus(true ) :: Nil , restpe, isContextual )
373373
374374 else
375375 super .recheckApply(tree, pt) match
@@ -474,14 +474,14 @@ class CheckCaptures extends Recheck, SymTransformer:
474474 .installAfter(preRecheckPhase)
475475
476476 // Next, update all parameter symbols to match expected formals
477- meth.paramSymss.head.lazyZip(ptformals).foreach { (psym, pformal) =>
477+ meth.paramSymss.head.lazyZip(ptformals).foreach: (psym, pformal) =>
478478 psym.updateInfoBetween(preRecheckPhase, thisPhase, pformal.mapExprType)
479- }
479+
480480 // Next, update types of parameter ValDefs
481- mdef.paramss.head.lazyZip(ptformals).foreach { (param, pformal) =>
481+ mdef.paramss.head.lazyZip(ptformals).foreach: (param, pformal) =>
482482 val ValDef (_, tpt, _) = param : @ unchecked
483483 tpt.rememberTypeAlways(pformal)
484- }
484+
485485 // Next, install a new completer reflecting the new parameters for the anonymous method
486486 val mt = meth.info.asInstanceOf [MethodType ]
487487 val completer = new LazyType :
@@ -521,6 +521,7 @@ class CheckCaptures extends Recheck, SymTransformer:
521521 * 2. The capture set of the self type of a class includes the capture set of the class.
522522 * 3. The capture set of the self type of a class includes the capture set of every class parameter,
523523 * unless the parameter is marked @constructorOnly.
524+ * 4. If the class extends a pure base class, the capture set of the self type must be empty.
524525 */
525526 override def recheckClassDef (tree : TypeDef , impl : Template , cls : ClassSymbol )(using Context ): Type =
526527 val saved = curEnv
@@ -534,7 +535,7 @@ class CheckCaptures extends Recheck, SymTransformer:
534535 for param <- cls.paramGetters do
535536 if ! param.hasAnnotation(defn.ConstructorOnlyAnnot ) then
536537 checkSubset(param.termRef.captureSet, thisSet, param.srcPos) // (3)
537- for pureBase <- cls.pureBaseClass do
538+ for pureBase <- cls.pureBaseClass do // (4)
538539 checkSubset(thisSet,
539540 CaptureSet .empty.withDescription(i " of pure base class $pureBase" ),
540541 tree.srcPos)
@@ -620,9 +621,8 @@ class CheckCaptures extends Recheck, SymTransformer:
620621 def checkNotUniversal (tp : Type ): Unit = tp.widenDealias match
621622 case wtp @ CapturingType (parent, refs) =>
622623 refs.disallowRootCapability { () =>
623- val kind = if tree.isInstanceOf [ValDef ] then " mutable variable" else " expression"
624624 report.error(
625- em """ The $kind 's type $wtp is not allowed to capture the root capability `cap`.
625+ em """ The expression 's type $wtp is not allowed to capture the root capability `cap`.
626626 |This usually means that a capability persists longer than its allowed lifetime. """ ,
627627 tree.srcPos)
628628 }
@@ -631,6 +631,16 @@ class CheckCaptures extends Recheck, SymTransformer:
631631 if ! allowUniversalInBoxed then checkNotUniversal(typeToCheck)
632632 super .recheckFinish(tpe, tree, pt)
633633
634+ // ------------------ Adaptation -------------------------------------
635+ //
636+ // Adaptations before checking conformance of actual vs expected:
637+ //
638+ // - Convert function to dependent function if expected type is a dependent function type
639+ // (c.f. alignDependentFunction).
640+ // - Relax expected capture set containing `this.type`s by adding references only
641+ // accessible through those types (c.f. addOuterRefs, also #14930 for a discussion).
642+ // - Adapt box status and environment capture sets by simulating box/unbox operations.
643+
634644 /** Massage `actual` and `expected` types using the methods below before checking conformance */
635645 override def checkConformsExpr (actual : Type , expected : Type , tree : Tree )(using Context ): Unit =
636646 val expected1 = alignDependentFunction(addOuterRefs(expected, actual), actual.stripCapturing)
@@ -656,26 +666,31 @@ class CheckCaptures extends Recheck, SymTransformer:
656666 recur(expected)
657667
658668 /** For the expected type, implement the rule outlined in #14390:
659- * - when checking an expression `a: Ca Ta ` against an expected type `Ce Te `,
669+ * - when checking an expression `a: Ta^Ca ` against an expected type `Te^Ce `,
660670 * - where the capture set `Ce` contains Cls.this,
661- * - and where and all method definitions enclosing `a` inside class `Cls`
671+ * - and where all method definitions enclosing `a` inside class `Cls`
662672 * have only pure parameters,
663673 * - add to `Ce` all references to variables or this-references in `Ca`
664674 * that are outside `Cls`. These are all accessed through `Cls.this`,
665675 * so we can assume they are already accounted for by `Ce` and adding
666676 * them explicitly to `Ce` changes nothing.
667677 */
668678 private def addOuterRefs (expected : Type , actual : Type )(using Context ): Type =
679+
669680 def isPure (info : Type ): Boolean = info match
670681 case info : PolyType => isPure(info.resType)
671682 case info : MethodType => info.paramInfos.forall(_.captureSet.isAlwaysEmpty) && isPure(info.resType)
672683 case _ => true
684+
673685 def isPureContext (owner : Symbol , limit : Symbol ): Boolean =
674686 if owner == limit then true
675687 else if ! owner.exists then false
676688 else isPure(owner.info) && isPureContext(owner.owner, limit)
689+
690+ // Augment expeced capture set `erefs` by all references in actual capture
691+ // set `arefs` that are outside some `this.type` reference in `erefs`
677692 def augment (erefs : CaptureSet , arefs : CaptureSet ): CaptureSet =
678- (erefs /: erefs.elems) { (erefs, eref) =>
693+ (erefs /: erefs.elems): (erefs, eref) =>
679694 eref match
680695 case eref : ThisType if isPureContext(ctx.owner, eref.cls) =>
681696 erefs ++ arefs.filter {
@@ -685,7 +700,7 @@ class CheckCaptures extends Recheck, SymTransformer:
685700 }
686701 case _ =>
687702 erefs
688- }
703+
689704 expected match
690705 case CapturingType (ecore, erefs) =>
691706 val erefs1 = augment(erefs, actual.captureSet)
@@ -694,6 +709,7 @@ class CheckCaptures extends Recheck, SymTransformer:
694709 expected.derivedCapturingType(ecore, erefs1)
695710 case _ =>
696711 expected
712+ end addOuterRefs
697713
698714 /** Adapt `actual` type to `expected` type by inserting boxing and unboxing conversions
699715 *
@@ -703,8 +719,8 @@ class CheckCaptures extends Recheck, SymTransformer:
703719
704720 /** Adapt function type `actual`, which is `aargs -> ares` (possibly with dependencies)
705721 * to `expected` type.
706- * It returns the adapted type along with the additionally captured variable
707- * during adaptation.
722+ * It returns the adapted type along with a capture set consisting of the references
723+ * that were additionally captured during adaptation.
708724 * @param reconstruct how to rebuild the adapted function type
709725 */
710726 def adaptFun (actual : Type , aargs : List [Type ], ares : Type , expected : Type ,
0 commit comments