Skip to content

Commit 5c80127

Browse files
committed
Fix Recheck.rememberTypeAlways
And further simplifications and comments
1 parent 74fcbf1 commit 5c80127

File tree

2 files changed

+39
-23
lines changed

2 files changed

+39
-23
lines changed

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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,

compiler/src/dotty/tools/dotc/transform/Recheck.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ object Recheck:
8484
* type stored in the tree itself
8585
*/
8686
def rememberTypeAlways(tpe: Type)(using Context): Unit =
87-
if tpe ne tree.tpe then tree.putAttachment(RecheckedType, tpe)
87+
if tpe ne tree.knownType then tree.putAttachment(RecheckedType, tpe)
8888

8989
/** The remembered type of the tree, or if none was installed, the original type */
9090
def knownType: Type =

0 commit comments

Comments
 (0)