Skip to content

Commit 87757ab

Browse files
committed
Turn capture checking on when language import is present
1 parent 292e56f commit 87757ab

File tree

7 files changed

+32
-6
lines changed

7 files changed

+32
-6
lines changed

compiler/src/dotty/tools/dotc/CompilationUnit.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import util.{FreshNameCreator, SourceFile, NoSource}
99
import util.Spans.Span
1010
import ast.{tpd, untpd}
1111
import tpd.{Tree, TreeTraverser}
12+
import ast.Trees.Import
1213
import typer.Nullables
1314
import transform.SymUtils._
1415
import core.Decorators._
1516
import config.SourceVersion
17+
import StdNames.nme
1618
import scala.annotation.internal.sharable
1719

1820
class CompilationUnit protected (val source: SourceFile) {
@@ -51,6 +53,9 @@ class CompilationUnit protected (val source: SourceFile) {
5153
*/
5254
var needsStaging: Boolean = false
5355

56+
/** Will be set to true if the unit contains a captureChecking language import */
57+
var needsCaptureChecking: Boolean = false
58+
5459
var suspended: Boolean = false
5560
var suspendedAtInliningPhase: Boolean = false
5661

@@ -111,6 +116,7 @@ object CompilationUnit {
111116
force.traverse(unit1.tpdTree)
112117
unit1.needsStaging = force.containsQuote
113118
unit1.needsInlining = force.containsInline
119+
unit1.needsCaptureChecking = force.containsCaptureChecking
114120
}
115121
unit1
116122
}
@@ -138,11 +144,17 @@ object CompilationUnit {
138144
private class Force extends TreeTraverser {
139145
var containsQuote = false
140146
var containsInline = false
147+
var containsCaptureChecking = false
141148
def traverse(tree: Tree)(using Context): Unit = {
142149
if (tree.symbol.isQuote)
143150
containsQuote = true
144151
if tree.symbol.is(Flags.Inline) then
145152
containsInline = true
153+
tree match
154+
case Import(qual, selectors)
155+
if tpd.languageImport(qual).isDefined && selectors.contains(nme.captureChecking) =>
156+
containsCaptureChecking = true
157+
case _ =>
146158
traverseChildren(tree)
147159
}
148160
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ object CheckCaptures:
2626

2727
class Pre extends PreRecheck, SymTransformer:
2828

29-
override def isEnabled(using Context) = Feature.ccEnabled
29+
override def isEnabled(using Context) = true
3030

3131
/** Reset `private` flags of parameter accessors so that we can refine them
3232
* in Setup if they have non-empty capture sets. Special handling of some
@@ -133,13 +133,14 @@ class CheckCaptures extends Recheck, SymTransformer:
133133
import CheckCaptures.*
134134

135135
def phaseName: String = "cc"
136-
override def isEnabled(using Context) = Feature.ccEnabled
136+
override def isEnabled(using Context) = true
137137

138138
def newRechecker()(using Context) = CaptureChecker(ctx)
139139

140140
override def run(using Context): Unit =
141-
checkOverrides.traverse(ctx.compilationUnit.tpdTree)
142-
super.run
141+
if Feature.ccEnabled then
142+
checkOverrides.traverse(ctx.compilationUnit.tpdTree)
143+
super.run
143144

144145
override def transformSym(sym: SymDenotation)(using Context): SymDenotation =
145146
if Synthetics.needsTransform(sym) then Synthetics.transformFromCC(sym)

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ object Feature:
8282
def pureFunsEnabled(using Context) =
8383
enabled(pureFunctions) || ccEnabled
8484

85-
def ccEnabled(using Context) = enabled(captureChecking)
85+
def ccEnabled(using Context) =
86+
enabledBySetting(captureChecking) || ctx.compilationUnit.needsCaptureChecking
8687

8788
def sourceVersionSetting(using Context): SourceVersion =
8889
SourceVersion.valueOf(ctx.settings.source.value)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ object StdNames {
426426
val canEqual_ : N = "canEqual"
427427
val canEqualAny : N = "canEqualAny"
428428
val caps: N = "caps"
429+
val captureChecking: N = "captureChecking"
429430
val checkInitialized: N = "checkInitialized"
430431
val classOf: N = "classOf"
431432
val classType: N = "classType"

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3311,8 +3311,11 @@ object Parsers {
33113311
for
33123312
case ImportSelector(id @ Ident(imported), EmptyTree, _) <- selectors
33133313
do
3314-
if globalOnlyImports.contains(QualifiedName(prefix, imported.asTermName)) && !outermost then
3314+
val fullFeatureName = QualifiedName(prefix, imported.asTermName)
3315+
if globalOnlyImports.contains(fullFeatureName) && !outermost then
33153316
syntaxError(i"this language import is only allowed at the toplevel", id.span)
3317+
if fullFeatureName == Feature.captureChecking then
3318+
ctx.compilationUnit.needsCaptureChecking = true
33163319
if allSourceVersionNames.contains(imported) && prefix.isEmpty then
33173320
if !outermost then
33183321
syntaxError(i"source version import is only allowed at the toplevel", id.span)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object A:
2+
def f(x: => Int) = ()
3+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import language.experimental.captureChecking
2+
object B:
3+
def test(x: => Int) = A.f(x)
4+
5+

0 commit comments

Comments
 (0)