-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow macro annotation to transform companion (#19677)
### Allow MacroAnnotations to update the companion of a definition We extend the MacroAnnotation api to allow to modify the companion of a class or an object. ### Specification 1. Order of expansion - We expand the definitions in program order. - We expand the annotations of the outer scope first, then we expand the inner definitions. - Annotations are expanded from the outer annotation to the inner annotation. In the following example, we expand the annotations in this order: `a1`, `a2`, `a3`. ```scala @A1 @a2 class Foo: @A3 def foo = ??? ``` 2. Expansion of the companion We always expand the latest available tree. If an annotation defined on `class Foo` changes its companion (`object Foo`) and the `class` is defined before `object`, the expansion of the annotations on the `object` will be performed on the result of the expansion of `class`. 3. The program order is maintained We maintain the program order in the definitions that were expanded. 4. Backtrack and reprocess Example: ```scala @A1 class Foo @a2 object Foo ``` If the `@a2` annotation changes the definitions in `class Foo`, we will rerun the algorithm on the result of this new expansion. Please note that we don't allow to generate code with MacroAnnotations, the reason for rerunning the algorithm is to expand and inline possible macros that we generated. --- Closes #19676
- Loading branch information
Showing
75 changed files
with
631 additions
and
350 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
compiler/src/dotty/tools/dotc/ast/TreeMapWithTrackedStats.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package dotty.tools.dotc | ||
package ast | ||
|
||
import tpd.* | ||
import core.Contexts.* | ||
import core.Symbols.* | ||
import util.Property | ||
|
||
import scala.collection.mutable | ||
|
||
/** | ||
* It is safe to assume that the companion of a tree is in the same scope. | ||
* Therefore, when expanding MacroAnnotations, we will only keep track of | ||
* the trees in the same scope as the current transformed tree | ||
*/ | ||
abstract class TreeMapWithTrackedStats extends TreeMapWithImplicits: | ||
|
||
import TreeMapWithTrackedStats.* | ||
|
||
/** Fetch the corresponding tracked tree for a given symbol */ | ||
protected final def getTracked(sym: Symbol)(using Context): Option[MemberDef] = | ||
for trees <- ctx.property(TrackedTrees) | ||
tree <- trees.get(sym) | ||
yield tree | ||
|
||
/** Update the tracked trees */ | ||
protected final def updateTracked(tree: Tree)(using Context): Tree = | ||
tree match | ||
case tree: MemberDef => | ||
trackedTrees.update(tree.symbol, tree) | ||
tree | ||
case _ => tree | ||
end updateTracked | ||
|
||
/** Process a list of trees and give the priority to trakced trees */ | ||
private final def withUpdatedTrackedTrees(stats: List[Tree])(using Context) = | ||
val trackedTrees = TreeMapWithTrackedStats.trackedTrees | ||
stats.mapConserve: | ||
case tree: MemberDef if trackedTrees.contains(tree.symbol) => | ||
trackedTrees(tree.symbol) | ||
case stat => stat | ||
|
||
override def transform(tree: Tree)(using Context): Tree = | ||
tree match | ||
case PackageDef(_, stats) => | ||
inContext(trackedDefinitionsCtx(stats)): // Step I: Collect and memoize all the definition trees | ||
// Step II: Transform the tree | ||
val pkg@PackageDef(pid, stats) = super.transform(tree): @unchecked | ||
// Step III: Reconcile between the symbols in syms and the tree | ||
cpy.PackageDef(pkg)(pid = pid, stats = withUpdatedTrackedTrees(stats)) | ||
case block: Block => | ||
inContext(trackedDefinitionsCtx(block.stats)): // Step I: Collect all the member definitions in the block | ||
// Step II: Transform the tree | ||
val b@Block(stats, expr) = super.transform(tree): @unchecked | ||
// Step III: Reconcile between the symbols in syms and the tree | ||
cpy.Block(b)(expr = expr, stats = withUpdatedTrackedTrees(stats)) | ||
case TypeDef(_, impl: Template) => | ||
inContext(trackedDefinitionsCtx(impl.body)): // Step I: Collect and memoize all the stats | ||
// Step II: Transform the tree | ||
val newTree@TypeDef(name, impl: Template) = super.transform(tree): @unchecked | ||
// Step III: Reconcile between the symbols in syms and the tree | ||
cpy.TypeDef(newTree)(rhs = cpy.Template(impl)(body = withUpdatedTrackedTrees(impl.body))) | ||
case _ => super.transform(tree) | ||
|
||
end TreeMapWithTrackedStats | ||
|
||
object TreeMapWithTrackedStats: | ||
private val TrackedTrees = new Property.Key[mutable.Map[Symbol, tpd.MemberDef]] | ||
|
||
/** Fetch the tracked trees in the cuurent context */ | ||
private def trackedTrees(using Context): mutable.Map[Symbol, MemberDef] = | ||
ctx.property(TrackedTrees).get | ||
|
||
/** Build a context and track the provided MemberDef trees */ | ||
private def trackedDefinitionsCtx(stats: List[Tree])(using Context): Context = | ||
val treesToTrack = stats.collect { case m: MemberDef => (m.symbol, m) } | ||
ctx.fresh.setProperty(TrackedTrees, mutable.Map(treesToTrack*)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.