Skip to content

Commit e9a61df

Browse files
committed
Split the dependency collection
Instead of moving the entire sbt-deps phase, this leaves the phase as-is, and adds new instrumentation in the Inlining phase for the inlined code.
1 parent c387ef2 commit e9a61df

File tree

3 files changed

+122
-62
lines changed

3 files changed

+122
-62
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Compiler {
3535
List(new TyperPhase) :: // Compiler frontend: namer, typer
3636
List(CheckUnused.PostTyper(), CheckShadowing()) :: // Check for unused, shadowed elements
3737
List(new YCheckPositions) :: // YCheck positions
38+
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
3839
List(new semanticdb.ExtractSemanticDB.ExtractSemanticInfo) :: // Extract info into .semanticdb files
3940
List(new PostTyper) :: // Additional checks and cleanups after type checking
4041
List(new UnrollDefinitions) :: // Unroll annotated methods if detected in PostTyper
@@ -47,7 +48,6 @@ class Compiler {
4748
List(new Pickler) :: // Generate TASTY info
4849
List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks
4950
List(new Inlining) :: // Inline and execute macros
50-
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
5151
List(new PostInlining) :: // Add mirror support for inlined code
5252
List(new Staging) :: // Check staging levels and heal staged types
5353
List(new Splicing) :: // Replace level 1 splices with holes

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

Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import scala.language.unsafeNulls
55

66
import java.io.File
77
import java.nio.file.Path
8-
import java.util.{Arrays, EnumSet}
8+
import java.util.EnumSet
99

1010
import dotty.tools.dotc.ast.tpd
1111
import dotty.tools.dotc.classpath.FileUtils.{hasClassExtension, hasTastyExtension}
@@ -46,7 +46,6 @@ import scala.compiletime.uninitialized
4646
*
4747
* The following flags affect this phase:
4848
* -Yforce-sbt-phases
49-
* -Ydump-sbt-inc
5049
*
5150
* @see ExtractAPI
5251
*/
@@ -77,27 +76,6 @@ class ExtractDependencies extends Phase {
7776
val rec = unit.depRecorder
7877
val collector = ExtractDependenciesCollector(rec)
7978
collector.traverse(unit.tpdTree)
80-
81-
if (ctx.settings.YdumpSbtInc.value) {
82-
val deps = rec.foundDeps.iterator.map { case (clazz, found) => s"$clazz: ${found.classesString}" }.toArray[Object]
83-
val names = rec.foundDeps.iterator.map { case (clazz, found) => s"$clazz: ${found.namesString}" }.toArray[Object]
84-
Arrays.sort(deps)
85-
Arrays.sort(names)
86-
87-
val pw = io.File(unit.source.file.jpath).changeExtension(FileExtension.Inc).toFile.printWriter()
88-
// val pw = Console.out
89-
try {
90-
pw.println("Used Names:")
91-
pw.println("===========")
92-
names.foreach(pw.println)
93-
pw.println()
94-
pw.println("Dependencies:")
95-
pw.println("=============")
96-
deps.foreach(pw.println)
97-
} finally pw.close()
98-
}
99-
100-
rec.sendToZinc()
10179
}
10280
}
10381

@@ -129,6 +107,47 @@ object ExtractDependencies {
129107
report.error(em"Internal error in the incremental compiler while compiling ${ctx.compilationUnit.source}: $msg", pos)
130108
}
131109

110+
/** Extract the dependency information of a compilation unit.
111+
*
112+
* This extracts the symbol dependencies in the written code.
113+
* There are further extractions performed in the Inlining phase later.
114+
*
115+
* To understand why we track the used names see the section "Name hashing
116+
* algorithm" in http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html
117+
* To understand why we need to track dependencies introduced by inheritance
118+
* specially, see the subsection "Dependencies introduced by member reference and
119+
* inheritance" in the "Name hashing algorithm" section.
120+
*/
121+
private class ExtractDependenciesCollector(rec: DependencyRecorder) extends AbstractExtractDependenciesCollector(rec):
122+
import tpd.*
123+
124+
/** Traverse the tree of a source file and record the dependencies and used names which
125+
* can be retrieved using DependencyRecorder.
126+
*/
127+
override def traverse(tree: Tree)(using Context): Unit =
128+
try
129+
recordTree(tree)
130+
tree match
131+
case tree: Inlined if !tree.inlinedFromOuterScope =>
132+
// The inlined call is normally ignored by TreeTraverser but we need to
133+
// record it as a dependency
134+
traverse(tree.call)
135+
// traverseChildren(tree)
136+
case vd: ValDef if vd.symbol.is(ModuleVal) =>
137+
// Don't visit module val
138+
case t: Template if t.symbol.owner.is(ModuleClass) =>
139+
// Don't visit self type of module class
140+
traverse(t.constr)
141+
t.parents.foreach(traverse)
142+
t.body.foreach(traverse)
143+
case _ =>
144+
traverseChildren(tree)
145+
catch
146+
case ex: AssertionError =>
147+
println(i"asserted failed while traversing $tree")
148+
throw ex
149+
end ExtractDependenciesCollector
150+
132151
/** Extract the dependency information of a compilation unit.
133152
*
134153
* To understand why we track the used names see the section "Name hashing
@@ -137,7 +156,7 @@ object ExtractDependencies {
137156
* specially, see the subsection "Dependencies introduced by member reference and
138157
* inheritance" in the "Name hashing algorithm" section.
139158
*/
140-
private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.TreeTraverser { thisTreeTraverser =>
159+
trait AbstractExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.TreeTraverser { thisTreeTraverser =>
141160
import tpd.*
142161

143162
private def addMemberRefDependency(sym: Symbol)(using Context): Unit =
@@ -181,12 +200,8 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.
181200
// can happen for constructor proxies. Test case is pos-macros/i13532.
182201
true
183202

184-
185-
/** Traverse the tree of a source file and record the dependencies and used names which
186-
* can be retrieved using `foundDeps`.
187-
*/
188-
override def traverse(tree: Tree)(using Context): Unit = try {
189-
tree match {
203+
protected def recordTree(tree: Tree)(using Context): Unit =
204+
tree match
190205
case Match(selector, _) =>
191206
addPatMatDependency(selector.tpe)
192207
case Import(expr, selectors) =>
@@ -223,31 +238,7 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.
223238
addInheritanceDependencies(t)
224239
case t: Template =>
225240
addInheritanceDependencies(t)
226-
case _ =>
227-
}
228-
229-
tree match {
230-
case tree: Inlined =>
231-
// The inlined call is normally ignored by TreeTraverser but we need to
232-
// record it as a dependency
233-
if !tree.inlinedFromOuterScope then
234-
traverse(tree.call)
235-
traverseChildren(tree)
236-
case vd: ValDef if vd.symbol.is(ModuleVal) =>
237-
// Don't visit module val
238-
case t: Template if t.symbol.owner.is(ModuleClass) =>
239-
// Don't visit self type of module class
240-
traverse(t.constr)
241-
t.parents.foreach(traverse)
242-
t.body.foreach(traverse)
243-
case _ =>
244-
traverseChildren(tree)
245-
}
246-
} catch {
247-
case ex: AssertionError =>
248-
println(i"asserted failed while traversing $tree")
249-
throw ex
250-
}
241+
case _ => ()
251242

252243
/**Reused EqHashSet, safe to use as each TypeDependencyTraverser is used atomically
253244
* Avoid cycles by remembering both the types (testcase:

compiler/src/dotty/tools/dotc/transform/Inlining.scala

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package dotty.tools.dotc
22
package transform
33

4+
5+
import java.util.Arrays
6+
7+
import dotty.tools.io
48
import ast.tpd
59
import ast.Trees.*
610
import ast.TreeMapWithTrackedStats
@@ -15,12 +19,20 @@ import DenotTransformers.IdentityDenotTransformer
1519
import MacroAnnotations.hasMacroAnnotation
1620
import inlines.Inlines
1721
import quoted.*
22+
import sbt.{ AbstractExtractDependenciesCollector, DependencyRecorder }
1823
import staging.StagingLevel
1924
import util.Property
2025

2126
import scala.collection.mutable
22-
23-
/** Inlines all calls to inline methods that are not in an inline method or a quote */
27+
import scala.io.Codec
28+
29+
/**
30+
* Inlines all calls to inline methods that are not in an inline method or a quote.
31+
*
32+
* The following flags affect this phase:
33+
* -Ydump-sbt-inc
34+
*
35+
*/
2436
class Inlining extends MacroTransform, IdentityDenotTransformer {
2537
self =>
2638

@@ -35,8 +47,32 @@ class Inlining extends MacroTransform, IdentityDenotTransformer {
3547
override def changesMembers: Boolean = true
3648

3749
override def run(using Context): Unit =
38-
if ctx.compilationUnit.needsInlining || ctx.compilationUnit.hasMacroAnnotations then
50+
val unit = ctx.compilationUnit
51+
val rec = ctx.compilationUnit.depRecorder
52+
if unit.needsInlining || unit.hasMacroAnnotations then
3953
super.run
54+
rec.sendToZinc()
55+
56+
if ctx.settings.YdumpSbtInc.value then
57+
val deps = rec.foundDeps.iterator.map { case (clazz, found) => s"$clazz: ${found.classesString}" }.toArray[Object]
58+
val names = rec.foundDeps.iterator.map { case (clazz, found) => s"$clazz: ${found.namesString}" }.toArray[Object]
59+
Arrays.sort(deps)
60+
Arrays.sort(names)
61+
62+
unit.source.file.jpath match
63+
case jpath: io.JPath =>
64+
val pw = io.File(jpath)(using Codec.UTF8).changeExtension(io.FileExtension.Inc).toFile.printWriter()
65+
// val pw = Console.out
66+
try
67+
pw.println("Used Names:")
68+
pw.println("===========")
69+
names.foreach(pw.println)
70+
pw.println()
71+
pw.println("Dependencies:")
72+
pw.println("=============")
73+
deps.foreach(pw.println)
74+
finally pw.close()
75+
case null => ()
4076

4177
override def checkPostCondition(tree: Tree)(using Context): Unit =
4278
tree match {
@@ -54,18 +90,49 @@ class Inlining extends MacroTransform, IdentityDenotTransformer {
5490

5591
def newTransformer(using Context): Transformer = new Transformer {
5692
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
57-
InliningTreeMap().transform(tree)
93+
val rec = ctx.compilationUnit.depRecorder
94+
val collector = ExtractInlineDependenciesCollector(rec)
95+
InliningTreeMap(collector).transform(tree)
5896
}
5997

60-
private class InliningTreeMap extends TreeMapWithTrackedStats {
98+
private class ExtractInlineDependenciesCollector(rec: DependencyRecorder) extends AbstractExtractDependenciesCollector(rec):
99+
/** Traverse the tree of a source file and record the dependencies and used names which
100+
* can be retrieved using `rec`.
101+
*/
102+
override def traverse(tree: Tree)(using Context): Unit =
103+
recordTree(tree)
104+
traverseChildren(tree)
105+
end ExtractInlineDependenciesCollector
106+
107+
private class InliningTreeMap(collector: ExtractInlineDependenciesCollector) extends TreeMapWithTrackedStats {
61108

62109
/** List of top level classes added by macro annotation in a package object.
63110
* These are added to the PackageDef that owns this particular package object.
64111
*/
65112
private val newTopClasses = MutableSymbolMap[mutable.ListBuffer[Tree]]()
66113

114+
val inlineFinder = new tpd.TreeTraverser:
115+
override def traverse(tree: Tree)(using Context): Unit =
116+
try
117+
tree match
118+
case tree: Inlined =>
119+
collector.traverse(tree)
120+
case vd: ValDef if vd.symbol.is(ModuleVal) =>
121+
// Don't visit module val
122+
case t: Template if t.symbol.owner.is(ModuleClass) =>
123+
// Don't visit self type of module class
124+
traverse(t.constr)
125+
t.parents.foreach(traverse)
126+
t.body.foreach(traverse)
127+
case _ =>
128+
traverseChildren(tree)
129+
catch
130+
case ex: AssertionError =>
131+
println(i"asserted failed while traversing $tree")
132+
throw ex
133+
67134
override def transform(tree: Tree)(using Context): Tree = {
68-
tree match
135+
val result = tree match
69136
case tree: MemberDef =>
70137
// Fetch the latest tracked tree (It might have already been transformed by its companion)
71138
transformMemberDef(getTracked(tree.symbol).getOrElse(tree))
@@ -93,6 +160,8 @@ class Inlining extends MacroTransform, IdentityDenotTransformer {
93160
if tree1.tpe.isError then tree1
94161
else Inlines.inlineCall(tree1)
95162
else super.transform(tree)
163+
inlineFinder.traverse(result)
164+
result
96165
}
97166

98167
private def transformMemberDef(tree: MemberDef)(using Context) : Tree =

0 commit comments

Comments
 (0)