@@ -301,9 +301,9 @@ class CheckCaptures extends Recheck, SymTransformer:
301
301
302
302
/** A specialized implementation of the selection rule.
303
303
*
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
307
307
*
308
308
* The implementation picks as `C` one of `{f}` or `Cr`, depending on the
309
309
* outcome of a `mightSubcapture` test. It picks `{f}` if this might subcapture Cr
@@ -343,10 +343,10 @@ class CheckCaptures extends Recheck, SymTransformer:
343
343
344
344
/** A specialized implementation of the apply rule.
345
345
*
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
350
350
*
351
351
* The implementation picks as `C` one of `{f, a}` or `Cr`, depending on the
352
352
* outcome of a `mightSubcapture` test. It picks `{f, a}` if this might subcapture Cr
@@ -368,8 +368,8 @@ class CheckCaptures extends Recheck, SymTransformer:
368
368
mapArgUsing(_.forceBoxStatus(false ))
369
369
else if meth == defn.Caps_unsafeBoxFunArg then
370
370
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 )
373
373
374
374
else
375
375
super .recheckApply(tree, pt) match
@@ -474,14 +474,14 @@ class CheckCaptures extends Recheck, SymTransformer:
474
474
.installAfter(preRecheckPhase)
475
475
476
476
// 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) =>
478
478
psym.updateInfoBetween(preRecheckPhase, thisPhase, pformal.mapExprType)
479
- }
479
+
480
480
// Next, update types of parameter ValDefs
481
- mdef.paramss.head.lazyZip(ptformals).foreach { (param, pformal) =>
481
+ mdef.paramss.head.lazyZip(ptformals).foreach: (param, pformal) =>
482
482
val ValDef (_, tpt, _) = param : @ unchecked
483
483
tpt.rememberTypeAlways(pformal)
484
- }
484
+
485
485
// Next, install a new completer reflecting the new parameters for the anonymous method
486
486
val mt = meth.info.asInstanceOf [MethodType ]
487
487
val completer = new LazyType :
@@ -521,6 +521,7 @@ class CheckCaptures extends Recheck, SymTransformer:
521
521
* 2. The capture set of the self type of a class includes the capture set of the class.
522
522
* 3. The capture set of the self type of a class includes the capture set of every class parameter,
523
523
* 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.
524
525
*/
525
526
override def recheckClassDef (tree : TypeDef , impl : Template , cls : ClassSymbol )(using Context ): Type =
526
527
val saved = curEnv
@@ -534,7 +535,7 @@ class CheckCaptures extends Recheck, SymTransformer:
534
535
for param <- cls.paramGetters do
535
536
if ! param.hasAnnotation(defn.ConstructorOnlyAnnot ) then
536
537
checkSubset(param.termRef.captureSet, thisSet, param.srcPos) // (3)
537
- for pureBase <- cls.pureBaseClass do
538
+ for pureBase <- cls.pureBaseClass do // (4)
538
539
checkSubset(thisSet,
539
540
CaptureSet .empty.withDescription(i " of pure base class $pureBase" ),
540
541
tree.srcPos)
@@ -620,9 +621,8 @@ class CheckCaptures extends Recheck, SymTransformer:
620
621
def checkNotUniversal (tp : Type ): Unit = tp.widenDealias match
621
622
case wtp @ CapturingType (parent, refs) =>
622
623
refs.disallowRootCapability { () =>
623
- val kind = if tree.isInstanceOf [ValDef ] then " mutable variable" else " expression"
624
624
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`.
626
626
|This usually means that a capability persists longer than its allowed lifetime. """ ,
627
627
tree.srcPos)
628
628
}
@@ -631,6 +631,16 @@ class CheckCaptures extends Recheck, SymTransformer:
631
631
if ! allowUniversalInBoxed then checkNotUniversal(typeToCheck)
632
632
super .recheckFinish(tpe, tree, pt)
633
633
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
+
634
644
/** Massage `actual` and `expected` types using the methods below before checking conformance */
635
645
override def checkConformsExpr (actual : Type , expected : Type , tree : Tree )(using Context ): Unit =
636
646
val expected1 = alignDependentFunction(addOuterRefs(expected, actual), actual.stripCapturing)
@@ -656,26 +666,31 @@ class CheckCaptures extends Recheck, SymTransformer:
656
666
recur(expected)
657
667
658
668
/** 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 `,
660
670
* - 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`
662
672
* have only pure parameters,
663
673
* - add to `Ce` all references to variables or this-references in `Ca`
664
674
* that are outside `Cls`. These are all accessed through `Cls.this`,
665
675
* so we can assume they are already accounted for by `Ce` and adding
666
676
* them explicitly to `Ce` changes nothing.
667
677
*/
668
678
private def addOuterRefs (expected : Type , actual : Type )(using Context ): Type =
679
+
669
680
def isPure (info : Type ): Boolean = info match
670
681
case info : PolyType => isPure(info.resType)
671
682
case info : MethodType => info.paramInfos.forall(_.captureSet.isAlwaysEmpty) && isPure(info.resType)
672
683
case _ => true
684
+
673
685
def isPureContext (owner : Symbol , limit : Symbol ): Boolean =
674
686
if owner == limit then true
675
687
else if ! owner.exists then false
676
688
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`
677
692
def augment (erefs : CaptureSet , arefs : CaptureSet ): CaptureSet =
678
- (erefs /: erefs.elems) { (erefs, eref) =>
693
+ (erefs /: erefs.elems): (erefs, eref) =>
679
694
eref match
680
695
case eref : ThisType if isPureContext(ctx.owner, eref.cls) =>
681
696
erefs ++ arefs.filter {
@@ -685,7 +700,7 @@ class CheckCaptures extends Recheck, SymTransformer:
685
700
}
686
701
case _ =>
687
702
erefs
688
- }
703
+
689
704
expected match
690
705
case CapturingType (ecore, erefs) =>
691
706
val erefs1 = augment(erefs, actual.captureSet)
@@ -694,6 +709,7 @@ class CheckCaptures extends Recheck, SymTransformer:
694
709
expected.derivedCapturingType(ecore, erefs1)
695
710
case _ =>
696
711
expected
712
+ end addOuterRefs
697
713
698
714
/** Adapt `actual` type to `expected` type by inserting boxing and unboxing conversions
699
715
*
@@ -703,8 +719,8 @@ class CheckCaptures extends Recheck, SymTransformer:
703
719
704
720
/** Adapt function type `actual`, which is `aargs -> ares` (possibly with dependencies)
705
721
* 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.
708
724
* @param reconstruct how to rebuild the adapted function type
709
725
*/
710
726
def adaptFun (actual : Type , aargs : List [Type ], ares : Type , expected : Type ,
0 commit comments