Skip to content

Commit

Permalink
Handle types touched during Macro macro expansion
Browse files Browse the repository at this point in the history
We still don't have a good way to get touched types, in the future this can be provided by compiler via attachment
  • Loading branch information
Friendseeker committed Dec 27, 2023
1 parent c68a4c2 commit 8fac3a0
Show file tree
Hide file tree
Showing 16 changed files with 139 additions and 10 deletions.
35 changes: 31 additions & 4 deletions internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
val memberRef = processDependency(DependencyByMemberRef, false) _
val inheritance = processDependency(DependencyByInheritance, true) _
val localInheritance = processDependency(LocalDependencyByInheritance, true) _
val macroExpansion = processDependency(DependencyByMacroExpansion, false) _

@deprecated("Use processDependency that takes allowLocal.", "1.1.0")
def processDependency(context: DependencyContext)(dep: ClassDependency): Unit =
Expand Down Expand Up @@ -214,6 +215,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
private val _memberRefCache = new JavaSet[ClassDependency]()
private val _inheritanceCache = new JavaSet[ClassDependency]()
private val _localInheritanceCache = new JavaSet[ClassDependency]()
private val _dependencyByMacroExpansionCache = new JavaSet[ClassDependency]()
private val _topLevelImportCache = new JavaSet[Symbol]()

private var _currentDependencySource: Symbol = _
Expand Down Expand Up @@ -363,10 +365,28 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
override def addDependency(symbol: global.Symbol) = handler(symbol)
}

def addTypeDependencies(tpe: Type): Unit = {
object TypeDependencyTraverserForMacro extends TypeDependencyTraverser {
private var owner: Symbol = _
def setOwner(symbol: Symbol) = owner = symbol
override def addDependency(symbol: global.Symbol): Unit = {
addClassDependency(
_dependencyByMacroExpansionCache,
processor.macroExpansion,
owner,
symbol
)
}
}

def addTypeDependencies(tpe: Type, forMacro: Boolean = false): Unit = {
val fromClass = resolveDependencySource
TypeDependencyTraverser.setOwner(fromClass)
TypeDependencyTraverser.traverse(tpe)
if (forMacro) {
TypeDependencyTraverserForMacro.setOwner(fromClass)
TypeDependencyTraverserForMacro.traverse(tpe)
} else {
TypeDependencyTraverser.setOwner(fromClass)
TypeDependencyTraverser.traverse(tpe)
}
}

private def addInheritanceDependency(dep: Symbol): Unit = {
Expand Down Expand Up @@ -441,7 +461,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
addDependency(symbol)
}

inheritanceTypes.foreach(addTypeDependencies)
inheritanceTypes.foreach(addTypeDependencies(_, false))
addTypeDependencies(self.tpt.tpe)

traverseTrees(body)
Expand All @@ -462,6 +482,13 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
addTypeDependencies(typeTree.tpe)

case m @ MacroExpansionOf(original) if inspectedOriginalTrees.add(original) =>
// TODO: typesTouchedDuringMacroExpansion can be provided by compiler
// in the form of tree attachment
val typesTouchedDuringMacroExpansion = original match {
case TypeApply(_, args) => args.map(_.tpe)
case _ => List.empty[Type]
}
typesTouchedDuringMacroExpansion.foreach(addTypeDependencies(_, true))
traverse(original)
super.traverse(m)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public enum DependencyContext {
* }
* }
*/
LocalDependencyByInheritance;
LocalDependencyByInheritance,
/** Represents a dependency to symbols inspected during macro expansion */
DependencyByMacroExpansion;

}
6 changes: 6 additions & 0 deletions internal/compiler-interface/src/main/contraband/other.json
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@
" }",
" }"
]
},
{
"name": "DependencyByMacroExpansion",
"doc": [
"Represents a dependency to symbols inspected during macro expansion"
]
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ private[inc] class IncrementalNameHashingCommon(
log.debug(s"Getting classes that directly depend on (external) $modifiedBinaryClassName.")
val memberRefB = memberRefInv(relations.memberRef.external)(modifiedBinaryClassName)

transitiveInheritance ++ localInheritance ++ memberRefA ++ memberRefB
val macroExpansion = relations.macroExpansion.external.reverse(modifiedBinaryClassName)

transitiveInheritance ++ localInheritance ++ memberRefA ++ memberRefB ++ macroExpansion
}

private def invalidateByInheritance(relations: Relations, modified: String): Set[String] = {
Expand All @@ -139,6 +141,13 @@ private[inc] class IncrementalNameHashingCommon(
localInheritanceDeps
}

private def invalidateByMacroExpansion(relations: Relations, modified: String): Set[String] = {
val macroExpansionDeps = relations.macroExpansion.internal.reverse(modified)
if (macroExpansionDeps.nonEmpty)
log.debug(s"Invalidate by macro expansion: $modified -> $macroExpansionDeps")
macroExpansionDeps
}

/** @inheritdoc */
override protected def invalidateClassesInternally(
relations: Relations,
Expand All @@ -161,7 +170,11 @@ private[inc] class IncrementalNameHashingCommon(
val reason3 = s"The invalidated class names refer directly or transitively to $modifiedClass."
profiler.registerEvent(MemberReferenceKind, transitiveInheritance, memberRef, reason3)

val all = transitiveInheritance ++ localInheritance ++ memberRef
val macroExpansion = invalidateByMacroExpansion(relations, modifiedClass)
val reason4 = s"The invalidated class is touched by macro expansion in ${modifiedClass}"
profiler.registerEvent(MacroExpansionKind, List(modifiedClass), macroExpansion, reason4)

val all = transitiveInheritance ++ localInheritance ++ memberRef ++ macroExpansion
log.debug {
if (all.isEmpty) s"Change $change does not affect any class."
else {
Expand All @@ -171,6 +184,7 @@ private[inc] class IncrementalNameHashingCommon(
| > ${ppxs("by transitive inheritance", transitiveInheritance)}
| > ${ppxs("by local inheritance", localInheritance)}
| > ${ppxs("by member reference", memberRef)}
| > ${ppxs("by macro expansion", memberRef)}
""".stripMargin
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ trait InvalidationProfilerUtils {
final val LocalInheritanceKind = "local inheritance"
final val InheritanceKind = "inheritance"
final val MemberReferenceKind = "member reference"
final val MacroExpansionKind = "macro expansion"
}

// So that others users from outside [[IncrementalCommon]] can use the labels
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ trait Relations {
*/
private[inc] def localInheritance: ClassDependencies

private[inc] def macroExpansion: ClassDependencies

/** The relation between a source file and the fully qualified names of classes generated from it.*/
def classes: Relation[VirtualFileRef, String]

Expand Down Expand Up @@ -593,6 +595,12 @@ private class MRelationsNameHashing(
externalDependencies.dependencies.getOrElse(DependencyByMemberRef, Relation.empty)
)

override def macroExpansion: ClassDependencies =
new ClassDependencies(
internalDependencies.dependencies.getOrElse(DependencyByMacroExpansion, Relation.empty),
externalDependencies.dependencies.getOrElse(DependencyByMacroExpansion, Relation.empty)
)

def ++(o: Relations): Relations = {
new MRelationsNameHashing(
srcProd ++ o.srcProd,
Expand Down
1 change: 1 addition & 0 deletions internal/zinc-persist-core/src/main/protobuf/schema.proto
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ message Relations {
ClassDependencies memberRef = 7;
ClassDependencies inheritance = 8;
ClassDependencies localInheritance = 9;
ClassDependencies macroExpansion = 10;
}

message Analysis {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,9 @@ final class ProtobufReaders(mapper: ReadMapper, currentVersion: Schema.Version)
val localInheritance =
if (relations.hasLocalInheritance) fromClassDependencies(relations.getLocalInheritance)
else expected("local inheritance").!!
val macroExpansion =
if (relations.hasMacroExpansion) fromClassDependencies(relations.getMacroExpansion)
else expected("macro expansion").!!
val classes = fromMap(relations.getClassesMap, stringToSource, stringId)
val productClassName =
fromMap(relations.getProductClassNameMap, stringId, stringId)
Expand All @@ -784,14 +787,16 @@ final class ProtobufReaders(mapper: ReadMapper, currentVersion: Schema.Version)
Map(
DependencyContext.DependencyByMemberRef -> memberRef.internal,
DependencyContext.DependencyByInheritance -> inheritance.internal,
DependencyContext.LocalDependencyByInheritance -> localInheritance.internal
DependencyContext.LocalDependencyByInheritance -> localInheritance.internal,
DependencyContext.DependencyByMacroExpansion -> macroExpansion.internal,
)
)
val external = ExternalDependencies(
Map(
DependencyContext.DependencyByMemberRef -> memberRef.external,
DependencyContext.DependencyByInheritance -> inheritance.external,
DependencyContext.LocalDependencyByInheritance -> localInheritance.external
DependencyContext.LocalDependencyByInheritance -> localInheritance.external,
DependencyContext.DependencyByMacroExpansion -> macroExpansion.external,
)
)
Relations.make(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ trait RelationsTextFormat extends FormatCommons {
stringsDescriptor("inheritance external dependencies", _.inheritance.external),
stringsDescriptor("local internal inheritance dependencies", _.localInheritance.internal),
stringsDescriptor("local external inheritance dependencies", _.localInheritance.external),
stringsDescriptor("macro expansion internal dependencies", _.macroExpansion.internal),
stringsDescriptor("macro expansion external dependencies", _.macroExpansion.external),
descriptor("class names", _.classes, sourcesMapper, Mapper.forString),
Descriptor("used names", _.names.toMultiMap, Mapper.forString, Mapper.forUsedName),
stringsDescriptor("product class names", _.productClassName)
Expand Down Expand Up @@ -108,7 +110,7 @@ trait RelationsTextFormat extends FormatCommons {
*/
private def construct(relations: List[Map[_, Set[_]]]) =
relations match {
case p :: bin :: lcn :: mri :: mre :: ii :: ie :: lii :: lie :: cn :: un :: bcn :: Nil =>
case p :: bin :: lcn :: mri :: mre :: ii :: ie :: lii :: lie :: mei :: mee :: cn :: un :: bcn :: Nil =>
def toMultiMap[K, V](m: Map[_, _]): Map[K, Set[V]] = m.asInstanceOf[Map[K, Set[V]]]
def toRelation[K, V](m: Map[_, _]): Relation[K, V] = Relation.reconstruct(toMultiMap(m))

Expand All @@ -117,13 +119,15 @@ trait RelationsTextFormat extends FormatCommons {
DependencyByMemberRef -> toRelation(mri),
DependencyByInheritance -> toRelation(ii),
LocalDependencyByInheritance -> toRelation(lii),
DependencyByMacroExpansion -> toRelation(mei),
)
)
val external = ExternalDependencies(
Map(
DependencyByMemberRef -> toRelation(mre),
DependencyByInheritance -> toRelation(ie),
LocalDependencyByInheritance -> toRelation(lie),
DependencyByMacroExpansion -> toRelation(mee),
)
)
Relations.make(
Expand Down
2 changes: 2 additions & 0 deletions zinc/src/sbt-test/macros/macro-type-change-2/A/A.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package A
class A
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package A
class A {
val hello: String = ""
}
12 changes: 12 additions & 0 deletions zinc/src/sbt-test/macros/macro-type-change-2/app/App.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app

import Macros._
import A.A

object App {
def main(args: Array[String]): Unit = {
val expected = args(0).toBoolean
val actual = Macros.hasAnyField[A]
assert(expected == actual, s"Expected $expected, obtained $actual")
}
}
20 changes: 20 additions & 0 deletions zinc/src/sbt-test/macros/macro-type-change-2/build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"projects": [
{
"name": "app",
"dependsOn": [
"macros",
"A"
],
"scalaVersion": "2.13.12"
},
{
"name": "macros",
"scalaVersion": "2.13.12"
},
{
"name": "A",
"scalaVersion": "2.13.12"
}
]
}
20 changes: 20 additions & 0 deletions zinc/src/sbt-test/macros/macro-type-change-2/macros/Macros.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package Macros

import scala.language.experimental.macros

import scala.reflect.macros.blackbox.Context

object Macros {
def hasAnyField[T]: Boolean = macro hasAnyFieldImpl[T]

def hasAnyFieldImpl[T: c.WeakTypeTag](c: Context): c.Expr[Boolean] = {
import c.universe._

val hasField = weakTypeOf[T].members.exists {
case m: TermSymbol => m.isVal || m.isVar
case _ => false
}

c.Expr[Boolean](Literal(Constant(hasField)))
}
}
3 changes: 3 additions & 0 deletions zinc/src/sbt-test/macros/macro-type-change-2/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
> app/run false
$ copy-file A/changes/A.scala A/A.scala
> app/run true

0 comments on commit 8fac3a0

Please sign in to comment.