Skip to content

Commit 4569955

Browse files
committed
Also check assignments to mutable fields
Treat them analogous to calls of update methods
1 parent 6642e08 commit 4569955

File tree

6 files changed

+179
-36
lines changed

6 files changed

+179
-36
lines changed

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -726,14 +726,8 @@ class CheckCaptures extends Recheck, SymTransformer:
726726
// Don't allow update methods to be called unless the qualifier captures
727727
// an exclusive reference.
728728
if tree.symbol.isUpdateMethod then
729-
qualType.exclusivityInContext match
730-
case Exclusivity.OK =>
731-
capt.println(i"exclusive $qualType in ${ctx.owner}, ${qualType.derivesFrom(defn.Caps_Mutable)}")
732-
case err =>
733-
report.error(
734-
em"""cannot call update ${tree.symbol} from $qualType,
735-
|since ${err.description(qualType)}""",
736-
tree.srcPos)
729+
checkUpdate(qualType, tree.srcPos):
730+
i"Cannot call update ${tree.symbol} of ${qualType.showRef}"
737731

738732
val origSelType = recheckSelection(tree, qualType, name, disambiguate)
739733
val selType = mapResultRoots(origSelType, tree.symbol)
@@ -771,6 +765,12 @@ class CheckCaptures extends Recheck, SymTransformer:
771765
selType
772766
}//.showing(i"recheck sel $tree, $qualType = $result")
773767

768+
def checkUpdate(qualType: Type, pos: SrcPos)(msg: => String)(using Context): Unit =
769+
qualType.exclusivityInContext match
770+
case Exclusivity.OK =>
771+
case err =>
772+
report.error(em"$msg\nsince ${err.description(qualType)}.", pos)
773+
774774
/** Recheck applications, with special handling of unsafeAssumePure.
775775
* More work is done in `recheckApplication`, `recheckArg` and `instantiate` below.
776776
*/
@@ -1000,6 +1000,15 @@ class CheckCaptures extends Recheck, SymTransformer:
10001000
report.error(em"$refArg is not a tracked capability", refArg.srcPos)
10011001
case _ =>
10021002

1003+
override def recheckAssign(tree: Assign)(using Context): Type =
1004+
val lhsType = recheck(tree.lhs, LhsProto)
1005+
recheck(tree.rhs, lhsType.widen)
1006+
lhsType match
1007+
case lhsType @ TermRef(qualType, _) if qualType ne NoPrefix =>
1008+
checkUpdate(qualType, tree.srcPos)(i"Cannot assign to field ${lhsType.name} of ${qualType.showRef}")
1009+
case _ =>
1010+
defn.UnitType
1011+
10031012
/** Recheck Closure node: add the captured vars of the anonymoys function
10041013
* to the result type. See also `recheckClosureBlock` which rechecks the
10051014
* block containing the anonymous function and the Closure node.

tests/neg-custom-args/captures/mutability.check

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
-- Error: tests/neg-custom-args/captures/mutability.scala:4:24 ---------------------------------------------------------
22
4 | def hide(x: T) = this.set(x) // error
33
| ^^^^^^^^
4-
| cannot call update method set from (Ref.this : Ref[T]^),
5-
| since the access is in method hide, which is not an update method
6-
|
7-
| where: ^ refers to a fresh root capability classified as Mutable in the type of class Ref
4+
| Cannot call update method set of Ref.this
5+
| since the access is in method hide, which is not an update method.
86
-- Error: tests/neg-custom-args/captures/mutability.scala:9:9 ----------------------------------------------------------
97
9 | self.set(x) // error
108
| ^^^^^^^^
11-
| cannot call update method set from (self : Ref[T^{}]^{Ref.this.rd}),
12-
| since its capture set {self} is read-only
9+
| Cannot call update method set of self
10+
| since its capture set {self} is read-only.
1311
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mutability.scala:10:25 -----------------------------------
1412
10 | val self2: Ref[T]^ = this // error
1513
| ^^^^
@@ -27,8 +25,8 @@
2725
-- Error: tests/neg-custom-args/captures/mutability.scala:14:12 --------------------------------------------------------
2826
14 | self3().set(x) // error
2927
| ^^^^^^^^^^^
30-
| cannot call update method set from Ref[T^{}]^{Ref.this.rd},
31-
| since its capture set {Ref.this.rd} of value self3 is read-only
28+
| Cannot call update method set of Ref[T^{}]^{Ref.this.rd}
29+
| since its capture set {Ref.this.rd} of value self3 is read-only.
3230
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mutability.scala:15:31 -----------------------------------
3331
15 | val self4: () => Ref[T]^ = () => this // error
3432
| ^^^^^^^^^^
@@ -47,8 +45,8 @@
4745
-- Error: tests/neg-custom-args/captures/mutability.scala:19:12 --------------------------------------------------------
4846
19 | self5().set(x) // error
4947
| ^^^^^^^^^^^
50-
| cannot call update method set from Ref[T^{}]^{Ref.this.rd},
51-
| since its capture set {Ref.this.rd} of method self5 is read-only
48+
| Cannot call update method set of Ref[T^{}]^{Ref.this.rd}
49+
| since its capture set {Ref.this.rd} of method self5 is read-only.
5250
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mutability.scala:20:27 -----------------------------------
5351
20 | def self6(): Ref[T]^ = this // error
5452
| ^^^^
@@ -66,22 +64,18 @@
6664
-- Error: tests/neg-custom-args/captures/mutability.scala:25:25 --------------------------------------------------------
6765
25 | def set(x: T) = this.x.set(x) // error
6866
| ^^^^^^^^^^
69-
| cannot call update method set from (Ref2.this.x : Ref[T^{}]^),
70-
| since the access is in method set, which is not an update method
71-
|
72-
| where: ^ refers to a fresh root capability classified as Mutable in the type of value x
67+
| Cannot call update method set of Ref2.this.x
68+
| since the access is in method set, which is not an update method.
7369
-- Error: tests/neg-custom-args/captures/mutability.scala:32:5 ---------------------------------------------------------
7470
32 | r1.set(33) // error
7571
| ^^^^^^
76-
| cannot call update method set from (r1 : Ref[Int]),
77-
| since its capture set {r1} is read-only
72+
| Cannot call update method set of r1
73+
| since its capture set {r1} is read-only.
7874
-- Error: tests/neg-custom-args/captures/mutability.scala:37:7 ---------------------------------------------------------
7975
37 | r3.x.set(33) // error
8076
| ^^^^^^^^
81-
| cannot call update method set from (r3.x : Ref[Int]^),
82-
| since the capture set {r3} of its prefix (r3 : Ref2[Int]) is read-only
83-
|
84-
| where: ^ refers to a fresh root capability classified as Mutable in the type of value x
77+
| Cannot call update method set of r3.x
78+
| since the capture set {r3} of its prefix (r3 : Ref2[Int]) is read-only.
8579
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mutability.scala:42:29 -----------------------------------
8680
42 | val r5: () => Ref2[Int]^ = () => ref2 // error
8781
| ^^^^^^^^^^
@@ -100,7 +94,5 @@
10094
-- Error: tests/neg-custom-args/captures/mutability.scala:45:9 ---------------------------------------------------------
10195
45 | r6().x.set(33) // error
10296
| ^^^^^^^^^^
103-
| cannot call update method set from Ref[Int]^{cap.rd},
104-
| since its capture set {cap.rd} is read-only
105-
|
106-
| where: cap is a fresh root capability classified as Mutable in the type of value r6
97+
| Cannot call update method set of Ref[Int]^{cap.rd}
98+
| since its capture set {cap.rd} is read-only.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
-- Error: tests/neg-custom-args/captures/mutvars.scala:3:28 ------------------------------------------------------------
2+
3 | def hide(x: T) = this.fld = x // error
3+
| ^^^^^^^^^^^^
4+
| Cannot assign to field fld of Ref.this
5+
| since the access is in method hide, which is not an update method.
6+
-- Error: tests/neg-custom-args/captures/mutvars.scala:8:13 ------------------------------------------------------------
7+
8 | self.fld = x // error
8+
| ^^^^^^^^^^^^
9+
| Cannot assign to field fld of self
10+
| since its capture set {self} is read-only.
11+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mutvars.scala:9:25 ---------------------------------------
12+
9 | val self2: Ref[T]^ = this // error
13+
| ^^^^
14+
| Found: Ref[T]^{Ref.this.rd}
15+
| Required: Ref[T]^
16+
|
17+
| Note that capability Ref.this.rd is not included in capture set {}.
18+
|
19+
| Note that {cap} is an exclusive capture set of the mutable type Ref[T]^,
20+
| it cannot subsume a read-only capture set of the mutable type Ref[T]^{Ref.this.rd}.
21+
|
22+
| where: ^ and cap refer to a fresh root capability classified as Mutable in the type of value self2
23+
|
24+
| longer explanation available when compiling with `-explain`
25+
-- Error: tests/neg-custom-args/captures/mutvars.scala:13:16 -----------------------------------------------------------
26+
13 | self3().fld = x // error
27+
| ^^^^^^^^^^^^^^^
28+
| Cannot assign to field fld of Ref[T^{}]^{Ref.this.rd}
29+
| since its capture set {Ref.this.rd} of value self3 is read-only.
30+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mutvars.scala:14:31 --------------------------------------
31+
14 | val self4: () => Ref[T]^ = () => this // error
32+
| ^^^^^^^^^^
33+
| Found: () ->{Ref.this} Ref[T^'s1]^{Ref.this.rd}
34+
| Required: () => Ref[T]^
35+
|
36+
| Note that capability Ref.this.rd is not included in capture set {}.
37+
|
38+
| Note that {cap} is an exclusive capture set of the mutable type Ref[T]^,
39+
| it cannot subsume a read-only capture set of the mutable type Ref[T^'s1]^{Ref.this.rd}.
40+
|
41+
| where: => refers to a fresh root capability in the type of value self4
42+
| ^ and cap refer to a fresh root capability classified as Mutable in the type of value self4
43+
|
44+
| longer explanation available when compiling with `-explain`
45+
-- Error: tests/neg-custom-args/captures/mutvars.scala:18:16 -----------------------------------------------------------
46+
18 | self5().fld = x // error
47+
| ^^^^^^^^^^^^^^^
48+
| Cannot assign to field fld of Ref[T^{}]^{Ref.this.rd}
49+
| since its capture set {Ref.this.rd} of method self5 is read-only.
50+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mutvars.scala:19:27 --------------------------------------
51+
19 | def self6(): Ref[T]^ = this // error
52+
| ^^^^
53+
| Found: Ref[T]^{Ref.this.rd}
54+
| Required: Ref[T]^
55+
|
56+
| Note that capability Ref.this.rd is not included in capture set {}.
57+
|
58+
| Note that {cap} is an exclusive capture set of the mutable type Ref[T]^,
59+
| it cannot subsume a read-only capture set of the mutable type Ref[T]^{Ref.this.rd}.
60+
|
61+
| where: ^ and cap refer to a fresh root capability classified as Mutable in the result type of method self6
62+
|
63+
| longer explanation available when compiling with `-explain`
64+
-- Error: tests/neg-custom-args/captures/mutvars.scala:24:29 -----------------------------------------------------------
65+
24 | def set(x: T) = this.x.fld = x // error
66+
| ^^^^^^^^^^^^^^
67+
| Cannot assign to field fld of Ref2.this.x
68+
| since the access is in method set, which is not an update method.
69+
-- Error: tests/neg-custom-args/captures/mutvars.scala:31:9 ------------------------------------------------------------
70+
31 | r1.fld = 33 // error
71+
| ^^^^^^^^^^^
72+
| Cannot assign to field fld of r1
73+
| since its capture set {r1} is read-only.
74+
-- Error: tests/neg-custom-args/captures/mutvars.scala:36:11 -----------------------------------------------------------
75+
36 | r3.x.fld = 33 // error
76+
| ^^^^^^^^^^^^^
77+
| Cannot assign to field fld of r3.x
78+
| since the capture set {r3} of its prefix (r3 : Ref2[Int]) is read-only.
79+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/mutvars.scala:41:29 --------------------------------------
80+
41 | val r5: () => Ref2[Int]^ = () => ref2 // error
81+
| ^^^^^^^^^^
82+
| Found: () ->{ref2} Ref2[Int]^{ref2}
83+
| Required: () => Ref2[Int]^
84+
|
85+
| Note that capability ref2 is not included in capture set {}.
86+
|
87+
| Note that {cap} is an exclusive capture set of the mutable type Ref2[Int]^,
88+
| it cannot subsume a read-only capture set of the mutable type Ref2[Int]^{ref2}.
89+
|
90+
| where: => refers to a fresh root capability in the type of value r5
91+
| ^ and cap refer to a fresh root capability classified as Mutable in the type of value r5
92+
|
93+
| longer explanation available when compiling with `-explain`
94+
-- Error: tests/neg-custom-args/captures/mutvars.scala:44:13 -----------------------------------------------------------
95+
44 | r6().x.fld = 33 // error
96+
| ^^^^^^^^^^^^^^^
97+
| Cannot assign to field fld of Ref[Int]^{cap.rd}
98+
| since its capture set {cap.rd} is read-only.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
class Ref[T](init: T) extends caps.Mutable:
2+
var fld: T = init
3+
def hide(x: T) = this.fld = x // error
4+
update def hide2(x: T) = this.fld = x // ok
5+
6+
def sneakyHide(x: T) =
7+
val self = this
8+
self.fld = x // error
9+
val self2: Ref[T]^ = this // error
10+
self2.fld = x
11+
12+
val self3 = () => this
13+
self3().fld = x // error
14+
val self4: () => Ref[T]^ = () => this // error
15+
self4().fld = x
16+
17+
def self5() = this
18+
self5().fld = x // error
19+
def self6(): Ref[T]^ = this // error
20+
self6().fld = x
21+
22+
class Ref2[T](init: T) extends caps.Mutable:
23+
val x = Ref[T](init)
24+
def set(x: T) = this.x.fld = x // error
25+
update def set2(x: T) = this.x.fld = x // ok
26+
27+
def test =
28+
val r = Ref(22)
29+
r.fld = 33 // ok
30+
val r1: Ref[Int] = Ref(22)
31+
r1.fld = 33 // error
32+
33+
val r2 = Ref2(22)
34+
r2.x.fld = 33 // ok
35+
val r3: Ref2[Int] = Ref2(22)
36+
r3.x.fld = 33 // error
37+
38+
val r4 = () => Ref2(22)
39+
r4().x.fld = 33 // ok
40+
val ref2: Ref2[Int] = Ref2(22)
41+
val r5: () => Ref2[Int]^ = () => ref2 // error
42+
r5().x.fld = 33
43+
val r6: () => Ref2[Int] = () => ref2
44+
r6().x.fld = 33 // error

tests/neg-custom-args/captures/readOnly.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@
1919
-- Error: tests/neg-custom-args/captures/readOnly.scala:20:23 ----------------------------------------------------------
2020
20 | val doit = () => z.put(x.get max y.get) // error
2121
| ^^^^^
22-
| cannot call update method put from (z : Ref),
23-
| since its capture set {z} is read-only
22+
| Cannot call update method put of z
23+
| since its capture set {z} is read-only.

tests/neg-custom-args/captures/ro-mut-conformance.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
-- Error: tests/neg-custom-args/captures/ro-mut-conformance.scala:10:4 -------------------------------------------------
22
10 | a.set(42) // error
33
| ^^^^^
4-
| cannot call update method set from (a : Ref),
5-
| since its capture set {a} is read-only
4+
| Cannot call update method set of a
5+
| since its capture set {a} is read-only.
66
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/ro-mut-conformance.scala:12:21 ---------------------------
77
12 | val t: Ref^{cap} = a // error
88
| ^

0 commit comments

Comments
 (0)