Skip to content

Commit a4df033

Browse files
committed
Check that SharedCapabilities don't capture cap.
1 parent ee23239 commit a4df033

File tree

3 files changed

+32
-7
lines changed

3 files changed

+32
-7
lines changed

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
8585
/** Drops `private` from the flags of `symd` provided it is
8686
* a parameter accessor that's not `constructorOnly` or `uncheckedCaptured`
8787
* and that contains at least one @retains in co- or in-variant position.
88-
* The @retains mught be implicit for a type deriving from `Capability`.
88+
* The @retains might be implicit for a type deriving from `Capability`.
8989
*/
9090
private def newFlagsFor(symd: SymDenotation)(using Context): FlagSet =
9191

@@ -303,6 +303,10 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
303303
* 6. Perform normalizeCaptures
304304
*/
305305
private def transformExplicitType(tp: Type, tptToCheck: Tree = EmptyTree)(using Context): Type =
306+
307+
def fail(msg: Message) =
308+
if !tptToCheck.isEmpty then report.error(msg, tptToCheck.srcPos)
309+
306310
val toCapturing = new DeepTypeMap with FollowAliasesMap:
307311
override def toString = "expand aliases"
308312

@@ -332,7 +336,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
332336
else fntpe
333337

334338
/** If C derives from Capability and we have a C^cs in source, we leave it as is
335-
* instead of expanding it to C^{cap}^cs. We do this by stripping capability-generated
339+
* instead of expanding it to C^{cap.rd}^cs. We do this by stripping capability-generated
336340
* universal capture sets from the parent of a CapturingType.
337341
*/
338342
def stripImpliedCaptureSet(tp: Type): Type = tp match
@@ -341,18 +345,28 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
341345
parent
342346
case _ => tp
343347

348+
def checkSharedOK(tp: Type): tp.type =
349+
tp match
350+
case CapturingType(parent, refs)
351+
if refs.isUniversal && parent.derivesFrom(defn.Caps_SharedCapability) =>
352+
fail(em"$tp extends SharedCapability, so it cannot capture `cap`")
353+
case _ =>
354+
tp
355+
344356
def apply(t: Type) =
345357
t match
346358
case t @ CapturingType(parent, refs) =>
347-
t.derivedCapturingType(stripImpliedCaptureSet(this(parent)), refs)
359+
checkSharedOK:
360+
t.derivedCapturingType(stripImpliedCaptureSet(this(parent)), refs)
348361
case t @ AnnotatedType(parent, ann) =>
349362
val parent1 = this(parent)
350363
if ann.symbol.isRetains then
351364
val parent2 = stripImpliedCaptureSet(parent1)
352365
if !tptToCheck.isEmpty then
353366
checkWellformedLater(parent2, ann.tree, tptToCheck)
354367
try
355-
CapturingType(parent2, ann.tree.toCaptureSet)
368+
checkSharedOK:
369+
CapturingType(parent2, ann.tree.toCaptureSet)
356370
catch case ex: IllegalCaptureRef =>
357371
report.error(em"Illegal capture reference: ${ex.getMessage.nn}", tptToCheck.srcPos)
358372
parent2
@@ -369,9 +383,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
369383
else normalizeCaptures(mapFollowingAliases(t))
370384
end toCapturing
371385

372-
def fail(msg: Message) =
373-
if !tptToCheck.isEmpty then report.error(msg, tptToCheck.srcPos)
374-
375386
val tp1 = toCapturing(tp)
376387
val tp2 = Existential.mapCapInResults(fail)(tp1)
377388
if tp2 ne tp then capt.println(i"expanded explicit in ${ctx.owner}: $tp --> $tp1 --> $tp2")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Error: tests/neg-custom-args/captures/shared-capability.scala:9:13 --------------------------------------------------
2+
9 |def test2(a: Async^): Object^ = a // error
3+
| ^^^^^^
4+
| Async^ extends SharedCapability, so it cannot capture `cap`
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
import language.future // sepchecks on
3+
import caps.SharedCapability
4+
5+
class Async extends SharedCapability
6+
7+
def test1(a: Async): Object^ = a // OK
8+
9+
def test2(a: Async^): Object^ = a // error
10+

0 commit comments

Comments
 (0)