Skip to content

Commit ebe09ac

Browse files
committed
Prioritize TASTy files over classfiles on classpath aggregation
In most cases the TASTy file is chosen over the classfile in a classpath because they are packaged together. However, for the `scala-library` (Scala 2 compiled library) and `scala2-library-tasty` (Scala 3 compiled Scala 2 library) we have the classfiles in one jar and the TASTy files in another jar. Given that the classpaths order in guaranteed to be deterministic we might end up with the classfile being loaded first and the TASTy file second. The aggregator must be capable of choosing the TASTy file over the classfile in this case as well. The aggregator will only choose the new TASTy over the old classfile if the TASTy file has no classfile in its classpath. In other words, we only use this new behaviour for TASTy only classpaths. This also implies that we can just add the `scala2-library-tasty` as a dependency in any project to use it. Note that this jar is not published yet.
1 parent fec54ec commit ebe09ac

File tree

7 files changed

+44
-24
lines changed

7 files changed

+44
-24
lines changed

compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,15 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
102102
ClassPathEntries(distinctPackages, distinctClassesAndSources)
103103
}
104104

105-
/**
106-
* Returns only one entry for each name. If there's both a source and a class entry, it
107-
* creates an entry containing both of them. If there would be more than one class or source
108-
* entries for the same class it always would use the first entry of each type found on a classpath.
105+
/** Returns only one entry for each name.
106+
*
107+
* If there's both a source and a class entry, it
108+
* creates an entry containing both of them. If there would be more than one class or source
109+
* entries for the same class it always would use the first entry of each type found on a classpath.
110+
*
111+
* A TASTy file with no class file entry will be chosen over a class file entry. This can happen if we load
112+
* the Scala 2 library as it has one JAR containing the class files and one JAR containing the TASTy files.
113+
* As classpath orders are not guaranteed to be deterministic we might end up having the TASTy in a later classpath entry.
109114
*/
110115
private def mergeClassesAndSources(entries: scala.collection.Seq[ClassRepresentation]): Seq[ClassRepresentation] = {
111116
// based on the implementation from MergedClassPath
@@ -124,6 +129,12 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
124129
mergedEntries(index) = BinaryAndSourceFilesEntry(existing, entry)
125130
case (entry: BinaryFileEntry, existing: SourceFileEntry) =>
126131
mergedEntries(index) = BinaryAndSourceFilesEntry(entry, existing)
132+
case (entry: StandaloneTastyFileEntry, _: ClassFileEntry) =>
133+
// Here we do not create a TastyWithClassFileEntry because the TASTy and the classfile
134+
// come from different classpaths. These may not have the same TASTy UUID.
135+
mergedEntries(index) = entry
136+
case (entry: StandaloneTastyFileEntry, BinaryAndSourceFilesEntry(_: ClassFileEntry, sourceEntry)) =>
137+
mergedEntries(index) = BinaryAndSourceFilesEntry(entry, sourceEntry)
127138
case _ =>
128139
}
129140
else {

compiler/src/dotty/tools/dotc/classpath/ClassPath.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ sealed trait BinaryFileEntry extends ClassRepresentation {
5252
object BinaryFileEntry {
5353
def apply(file: AbstractFile): BinaryFileEntry =
5454
if file.isTasty then
55-
TastyWithClassFileEntry(file)
55+
if file.resolveSiblingWithExtension("class") != null then TastyWithClassFileEntry(file)
56+
else StandaloneTastyFileEntry(file)
5657
else
5758
ClassFileEntry(file)
5859
}
@@ -67,6 +68,11 @@ private[dotty] final case class TastyWithClassFileEntry(file: AbstractFile) exte
6768
def binary: Option[AbstractFile] = Some(file)
6869
}
6970

71+
/** A TASTy file that does not have an associated class file */
72+
private[dotty] final case class StandaloneTastyFileEntry(file: AbstractFile) extends BinaryFileEntry {
73+
def binary: Option[AbstractFile] = Some(file)
74+
}
75+
7076
private[dotty] final case class SourceFileEntry(file: AbstractFile) extends ClassRepresentation {
7177
final def fileName: String = file.name
7278
def name: String = FileUtils.stripSourceExtension(file.name)

compiler/src/dotty/tools/io/AbstractFile.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ abstract class AbstractFile extends Iterable[AbstractFile] {
258258
final def resolveSibling(name: String): AbstractFile | Null =
259259
container.lookupName(name, directory = false)
260260

261+
final def resolveSiblingWithExtension(extension: String): AbstractFile | Null =
262+
resolveSibling(name.stripSuffix(this.extension) + extension)
263+
261264
private def fileOrSubdirectoryNamed(name: String, isDir: Boolean): AbstractFile =
262265
lookupName(name, isDir) match {
263266
case null =>

project/Build.scala

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -785,24 +785,21 @@ object Build {
785785
else if (debugFromTasty) "dotty.tools.dotc.fromtasty.Debug"
786786
else "dotty.tools.dotc.Main"
787787

788-
val scala2LibraryTasty = scala2Library.value match {
789-
case Scala2LibraryJar => Seq.empty
788+
var extraClasspath = Seq(scalaLib, dottyLib)
789+
790+
scala2Library.value match {
791+
case Scala2LibraryJar =>
790792
case Scala2LibraryTasty =>
791793
jars.get("scala2-library-tasty") match {
792-
case Some(jar) => Seq(jar)
793-
case None =>
794-
log.warn("Scala2LibraryTasty is ignored on non-bootstrapped compiler")
795-
Seq.empty
796-
}
794+
case Some(jar) => extraClasspath :+= jar
795+
case None => log.warn("Scala2LibraryTasty is ignored on non-bootstrapped compiler")
796+
};
797797
case Scala2LibraryCCTasty =>
798798
jars.get("scala2-library-cc-tasty") match {
799-
case Some(jar) => Seq(jar)
800-
case None =>
801-
log.warn("Scala2LibraryCCTasty is ignored on non-bootstrapped compiler")
802-
Seq.empty
799+
case Some(jar) => extraClasspath :+= jar
800+
case None => log.warn("Scala2LibraryCCTasty is ignored on non-bootstrapped compiler")
803801
}
804802
}
805-
var extraClasspath = scala2LibraryTasty ++ Seq(scalaLib, dottyLib)
806803

807804
if (decompile && !args.contains("-classpath"))
808805
extraClasspath ++= Seq(".")
@@ -1270,13 +1267,6 @@ object Build {
12701267
Compile / compile / fullClasspath ~= {
12711268
_.filterNot(file => file.data.getName == s"scala-library-$stdlibBootstrappedVersion.jar")
12721269
},
1273-
Compile / compile / dependencyClasspath := {
1274-
// make sure that the scala2-library (tasty of `scala2-library-tasty`) is listed before the scala-library (classfiles)
1275-
val (bootstrappedLib, otherLibs) =
1276-
(Compile / compile / dependencyClasspath).value
1277-
.partition(_.data.getName == s"scala2-library-${dottyVersion}.jar")
1278-
bootstrappedLib ++ otherLibs
1279-
},
12801270
)
12811271

12821272
lazy val `scala3-sbt-bridge` = project.in(file("sbt-bridge/src")).
@@ -1923,6 +1913,7 @@ object Build {
19231913
(`scala3-interfaces` / publishLocalBin),
19241914
(`scala3-compiler-bootstrapped` / publishLocalBin),
19251915
(`scala3-library-bootstrapped` / publishLocalBin),
1916+
(`scala2-library-tasty` / publishLocal),
19261917
(`scala3-library-bootstrappedJS` / publishLocalBin),
19271918
(`tasty-core-bootstrapped` / publishLocalBin),
19281919
(`scala3-staging` / publishLocalBin),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
scalaVersion := sys.props("plugin.scalaVersion")
2+
3+
libraryDependencies += "org.scala-lang" %% "scala2-library-tasty" % scalaVersion.value
4+
scalacOptions += "-Yscala2-unpickler:never" // check that we do not load symbol from the Scala 2 library classfiles (use TASTy)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package hello
2+
3+
@main def hello: Unit =
4+
println(Some("Hello world!")) // load Some form the Scala 2 library TASTy
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
> run

0 commit comments

Comments
 (0)