Skip to content

Commit 996f70b

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 8f87eef commit 996f70b

File tree

18 files changed

+58
-75
lines changed

18 files changed

+58
-75
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ 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

tests/neg-custom-args/captures/outer-var.check

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,10 @@
3535
| cannot be included in outer capture set {p} of variable y which is associated with method test
3636
|
3737
| longer explanation available when compiling with `-explain`
38-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/outer-var.scala:16:65 ------------------------------------
38+
-- Error: tests/neg-custom-args/captures/outer-var.scala:16:53 ---------------------------------------------------------
3939
16 | var finalizeActions = collection.mutable.ListBuffer[() => Unit]() // error
40-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41-
| Found: scala.collection.mutable.ListBuffer[box () => Unit]
42-
| Required: scala.collection.mutable.ListBuffer[box () ->? Unit]^?
43-
|
44-
| Note that the universal capability `cap`
45-
| cannot be included in capture set ? of variable finalizeActions
46-
|
47-
| longer explanation available when compiling with `-explain`
40+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41+
| Sealed type variable A cannot be instantiated to box () => Unit since
42+
| that type captures the root capability `cap`.
43+
| This is often caused by a local capability in an argument of method apply
44+
| leaking as part of its result.

tests/neg-custom-args/captures/sealed-classes.scala

Lines changed: 0 additions & 21 deletions
This file was deleted.

tests/pos-custom-args/captures/i15922.scala renamed to tests/pending/pos-custom-args/captures/i15922.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ def withCap[X](op: (Cap^) => X): X = {
1111
def leaking(c: Cap^): Id[Cap^{c}] = mkId(c)
1212

1313
def test =
14-
val bad = withCap(leaking)
14+
val ll = (c: Cap^) => leaking(c)
15+
val bad1 = withCap(ll) // used to work, now error (?)
16+
val bad2 = withCap(leaking) // used to work, now error (?)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ class ArrayBuffer[sealed A: ClassTag] extends Buffer[A]:
88
def at(i: Int): A = ???
99

1010
class ArrayBufferBAD[A: ClassTag] extends Buffer[A]:
11-
var elems: Array[A] = new Array[A](10) // error // error
11+
var elems: Array[A] = new Array[A](10)
1212
def add(x: A): this.type = ???
1313
def at(i: Int): A = ???
1414

1515
object ArrayBuffer:
16-
def make[A: ClassTag](xs: A*) = new ArrayBuffer: // error
16+
def make[A: ClassTag](xs: A*) = new ArrayBuffer:
1717
elems = xs.toArray
1818
def apply[sealed A: ClassTag](xs: A*) = new ArrayBuffer:
1919
elems = xs.toArray // ok
2020

2121
class EncapsArray[A: ClassTag]:
22-
val x: Array[A] = new Array[A](10) // error
22+
val x: Array[A] = new Array[A](10)
2323

2424

2525

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import annotation.unchecked.uncheckedCaptures
12
def test =
2-
val tasks = new collection.mutable.ArrayBuffer[() => Unit]
3+
val tasks = new collection.mutable.ArrayBuffer[(() => Unit) @uncheckedCaptures]
34
val _: Unit = tasks.foreach(((task: () => Unit) => task()))

tests/pos-custom-args/captures/logger.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ class Pair[+A, +B](x: A, y: B):
5252
def test2(ct: CanThrow[Exception], fs: FileSystem) =
5353
def x: Int ->{ct} String = ???
5454
def y: Logger^{fs} = ???
55-
def p = Pair(x, y)
55+
def p = Pair[Int ->{ct} String, Logger^{fs}](x, y)
56+
def p3 = Pair(x, y)
5657
def f = () => p.fst
5758

5859

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import annotation.unchecked.uncheckedCaptures
22
def test =
3-
@uncheckedCaptures
4-
var finalizeActions = collection.mutable.ListBuffer[() => Unit]()
3+
val finalizeActionsInit = collection.mutable.ListBuffer[(() => Unit) @uncheckedCaptures]()
4+
var finalizeActions = finalizeActionsInit
55
val action = finalizeActions.remove(0)
66

77

0 commit comments

Comments
 (0)