Skip to content

Commit cdaee68

Browse files
committed
Implement deep check for variables
Scope extrusion can also happen for nested types, so we need to prevent {*} capturesets anywhere in the type of a mutable variable.
1 parent 8f5ef66 commit cdaee68

File tree

3 files changed

+43
-21
lines changed

3 files changed

+43
-21
lines changed

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

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -403,30 +403,46 @@ class CheckCaptures extends Recheck:
403403
show(unit.tpdTree) // this dows not print tree, but makes its variables visible for dependency printing
404404
}
405405

406+
def checkNotGlobal(tree: Tree, tp: Type, allArgs: Tree*)(using Context): Unit =
407+
for ref <-tp.captureSet.elems do
408+
val isGlobal = ref match
409+
case ref: TermRef =>
410+
ref.isRootCapability || ref.prefix != NoPrefix && ref.symbol.hasAnnotation(defn.AbilityAnnot)
411+
case _ => false
412+
if isGlobal then
413+
val what = if ref.isRootCapability then "universal" else "global"
414+
val notAllowed = i" is not allowed to capture the $what capability $ref"
415+
def msg =
416+
if allArgs.isEmpty then
417+
i"type of mutable variable ${knownType(tree)}$notAllowed"
418+
else tree match
419+
case tree: InferredTypeTree =>
420+
i"""inferred type argument ${knownType(tree)}$notAllowed
421+
|
422+
|The inferred arguments are: [${allArgs.map(knownType)}%, %]"""
423+
case _ => s"type argument$notAllowed"
424+
report.error(msg, tree.srcPos)
425+
406426
def checkNotGlobal(tree: Tree, allArgs: Tree*)(using Context): Unit =
407427
if disallowGlobal then
408428
tree match
409429
case LambdaTypeTree(_, restpt) =>
410430
checkNotGlobal(restpt, allArgs*)
411431
case _ =>
412-
for ref <- knownType(tree).captureSet.elems do
413-
val isGlobal = ref match
414-
case ref: TermRef =>
415-
ref.isRootCapability || ref.prefix != NoPrefix && ref.symbol.hasAnnotation(defn.AbilityAnnot)
416-
case _ => false
417-
val what = if ref.isRootCapability then "universal" else "global"
418-
if isGlobal then
419-
val notAllowed = i" is not allowed to capture the $what capability $ref"
420-
def msg =
421-
if allArgs.isEmpty then
422-
i"type of mutable variable ${knownType(tree)}$notAllowed"
423-
else tree match
424-
case tree: InferredTypeTree =>
425-
i"""inferred type argument ${knownType(tree)}$notAllowed
426-
|
427-
|The inferred arguments are: [${allArgs.map(knownType)}%, %]"""
428-
case _ => s"type argument$notAllowed"
429-
report.error(msg, tree.srcPos)
432+
checkNotGlobal(tree, knownType(tree), allArgs*)
433+
434+
def checkNotGlobalDeep(tree: Tree)(using Context): Unit =
435+
val checker = new TypeTraverser:
436+
def traverse(tp: Type): Unit = tp match
437+
case tp: TypeRef =>
438+
tp.info match
439+
case TypeBounds(_, hi) => traverse(hi)
440+
case _ =>
441+
case tp: TermRef =>
442+
case _ =>
443+
checkNotGlobal(tree, tp)
444+
traverseChildren(tp)
445+
checker.traverse(knownType(tree))
430446

431447
object PostRefinerCheck extends TreeTraverser:
432448
def traverse(tree: Tree)(using Context) =
@@ -467,7 +483,7 @@ class CheckCaptures extends Recheck:
467483
case _ =>
468484
inferred.foreachPart(checkPure, StopAt.Static)
469485
case t: ValDef if t.symbol.is(Mutable) =>
470-
checkNotGlobal(t.tpt)
486+
checkNotGlobalDeep(t.tpt)
471487
case _ =>
472488
traverseChildren(tree)
473489

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ longer explanation available when compiling with `-explain`
99
13 | var a: {*} String => String = f // error
1010
| ^^^^^^^^^^^^^^^^^^^
1111
| type of mutable variable box {*} String => String is not allowed to capture the universal capability (* : Any)
12-
-- Error: tests/neg-custom-args/captures/vars.scala:27:2 ---------------------------------------------------------------
13-
27 | local { cap3 => // error
12+
-- Error: tests/neg-custom-args/captures/vars.scala:14:9 ---------------------------------------------------------------
13+
14 | var b: List[{*} String => String] = Nil // error
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|type of mutable variable List[box {*} String => String] is not allowed to capture the universal capability (* : Any)
16+
-- Error: tests/neg-custom-args/captures/vars.scala:29:2 ---------------------------------------------------------------
17+
29 | local { cap3 => // error
1418
| ^^^^^
1519
|inferred type argument {*} (x$0: ? String) => ? String is not allowed to capture the universal capability (* : Any)
1620
|

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ def test(cap1: Cap, cap2: Cap) =
1111
val z2c: () => Unit = z2 // error
1212

1313
var a: {*} String => String = f // error
14+
var b: List[{*} String => String] = Nil // error
1415

1516
def scope =
1617
val cap3: Cap = CC()
1718
def g(x: String): String = if cap3 == cap3 then "" else "a"
1819
a = g
20+
b = List(g)
1921
val gc = g
2022
g
2123

0 commit comments

Comments
 (0)