Skip to content

Commit ca04038

Browse files
committed
Bug fixes
1. Fix canBeTracked for TermRefs only TermRefs where prefix is NoPrefix or `this` can be tracked. The others have to be widened. 2. Fix rule for comparing capture refs on the left 3. Be more careful where comparisons are frozen
1 parent a7ad939 commit ca04038

File tree

6 files changed

+42
-33
lines changed

6 files changed

+42
-33
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,11 @@ extension (tp: Type)
8282
tp.tp1.canHaveInferredCapture || tp.tp2.canHaveInferredCapture
8383
case _ =>
8484
false
85+
86+
def stripCapturing(using Context): Type = tp.dealiasKeepAnnots match
87+
case CapturingType(parent, _) =>
88+
parent.stripCapturing
89+
case atd @ AnnotatedType(parent, annot) =>
90+
atd.derivedAnnotatedType(parent.stripCapturing, annot)
91+
case _ =>
92+
tp

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,26 +79,23 @@ sealed abstract class CaptureSet extends Showable:
7979
def accountsFor(x: CaptureRef)(using Context): Boolean =
8080
reporting.trace(i"$this accountsFor $x, ${x.captureSetOfInfo}?", show = true) {
8181
elems.contains(x)
82-
|| !x.isRootCapability && (x.captureSetOfInfo frozen_<:< this) == CompareResult.OK
82+
|| !x.isRootCapability
83+
&& x.captureSetOfInfo.subCaptures(this, frozen = true) == CompareResult.OK
8384
}
8485

8586
/** The subcapturing test */
86-
def <:< (that: CaptureSet)(using Context): CompareResult =
87-
subcaptures(that)(using ctx, VarState())
87+
def subCaptures(that: CaptureSet, frozen: Boolean)(using Context): CompareResult =
88+
subCaptures(that)(using ctx, if frozen then FrozenState else VarState())
8889

89-
/** The subcapturing test, where all variables are treated as frozen */
90-
def frozen_<:<(that: CaptureSet)(using Context): CompareResult =
91-
subcaptures(that)(using ctx, FrozenState)
92-
93-
def =:= (that: CaptureSet)(using Context): Boolean =
94-
(this frozen_<:< that) == CompareResult.OK
95-
&& (that frozen_<:< this) == CompareResult.OK
96-
97-
private def subcaptures(that: CaptureSet)(using Context, VarState): CompareResult =
90+
private def subCaptures(that: CaptureSet)(using Context, VarState): CompareResult =
9891
val result = that.tryInclude(elems)
9992
if result == CompareResult.OK then addSuper(that) else varState.abort()
10093
result
10194

95+
def =:= (that: CaptureSet)(using Context): Boolean =
96+
this.subCaptures(that, frozen = true) == CompareResult.OK
97+
&& that.subCaptures(this, frozen = true) == CompareResult.OK
98+
10299
/** The smallest capture set (via <:<) that is a superset of both
103100
* `this` and `that`
104101
*/
@@ -230,6 +227,7 @@ object CaptureSet:
230227
def addNewElems(newElems: Refs)(using Context, VarState): CompareResult =
231228
if recordElemsState() then
232229
elems ++= newElems
230+
// assert(id != 2 || elems.size != 2, this)
233231
val depsIt = deps.iterator
234232
while depsIt.hasNext do
235233
val result = depsIt.next.tryInclude(newElems)

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import typer.Applications.productSelectorTypes
2424
import reporting.trace
2525
import NullOpsDecorator._
2626
import annotation.constructorOnly
27-
import cc.{CapturingType, derivedCapturingType, CaptureSet}
27+
import cc.{CapturingType, derivedCapturingType, CaptureSet, stripCapturing}
2828

2929
/** Provides methods to compare types.
3030
*/
@@ -491,8 +491,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
491491
// under -Ycheck. Test case is i7965.scala.
492492

493493
case CapturingType(parent1, refs1) =>
494-
if refs1 <:< tp2.captureSet == CaptureSet.CompareResult.OK then recur(parent1, tp2)
495-
else thirdTry
494+
if refs1.subCaptures(tp2.captureSet, frozenConstraint) == CaptureSet.CompareResult.OK then
495+
recur(parent1, tp2)
496+
else
497+
thirdTry
496498
case tp1: AnnotatedType if !tp1.isRefining =>
497499
recur(tp1.parent, tp2)
498500
case tp1: MatchType =>
@@ -810,15 +812,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
810812
}
811813
case _ => false
812814
comparePaths || {
813-
val tp2n = tp1 match
814-
case tp1: CaptureRef
815-
if tp1.isTracked && tp2.captureSet.accountsFor(tp1) =>
816-
// If `{x}` subcaptures the capture set of the RHS, we can dispense
817-
// with capture checking. This is achieved by widening the RHS with
818-
// the universal capture set `{*}`.
819-
CapturingType(tp2, CaptureSet.universal)
820-
case _ => tp2
821-
isSubType(tp1.underlying.widenExpr, tp2n, approx.addLow)
815+
var tp1w = tp1.underlying.widenExpr
816+
tp1 match
817+
case tp1: CaptureRef if tp1.isTracked =>
818+
val stripped = tp1w.stripCapturing
819+
tp1w = CapturingType(stripped, tp1.singletonCaptureSet)
820+
case _ =>
821+
isSubType(tp1w, tp2, approx.addLow)
822822
}
823823
case tp1: RefinedType =>
824824
isNewSubType(tp1.parent)
@@ -2382,8 +2382,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
23822382
case tp1: TypeVar if tp1.isInstantiated =>
23832383
tp1.underlying & tp2
23842384
case CapturingType(parent1, refs1) =>
2385-
if tp2.captureSet <:< refs1 == CaptureSet.CompareResult.OK then parent1 & tp2
2386-
else tp1.derivedCapturingType(parent1 & tp2, refs1)
2385+
if tp2.captureSet.subCaptures(refs1, frozenConstraint) == CaptureSet.CompareResult.OK then
2386+
parent1 & tp2
2387+
else
2388+
tp1.derivedCapturingType(parent1 & tp2, refs1)
23872389
case tp1: AnnotatedType if !tp1.isRefining =>
23882390
tp1.underlying & tp2
23892391
case _ =>

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ object TypeOps:
165165
// corrective steps, so no widening is wanted.
166166
simplify(l, theMap) | simplify(r, theMap)
167167
case CapturingType(parent, refs) =>
168-
if !ctx.mode.is(Mode.Type) && refs <:< parent.captureSet == CompareResult.OK then
168+
if !ctx.mode.is(Mode.Type)
169+
&& refs.subCaptures(parent.captureSet, frozen = true) == CompareResult.OK then
169170
simplify(parent, theMap)
170171
else
171172
mapOver

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,7 +1848,7 @@ object Types {
18481848
if captureSet.accountsFor(ref) then this else CapturingType(this, ref.singletonCaptureSet)
18491849

18501850
def capturing(cs: CaptureSet)(using Context): Type =
1851-
if cs.isConst && (cs frozen_<:< captureSet) == CompareResult.OK then this
1851+
if cs.isConst && cs.subCaptures(captureSet, frozen = true) == CompareResult.OK then this
18521852
else this match
18531853
case CapturingType(parent, cs1) => parent.capturing(cs1 ++ cs)
18541854
case _ => CapturingType(this, cs)
@@ -2703,7 +2703,7 @@ object Types {
27032703
*/
27042704
def canBeTracked(using Context) =
27052705
((prefix eq NoPrefix)
2706-
|| symbol.is(ParamAccessor)
2706+
|| symbol.is(ParamAccessor) && (prefix eq symbol.owner.thisType)
27072707
|| symbol.hasAnnotation(defn.AbilityAnnot)
27082708
|| isRootCapability
27092709
) && !symbol.is(Method)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ object CheckCaptures:
6363
def checkWellformedPost(tp: Type, pos: SrcPos)(using Context): Unit = tp match
6464
case CapturingType(parent, refs) =>
6565
for ref <- refs.elems do
66-
if (ref.captureSet frozen_<:< CaptureSet.empty) == CompareResult.OK then
66+
if ref.captureSet.subCaptures(CaptureSet.empty, frozen = true) == CompareResult.OK then
6767
report.error(em"$ref cannot be tracked since its capture set is empty", pos)
6868
else if parent.captureSet.accountsFor(ref) then
6969
report.warning(em"redundant capture: $parent already accounts for $ref", pos)
@@ -164,15 +164,15 @@ class CheckCaptures extends Recheck:
164164
checkSubset(targetSet, curEnv.captured, pos)
165165

166166
def assertSub(cs1: CaptureSet, cs2: CaptureSet)(using Context) =
167-
assert((cs1 <:< cs2) == CompareResult.OK, i"$cs1 is not a subset of $cs2")
167+
assert(cs1.subCaptures(cs2, frozen = false) == CompareResult.OK, i"$cs1 is not a subset of $cs2")
168168

169169
def checkElem(elem: CaptureRef, cs: CaptureSet, pos: SrcPos)(using Context) =
170-
val res = elem.singletonCaptureSet <:< cs
170+
val res = elem.singletonCaptureSet.subCaptures(cs, frozen = false)
171171
if res != CompareResult.OK then
172172
report.error(i"$elem cannot be referenced here; it is not included in allowed capture set ${res.blocking}", pos)
173173

174174
def checkSubset(cs1: CaptureSet, cs2: CaptureSet, pos: SrcPos)(using Context) =
175-
val res = cs1 <:< cs2
175+
val res = cs1.subCaptures(cs2, frozen = false)
176176
if res != CompareResult.OK then
177177
report.error(i"references $cs1 are not all included in allowed capture set ${res.blocking}", pos)
178178

0 commit comments

Comments
 (0)