@@ -21,7 +21,7 @@ import util.chaining.tap
2121import transform .{Recheck , PreRecheck , CapturedVars }
2222import Recheck .*
2323import scala .collection .mutable
24- import CaptureSet .{withCaptureSetsExplained , IncludeFailure , MutAdaptFailure }
24+ import CaptureSet .{withCaptureSetsExplained , IncludeFailure , MutAdaptFailure , VarState }
2525import CCState .*
2626import StdNames .nme
2727import NameKinds .{DefaultGetterName , WildcardParamName , UniqueNameKind }
@@ -916,7 +916,10 @@ class CheckCaptures extends Recheck, SymTransformer:
916916 val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol
917917 if ! getter.is(Private ) && getter.hasTrackedParts then
918918 refined = refined.refinedOverride(getterName, argType.unboxed) // Yichen you might want to check this
919- allCaptures ++= argType.captureSet
919+ if getter.hasAnnotation(defn.ConsumeAnnot ) then
920+ () // We make sure in checkClassDef, point (6), that consume parameters don't
921+ // contribute to the class capture set
922+ else allCaptures ++= argType.captureSet
920923 (refined, allCaptures)
921924
922925 /** Augment result type of constructor with refinements and captures.
@@ -971,8 +974,6 @@ class CheckCaptures extends Recheck, SymTransformer:
971974 setup.fieldsWithExplicitTypes // pick fields with explicit types for classes in this compilation unit
972975 .getOrElse(cls, cls.info.decls.toList) // pick all symbols in class scope for other classes
973976
974- val isSeparate = cls.typeRef.isMutableType
975-
976977 /** The classifiers of the fresh caps in the span capture sets of all fields
977978 * in the given class `cls`.
978979 */
@@ -997,7 +998,7 @@ class CheckCaptures extends Recheck, SymTransformer:
997998 false
998999
9991000 def maybeRO (ref : Capability ) =
1000- if ! isSeparate && impliedReadOnly(cls) then ref.readOnly else ref
1001+ if ! cls. isSeparate && impliedReadOnly(cls) then ref.readOnly else ref
10011002
10021003 def fresh =
10031004 FreshCap (Origin .NewInstance (core)).tap: fresh =>
@@ -1006,7 +1007,7 @@ class CheckCaptures extends Recheck, SymTransformer:
10061007 report.echo(infos.mkString(" \n " ), ctx.owner.srcPos)
10071008
10081009 var implied = impliedClassifiers(cls)
1009- if isSeparate then implied = cls.classifier :: implied
1010+ if cls. isSeparate then implied = dominators( cls.classifier :: Nil , implied)
10101011 knownFresh.getOrElseUpdate(cls, implied) match
10111012 case Nil => CaptureSet .empty
10121013 case cl :: Nil =>
@@ -1377,29 +1378,47 @@ class CheckCaptures extends Recheck, SymTransformer:
13771378 /** Recheck classDef by enforcing the following class-specific capture set relations:
13781379 * 1. The capture set of a class includes the capture sets of its parents.
13791380 * 2. The capture set of the self type of a class includes the capture set of the class.
1380- * 3. The capture set of the self type of a class includes the capture set of every class parameter,
1381- * unless the parameter is marked @constructorOnly or @untrackedCaptures.
1381+ * 3. The capture set of the self type of a class includes the capture set of every class
1382+ * parameter, unless the parameter is marked @constructorOnly or @untrackedCaptures.
13821383 * 4. If the class extends a pure base class, the capture set of the self type must be empty.
1383- * Also, check that trait parents represented as applied types don't have cap in their
1384- * type arguments. Other generic parents are represented as TypeApplys, where the same check
1385- * is already done in the TypeApply.
1384+ * 5. Check that trait parents represented as applied types don't have cap in their
1385+ * type arguments. Charge deep capture sets of type arguments to non-reserved typevars
1386+ * to the environment. Other generic parents are represented as TypeApplys, where the
1387+ * same check is already done in the TypeApply.
1388+ * 6. Consume parameters are only allowed for classes producing a fresh cap
1389+ * for their constructor, and they don't contribute to the capture set. Example:
1390+ *
1391+ * class A(consume val x: B^) extends caps.Separate
1392+ * val a = A(b)
1393+ *
1394+ * Here, `a` is of type A{val x: B^}^, and the outer `^` does not hide `b`.
1395+ * That's necessary since we would otherwise get consume/use conflicts on `b`.
13861396 */
13871397 override def recheckClassDef (tree : TypeDef , impl : Template , cls : ClassSymbol )(using Context ): Type =
13881398 if Feature .enabled(Feature .separationChecking) then sepChecksEnabled = true
13891399 val localSet = capturedVars(cls)
1400+
1401+ // (1) Capture set of a class includes the capture sets of its parents
13901402 for parent <- impl.parents do // (1)
13911403 checkSubset(capturedVars(parent.tpe.classSymbol), localSet, parent.srcPos,
13921404 i " \n of the references allowed to be captured by $cls" )
1405+
13931406 val saved = curEnv
13941407 curEnv = Env (cls, EnvKind .Regular , localSet, curEnv)
13951408 try
1409+ // (2) Capture set of self type includes capture set of class
13961410 val thisSet = cls.classInfo.selfType.captureSet.withDescription(i " of the self type of $cls" )
1397- checkSubset(localSet, thisSet, tree.srcPos) // (2)
1411+ checkSubset(localSet, thisSet, tree.srcPos)
1412+
1413+ // (3) Capture set of self type includes capture sets of parameters
13981414 for param <- cls.paramGetters do
13991415 if ! param.hasAnnotation(defn.ConstructorOnlyAnnot )
1400- && ! param.hasAnnotation(defn.UntrackedCapturesAnnot ) then
1416+ && ! param.hasAnnotation(defn.UntrackedCapturesAnnot )
1417+ then
14011418 withCapAsRoot : // OK? We need this here since self types use `cap` instead of `fresh`
1402- checkSubset(param.termRef.captureSet, thisSet, param.srcPos) // (3)
1419+ checkSubset(param.termRef.captureSet, thisSet, param.srcPos)
1420+
1421+ // (4) If class extends Pure, capture set of self type is empty
14031422 for pureBase <- cls.pureBaseClass do // (4)
14041423 def selfTypeTree = impl.body
14051424 .collect:
@@ -1410,15 +1429,41 @@ class CheckCaptures extends Recheck, SymTransformer:
14101429 checkSubset(thisSet,
14111430 CaptureSet .empty.withDescription(i " of pure base class $pureBase" ),
14121431 selfTypeTree.srcPos, cs1description = " captured by this self type" )
1432+
1433+ // (5) Check AppliedType parents
14131434 for case tpt : TypeTree <- impl.parents do
14141435 tpt.tpe match
14151436 case AppliedType (fn, args) =>
14161437 markFreeTypeArgs(tpt, fn.typeSymbol, args.map(TypeTree (_)))
14171438 case _ =>
1439+
1440+ // (6) Check that consume parameters are covered by an implied FreshCap
1441+ for getter <- cls.paramGetters do
1442+ if ! getter.is(Private ) // Setup makes sure that getters with capture sets are not private
1443+ && getter.hasAnnotation(defn.ConsumeAnnot )
1444+ then
1445+ val implied = captureSetImpliedByFields(cls, cls.appliedRef)
1446+ val getterCS = getter.info.captureSet
1447+
1448+ val hasCoveringFresh = implied.elems.exists:
1449+ case fresh : FreshCap =>
1450+ getterCS.elems.forall: elem =>
1451+ given VarState = VarState .Unrecorded // make sure we don't add to fresh's hidden set
1452+ fresh.maxSubsumes(elem, canAddHidden = true )
1453+ case _ =>
1454+ false
1455+
1456+ if ! hasCoveringFresh then
1457+ report.error(
1458+ em """ A consume parameter is only allowed for classes producing a `cap` in their constructor.
1459+ |This can be achieved by having the class extend caps.Separate. """ ,
1460+ getter.srcPos)
1461+
14181462 super .recheckClassDef(tree, impl, cls)
14191463 finally
14201464 completed += cls
14211465 curEnv = saved
1466+ end recheckClassDef
14221467
14231468 /** If type is of the form `T @requiresCapability(x)`,
14241469 * mark `x` as free in the current environment. This is used to require the
0 commit comments