Skip to content

Commit df44731

Browse files
committed
Notify Zinc about non-local classes early
For pipelining Zinc needs to know about non-local classes early. e.g. it enables Zinc to disable pipelining if a non-local class contains macros. The changes in this commit are based of changes made originally in Zinc: sbt/zinc@856d416
1 parent d3a67f6 commit df44731

File tree

6 files changed

+118
-31
lines changed

6 files changed

+118
-31
lines changed

Diff for: compiler/src/dotty/tools/backend/jvm/CodeGen.scala

+4-5
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,16 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
125125

126126
// Creates a callback that will be evaluated in PostProcessor after creating a file
127127
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: util.SourceFile)(using Context): AbstractFile => Unit = {
128-
val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) {
129-
(ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal)
128+
val isLocal = atPhase(sbtExtractDependenciesPhase) {
129+
claszSymbol.isLocal
130130
}
131131
clsFile => {
132132
val className = cls.name.replace('/', '.')
133133
if (ctx.compilerCallback != null)
134134
ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className)
135135

136-
ctx.withIncCallback: cb =>
137-
if (isLocal) cb.generatedLocalClass(sourceFile, clsFile.jpath)
138-
else cb.generatedNonLocalClass(sourceFile, clsFile.jpath, className, fullClassName)
136+
if isLocal then
137+
ctx.withIncCallback(_.generatedLocalClass(sourceFile, clsFile.jpath))
139138
}
140139
}
141140

Diff for: compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

+56-3
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ import Trees.*
1515
import Types.*
1616
import Symbols.*
1717
import Names.*
18+
import StdNames.str
1819
import NameOps.*
1920
import inlines.Inlines
2021
import transform.ValueClasses
21-
import dotty.tools.io.{File, FileExtension}
22+
import dotty.tools.io.{File, FileExtension, JarArchive}
23+
import util.{Property, SourceFile}
2224
import java.io.PrintWriter
2325

26+
import ExtractAPI.NonLocalClassSymbolsInCurrentUnits
2427

2528
import scala.collection.mutable
2629
import scala.util.hashing.MurmurHash3
@@ -64,13 +67,59 @@ class ExtractAPI extends Phase {
6467
// definitions, and `PostTyper` does not change definitions).
6568
override def runsAfter: Set[String] = Set(transform.PostTyper.name)
6669

70+
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
71+
val nonLocalClassSymbols = new mutable.HashSet[Symbol]
72+
val ctx0 = ctx.withProperty(NonLocalClassSymbolsInCurrentUnits, Some(nonLocalClassSymbols))
73+
val units0 = super.runOn(units)(using ctx0)
74+
ctx.withIncCallback(recordNonLocalClasses(nonLocalClassSymbols, _))
75+
units0
76+
end runOn
77+
78+
private def recordNonLocalClasses(nonLocalClassSymbols: mutable.HashSet[Symbol], cb: interfaces.IncrementalCallback)(using Context): Unit =
79+
for cls <- nonLocalClassSymbols do
80+
val sourceFile = cls.source
81+
if sourceFile.exists && cls.isDefinedInCurrentRun then
82+
recordNonLocalClass(cls, sourceFile, cb)
83+
cb.apiPhaseCompleted()
84+
cb.dependencyPhaseCompleted()
85+
86+
private def recordNonLocalClass(cls: Symbol, sourceFile: SourceFile, cb: interfaces.IncrementalCallback)(using Context): Unit =
87+
def registerProductNames(fullClassName: String, binaryClassName: String) =
88+
val pathToClassFile = s"${binaryClassName.replace('.', java.io.File.separatorChar)}.class"
89+
90+
val classFile = {
91+
ctx.settings.outputDir.value match {
92+
case jar: JarArchive =>
93+
new java.io.File(s"$jar!$pathToClassFile")
94+
case outputDir =>
95+
new java.io.File(outputDir.file, pathToClassFile)
96+
}
97+
}
98+
99+
cb.generatedNonLocalClass(sourceFile, classFile.toPath(), binaryClassName, fullClassName)
100+
end registerProductNames
101+
102+
val fullClassName = atPhase(sbtExtractDependenciesPhase) {
103+
ExtractDependencies.classNameAsString(cls)
104+
}
105+
val binaryClassName = cls.binaryClassName
106+
registerProductNames(fullClassName, binaryClassName)
107+
108+
// Register the names of top-level module symbols that emit two class files
109+
val isTopLevelUniqueModule =
110+
cls.owner.is(PackageClass) && cls.is(ModuleClass) && cls.companionClass == NoSymbol
111+
if isTopLevelUniqueModule then
112+
registerProductNames(fullClassName, binaryClassName.stripSuffix(str.MODULE_SUFFIX))
113+
end recordNonLocalClass
114+
67115
override def run(using Context): Unit = {
68116
val unit = ctx.compilationUnit
69117
val sourceFile = unit.source
70118
ctx.withIncCallback: cb =>
71119
cb.startSource(sourceFile)
72120

73-
val apiTraverser = new ExtractAPICollector
121+
val nonLocalClassSymbols = ctx.property(NonLocalClassSymbolsInCurrentUnits).get
122+
val apiTraverser = ExtractAPICollector(nonLocalClassSymbols)
74123
val classes = apiTraverser.apiSource(unit.tpdTree)
75124
val mainClasses = apiTraverser.mainClasses
76125

@@ -94,6 +143,8 @@ object ExtractAPI:
94143
val name: String = "sbt-api"
95144
val description: String = "sends a representation of the API of classes to sbt"
96145

146+
private val NonLocalClassSymbolsInCurrentUnits: Property.Key[mutable.HashSet[Symbol]] = Property.Key()
147+
97148
/** Extracts full (including private members) API representation out of Symbols and Types.
98149
*
99150
* The exact representation used for each type is not important: the only thing
@@ -136,7 +187,7 @@ object ExtractAPI:
136187
* without going through an intermediate representation, see
137188
* http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html#Hashing+an+API+representation
138189
*/
139-
private class ExtractAPICollector(using Context) extends ThunkHolder {
190+
private class ExtractAPICollector(nonLocalClassSymbols: mutable.HashSet[Symbol])(using Context) extends ThunkHolder {
140191
import tpd.*
141192
import xsbti.api
142193

@@ -254,6 +305,8 @@ private class ExtractAPICollector(using Context) extends ThunkHolder {
254305
childrenOfSealedClass, topLevel, tparams)
255306

256307
allNonLocalClassesInSrc += cl
308+
if !sym.isLocal then
309+
nonLocalClassSymbols += sym
257310

258311
if (sym.isStatic && !sym.is(Trait) && ctx.platform.hasMainMethod(sym)) {
259312
// If sym is an object, all main methods count, otherwise only @static ones count.

Diff for: compiler/src/dotty/tools/dotc/sbt/interfaces/IncrementalCallback.java

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
/* User code should not implement this interface, it is intended to be a wrapper around xsbti.AnalysisCallback. */
99
public interface IncrementalCallback {
10+
1011
default void api(SourceFile sourceFile, xsbti.api.ClassLike classApi) {
1112
}
1213

@@ -36,4 +37,10 @@ default void generatedLocalClass(SourceFile source, Path classFile) {
3637
default void generatedNonLocalClass(SourceFile source, Path classFile, String binaryClassName,
3738
String srcClassName) {
3839
}
40+
41+
default void apiPhaseCompleted() {
42+
}
43+
44+
default void dependencyPhaseCompleted() {
45+
}
3946
}

Diff for: sbt-bridge/src/dotty/tools/xsbt/IncrementalCallback.java

+10
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,14 @@ public void generatedLocalClass(SourceFile source, java.nio.file.Path classFile)
5757
public void generatedNonLocalClass(SourceFile source, java.nio.file.Path classFile, String binaryClassName, String srcClassName) {
5858
delegate.generatedNonLocalClass(asVirtualFile.apply(source), classFile, binaryClassName, srcClassName);
5959
}
60+
61+
@Override
62+
public void apiPhaseCompleted() {
63+
delegate.apiPhaseCompleted();
64+
}
65+
66+
@Override
67+
public void dependencyPhaseCompleted() {
68+
delegate.dependencyPhaseCompleted();
69+
}
6070
}

Diff for: sbt-bridge/src/dotty/tools/xsbt/OldIncrementalCallback.java

+10
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,14 @@ public void generatedLocalClass(SourceFile source, java.nio.file.Path classFile)
7171
public void generatedNonLocalClass(SourceFile source, java.nio.file.Path classFile, String binaryClassName, String srcClassName) {
7272
delegate.generatedNonLocalClass(asJavaFile(source), classFile.toFile(), binaryClassName, srcClassName);
7373
}
74+
75+
@Override
76+
public void apiPhaseCompleted() {
77+
delegate.apiPhaseCompleted();
78+
}
79+
80+
@Override
81+
public void dependencyPhaseCompleted() {
82+
delegate.dependencyPhaseCompleted();
83+
}
7484
}

Diff for: sbt-test/source-dependencies/compactify/src/main/scala/Nested.scala

+31-23
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,35 @@ package test
22

33
object TopLevelModule1
44
{
5-
object InnerModule1
6-
{
7-
object InnerModule2
8-
{
9-
trait Z { def q = 3 }
10-
def x = 3
11-
}
12-
}
13-
class InnerClass1
14-
{
15-
class InnerClass2
16-
{
17-
val z = new TopLevelModule1.InnerClass2
18-
}
19-
object InnerModule3
20-
{
21-
val y = new TopLevel1 with InnerModule1.InnerModule2.Z { val x = 4 }
22-
}
23-
}
24-
class InnerClass2
5+
object InnerModule1
6+
{
7+
object InnerModule2
8+
{
9+
trait Z { def q = 3 }
10+
def x = 3
11+
}
12+
}
13+
class InnerClass1
14+
{
15+
class InnerClass2
16+
{
17+
val z = new TopLevelModule1.InnerClass2
18+
}
19+
object InnerModule3
20+
{
21+
val y = new TopLevel1 with InnerModule1.InnerModule2.Z { val x = 4 }
22+
}
23+
}
24+
class InnerClass2
2525
}
2626
class TopLevel1
2727
{
28-
object Inner1_1
28+
object Inner1_1
2929
}
3030
object TopLevel1
3131
{
32-
class Inner1_2
33-
object Inner1_2
32+
class Inner1_2
33+
object Inner1_2
3434
}
3535

3636
object TopLevel2
@@ -41,3 +41,11 @@ object TopLevel3
4141
class TopLevel4
4242

4343
object TopLevelModuleSuffix$
44+
45+
// will generate a package object wrapper
46+
val topLevelVal = 23
47+
48+
// explicit package object
49+
package object inner {
50+
val innerVal = 23
51+
}

0 commit comments

Comments
 (0)