Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 46 additions & 53 deletions compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import scala.language.unsafeNulls

import java.io.File
import java.nio.file.Path
import java.util.{Arrays, EnumSet}
import java.util.EnumSet

import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.classpath.FileUtils.{hasClassExtension, hasTastyExtension}
Expand Down Expand Up @@ -46,7 +46,6 @@ import scala.compiletime.uninitialized
*
* The following flags affect this phase:
* -Yforce-sbt-phases
* -Ydump-sbt-inc
*
* @see ExtractAPI
*/
Expand Down Expand Up @@ -77,27 +76,6 @@ class ExtractDependencies extends Phase {
val rec = unit.depRecorder
val collector = ExtractDependenciesCollector(rec)
collector.traverse(unit.tpdTree)

if (ctx.settings.YdumpSbtInc.value) {
val deps = rec.foundDeps.iterator.map { case (clazz, found) => s"$clazz: ${found.classesString}" }.toArray[Object]
val names = rec.foundDeps.iterator.map { case (clazz, found) => s"$clazz: ${found.namesString}" }.toArray[Object]
Arrays.sort(deps)
Arrays.sort(names)

val pw = io.File(unit.source.file.jpath).changeExtension(FileExtension.Inc).toFile.printWriter()
// val pw = Console.out
try {
pw.println("Used Names:")
pw.println("===========")
names.foreach(pw.println)
pw.println()
pw.println("Dependencies:")
pw.println("=============")
deps.foreach(pw.println)
} finally pw.close()
}

rec.sendToZinc()
}
}

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

/** Extract the dependency information of a compilation unit.
*
* This extracts the symbol dependencies in the written code.
* There are further extractions performed in the Inlining phase later.
*
* To understand why we track the used names see the section "Name hashing
* algorithm" in http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html
* To understand why we need to track dependencies introduced by inheritance
* specially, see the subsection "Dependencies introduced by member reference and
* inheritance" in the "Name hashing algorithm" section.
*/
private class ExtractDependenciesCollector(rec: DependencyRecorder) extends AbstractExtractDependenciesCollector(rec):
import tpd.*

/** Traverse the tree of a source file and record the dependencies and used names which
* can be retrieved using DependencyRecorder.
*/
override def traverse(tree: Tree)(using Context): Unit =
try
recordTree(tree)
tree match
case tree: Inlined if !tree.inlinedFromOuterScope =>
// The inlined call is normally ignored by TreeTraverser but we need to
// record it as a dependency
traverse(tree.call)
// traverseChildren(tree)
case vd: ValDef if vd.symbol.is(ModuleVal) =>
// Don't visit module val
case t: Template if t.symbol.owner.is(ModuleClass) =>
// Don't visit self type of module class
traverse(t.constr)
t.parents.foreach(traverse)
t.body.foreach(traverse)
case _ =>
traverseChildren(tree)
catch
case ex: AssertionError =>
println(i"asserted failed while traversing $tree")
throw ex
end ExtractDependenciesCollector

/** Extract the dependency information of a compilation unit.
*
* To understand why we track the used names see the section "Name hashing
Expand All @@ -137,7 +156,7 @@ object ExtractDependencies {
* specially, see the subsection "Dependencies introduced by member reference and
* inheritance" in the "Name hashing algorithm" section.
*/
private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.TreeTraverser { thisTreeTraverser =>
trait AbstractExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.TreeTraverser { thisTreeTraverser =>
import tpd.*

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


/** Traverse the tree of a source file and record the dependencies and used names which
* can be retrieved using `foundDeps`.
*/
override def traverse(tree: Tree)(using Context): Unit = try {
tree match {
protected def recordTree(tree: Tree)(using Context): Unit =
tree match
case Match(selector, _) =>
addPatMatDependency(selector.tpe)
case Import(expr, selectors) =>
Expand Down Expand Up @@ -223,29 +238,7 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd.
addInheritanceDependencies(t)
case t: Template =>
addInheritanceDependencies(t)
case _ =>
}

tree match {
case tree: Inlined if !tree.inlinedFromOuterScope =>
// The inlined call is normally ignored by TreeTraverser but we need to
// record it as a dependency
traverse(tree.call)
case vd: ValDef if vd.symbol.is(ModuleVal) =>
// Don't visit module val
case t: Template if t.symbol.owner.is(ModuleClass) =>
// Don't visit self type of module class
traverse(t.constr)
t.parents.foreach(traverse)
t.body.foreach(traverse)
case _ =>
traverseChildren(tree)
}
} catch {
case ex: AssertionError =>
println(i"asserted failed while traversing $tree")
throw ex
}
case _ => ()

/**Reused EqHashSet, safe to use as each TypeDependencyTraverser is used atomically
* Avoid cycles by remembering both the types (testcase:
Expand Down
81 changes: 75 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/Inlining.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package dotty.tools.dotc
package transform


import java.util.Arrays

import dotty.tools.io
import ast.tpd
import ast.Trees.*
import ast.TreeMapWithTrackedStats
Expand All @@ -15,12 +19,20 @@ import DenotTransformers.IdentityDenotTransformer
import MacroAnnotations.hasMacroAnnotation
import inlines.Inlines
import quoted.*
import sbt.{ AbstractExtractDependenciesCollector, DependencyRecorder }
import staging.StagingLevel
import util.Property

import scala.collection.mutable

/** Inlines all calls to inline methods that are not in an inline method or a quote */
import scala.io.Codec

/**
* Inlines all calls to inline methods that are not in an inline method or a quote.
*
* The following flags affect this phase:
* -Ydump-sbt-inc
*
*/
class Inlining extends MacroTransform, IdentityDenotTransformer {
self =>

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

override def run(using Context): Unit =
if ctx.compilationUnit.needsInlining || ctx.compilationUnit.hasMacroAnnotations then
val unit = ctx.compilationUnit
val rec = ctx.compilationUnit.depRecorder
if unit.needsInlining || unit.hasMacroAnnotations then
super.run
rec.sendToZinc()

if ctx.settings.YdumpSbtInc.value then
val deps = rec.foundDeps.iterator.map { case (clazz, found) => s"$clazz: ${found.classesString}" }.toArray[Object]
val names = rec.foundDeps.iterator.map { case (clazz, found) => s"$clazz: ${found.namesString}" }.toArray[Object]
Arrays.sort(deps)
Arrays.sort(names)

unit.source.file.jpath match
case jpath: io.JPath =>
val pw = io.File(jpath)(using Codec.UTF8).changeExtension(io.FileExtension.Inc).toFile.printWriter()
// val pw = Console.out
try
pw.println("Used Names:")
pw.println("===========")
names.foreach(pw.println)
pw.println()
pw.println("Dependencies:")
pw.println("=============")
deps.foreach(pw.println)
finally pw.close()
case null => ()

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

def newTransformer(using Context): Transformer = new Transformer {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
InliningTreeMap().transform(tree)
val rec = ctx.compilationUnit.depRecorder
val collector = ExtractInlineDependenciesCollector(rec)
InliningTreeMap(collector).transform(tree)
}

private class InliningTreeMap extends TreeMapWithTrackedStats {
private class ExtractInlineDependenciesCollector(rec: DependencyRecorder) extends AbstractExtractDependenciesCollector(rec):
/** Traverse the tree of a source file and record the dependencies and used names which
* can be retrieved using `rec`.
*/
override def traverse(tree: Tree)(using Context): Unit =
recordTree(tree)
traverseChildren(tree)
end ExtractInlineDependenciesCollector

private class InliningTreeMap(collector: ExtractInlineDependenciesCollector) extends TreeMapWithTrackedStats {

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

val inlineFinder = new tpd.TreeTraverser:
override def traverse(tree: Tree)(using Context): Unit =
try
tree match
case tree: Inlined =>
collector.traverse(tree)
case vd: ValDef if vd.symbol.is(ModuleVal) =>
// Don't visit module val
case t: Template if t.symbol.owner.is(ModuleClass) =>
// Don't visit self type of module class
traverse(t.constr)
t.parents.foreach(traverse)
t.body.foreach(traverse)
case _ =>
traverseChildren(tree)
catch
case ex: AssertionError =>
println(i"asserted failed while traversing $tree")
throw ex

override def transform(tree: Tree)(using Context): Tree = {
tree match
val result = tree match
case tree: MemberDef =>
// Fetch the latest tracked tree (It might have already been transformed by its companion)
transformMemberDef(getTracked(tree.symbol).getOrElse(tree))
Expand Down Expand Up @@ -93,6 +160,8 @@ class Inlining extends MacroTransform, IdentityDenotTransformer {
if tree1.tpe.isError then tree1
else Inlines.inlineCall(tree1)
else super.transform(tree)
inlineFinder.traverse(result)
result
}

private def transformMemberDef(tree: MemberDef)(using Context) : Tree =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package example

class D0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package example

class D1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package example

class D2
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package example

import com.softwaremill.macwire.*

object Test:
try
val d = wire[D0]
val d1 = wire[D1]
val d2 = wire[D2]
catch
case e: Throwable =>
e.printStackTrace()
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name := "add-dep"
libraryDependencies ++= Seq(
"com.softwaremill.macwire" %% "macros" % "2.6.6" % Provided,
"com.softwaremill.macwire" %% "util" % "2.6.6",
)
Compile / incOptions ~= { _.withRecompileAllFraction(1.0) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package example

class D0(de1: D1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import sbt._
import Keys._

object DottyInjectedPlugin extends AutoPlugin {
override def requires = plugins.JvmPlugin
override def trigger = allRequirements

override val projectSettings = Seq(
scalaVersion := sys.props("plugin.scalaVersion")
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
> compile

$ copy-file changes/D0.scala D0.scala

-> compile
Loading