Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalidate macro client when type parameter changes #1316

Merged
merged 6 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
36 changes: 32 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) _
Friendseeker marked this conversation as resolved.
Show resolved Hide resolved

@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,14 @@ 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 Apply(TypeApply(_, args), _) => args.map(_.tpe)
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", macroExpansion)}
""".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 @@ -739,6 +739,8 @@ final class ProtobufWriters(mapper: WriteMapper) {
val inheritanceExternal = toMap(relations.inheritance.external, stringId, stringId)
val localInheritanceInternal = toMap(relations.localInheritance.internal, stringId, stringId)
val localInheritanceExternal = toMap(relations.localInheritance.external, stringId, stringId)
val macroExpansionInternal = toMap(relations.macroExpansion.internal, stringId, stringId)
val macroExpansionExternal = toMap(relations.macroExpansion.external, stringId, stringId)
val classes = toMap(relations.classes, sourceToString, stringId)
val productClassName = toMap(relations.productClassName, stringId, stringId)
val names = toUsedNamesMap(relations.names)
Expand All @@ -763,6 +765,13 @@ final class ProtobufWriters(mapper: WriteMapper) {
builder.build
}

val macroExpansion = {
val builder = Schema.ClassDependencies.newBuilder
macroExpansionInternal.foreach { case (k, v) => builder.putInternal(k, v) }
macroExpansionExternal.foreach { case (k, v) => builder.putExternal(k, v) }
builder.build
}

val builder = Schema.Relations.newBuilder
srcProd.foreach { case (k, v) => builder.putSrcProd(k, v) }
libraryDep.foreach { case (k, v) => builder.putLibraryDep(k, v) }
Expand All @@ -774,6 +783,7 @@ final class ProtobufWriters(mapper: WriteMapper) {
.setMemberRef(memberRef)
.setInheritance(inheritance)
.setLocalInheritance(localInheritance)
.setMacroExpansion(macroExpansion)
.build
}

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
2 changes: 2 additions & 0 deletions zinc/src/sbt-test/macros/macro-type-change-3/A/A.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package A
class A
2 changes: 2 additions & 0 deletions zinc/src/sbt-test/macros/macro-type-change-3/A/B.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package A
class B extends 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-3/app/App.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app

import Macros._
import A.B

object App {
def main(args: Array[String]): Unit = {
val expected = args(0).toBoolean
val actual = Macros.hasAnyField[B]
assert(expected == actual, s"Expected $expected, obtained $actual")
}
}
20 changes: 20 additions & 0 deletions zinc/src/sbt-test/macros/macro-type-change-3/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"
}
]
}
Loading