Skip to content

Commit d8a6751

Browse files
authored
Add capture checking the compiler as a test (#16292)
- Copy compiler code base into a separate test - Adapt code base so that it can be capture checked Based on #16254 Only the last two commits are new: - [57527c4](57527c4) copies the compiler codebase as a separate test. - [6dfeaa7](6dfeaa7) makes the changes so that it passes capture checking.
2 parents a21791b + 8e9327b commit d8a6751

File tree

449 files changed

+146583
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

449 files changed

+146583
-1
lines changed

compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class BootstrappedOnlyCompilationTests {
3636
).checkCompile()
3737
}
3838

39+
@Test def posWithCompilerCC: Unit =
40+
implicit val testGroup: TestGroup = TestGroup("compilePosWithCompilerCC")
41+
aggregateTests(
42+
compileDir("tests/pos-with-compiler-cc/dotc", withCompilerOptions.and("-language:experimental.captureChecking"))
43+
).checkCompile()
44+
3945
@Test def posWithCompiler: Unit = {
4046
implicit val testGroup: TestGroup = TestGroup("compilePosWithCompiler")
4147
aggregateTests(

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ class CompilationTests {
242242
).checkCompile()
243243
}
244244

245-
@Test def recheck: Unit =
245+
//@Test disabled in favor of posWithCompilerCC to save time.
246+
def recheck: Unit =
246247
given TestGroup = TestGroup("recheck")
247248
aggregateTests(
248249
compileFilesInDir("tests/new", recheckOptions),
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package dotty.tools
2+
package dotc
3+
4+
import core.Contexts._
5+
import reporting.Reporter
6+
import io.AbstractFile
7+
8+
import scala.annotation.internal.sharable
9+
10+
/** A main class for running compiler benchmarks. Can instantiate a given
11+
* number of compilers and run each (sequentially) a given number of times
12+
* on the same sources.
13+
*/
14+
object Bench extends Driver:
15+
16+
@sharable private var numRuns = 1
17+
18+
private def ntimes(n: Int)(op: => Reporter): Reporter =
19+
(0 until n).foldLeft(emptyReporter)((_, _) => op)
20+
21+
@sharable private var times: Array[Int] = _
22+
23+
override def doCompile(compiler: Compiler, files: List[AbstractFile])(using Context): Reporter =
24+
times = new Array[Int](numRuns)
25+
var reporter: Reporter = emptyReporter
26+
for i <- 0 until numRuns do
27+
val start = System.nanoTime()
28+
reporter = super.doCompile(compiler, files)
29+
times(i) = ((System.nanoTime - start) / 1000000).toInt
30+
println(s"time elapsed: ${times(i)}ms")
31+
if ctx.settings.Xprompt.value then
32+
print("hit <return> to continue >")
33+
System.in.nn.read()
34+
println()
35+
reporter
36+
37+
def extractNumArg(args: Array[String], name: String, default: Int = 1): (Int, Array[String]) = {
38+
val pos = args indexOf name
39+
if (pos < 0) (default, args)
40+
else (args(pos + 1).toInt, (args take pos) ++ (args drop (pos + 2)))
41+
}
42+
43+
def reportTimes() =
44+
val best = times.sorted
45+
val measured = numRuns / 3
46+
val avgBest = best.take(measured).sum / measured
47+
val avgLast = times.reverse.take(measured).sum / measured
48+
println(s"best out of $numRuns runs: ${best(0)}")
49+
println(s"average out of best $measured: $avgBest")
50+
println(s"average out of last $measured: $avgLast")
51+
52+
override def process(args: Array[String], rootCtx: Context): Reporter =
53+
val (numCompilers, args1) = extractNumArg(args, "#compilers")
54+
val (numRuns, args2) = extractNumArg(args1, "#runs")
55+
this.numRuns = numRuns
56+
var reporter: Reporter = emptyReporter
57+
for i <- 0 until numCompilers do
58+
reporter = super.process(args2, rootCtx)
59+
reportTimes()
60+
reporter
61+
62+
end Bench
63+
64+
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package dotty.tools
2+
package dotc
3+
4+
import core._
5+
import Contexts._
6+
import SymDenotations.ClassDenotation
7+
import Symbols._
8+
import util.{FreshNameCreator, SourceFile, NoSource}
9+
import util.Spans.Span
10+
import ast.{tpd, untpd}
11+
import tpd.{Tree, TreeTraverser}
12+
import ast.Trees.{Import, Ident}
13+
import typer.Nullables
14+
import transform.SymUtils._
15+
import core.Decorators._
16+
import config.{SourceVersion, Feature}
17+
import StdNames.nme
18+
import scala.annotation.internal.sharable
19+
20+
class CompilationUnit protected (val source: SourceFile) {
21+
22+
override def toString: String = source.toString
23+
24+
var untpdTree: untpd.Tree = untpd.EmptyTree
25+
26+
var tpdTree: tpd.Tree = tpd.EmptyTree
27+
28+
/** Is this the compilation unit of a Java file */
29+
def isJava: Boolean = source.file.name.endsWith(".java")
30+
31+
/** The source version for this unit, as determined by a language import */
32+
var sourceVersion: Option[SourceVersion] = None
33+
34+
/** Pickled TASTY binaries, indexed by class. */
35+
var pickled: Map[ClassSymbol, () => Array[Byte]] = Map()
36+
37+
/** The fresh name creator for the current unit.
38+
* FIXME(#7661): This is not fine-grained enough to enable reproducible builds,
39+
* see https://github.com/scala/scala/commit/f50ec3c866263448d803139e119b33afb04ec2bc
40+
*/
41+
val freshNames: FreshNameCreator = new FreshNameCreator.Default
42+
43+
/** Will be set to `true` if there are inline call that must be inlined after typer.
44+
* The information is used in phase `Inlining` in order to avoid traversing trees that need no transformations.
45+
*/
46+
var needsInlining: Boolean = false
47+
48+
/** Set to `true` if inliner added anonymous mirrors that need to be completed */
49+
var needsMirrorSupport: Boolean = false
50+
51+
/** Will be set to `true` if contains `Quote`.
52+
* The information is used in phase `Staging`/`Splicing`/`PickleQuotes` in order to avoid traversing trees that need no transformations.
53+
*/
54+
var needsStaging: Boolean = false
55+
56+
/** Will be set to true if the unit contains a captureChecking language import */
57+
var needsCaptureChecking: Boolean = false
58+
59+
/** Will be set to true if the unit contains a pureFunctions language import */
60+
var knowsPureFuns: Boolean = false
61+
62+
var suspended: Boolean = false
63+
var suspendedAtInliningPhase: Boolean = false
64+
65+
/** Can this compilation unit be suspended */
66+
def isSuspendable: Boolean = true
67+
68+
/** Suspends the compilation unit by thowing a SuspendException
69+
* and recording the suspended compilation unit
70+
*/
71+
def suspend()(using Context): Nothing =
72+
assert(isSuspendable)
73+
if !suspended then
74+
if (ctx.settings.XprintSuspension.value)
75+
report.echo(i"suspended: $this")
76+
suspended = true
77+
ctx.run.nn.suspendedUnits += this
78+
if ctx.phase == Phases.inliningPhase then
79+
suspendedAtInliningPhase = true
80+
throw CompilationUnit.SuspendException()
81+
82+
private var myAssignmentSpans: Map[Int, List[Span]] | Null = null
83+
84+
/** A map from (name-) offsets of all local variables in this compilation unit
85+
* that can be tracked for being not null to the list of spans of assignments
86+
* to these variables.
87+
*/
88+
def assignmentSpans(using Context): Map[Int, List[Span]] =
89+
if myAssignmentSpans == null then myAssignmentSpans = Nullables.assignmentSpans
90+
myAssignmentSpans.nn
91+
}
92+
93+
@sharable object NoCompilationUnit extends CompilationUnit(NoSource) {
94+
95+
override def isJava: Boolean = false
96+
97+
override def suspend()(using Context): Nothing =
98+
throw CompilationUnit.SuspendException()
99+
100+
override def assignmentSpans(using Context): Map[Int, List[Span]] = Map.empty
101+
}
102+
103+
object CompilationUnit {
104+
105+
class SuspendException extends Exception
106+
107+
/** Make a compilation unit for top class `clsd` with the contents of the `unpickled` tree */
108+
def apply(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit =
109+
val file = clsd.symbol.associatedFile.nn
110+
apply(SourceFile(file, Array.empty[Char]), unpickled, forceTrees)
111+
112+
/** Make a compilation unit, given picked bytes and unpickled tree */
113+
def apply(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit = {
114+
assert(!unpickled.isEmpty, unpickled)
115+
val unit1 = new CompilationUnit(source)
116+
unit1.tpdTree = unpickled
117+
if (forceTrees) {
118+
val force = new Force
119+
force.traverse(unit1.tpdTree)
120+
unit1.needsStaging = force.containsQuote
121+
unit1.needsInlining = force.containsInline
122+
}
123+
unit1
124+
}
125+
126+
/** Create a compilation unit corresponding to `source`.
127+
* If `mustExist` is true, this will fail if `source` does not exist.
128+
*/
129+
def apply(source: SourceFile, mustExist: Boolean = true)(using Context): CompilationUnit = {
130+
val src =
131+
if (!mustExist)
132+
source
133+
else if (source.file.isDirectory) {
134+
report.error(s"expected file, received directory '${source.file.path}'")
135+
NoSource
136+
}
137+
else if (!source.file.exists) {
138+
report.error(s"source file not found: ${source.file.path}")
139+
NoSource
140+
}
141+
else source
142+
new CompilationUnit(src)
143+
}
144+
145+
/** Force the tree to be loaded */
146+
private class Force extends TreeTraverser {
147+
var containsQuote = false
148+
var containsInline = false
149+
var containsCaptureChecking = false
150+
def traverse(tree: Tree)(using Context): Unit = {
151+
if (tree.symbol.isQuote)
152+
containsQuote = true
153+
if tree.symbol.is(Flags.Inline) then
154+
containsInline = true
155+
tree match
156+
case Import(qual, selectors) =>
157+
tpd.languageImport(qual) match
158+
case Some(prefix) =>
159+
for case untpd.ImportSelector(untpd.Ident(imported), untpd.EmptyTree, _) <- selectors do
160+
Feature.handleGlobalLanguageImport(prefix, imported)
161+
case _ =>
162+
case _ =>
163+
traverseChildren(tree)
164+
}
165+
}
166+
}

0 commit comments

Comments
 (0)