Skip to content

Commit c2abb49

Browse files
authored
Merge pull request #9694 from dotty-staging/fix-incremental-inner
2 parents f081910 + 0e350d5 commit c2abb49

File tree

18 files changed

+148
-18
lines changed

18 files changed

+148
-18
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ object Symbols {
250250

251251
/** The source or class file from which this class or
252252
* the class containing this symbol was generated, null if not applicable.
253+
* Note that this the returned classfile might be the top-level class
254+
* containing this symbol instead of the directly enclosing class.
253255
* Overridden in ClassSymbol
254256
*/
255257
def associatedFile(using Context): AbstractFile =

compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala

+26-18
Original file line numberDiff line numberDiff line change
@@ -112,32 +112,22 @@ class ExtractDependencies extends Phase {
112112
def binaryDependency(file: File, binaryClassName: String) =
113113
ctx.sbtCallback.binaryDependency(file, binaryClassName, fromClassName, sourceFile, dep.context)
114114

115-
def processExternalDependency(depFile: AbstractFile) = {
116-
def binaryClassName(classSegments: List[String]) =
117-
classSegments.mkString(".").stripSuffix(".class")
118-
115+
def processExternalDependency(depFile: AbstractFile, binaryClassName: String) = {
119116
depFile match {
120117
case ze: ZipArchive#Entry => // The dependency comes from a JAR
121-
for (zip <- ze.underlyingSource; zipFile <- Option(zip.file)) {
122-
val classSegments = io.File(ze.path).segments
123-
binaryDependency(zipFile, binaryClassName(classSegments))
124-
}
125-
118+
ze.underlyingSource match
119+
case Some(zip) if zip.file != null =>
120+
binaryDependency(zip.file, binaryClassName)
121+
case _ =>
126122
case pf: PlainFile => // The dependency comes from a class file
127-
val packages = dep.to.ownersIterator
128-
.count(x => x.is(PackageClass) && !x.isEffectiveRoot)
129-
// We can recover the fully qualified name of a classfile from
130-
// its path
131-
val classSegments = pf.givenPath.segments.takeRight(packages + 1)
132123
// FIXME: pf.file is null for classfiles coming from the modulepath
133124
// (handled by JrtClassPath) because they cannot be represented as
134125
// java.io.File, since the `binaryDependency` callback must take a
135126
// java.io.File, this means that we cannot record dependencies coming
136127
// from the modulepath. For now this isn't a big deal since we only
137128
// support having the standard Java library on the modulepath.
138-
if (pf.file != null)
139-
binaryDependency(pf.file, binaryClassName(classSegments))
140-
129+
if pf.file != null then
130+
binaryDependency(pf.file, binaryClassName)
141131
case _ =>
142132
report.warning(s"sbt-deps: Ignoring dependency $depFile of class ${depFile.getClass}}")
143133
}
@@ -149,7 +139,25 @@ class ExtractDependencies extends Phase {
149139
def allowLocal = dep.context == DependencyByInheritance || dep.context == LocalDependencyByInheritance
150140
if (depFile.extension == "class") {
151141
// Dependency is external -- source is undefined
152-
processExternalDependency(depFile)
142+
143+
// The fully qualified name on the JVM of the class corresponding to `dep.to`
144+
val binaryClassName = {
145+
val builder = new StringBuilder
146+
val pkg = dep.to.enclosingPackageClass
147+
if (!pkg.isEffectiveRoot) {
148+
builder.append(pkg.fullName.mangledString)
149+
builder.append(".")
150+
}
151+
val flatName = dep.to.flatName
152+
// We create fake companion object symbols to hold the static members
153+
// of Java classes, make sure to use the name of the actual Java class
154+
// here.
155+
val clsFlatName = if (dep.to.is(JavaDefined)) flatName.stripModuleClassSuffix else flatName
156+
builder.append(clsFlatName.mangledString)
157+
builder.toString
158+
}
159+
160+
processExternalDependency(depFile, binaryClassName)
153161
} else if (allowLocal || depFile.file != sourceFile) {
154162
// We cannot ignore dependencies coming from the same source file because
155163
// the dependency info needs to propagate. See source-dependencies/trait-trait-211.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object A {
2+
class InnerClass {
3+
def foo: Int = 1
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object B {
2+
def main(args: Array[String]): Unit = {
3+
val innerClass = new A.InnerClass
4+
val x = innerClass.foo
5+
println(x)
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import complete.DefaultParsers._
2+
3+
val checkIterations = inputKey[Unit]("Verifies the accumlated number of iterations of incremental compilation.")
4+
5+
checkIterations := {
6+
val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis]
7+
8+
val expected: Int = (Space ~> NatBasic).parsed
9+
val actual: Int = analysis.compilations.allCompilations.size
10+
assert(expected == actual, s"Expected $expected compilations, got $actual")
11+
}
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object A {
2+
class InnerClass {
3+
def foo: String = "foo"
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object B {
2+
def main(args: Array[String]): Unit = {
3+
val innerClass = new A.InnerClass
4+
val x = innerClass.foo
5+
println(x)
6+
}
7+
val forceRecompileDummy = ""
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := sys.props("plugin.scalaVersion")
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
> run
2+
3+
# Recompile B (no meaningful change, this is just so that the dependencies on A.InnerClass are
4+
# recorded using the `binaryDependency` sbt callback, which will only work correctly
5+
# if we call it with the inner class binary name and not the top-level class name).
6+
$ copy-file changes/B2.scala B.scala
7+
> compile
8+
9+
# Change the signature of A.Inner#foo, this requires B to be recompiled,
10+
# otherwise run will fail:
11+
$ copy-file changes/A2.scala A.scala
12+
> run
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object A {
2+
object InnerObject {
3+
def bla: Int = 2
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object B {
2+
def main(args: Array[String]): Unit = {
3+
val o = A.InnerObject.bla
4+
println(o)
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import complete.DefaultParsers._
2+
3+
val checkIterations = inputKey[Unit]("Verifies the accumlated number of iterations of incremental compilation.")
4+
5+
checkIterations := {
6+
val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis]
7+
8+
val expected: Int = (Space ~> NatBasic).parsed
9+
val actual: Int = analysis.compilations.allCompilations.size
10+
assert(expected == actual, s"Expected $expected compilations, got $actual")
11+
}
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object A {
2+
object InnerObject {
3+
def bla: String = "bla"
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object B {
2+
def main(args: Array[String]): Unit = {
3+
val o = A.InnerObject.bla
4+
println(o)
5+
}
6+
val forceRecompileDummy = ""
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := sys.props("plugin.scalaVersion")
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
> run
2+
3+
# Recompile B (no meaningful change, this is just so that the dependency on A.InnerObject are
4+
# recorded using the `binaryDependency` sbt callback, which will only work correctly
5+
# if we call it with the inner object binary name and not the top-level class name).
6+
$ copy-file changes/B2.scala B.scala
7+
> compile
8+
9+
# Change the signature of A.Inner#foo, this requires B to be recompiled,
10+
# otherwise run will fail:
11+
$ copy-file changes/A2.scala A.scala
12+
> run

0 commit comments

Comments
 (0)