Skip to content

Commit 1fb0619

Browse files
committed
Require array element types to be sealed
1 parent b5fe6d2 commit 1fb0619

File tree

6 files changed

+90
-10
lines changed

6 files changed

+90
-10
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ extension (tp: Type)
206206
case _: TypeRef | _: AppliedType => tp.typeSymbol.hasAnnotation(defn.CapabilityAnnot)
207207
case _ => false
208208

209+
def isSealed(using Context): Boolean = tp match
210+
case tp: TypeParamRef => tp.underlying.isSealed
211+
case tp: TypeBounds => tp.hi.hasAnnotation(defn.Caps_SealedAnnot)
212+
case tp: TypeRef => tp.symbol.is(Sealed) || tp.info.isSealed // TODO: drop symbol flag?
213+
case _ => false
214+
209215
/** Drop @retains annotations everywhere */
210216
def dropAllRetains(using Context): Type = // TODO we should drop retains from inferred types before unpickling
211217
val tm = new TypeMap:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ object CaptureSet:
872872
upper.isAlwaysEmpty || upper.isConst && upper.elems.size == 1 && upper.elems.contains(r1)
873873
if variance > 0 || isExact then upper
874874
else if variance < 0 then CaptureSet.empty
875+
else if ctx.mode.is(Mode.Printing) then upper
875876
else assert(false, i"trying to add $upper from $r via ${tm.getClass} in a non-variant setting")
876877

877878
/** Apply `f` to each element in `xs`, and join result sets with `++` */

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ object CheckCaptures:
148148
val check = new TypeTraverser:
149149

150150
extension (tparam: Symbol) def isParametricIn(carrier: Symbol): Boolean =
151-
val encl = carrier.owner.enclosingMethodOrClass
151+
val encl = carrier.maybeOwner.enclosingMethodOrClass
152152
if encl.isClass then tparam.isParametricIn(encl)
153153
else
154154
def recur(encl: Symbol): Boolean =
@@ -160,11 +160,9 @@ object CheckCaptures:
160160
def traverse(t: Type) =
161161
t.dealiasKeepAnnots match
162162
case t: TypeRef =>
163-
capt.println(i"disallow $t, $tp, $what, ${t.symbol.is(Sealed)}")
163+
capt.println(i"disallow $t, $tp, $what, ${t.isSealed}")
164164
t.info match
165-
case TypeBounds(_, hi)
166-
if !t.symbol.is(Sealed) && !hi.hasAnnotation(defn.Caps_SealedAnnot)
167-
&& !t.symbol.isParametricIn(carrier) =>
165+
case TypeBounds(_, hi) if !t.isSealed && !t.symbol.isParametricIn(carrier) =>
168166
if hi.isAny then
169167
report.error(
170168
em"""$what cannot $have $tp since
@@ -543,8 +541,8 @@ class CheckCaptures extends Recheck, SymTransformer:
543541
val TypeApply(fn, args) = tree
544542
val polyType = atPhase(thisPhase.prev):
545543
fn.tpe.widen.asInstanceOf[TypeLambda]
546-
for case (arg: TypeTree, pinfo, pname) <- args.lazyZip(polyType.paramInfos).lazyZip((polyType.paramNames)) do
547-
if pinfo.bounds.hi.hasAnnotation(defn.Caps_SealedAnnot) then
544+
for case (arg: TypeTree, formal, pname) <- args.lazyZip(polyType.paramRefs).lazyZip((polyType.paramNames)) do
545+
if formal.isSealed then
548546
def where = if fn.symbol.exists then i" in an argument of ${fn.symbol}" else ""
549547
disallowRootCapabilitiesIn(arg.knownType, fn.symbol,
550548
i"Sealed type variable $pname", "be instantiated to",
@@ -1313,6 +1311,23 @@ class CheckCaptures extends Recheck, SymTransformer:
13131311
traverseChildren(tp)
13141312
check.traverse(info)
13151313

1314+
def checkArraysAreSealedIn(tp: Type, pos: SrcPos)(using Context): Unit =
1315+
val check = new TypeTraverser:
1316+
def traverse(t: Type): Unit =
1317+
t match
1318+
case AppliedType(tycon, arg :: Nil) if tycon.typeSymbol == defn.ArrayClass =>
1319+
if !(pos.span.isSynthetic && ctx.reporter.errorsReported) then
1320+
CheckCaptures.disallowRootCapabilitiesIn(arg, NoSymbol,
1321+
"Array", "have element type",
1322+
"Since arrays are mutable, they have to be treated like variables,\nso their element type must be sealed.",
1323+
pos)
1324+
traverseChildren(t)
1325+
case defn.RefinedFunctionOf(rinfo: MethodType) =>
1326+
traverse(rinfo)
1327+
case _ =>
1328+
traverseChildren(t)
1329+
check.traverse(tp)
1330+
13161331
/** Perform the following kinds of checks
13171332
* - Check all explicitly written capturing types for well-formedness using `checkWellFormedPost`.
13181333
* - Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
@@ -1338,6 +1353,8 @@ class CheckCaptures extends Recheck, SymTransformer:
13381353
case _ =>
13391354
case _: ValOrDefDef | _: TypeDef =>
13401355
checkNoLocalRootIn(tree.symbol, tree.symbol.info, tree.symbol.srcPos)
1356+
case tree: TypeTree =>
1357+
checkArraysAreSealedIn(tree.tpe, tree.srcPos)
13411358
case _ =>
13421359
end check
13431360
end checker

compiler/src/dotty/tools/dotc/transform/Recheck.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -596,9 +596,9 @@ abstract class Recheck extends Phase, SymTransformer:
596596

597597
/** Show tree with rechecked types instead of the types stored in the `.tpe` field */
598598
override def show(tree: untpd.Tree)(using Context): String =
599-
atPhase(thisPhase) {
600-
super.show(addRecheckedTypes.transform(tree.asInstanceOf[tpd.Tree]))
601-
}
599+
atPhase(thisPhase):
600+
withMode(Mode.Printing):
601+
super.show(addRecheckedTypes.transform(tree.asInstanceOf[tpd.Tree]))
602602
end Recheck
603603

604604
/** A class that can be used to test basic rechecking without any customaization */
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-- Error: tests/neg-custom-args/captures/buffers.scala:11:6 ------------------------------------------------------------
2+
11 | var elems: Array[A] = new Array[A](10) // error // error
3+
| ^
4+
| mutable variable elems cannot have type Array[A] since
5+
| that type refers to the type variable A, which is not sealed.
6+
-- Error: tests/neg-custom-args/captures/buffers.scala:16:38 -----------------------------------------------------------
7+
16 | def make[A: ClassTag](xs: A*) = new ArrayBuffer: // error
8+
| ^^^^^^^^^^^
9+
| Sealed type variable A cannot be instantiated to box A^? since
10+
| that type refers to the type variable A, which is not sealed.
11+
| This is often caused by a local capability in an argument of constructor ArrayBuffer
12+
| leaking as part of its result.
13+
-- Error: tests/neg-custom-args/captures/buffers.scala:11:13 -----------------------------------------------------------
14+
11 | var elems: Array[A] = new Array[A](10) // error // error
15+
| ^^^^^^^^
16+
| Array cannot have element type A since
17+
| that type refers to the type variable A, which is not sealed.
18+
| Since arrays are mutable, they have to be treated like variables,
19+
| so their element type must be sealed.
20+
-- Error: tests/neg-custom-args/captures/buffers.scala:22:9 ------------------------------------------------------------
21+
22 | val x: Array[A] = new Array[A](10) // error
22+
| ^^^^^^^^
23+
| Array cannot have element type A since
24+
| that type refers to the type variable A, which is not sealed.
25+
| Since arrays are mutable, they have to be treated like variables,
26+
| so their element type must be sealed.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import reflect.ClassTag
2+
3+
class Buffer[A]
4+
5+
class ArrayBuffer[sealed A: ClassTag] extends Buffer[A]:
6+
var elems: Array[A] = new Array[A](10)
7+
def add(x: A): this.type = ???
8+
def at(i: Int): A = ???
9+
10+
class ArrayBufferBAD[A: ClassTag] extends Buffer[A]:
11+
var elems: Array[A] = new Array[A](10) // error // error
12+
def add(x: A): this.type = ???
13+
def at(i: Int): A = ???
14+
15+
object ArrayBuffer:
16+
def make[A: ClassTag](xs: A*) = new ArrayBuffer: // error
17+
elems = xs.toArray
18+
def apply[sealed A: ClassTag](xs: A*) = new ArrayBuffer:
19+
elems = xs.toArray // ok
20+
21+
class EncapsArray[A: ClassTag]:
22+
val x: Array[A] = new Array[A](10) // error
23+
24+
25+
26+
27+
28+
29+
30+

0 commit comments

Comments
 (0)