Skip to content

Commit c61605a

Browse files
committed
Always run the compiler with a bootstrapped dotty-library
The non-bootstrapped dotty-library may not be binary compatible with the compiler, this hasn't affected us so far because the non-bootstrapped dotty-library has always been compiled with Scala 2, but in the future it will be compiled by an old version of Dotty itself. To enforce this, I added a new flag -Yscala2-unpickler and set it so that scala-library is the only jar from which we're allowed to unpickle Scala 2 symbols in both the build and the tests.
1 parent 33d76d4 commit c61605a

File tree

5 files changed

+51
-17
lines changed

5 files changed

+51
-17
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class ScalaSettings extends Settings.SettingGroup {
9494
val YlogClasspath = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.")
9595
val YdisableFlatCpCaching = BooleanSetting("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
9696

97+
val Yscala2Unpickler = StringSetting("-Yscala2-unpickler", "", "Control where we may get Scala 2 symbols from. This is either \"always\", \"never\", or a classpath.", "always")
98+
9799
val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.")
98100
val YnoInline = BooleanSetting("-Yno-inline", "Suppress inlining.")
99101
val YnoGenericSig = BooleanSetting("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.")

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,20 @@ class ClassfileParser(
755755
}
756756

757757
def unpickleScala(bytes: Array[Byte]): Some[Embedded] = {
758+
val allowed = ctx.settings.Yscala2Unpickler.value
759+
760+
def failUnless(cond: Boolean) =
761+
assert(cond,
762+
s"Unpickling ${classRoot.symbol.showLocated} from ${classRoot.symbol.associatedFile} is not allowed with -Yscala2-unpickler $allowed")
763+
764+
if (allowed != "always") {
765+
failUnless(allowed != "never")
766+
val allowedList = allowed.split(":").toList
767+
val file = classRoot.symbol.associatedFile
768+
// Using `.toString.contains` isn't great, but it's good enough for a debug flag.
769+
failUnless(file == null || allowedList.exists(path => file.toString.contains(path)))
770+
}
771+
758772
val unpickler = new unpickleScala2.Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx)
759773
unpickler.run()(ctx.addMode(Scala2UnpicklingMode))
760774
Some(unpickler)

compiler/test/dotty/Jars.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ object Jars {
1818
lazy val scalaAsm: String =
1919
findJarFromRuntime("scala-asm-6.0.0-scala-1")
2020

21+
/** scala-xml jar */
22+
lazy val scalaXml: String =
23+
findJarFromRuntime("scala-xml")
24+
2125
/** JLine Jar */
2226
lazy val jline: String =
2327
findJarFromRuntime("jline-3.7.0")

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ object TestConfiguration {
1010
)
1111

1212
val checkOptions = Array(
13+
"-Yscala2-unpickler", s"${Jars.scalaLibrary}:${Jars.scalaXml}",
1314
"-Yno-deep-subtypes",
1415
"-Yno-double-bindings",
1516
"-Yforce-sbt-phases",

project/Build.scala

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,13 @@ object Build {
227227
// otherwise sbt 0.13 incremental compilation breaks (https://github.com/sbt/sbt/issues/3142)
228228
scalacOptions ++= Seq("-bootclasspath", sys.props("sun.boot.class.path")),
229229

230+
// Enforce that the only Scala 2 classfiles we unpickle come from scala-library
231+
scalacOptions ++= {
232+
val attList = (dependencyClasspath in `dotty-library` in Compile).value
233+
val scalaLib = findLib(attList, "scala-library")
234+
Seq("-Yscala2-unpickler", scalaLib)
235+
},
236+
230237
// sbt gets very unhappy if two projects use the same target
231238
target := baseDirectory.value / ".." / "out" / "bootstrap" / name.value,
232239

@@ -427,6 +434,11 @@ object Build {
427434
(testOnly in Test).toTask(cmd)
428435
}
429436

437+
def findLib(attList: Seq[Attributed[File]], name: String) = attList
438+
.map(_.data.getAbsolutePath)
439+
.find(_.contains(name))
440+
.toList.mkString(":")
441+
430442
// Settings shared between dotty-compiler and dotty-compiler-bootstrapped
431443
lazy val commonDottyCompilerSettings = Seq(
432444

@@ -573,20 +585,15 @@ object Build {
573585
jarOpts ::: tuning ::: agentOptions ::: ci_build ::: path.toList
574586
},
575587

576-
testCompilation := testOnlyFiltered("dotty.tools.dotc.CompilationTests", "--exclude-categories=dotty.SlowTests").evaluated,
588+
testCompilation := testOnlyFiltered("dotty.tools.dotc.*CompilationTests", "--exclude-categories=dotty.SlowTests").evaluated,
577589
testFromTasty := testOnlyFiltered("dotty.tools.dotc.FromTastyTests", "").evaluated,
578590

579591
dotr := {
580592
val args: List[String] = spaceDelimited("<arg>").parsed.toList
581593
val attList = (dependencyClasspath in Runtime).value
582594
val jars = packageAll.value
583595

584-
def findLib(name: String) = attList
585-
.map(_.data.getAbsolutePath)
586-
.find(_.contains(name))
587-
.toList.mkString(":")
588-
589-
val scalaLib = findLib("scala-library")
596+
val scalaLib = findLib(attList, "scala-library")
590597
val dottyLib = jars("dotty-library")
591598

592599
def run(args: List[String]): Unit = {
@@ -600,7 +607,7 @@ object Build {
600607
println("Couldn't find scala-library on classpath, please run using script in bin dir instead")
601608
} else if (args.contains("-with-compiler")) {
602609
val args1 = args.filter(_ != "-with-compiler")
603-
val asm = findLib("scala-asm")
610+
val asm = findLib(attList, "scala-asm")
604611
val dottyCompiler = jars("dotty-compiler")
605612
val dottyInterfaces = jars("dotty-interfaces")
606613
run(insertClasspathInArgs(args1, s"$dottyCompiler:$dottyInterfaces:$asm"))
@@ -679,20 +686,26 @@ object Build {
679686

680687
lazy val nonBootstrapedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq(
681688
// packageAll packages all and then returns a map with the abs location
682-
packageAll := {
683-
Map(
684-
"dotty-interfaces" -> packageBin.in(`dotty-interfaces`, Compile).value,
685-
"dotty-compiler" -> packageBin.in(Compile).value,
686-
"dotty-library" -> packageBin.in(`dotty-library`, Compile).value
687-
).mapValues(_.getAbsolutePath)
688-
}
689+
packageAll := Def.taskDyn { // Use a dynamic task to avoid loops when loading the settings
690+
Def.task {
691+
Map(
692+
"dotty-interfaces" -> packageBin.in(`dotty-interfaces`, Compile).value,
693+
"dotty-compiler" -> packageBin.in(Compile).value,
694+
695+
// NOTE: Using dotty-library-bootstrapped here is intentional: when
696+
// running the compiler, we should always have the bootstrapped
697+
// library on the compiler classpath since the non-bootstrapped one
698+
// may not be binary-compatible.
699+
"dotty-library" -> packageBin.in(`dotty-library-bootstrapped`, Compile).value
700+
).mapValues(_.getAbsolutePath)
701+
}
702+
}.value
689703
)
690704

691705
lazy val bootstrapedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq(
692706
packageAll := {
693707
packageAll.in(`dotty-compiler`).value ++ Seq(
694-
"dotty-compiler" -> packageBin.in(Compile).value.getAbsolutePath,
695-
"dotty-library" -> packageBin.in(`dotty-library-bootstrapped`, Compile).value.getAbsolutePath
708+
"dotty-compiler" -> packageBin.in(Compile).value.getAbsolutePath
696709
)
697710
}
698711
)

0 commit comments

Comments
 (0)