Skip to content

Commit 5ade6a4

Browse files
committed
Disallow to box or unbox existentials
1 parent c547c2a commit 5ade6a4

File tree

4 files changed

+38
-12
lines changed

4 files changed

+38
-12
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ sealed abstract class CaptureSet extends Showable:
8585
final def isUniversal(using Context) =
8686
elems.exists(_.isRootCapability)
8787

88+
final def isUnboxable(using Context) =
89+
elems.exists(elem => elem.isRootCapability || Existential.isExistentialVar(elem))
90+
8891
/** Try to include an element in this capture set.
8992
* @param elem The element to be added
9093
* @param origin The set that originated the request, or `empty` if the request came from outside.
@@ -331,7 +334,7 @@ sealed abstract class CaptureSet extends Showable:
331334

332335
/** Invoke handler if this set has (or later aquires) the root capability `cap` */
333336
def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type =
334-
if isUniversal then handler()
337+
if isUnboxable then handler()
335338
this
336339

337340
/** Invoke handler on the elements to ensure wellformedness of the capture set.
@@ -521,7 +524,8 @@ object CaptureSet:
521524
res.addToTrace(this)
522525

523526
private def levelOK(elem: CaptureRef)(using Context): Boolean =
524-
if elem.isRootCapability then !noUniversal
527+
if elem.isRootCapability || Existential.isExistentialVar(elem) then
528+
!noUniversal
525529
else elem match
526530
case elem: TermRef if level.isDefined =>
527531
elem.symbol.ccLevel <= level

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

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ In Setup:
3333
3434
- Conversion is done with a BiTypeMap in `Existential.mapCap`.
3535
36-
In adapt:
36+
In reckeckApply and recheckTypeApply:
3737
38-
- If an EX is toplevel in actual type, replace its bound variable
38+
- If an EX is toplevel in the result type, replace its bound variable
3939
occurrences with `cap`.
4040
4141
Level checking and avoidance:
@@ -54,6 +54,7 @@ Level checking and avoidance:
5454
don't, the others do.
5555
5656
- Capture set variables do not accept elements of level higher than the variable's level
57+
5758
- We use avoidance to heal such cases: If the level-incorrect ref appears
5859
+ covariantly: widen to underlying capture set, reject if that is cap and the variable does not allow it
5960
+ contravariantly: narrow to {}
@@ -65,10 +66,9 @@ In cv-computation (markFree):
6566
the owning method. They have to be widened to dcs(x), or, where this is not
6667
possible, it's an error.
6768
68-
In well-formedness checking of explicitly written type T:
69+
In box adaptation:
6970
70-
- If T is not the type of a parameter, check that no cap occurrence or EX-bound variable appears
71-
under a box.
71+
- Check that existential variables are not boxed or unboxed.
7272
7373
Subtype rules
7474
@@ -129,6 +129,18 @@ Subtype checking algorithm, steps to add for tp1 <:< tp2:
129129
assocExistentials(tp2).isDefined
130130
&& (assocExistentials(tp2).contains(tp1) || tp1 is not existentially bound)
131131
132+
Subtype checking algorithm, comparing two capture sets CS1 <:< CS2:
133+
134+
We need to map the (possibly to-be-added) existentials in CS1 to existentials
135+
in CS2 so that we can compare them. We use `assocExistentals` for that:
136+
To map an EX-variable V1 in CS1, pick the last (i.e. outermost, leading to the smallest
137+
type) EX-variable in `assocExistentials` that has V1 in its possible instances.
138+
To go the other way (and therby produce a BiTypeMap), map an EX-variable
139+
V2 in CS2 to the first (i.e. innermost) EX-variable it can be instantiated to.
140+
If either direction is not defined, we choose a special "bad-existetal" value
141+
that represents and out-of-scope existential. This leads to failure
142+
of the comparison.
143+
132144
Existential source syntax:
133145
134146
Existential types are ususally not written in source, since we still allow the `^`
@@ -142,7 +154,8 @@ Existential source syntax:
142154
Existential types can only at the top level of the result type
143155
of a function or method.
144156
145-
Restrictions on Existential Types:
157+
Restrictions on Existential Types: (to be implemented if we want to
158+
keep the source syntax for users).
146159
147160
- An existential capture ref must be the only member of its set. This is
148161
intended to model the idea that existential variables effectibely range
@@ -353,11 +366,14 @@ object Existential:
353366
case ref: TermParamRef => isExistentialMethod(ref.binder)
354367
case _ => false
355368

369+
/** An value signalling an out-of-scope existential that should
370+
* lead to a compare failure.
371+
*/
372+
def badExistential(using Context): TermParamRef =
373+
exMethodType(identity, nme.OOS_EXISTENTIAL).paramRefs.head
374+
356375
def isBadExistential(ref: CaptureRef) = ref match
357376
case ref: TermParamRef => ref.paramName == nme.OOS_EXISTENTIAL
358377
case _ => false
359378

360-
def badExistential(using Context): TermParamRef =
361-
exMethodType(identity, nme.OOS_EXISTENTIAL).paramRefs.head
362-
363379
end Existential

tests/neg-custom-args/captures/widen-reach.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,9 @@
1212
| Required: IO^ -> IO^{x*}
1313
|
1414
| longer explanation available when compiling with `-explain`
15+
-- Error: tests/neg-custom-args/captures/widen-reach.scala:8:18 --------------------------------------------------------
16+
8 |trait Bar extends Foo[IO^]: // error
17+
| ^
18+
| IO^{ex$3} cannot be box-converted to box IO^
19+
| since at least one of their capture sets contains the root capability `cap`
20+
9 | val foo: IO^ -> IO^ = x => x

tests/neg-custom-args/captures/widen-reach.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait IO
55
trait Foo[+T]:
66
val foo: IO^ -> T
77

8-
trait Bar extends Foo[IO^]:
8+
trait Bar extends Foo[IO^]: // error
99
val foo: IO^ -> IO^ = x => x
1010

1111
def test(x: Foo[IO^]): Unit =

0 commit comments

Comments
 (0)