Skip to content

Commit a70df7b

Browse files
committed
Always add cap to results of constructors with consume parameters
1 parent 3f7f640 commit a70df7b

File tree

5 files changed

+7
-38
lines changed

5 files changed

+7
-38
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,11 @@ extension (cls: ClassSymbol)
531531
else defn.AnyClass
532532

533533
def isSeparate(using Context): Boolean =
534-
cls.typeRef.isStatefulType || cls.derivesFrom(defn.Caps_Separate)
534+
cls.derivesFrom(defn.Caps_Separate)
535+
|| cls.typeRef.isStatefulType
536+
|| cls.paramGetters.exists: getter =>
537+
!getter.is(Private) // Setup makes sure that getters with capture sets are not private
538+
&& getter.hasAnnotation(defn.ConsumeAnnot)
535539

536540
extension (sym: Symbol)
537541

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

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,14 +1384,6 @@ class CheckCaptures extends Recheck, SymTransformer:
13841384
* type arguments. Charge deep capture sets of type arguments to non-reserved typevars
13851385
* to the environment. Other generic parents are represented as TypeApplys, where the
13861386
* same check is already done in the TypeApply.
1387-
* 6. Consume parameters are only allowed for classes producing a fresh cap
1388-
* for their constructor, and they don't contribute to the capture set. Example:
1389-
*
1390-
* class A(consume val x: B^) extends caps.Separate
1391-
* val a = A(b)
1392-
*
1393-
* Here, `a` is of type A{val x: B^}^, and the outer `^` does not hide `b`.
1394-
* That's necessary since we would otherwise get consume/use conflicts on `b`.
13951387
*/
13961388
override def recheckClassDef(tree: TypeDef, impl: Template, cls: ClassSymbol)(using Context): Type =
13971389
if Feature.enabled(Feature.separationChecking) then sepChecksEnabled = true
@@ -1436,28 +1428,6 @@ class CheckCaptures extends Recheck, SymTransformer:
14361428
markFreeTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree(_)))
14371429
case _ =>
14381430

1439-
// (6) Check that consume parameters are covered by an implied FreshCap
1440-
for getter <- cls.paramGetters do
1441-
if !getter.is(Private) // Setup makes sure that getters with capture sets are not private
1442-
&& getter.hasAnnotation(defn.ConsumeAnnot)
1443-
then
1444-
val implied = captureSetImpliedByFields(cls, cls.appliedRef)
1445-
val getterCS = getter.info.captureSet
1446-
1447-
val hasCoveringFresh = implied.elems.exists:
1448-
case fresh: FreshCap =>
1449-
getterCS.elems.forall: elem =>
1450-
given VarState = VarState.Unrecorded // make sure we don't add to fresh's hidden set
1451-
fresh.maxSubsumes(elem, canAddHidden = true)
1452-
case _ =>
1453-
false
1454-
1455-
if !hasCoveringFresh then
1456-
report.error(
1457-
em"""A consume parameter is only allowed for classes producing a `cap` in their constructor.
1458-
|This can be achieved by having the class extend caps.Separate.""",
1459-
getter.srcPos)
1460-
14611431
super.recheckClassDef(tree, impl, cls)
14621432
finally
14631433
completed += cls

docs/_docs/reference/experimental/capture-checking/mutability.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class Counter extends Stateful:
5656

5757
In class `Counter`, the `incr` method should be declared as an update method since it accesses `this` as an exclusive write capability by writing to the variable `this.count` in its environment.
5858

59-
`update` can also be used on an inner class of a class or object extending `Stateful`. It gives all code in the class the right to access exclusive capabilities in the class environment. Normal classes
59+
`update` can also be used on an inner class of a class or object extending `Stateful`. It gives all code in the inner class the right to access exclusive capabilities in the outer class environment. Normal classes
6060
can only access exclusive capabilities defined in the class or passed to it in parameters.
6161

6262
For instance, we can also define counter objects that update a shared variable that is external to the object:

tests/neg-custom-args/captures/consume-in-constructor.check

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
-- Error: tests/neg-custom-args/captures/consume-in-constructor.scala:11:21 --------------------------------------------
2-
11 |class A3(consume val b: B^) // error
3-
| ^
4-
| A consume parameter is only allowed for classes producing a `cap` in their constructor.
5-
| This can be achieved by having the class extend caps.Separate.
61
-- Error: tests/neg-custom-args/captures/consume-in-constructor.scala:20:10 --------------------------------------------
72
20 | println(b) // error
83
| ^

tests/neg-custom-args/captures/consume-in-constructor.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class A1(val b: B^):
88
class A2(consume val b: B^):
99
val bb: B^ = B()
1010

11-
class A3(consume val b: B^) // error
11+
class A3(consume val b: B^) // was error now ok
1212
class A4(consume val b: B^) extends Stateful { var x: Int = 1 } // ok
1313
def Test =
1414
val b: B^ = B()

0 commit comments

Comments
 (0)