Skip to content

Commit c1fc2c7

Browse files
committed
Treat all type parameters and abstract types as sealed
This means that no type parameter can be instantiated with a type that captures cap covariantly or invariantly in its type. Two exceptions/special cases: - Type arguments for isInstanceOf and asInstanceOf are excluded, they can capture cap anywhere. - Refining variables in class types can still contain cap since they describe what comes from the constructor. Test reclassifications: - i15922.scala was moved to pending. Not clear whether this should compile, and what changes would be necessary to get it there. - future-traverse.scala was moved to pending. Not clear how to make this compiler. - i15749a.scala was moved to neg. The issue description seems to indicate that the test should not compile, but I am not sure what the outcome should be.
1 parent 927dec1 commit c1fc2c7

File tree

18 files changed

+59
-76
lines changed

18 files changed

+59
-76
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,13 @@ object CaptureSet:
608608
override def toString = s"Var$id$elems"
609609
end Var
610610

611+
/** Variables that represent refinements of class parameters can have the universal
612+
* capture set, since they represent only what is the result of the constructor.
613+
* Test case: Without that tweak, logger.scala would not compile.
614+
*/
615+
class RefiningVar(directOwner: Symbol)(using Context) extends Var(directOwner):
616+
override def disallowRootCapability(handler: () => Context ?=> Unit)(using Context) = this
617+
611618
/** A variable that is derived from some other variable via a map or filter. */
612619
abstract class DerivedVar(owner: Symbol, initialElems: Refs)(using @constructorOnly ctx: Context)
613620
extends Var(owner, initialElems):

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

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -170,22 +170,7 @@ object CheckCaptures:
170170
t.dealiasKeepAnnots match
171171
case t: TypeRef =>
172172
if !seen.contains(t) then
173-
capt.println(i"disallow $t, $tp, $what, ${t.isSealed}")
174173
seen += t
175-
t.info match
176-
case TypeBounds(_, hi) if !t.isSealed && !t.symbol.isParametricIn(carrier) =>
177-
if hi.isAny then
178-
val detailStr =
179-
if t eq tp then "variable"
180-
else i"refers to the type variable $t, which"
181-
report.error(
182-
em"""$what cannot $have $tp since
183-
|that type $detailStr is not sealed.
184-
|$addendum""",
185-
pos)
186-
else
187-
traverse(hi)
188-
case _ =>
189174
traverseChildren(t)
190175
case AnnotatedType(_, ann) if ann.symbol == defn.UncheckedCapturesAnnot =>
191176
()
@@ -557,7 +542,7 @@ class CheckCaptures extends Recheck, SymTransformer:
557542
val polyType = atPhase(thisPhase.prev):
558543
fn.tpe.widen.asInstanceOf[TypeLambda]
559544
for case (arg: TypeTree, formal, pname) <- args.lazyZip(polyType.paramRefs).lazyZip((polyType.paramNames)) do
560-
if formal.isSealed then
545+
if !tree.symbol.isTypeTestOrCast then
561546
def where = if fn.symbol.exists then i" in an argument of ${fn.symbol}" else ""
562547
disallowRootCapabilitiesIn(arg.knownType, NoSymbol,
563548
i"Sealed type variable $pname", "be instantiated to",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
174174
val getterType =
175175
mapInferred(refine = false)(tp.memberInfo(getter)).strippedDealias
176176
RefinedType(core, getter.name,
177-
CapturingType(getterType, CaptureSet.Var(ctx.owner)))
177+
CapturingType(getterType, CaptureSet.RefiningVar(ctx.owner)))
178178
.showing(i"add capture refinement $tp --> $result", capt)
179179
else
180180
core

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,22 @@
3333
27 | def m() = if x == null then y else y
3434
|
3535
| longer explanation available when compiling with `-explain`
36+
-- Error: tests/neg-custom-args/captures/capt1.scala:32:12 -------------------------------------------------------------
37+
32 | val z2 = h[() -> Cap](() => x) // error // error
38+
| ^^^^^^^^^^^^
39+
| Sealed type variable X cannot be instantiated to () -> box C^ since
40+
| the part box C^ of that type captures the root capability `cap`.
41+
| This is often caused by a local capability in an argument of method h
42+
| leaking as part of its result.
3643
-- Error: tests/neg-custom-args/captures/capt1.scala:32:30 -------------------------------------------------------------
37-
32 | val z2 = h[() -> Cap](() => x) // error
44+
32 | val z2 = h[() -> Cap](() => x) // error // error
3845
| ^
3946
| (x : C^) cannot be referenced here; it is not included in the allowed capture set {}
4047
| of an enclosing function literal with expected type () -> box C^
48+
-- Error: tests/neg-custom-args/captures/capt1.scala:34:12 -------------------------------------------------------------
49+
34 | val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
51+
| Sealed type variable X cannot be instantiated to box () ->{x} Cap since
52+
| the part C^ of that type captures the root capability `cap`.
53+
| This is often caused by a local capability in an argument of method h
54+
| leaking as part of its result.

tests/neg-custom-args/captures/capt1.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ def h4(x: Cap, y: Int): A =
2929
def foo() =
3030
val x: C @retains(caps.cap) = ???
3131
def h[X](a: X)(b: X) = a
32-
val z2 = h[() -> Cap](() => x) // error
32+
val z2 = h[() -> Cap](() => x) // error // error
3333
(() => C())
34-
val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // ok
35-
val z4 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // what was inferred for z3
34+
val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // error
3635

tests/pos-custom-args/captures/i15749a.scala renamed to tests/neg-custom-args/captures/i15749a.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ object u extends Unit
44

55
type Top = Any^
66

7-
type Wrapper[T] = [X] -> (op: T ->{cap} X) -> X
7+
type Wrapper[+T] = [X] -> (op: T ->{cap} X) -> X
88

99
def test =
1010

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
| Required: (C^ => Unit) -> Unit
2323
|
2424
| longer explanation available when compiling with `-explain`
25-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:33 ---------------------------------------
25+
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:34 ---------------------------------------
2626
33 | val boxed2 : Observe[C]^ = box2(c) // error
27-
| ^^^^^^^
28-
| Found: (C{val arg: C^}^ => Unit) ->? Unit
29-
| Required: Observe[C]^
27+
| ^
28+
| Found: box C^
29+
| Required: box C{val arg: C^?}^?
30+
|
31+
| Note that the universal capability `cap`
32+
| cannot be included in capture set ?
3033
|
3134
| longer explanation available when compiling with `-explain`
3235
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:44:2 ----------------------------------------

tests/neg-custom-args/captures/i16114.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ def withCap[T](op: Cap^ => T): T = {
1313
def main(fs: Cap^): Unit = {
1414
def badOp(io: Cap^): Unit ->{} Unit = {
1515
val op1: Unit ->{io} Unit = (x: Unit) =>
16-
expect[Cap^] {
16+
expect[Cap^] { // error
1717
io.use()
1818
fs // error (limitation)
1919
}
2020

2121
val op2: Unit ->{fs} Unit = (x: Unit) =>
22-
expect[Cap^] {
22+
expect[Cap^] { // error
2323
fs.use()
2424
io // error (limitation)
2525
}
2626

27-
val op3: Unit ->{io} Unit = (x: Unit) => // ok
28-
expect[Cap^] {
27+
val op3: Unit ->{io} Unit = (x: Unit) =>
28+
expect[Cap^] { // error
2929
io.use()
3030
io
3131
}
@@ -34,7 +34,7 @@ def main(fs: Cap^): Unit = {
3434
expect[Cap^](io) // error
3535

3636
val op: Unit -> Unit = (x: Unit) =>
37-
expect[Cap^] {
37+
expect[Cap^] { // error
3838
io.use() // error
3939
io // error
4040
}

tests/neg-custom-args/captures/levels.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/levels.scala:6:16 -------------------------------------------------------------
2-
6 | private var v: T = init // error
3-
| ^
4-
| Mutable variable v cannot have type T since
5-
| that type variable is not sealed.
61
-- Error: tests/neg-custom-args/captures/levels.scala:17:13 ------------------------------------------------------------
72
17 | val _ = Ref[String => String]((x: String) => x) // error
83
| ^^^^^^^^^^^^^^^^^^^^^

tests/neg-custom-args/captures/levels.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ class CC
33
def test1(cap1: CC^) =
44

55
class Ref[T](init: T):
6-
private var v: T = init // error
6+
private var v: T = init
77
def setV(x: T): Unit = v = x
88
def getV: T = v
99

1010
def test2(cap1: CC^) =
1111

12-
class Ref[sealed T](init: T):
12+
class Ref[T](init: T):
1313
private var v: T = init
1414
def setV(x: T): Unit = v = x
1515
def getV: T = v

0 commit comments

Comments
 (0)