Skip to content

Commit a737c80

Browse files
committed
Treat empty capture set on a Mutable type specially
Needed to invalidate the relation C^{} <: C^ for Mutable types C
1 parent 410a777 commit a737c80

File tree

6 files changed

+51
-10
lines changed

6 files changed

+51
-10
lines changed

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ sealed abstract class CaptureSet extends Showable:
6767
* - take mutability from the set's sources (for DerivedVars)
6868
* - compute mutability on demand based on mutability of elements (for Consts)
6969
*/
70-
def associateWithMutable()(using Context): Unit
70+
def associateWithMutable()(using Context): CaptureSet
7171

7272
/** Is this capture set constant (i.e. not an unsolved capture variable)?
7373
* Solved capture variables count as constant.
@@ -566,9 +566,16 @@ object CaptureSet:
566566
val emptyRefs: Refs = SimpleIdentitySet.empty
567567

568568
/** The empty capture set `{}` */
569-
@sharable // sharable since the set is empty, so setMutable is a no-op
569+
@sharable // sharable since the set is empty, so mutability won't be set
570570
val empty: CaptureSet.Const = Const(emptyRefs)
571571

572+
/** The empty capture set `{}` of a Mutable type, with Reader status */
573+
@sharable // sharable since the set is empty, so mutability won't be set
574+
val emptyOfMutable: CaptureSet.Const =
575+
val cs = Const(emptyRefs)
576+
cs.mutability = Mutability.Reader
577+
cs
578+
572579
/** The universal capture set `{cap}` */
573580
def universal(using Context): Const =
574581
Const(SimpleIdentitySet(GlobalCap))
@@ -623,9 +630,11 @@ object CaptureSet:
623630

624631
private var isComplete = true
625632

626-
def associateWithMutable()(using Context): Unit =
627-
if !elems.isEmpty then
633+
def associateWithMutable()(using Context): CaptureSet =
634+
if elems.isEmpty then emptyOfMutable
635+
else
628636
isComplete = false // delay computation of Mutability status
637+
this
629638

630639
override def mutability(using Context): Mutability =
631640
if !isComplete then
@@ -718,8 +727,9 @@ object CaptureSet:
718727
*/
719728
var deps: Deps = SimpleIdentitySet.empty
720729

721-
def associateWithMutable()(using Context): Unit =
730+
def associateWithMutable()(using Context): CaptureSet =
722731
mutability = Mutable
732+
this
723733

724734
def isConst(using Context) = solved >= ccState.iterationId
725735
def isAlwaysEmpty(using Context) = isConst && elems.isEmpty
@@ -1036,7 +1046,7 @@ object CaptureSet:
10361046
addAsDependentTo(source)
10371047

10381048
/** Mutability is same as in source, except for readOnly */
1039-
override def associateWithMutable()(using Context): Unit = ()
1049+
override def associateWithMutable()(using Context): CaptureSet = this
10401050

10411051
override def mutableToReader(origin: CaptureSet)(using Context): Boolean =
10421052
super.mutableToReader(origin)

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ object CapturingType:
4040
case parent @ CapturingType(parent1, refs1) if boxed || !parent.isBoxed =>
4141
apply(parent1, refs ++ refs1, boxed)
4242
case _ =>
43-
if parent.derivesFromMutable then refs.associateWithMutable()
44-
refs.adoptClassifier(parent.inheritedClassifier)
45-
AnnotatedType(parent, CaptureAnnotation(refs, boxed)(defn.RetainsAnnot))
43+
val refs1 =
44+
if parent.derivesFromMutable then refs.associateWithMutable() else refs
45+
refs1.adoptClassifier(parent.inheritedClassifier)
46+
AnnotatedType(parent, CaptureAnnotation(refs1, boxed)(defn.RetainsAnnot))
4647

4748
/** An extractor for CapturingTypes. Capturing types are recognized if
4849
* - the annotation is a CaptureAnnotation and we are not past CheckCapturingPhase, or

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,9 @@ class CheckCaptures extends Recheck, SymTransformer:
14841484
else
14851485
trace.force(i"rechecking $tree with pt = $pt", recheckr, show = true):
14861486
super.recheck(tree, pt)
1487+
catch case ex: AssertionError =>
1488+
println(i"error while rechecking $tree against $pt")
1489+
throw ex
14871490
finally curEnv = saved
14881491
if tree.isTerm && !pt.isBoxedCapturing && pt != LhsProto then
14891492
markFree(res.boxedCaptureSet, tree)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
862862
def compareCapturing: Boolean =
863863
val refs1 = tp1.captureSet
864864
try
865-
if refs1.isAlwaysEmpty then recur(tp1, parent2)
865+
if refs1.isAlwaysEmpty && refs1.mutability == CaptureSet.Mutability.Ignored then
866+
recur(tp1, parent2)
866867
else
867868
// The singletonOK branch is because we sometimes have a larger capture set in a singleton
868869
// than in its underlying type. An example is `f: () -> () ->{x} T`, which might be
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mut-widen-empty.scala:12:24 ------------------------------
2+
12 | val c: Arr[String]^ = b // error
3+
| ^
4+
| Found: (b : Arr[String]^{})
5+
| Required: Arr[String]^
6+
|
7+
| Note that {cap} is an exclusive capture set of the mutable type Arr[String]^,
8+
| it cannot subsume a read-only capture set of the mutable type Arr[String]^{}.
9+
|
10+
| where: ^ and cap refer to a fresh root capability in the type of value c
11+
|
12+
| longer explanation available when compiling with `-explain`
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import caps.*
2+
3+
class Arr[T: reflect.ClassTag](len: Int) extends Mutable:
4+
private val arr: Array[T] = new Array[T](len)
5+
def get(i: Int): T = arr(i)
6+
update def update(i: Int, x: T): Unit = arr(i) = x
7+
8+
9+
def test2 =
10+
val a = Arr[String](2)
11+
val b: Arr[String]^{} = ???
12+
val c: Arr[String]^ = b // error
13+
c(2) = "a" // boom!
14+

0 commit comments

Comments
 (0)