Skip to content

Basic Design of Macro Annotations #15626

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

Closed
wants to merge 4 commits into from
Closed
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
9 changes: 8 additions & 1 deletion compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dotc

import core._
import Contexts._
import SymDenotations.ClassDenotation
import SymDenotations.{ClassDenotation, NoDenotation}
import Symbols._
import util.{FreshNameCreator, SourceFile, NoSource}
import util.Spans.Span
Expand Down Expand Up @@ -43,6 +43,8 @@ class CompilationUnit protected (val source: SourceFile) {
*/
var needsInlining: Boolean = false

var hasMacroAnnotations: Boolean = false

/** Set to `true` if inliner added anonymous mirrors that need to be completed */
var needsMirrorSupport: Boolean = false

Expand Down Expand Up @@ -111,6 +113,7 @@ object CompilationUnit {
force.traverse(unit1.tpdTree)
unit1.needsStaging = force.containsQuote
unit1.needsInlining = force.containsInline
unit1.hasMacroAnnotations = force.containsMacroAnnotation
}
unit1
}
Expand Down Expand Up @@ -138,11 +141,15 @@ object CompilationUnit {
private class Force extends TreeTraverser {
var containsQuote = false
var containsInline = false
var containsMacroAnnotation = false
def traverse(tree: Tree)(using Context): Unit = {
if (tree.symbol.isQuote)
containsQuote = true
if tree.symbol.is(Flags.Inline) then
containsInline = true
for annot <- tree.symbol.annotations do
if annot.tree.symbol.denot != NoDenotation && annot.tree.symbol.owner.derivesFrom(defn.QuotedMacroAnnotationClass) then
ctx.compilationUnit.hasMacroAnnotations = true
traverseChildren(tree)
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Compiler {
protected def frontendPhases: List[List[Phase]] =
List(new Parser) :: // Compiler frontend: scanner, parser
List(new TyperPhase) :: // Compiler frontend: namer, typer
List(new YCheckPositions) :: // YCheck positions
// List(new YCheckPositions) :: // YCheck positions
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files
List(new PostTyper) :: // Additional checks and cleanups after type checking
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,8 @@ class Definitions {
@tu lazy val QuotedTypeModule: Symbol = QuotedTypeClass.companionModule
@tu lazy val QuotedTypeModule_of: Symbol = QuotedTypeModule.requiredMethod("of")

@tu lazy val QuotedMacroAnnotationClass: ClassSymbol = requiredClass("scala.annotation.MacroAnnotation")

@tu lazy val CanEqualClass: ClassSymbol = getClassIfDefined("scala.Eql").orElse(requiredClass("scala.CanEqual")).asClass
def CanEqual_canEqualAny(using Context): TermSymbol =
val methodName = if CanEqualClass.name == tpnme.Eql then nme.eqlAny else nme.canEqualAny
Expand Down
70 changes: 65 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/Inlining.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import Contexts._
import Symbols._
import SymUtils._
import dotty.tools.dotc.ast.tpd

import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.quoted._
import dotty.tools.dotc.core.StagingContext._
import dotty.tools.dotc.inlines.Inlines
import dotty.tools.dotc.ast.TreeMapWithImplicits

import scala.annotation.tailrec
import scala.collection.mutable


/** Inlines all calls to inline methods that are not in an inline method or a quote */
class Inlining extends MacroTransform {
Expand All @@ -23,9 +27,11 @@ class Inlining extends MacroTransform {

override def allowsImplicitSearch: Boolean = true

override def run(using Context): Unit =
if ctx.compilationUnit.needsInlining then
try super.run
override def run(using ctx0: Context): Unit =
if ctx0.compilationUnit.needsInlining || ctx0.compilationUnit.hasMacroAnnotations then
try
val ctx = QuotesCache.init(ctx0.fresh)
super.run(using ctx)
catch case _: CompilationUnit.SuspendException => ()

override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
Expand Down Expand Up @@ -61,7 +67,21 @@ class Inlining extends MacroTransform {
tree match
case tree: DefTree =>
if tree.symbol.is(Inline) then tree
else super.transform(tree)
else
tree match
case _: Bind => super.transform(tree)
case tree if tree.symbol.is(Param) => super.transform(tree)
case tree if !tree.symbol.isPrimaryConstructor =>
val trees = MacroAnnotationTransformer.transform(List(tree), Set(tree.symbol))
flatTree(trees.map(super.transform(_)))
case tree => super.transform(tree)
case ObjectTrees(valT, clsT) =>
val trees = MacroAnnotationTransformer.transform(List(Thicket(valT, clsT)), Set(valT.symbol, clsT.symbol))
assert(trees.size >= 2)
flatTree(trees.map{ tree =>
if tree.symbol.is(Inline) then tree
else super.transform(tree)
})
case _: Typed | _: Block =>
super.transform(tree)
case _ if Inlines.needsInlining(tree) =>
Expand All @@ -75,6 +95,46 @@ class Inlining extends MacroTransform {
case _ =>
super.transform(tree)
}

override def transformStats[T](trees: List[Tree], exprOwner: Symbol, wrapResult: List[Tree] => Context ?=> T)(using Context): T =
@tailrec
def loop(mapped: mutable.ListBuffer[Tree] | Null, unchanged: List[Tree], pending: List[Tree])(using Context): T =
inline def recur(unchange: Boolean, stat1: Tree, rest: List[Tree])(using Context): T =
if unchange then
loop(mapped, unchanged, rest)
else
val buf = if mapped == null then new mutable.ListBuffer[Tree] else mapped
var xc = unchanged
while xc ne pending do
buf += xc.head
xc = xc.tail
stat1 match
case Thicket(stats1) => buf ++= stats1
case _ => buf += stat1
loop(buf, rest, rest)

pending match
case valT :: clsT :: rest if valT.symbol.is(ModuleVal) && clsT.symbol.is(ModuleClass) &&
valT.symbol.moduleClass == clsT.symbol =>
val stat1 = transform(Thicket(List(valT, clsT)))(using ctx)
val unchange = stat1 match
case Thicket(List(valT1, clsT1)) => (valT eq valT1) && (clsT eq clsT1)
case _ => false
recur(unchange, stat1, rest)(using ctx)
case stat :: rest =>
val statCtx = stat match
case _: DefTree | _: ImportOrExport => ctx
case _ => ctx.exprContext(stat, exprOwner)
val stat1 = transform(stat)(using statCtx)
val restCtx = stat match
case stat: Import => ctx.importContext(stat, stat.symbol)
case _ => ctx
recur(stat1 eq stat, stat1, rest)(using restCtx)
case nil =>
wrapResult(
if mapped == null then unchanged
else mapped.prependToList(unchanged))
loop(null, trees, trees)
}
}

Expand Down
Loading