Skip to content

Commit 0b4e36f

Browse files
committed
Add FromTasty compiler tests
Fixes FromTasty when info already loaded
1 parent 8f9c1b1 commit 0b4e36f

File tree

10 files changed

+225
-37
lines changed

10 files changed

+225
-37
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package dotty.tools
22
package dotc
33

4-
import dotty.tools.dotc.core.Types.Type
5-
import dotty.tools.dotc.core.tasty.{TastyUnpickler, TastyBuffer, TastyPickler}
4+
import dotty.tools.dotc.core.Types.Type // Do not remove me #3383
65
import util.SourceFile
76
import ast.{tpd, untpd}
87
import dotty.tools.dotc.ast.tpd.{ Tree, TreeTraverser }
@@ -28,6 +27,7 @@ object CompilationUnit {
2827

2928
/** Make a compilation unit for top class `clsd` with the contends of the `unpickled` */
3029
def mkCompilationUnit(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(implicit ctx: Context): CompilationUnit = {
30+
assert(!unpickled.isEmpty, unpickled)
3131
val unit1 = new CompilationUnit(new SourceFile(clsd.symbol.sourceFile, Seq()))
3232
unit1.tpdTree = unpickled
3333
if (forceTrees)

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,28 @@ object FromTasty extends Driver {
6969

7070
def readTASTY(unit: CompilationUnit)(implicit ctx: Context): CompilationUnit = unit match {
7171
case unit: TASTYCompilationUnit =>
72+
assert(ctx.settings.YretainTrees.value)
7273
val className = unit.className.toTypeName
73-
val clsd = ctx.base.staticRef(className)
74-
def cannotUnpickle(reason: String) = {
75-
ctx.error(s"class $className cannot be unpickled because $reason")
76-
unit
77-
}
78-
clsd match {
74+
ctx.base.staticRef(className) match {
7975
case clsd: ClassDenotation =>
80-
clsd.infoOrCompleter match {
76+
def cannotUnpickle(reason: String) =
77+
ctx.error(s"class $className cannot be unpickled because $reason")
78+
def tryToLoad = clsd.infoOrCompleter match {
8179
case info: ClassfileLoader =>
8280
info.load(clsd)
83-
val unpickled = clsd.symbol.asClass.tree
84-
if (unpickled != null) CompilationUnit.mkCompilationUnit(clsd, unpickled, forceTrees = true)
85-
else cannotUnpickle(s"its class file ${info.classfile} does not have a TASTY attribute")
81+
Option(clsd.symbol.asClass.tree).orElse {
82+
cannotUnpickle(s"its class file ${info.classfile} does not have a TASTY attribute")
83+
None
84+
}
85+
8686
case info =>
8787
cannotUnpickle(s"its info of type ${info.getClass} is not a ClassfileLoader")
88+
None
8889
}
90+
Option(clsd.symbol.asClass.tree).orElse(tryToLoad)
91+
.map(unpickled => CompilationUnit.mkCompilationUnit(clsd, unpickled, forceTrees = true))
92+
.getOrElse(unit)
93+
8994
case _ =>
9095
ctx.error(s"class not found: $className")
9196
unit
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package dotty
2+
package tools
3+
package dotc
4+
5+
import org.junit.{AfterClass, Test}
6+
import vulpix._
7+
8+
import scala.concurrent.duration._
9+
10+
class FromTastyTests extends ParallelTesting {
11+
import TestConfiguration._
12+
import FromTastyTests._
13+
14+
// Test suite configuration --------------------------------------------------
15+
16+
def maxDuration = 30.seconds
17+
def numberOfSlaves = 5
18+
def safeMode = Properties.testsSafeMode
19+
def isInteractive = SummaryReport.isInteractive
20+
def testFilter = Properties.testsFilter
21+
22+
23+
@Test def posTestFromTasty: Unit = {
24+
implicit val testGroup: TestGroup = TestGroup("posTestFromTasty")
25+
val (step1, step2) = {
26+
// compileTastyInDir("../tests/pos", defaultOptions) + // FIXME
27+
compileTastyInDir("../tests/pos-from-tasty", defaultOptions) +
28+
compileTasty("../tests/pos-from-tasty/simpleClass.scala", defaultOptions)
29+
}
30+
step1.keepOutput.checkCompile() // Compile all files to generate the class files with tasty
31+
step2.keepOutput.checkCompile() // Compile from tasty
32+
(step1 + step2).delete()
33+
}
34+
35+
@Test def runTestFromTasty: Unit = {
36+
implicit val testGroup: TestGroup = TestGroup("runTestFromTasty")
37+
val (step1, step2) = {
38+
// compileTastyInDir("../tests/run", defaultOptions) + // FIXME
39+
compileTastyInDir("../tests/run-from-tasty", defaultOptions)
40+
}
41+
step1.keepOutput.checkCompile() // Compile all files to generate the class files with tasty
42+
step2.keepOutput.checkRuns() // Compile from tasty and run the result
43+
(step1 + step2).delete()
44+
}
45+
46+
private implicit class tastyCompilationTuples(tup: (CompilationTest, CompilationTest)) {
47+
def +(that: (CompilationTest, CompilationTest)): (CompilationTest, CompilationTest) =
48+
(tup._1 + that._1, tup._2 + that._2)
49+
}
50+
}
51+
52+
object FromTastyTests {
53+
implicit val summaryReport: SummaryReporting = new SummaryReport
54+
@AfterClass def cleanup(): Unit = summaryReport.echoSummary()
55+
}

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 110 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ trait ParallelTesting extends RunnerOrchestration { self =>
132132
name: String,
133133
files: Array[JFile],
134134
flags: TestFlags,
135-
outDir: JFile
135+
outDir: JFile,
136+
fromTasty: Boolean = false
136137
) extends TestSource {
137138
def sourceFiles: Array[JFile] = files.filter(isSourceFile)
138139

@@ -213,7 +214,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
213214
private val filteredSources =
214215
if (!testFilter.isDefined) testSources
215216
else testSources.filter {
216-
case JointCompilationSource(_, files, _, _) =>
217+
case JointCompilationSource(_, files, _, _, _) =>
217218
files.exists(file => file.getAbsolutePath.contains(testFilter.get))
218219
case SeparateCompilationSource(_, dir, _, _) =>
219220
dir.getAbsolutePath.contains(testFilter.get)
@@ -336,15 +337,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
336337

337338
val files: Array[JFile] = files0.flatMap(flattenFiles)
338339

339-
def addOutDir(xs: Array[String]): Array[String] = {
340-
val (beforeCp, cpAndAfter) = xs.toList.span(_ != "-classpath")
341-
if (cpAndAfter.nonEmpty) {
342-
val (cp :: cpArg :: rest) = cpAndAfter
343-
(beforeCp ++ (cp :: (cpArg + s":${targetDir.getAbsolutePath}") :: rest)).toArray
344-
}
345-
else (beforeCp ++ ("-classpath" :: targetDir.getAbsolutePath :: Nil)).toArray
346-
}
347-
348340
def compileWithJavac(fs: Array[String]) = if (fs.nonEmpty) {
349341
val fullArgs = Array(
350342
"javac",
@@ -379,7 +371,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
379371
}
380372
}
381373

382-
val allArgs = addOutDir(flags.all)
374+
val allArgs = flags.withClasspath(targetDir.getAbsolutePath).all
383375

384376
// Compile with a try to catch any StackTrace generated by the compiler:
385377
try {
@@ -400,6 +392,30 @@ trait ParallelTesting extends RunnerOrchestration { self =>
400392
reporter
401393
}
402394

395+
protected def compileFromTasty(flags0: TestFlags, suppressErrors: Boolean, targetDir: JFile): TestReporter = {
396+
val tastyOutput = new JFile(targetDir.getPath + "_from-tasty")
397+
tastyOutput.mkdir()
398+
val flags = flags0 and ("-d", tastyOutput.getAbsolutePath)
399+
400+
def hasTastyFileToClassName(f: JFile): String =
401+
targetDir.toPath.relativize(f.toPath).toString.dropRight(".hasTasty".length).replace('/', '.')
402+
val classes = flattenFiles(targetDir).filter(isHasTastyFile).map(hasTastyFileToClassName)
403+
404+
val reporter =
405+
TestReporter.reporter(realStdout, logLevel =
406+
if (suppressErrors || suppressAllOutput) ERROR + 1 else ERROR)
407+
408+
// Compile with a try to catch any StackTrace generated by the compiler:
409+
try {
410+
dotc.FromTasty.process(flags.all ++ classes, reporter = reporter)
411+
}
412+
catch {
413+
case NonFatal(ex) => reporter.logStackTrace(ex)
414+
}
415+
416+
reporter
417+
}
418+
403419
private[ParallelTesting] def executeTestSuite(): this.type = {
404420
assert(_testSourcesCompleted == 0, "not allowed to re-use a `CompileRun`")
405421

@@ -440,24 +456,30 @@ trait ParallelTesting extends RunnerOrchestration { self =>
440456

441457
this
442458
}
459+
460+
/** Returns all files in directory or the file if not a directory */
461+
private def flattenFiles(f: JFile): Array[JFile] =
462+
if (f.isDirectory) f.listFiles.flatMap(flattenFiles)
463+
else Array(f)
443464
}
444465

445466
private final class PosTest(testSources: List[TestSource], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)(implicit summaryReport: SummaryReporting)
446467
extends Test(testSources, times, threadLimit, suppressAllOutput) {
447468
protected def encapsulatedCompilation(testSource: TestSource) = new LoggedRunnable {
448469
def checkTestSource(): Unit = tryCompile(testSource) {
449470
testSource match {
450-
case testSource @ JointCompilationSource(_, files, flags, outDir) => {
451-
val reporter = compile(testSource.sourceFiles, flags, false, outDir)
471+
case testSource @ JointCompilationSource(_, files, flags, outDir, fromTasty) =>
472+
val reporter =
473+
if (fromTasty) compileFromTasty(flags, false, outDir)
474+
else compile(testSource.sourceFiles, flags, false, outDir)
452475
registerCompletion(reporter.errorCount)
453476

454477
if (reporter.compilerCrashed || reporter.errorCount > 0) {
455478
logReporterContents(reporter)
456479
logBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount)
457480
}
458-
}
459481

460-
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
482+
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) =>
461483
val reporters = testSource.compilationGroups.map(files => compile(files, flags, false, outDir))
462484
val compilerCrashed = reporters.exists(_.compilerCrashed)
463485
val errorCount = reporters.foldLeft(0) { (acc, reporter) =>
@@ -475,7 +497,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
475497
reporters.foreach(logReporterContents)
476498
logBuildInstructions(reporters.head, testSource, errorCount, warningCount)
477499
}
478-
}
479500
}
480501
}
481502
}
@@ -546,7 +567,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
546567
protected def encapsulatedCompilation(testSource: TestSource) = new LoggedRunnable {
547568
def checkTestSource(): Unit = tryCompile(testSource) {
548569
val (compilerCrashed, errorCount, warningCount, verifier: Function0[Unit]) = testSource match {
549-
case testSource @ JointCompilationSource(_, files, flags, outDir) => {
570+
case testSource @ JointCompilationSource(_, files, flags, outDir, fromTasty) =>
550571
val checkFile = files.flatMap { file =>
551572
if (file.isDirectory) Nil
552573
else {
@@ -556,17 +577,18 @@ trait ParallelTesting extends RunnerOrchestration { self =>
556577
else Nil
557578
}
558579
}.headOption
559-
val reporter = compile(testSource.sourceFiles, flags, false, outDir)
580+
val reporter =
581+
if (fromTasty) compileFromTasty(flags, false, outDir)
582+
else compile(testSource.sourceFiles, flags, false, outDir)
560583

561584
if (reporter.compilerCrashed || reporter.errorCount > 0) {
562585
logReporterContents(reporter)
563586
logBuildInstructions(reporter, testSource, reporter.errorCount, reporter.warningCount)
564587
}
565588

566589
(reporter.compilerCrashed, reporter.errorCount, reporter.warningCount, () => verifyOutput(checkFile, outDir, testSource, reporter.warningCount))
567-
}
568590

569-
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
591+
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) =>
570592
val checkFile = new JFile(dir.getAbsolutePath.reverse.dropWhile(_ == '/').reverse + ".check")
571593
val reporters = testSource.compilationGroups.map(compile(_, flags, false, outDir))
572594
val compilerCrashed = reporters.exists(_.compilerCrashed)
@@ -584,7 +606,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
584606
}
585607

586608
(compilerCrashed, errorCount, warningCount, () => verifyOutput(Some(checkFile), outDir, testSource, warningCount))
587-
}
588609
}
589610

590611
if (!compilerCrashed && errorCount == 0) verifier()
@@ -655,7 +676,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
655676
}
656677

657678
val (compilerCrashed, expectedErrors, actualErrors, hasMissingAnnotations, errorMap) = testSource match {
658-
case testSource @ JointCompilationSource(_, files, flags, outDir) => {
679+
case testSource @ JointCompilationSource(_, files, flags, outDir, fromTasty) =>
659680
val sourceFiles = testSource.sourceFiles
660681
val (errorMap, expectedErrors) = getErrorMapAndExpectedCount(sourceFiles)
661682
val reporter = compile(sourceFiles, flags, true, outDir)
@@ -665,7 +686,6 @@ trait ParallelTesting extends RunnerOrchestration { self =>
665686
logReporterContents(reporter)
666687

667688
(reporter.compilerCrashed, expectedErrors, actualErrors, () => getMissingExpectedErrors(errorMap, reporter.errors), errorMap)
668-
}
669689

670690
case testSource @ SeparateCompilationSource(_, dir, flags, outDir) => {
671691
val compilationGroups = testSource.compilationGroups
@@ -941,7 +961,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
941961
*/
942962
def copyToTarget(): CompilationTest = new CompilationTest (
943963
targets.map {
944-
case target @ JointCompilationSource(_, files, _, outDir) =>
964+
case target @ JointCompilationSource(_, files, _, outDir, _) =>
945965
target.copy(files = files.map(copyToDir(outDir,_)))
946966
case target @ SeparateCompilationSource(_, dir, _, outDir) =>
947967
target.copy(dir = copyToDir(outDir, dir))
@@ -1056,6 +1076,30 @@ trait ParallelTesting extends RunnerOrchestration { self =>
10561076
new CompilationTest(target)
10571077
}
10581078

1079+
/** Compiles a single file from the string path `f` using the supplied flags */
1080+
def compileTasty(f: String, flags: TestFlags)(implicit testGroup: TestGroup): (CompilationTest, CompilationTest) = {
1081+
val sourceFile = new JFile(f)
1082+
val parent = sourceFile.getParentFile
1083+
val outDir =
1084+
defaultOutputDir + testGroup + "/" +
1085+
sourceFile.getName.substring(0, sourceFile.getName.lastIndexOf('.')) + "/"
1086+
1087+
require(
1088+
sourceFile.exists && !sourceFile.isDirectory &&
1089+
(parent ne null) && parent.exists && parent.isDirectory,
1090+
s"Source file: $f, didn't exist"
1091+
)
1092+
val tastySource = createOutputDirsForFile(sourceFile, parent, outDir)
1093+
val target = JointCompilationSource(
1094+
testGroup.name,
1095+
Array(sourceFile),
1096+
flags.withClasspath(tastySource.getPath) and "-Yretain-trees",
1097+
tastySource,
1098+
fromTasty = true
1099+
)
1100+
(compileFile(f, flags), new CompilationTest(target))
1101+
}
1102+
10591103
/** Compiles a directory `f` using the supplied `flags`. This method does
10601104
* deep compilation, that is - it compiles all files and subdirectories
10611105
* contained within the directory `f`.
@@ -1137,6 +1181,44 @@ trait ParallelTesting extends RunnerOrchestration { self =>
11371181
new CompilationTest(targets)
11381182
}
11391183

1184+
/** This function compiles the files and folders contained within directory
1185+
* `f` in a specific way. Once compiled, they are recompiled/run from tasty as sources.
1186+
*
1187+
* - Each file is compiled separately as a single compilation run
1188+
* - Each directory is compiled as a `SeparateCompilationTaret`, in this
1189+
* target all files are grouped according to the file suffix `_X` where `X`
1190+
* is a number. These groups are then ordered in ascending order based on
1191+
* the value of `X` and each group is compiled one after the other.
1192+
*
1193+
* For this function to work as expected, we use the same convention for
1194+
* directory layout as the old partest. That is:
1195+
*
1196+
* - Single files can have an associated check-file with the same name (but
1197+
* with file extension `.check`)
1198+
* - Directories can have an associated check-file, where the check file has
1199+
* the same name as the directory (with the file extension `.check`)
1200+
*/
1201+
def compileTastyInDir(f: String, flags0: TestFlags)(implicit testGroup: TestGroup): (CompilationTest, CompilationTest) = {
1202+
val outDir = defaultOutputDir + testGroup + "/"
1203+
val flags = flags0 and "-Yretain-trees"
1204+
val sourceDir = new JFile(f)
1205+
checkRequirements(f, sourceDir, outDir)
1206+
1207+
val (dirs, files) = compilationTargets(sourceDir)
1208+
1209+
val targets =
1210+
files.map { f =>
1211+
val classpath = createOutputDirsForFile(f, sourceDir, outDir)
1212+
JointCompilationSource(testGroup.name, Array(f), flags.withClasspath(classpath.getPath), classpath, fromTasty = true)
1213+
}
1214+
// TODO add SeparateCompilationSource from tasty?
1215+
1216+
// Create a CompilationTest and let the user decide whether to execute a pos or a neg test
1217+
val generateClassFiles = compileFilesInDir(f, flags0)
1218+
(generateClassFiles, new CompilationTest(targets))
1219+
}
1220+
1221+
11401222
/** This function behaves similar to `compileFilesInDir` but it ignores
11411223
* sub-directories and as such, does **not** perform separate compilation
11421224
* tests.
@@ -1165,4 +1247,7 @@ object ParallelTesting {
11651247
val name = f.getName
11661248
name.endsWith(".scala") || name.endsWith(".java")
11671249
}
1250+
1251+
def isHasTastyFile(f: JFile): Boolean =
1252+
f.getName.endsWith(".hasTasty")
11681253
}

compiler/test/dotty/tools/vulpix/TestFlags.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ final case class TestFlags(
1111
def without(flags: String*): TestFlags =
1212
TestFlags(defaultClassPath, runClassPath, options diff flags)
1313

14+
def withClasspath(classPath: String): TestFlags =
15+
TestFlags(s"$defaultClassPath:$classPath", runClassPath, options)
16+
1417
def all: Array[String] = Array("-classpath", defaultClassPath) ++ options
1518
}
1619

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package foo
2+
3+
class A
4+
5+
class B extends A

tests/run-from-tasty/innerClass.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
foo.A
2+
foo.A$B

0 commit comments

Comments
 (0)