Skip to content

Commit

Permalink
Add reflect Symbol.newModule
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Dec 16, 2022
1 parent 4167e54 commit e488e10
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class MacroAnnotations(thisPhase: DenotTransformer):
report.error(i"macro annotation $annot added $sym with an inconsistent owner. Expected it to be owned by ${annotated.owner} but was owned by ${sym.owner}.", annot.tree)
else if annotated.isClass && annotated.owner.is(Package) /*&& !sym.isClass*/ then
report.error(i"macro annotation can not add top-level ${sym.showKind}. $annot tried to add $sym.", annot.tree)
else
else if !sym.is(Module) then // To avoid entering it twice
sym.enteredAfter(thisPhase)

object MacroAnnotations:
Expand Down
26 changes: 21 additions & 5 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.core.Annotations
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Types
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.NameKinds
import dotty.tools.dotc.core.NameOps._
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.quoted.reflect._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Types
import dotty.tools.dotc.NoCompilationUnit

import dotty.tools.dotc.quoted.{MacroExpansion, PickledQuotes}
import dotty.tools.dotc.quoted.MacroExpansion
import dotty.tools.dotc.quoted.PickledQuotes
import dotty.tools.dotc.quoted.reflect._

import scala.quoted.runtime.{QuoteUnpickler, QuoteMatching}
import scala.quoted.runtime.impl.printers._
Expand Down Expand Up @@ -2481,6 +2482,21 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
for sym <- decls(cls) do cls.enter(sym)
cls

def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol =
assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class")
val mod = dotc.core.Symbols.newCompleteModuleSymbol(
owner,
name.toTermName,
modFlags | Flags.Final | Flags.Lazy | Flags.Module,
clsFlags | Flags.Final | Flags.Module,
parents.asInstanceOf, // FIXME
dotc.core.Scopes.newScope,
privateWithin)
val cls = mod.moduleClass.asClass
cls.enter(dotc.core.Symbols.newConstructor(cls, dotc.core.Flags.Synthetic, Nil, Nil))
for sym <- decls(cls) do cls.enter(sym)
mod

def newMethod(owner: Symbol, name: String, tpe: TypeRepr): Symbol =
newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol)
def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
Expand Down
3 changes: 3 additions & 0 deletions library/src/scala/quoted/Quotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3641,6 +3641,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
// TODO: add flags and privateWithin
@experimental def newClass(parent: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr]): Symbol

// TODO docs
@experimental def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol

/** Generates a new method symbol with the given parent, name and type.
*
* To define a member method of a class, use the `newMethod` within the `decls` function of `newClass`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ val experimentalDefinitionInLibrary = Set(
// Need experimental annotation macros to check that design works.
"scala.quoted.Quotes.reflectModule.ClassDefModule.apply",
"scala.quoted.Quotes.reflectModule.SymbolModule.newClass",
"scala.quoted.Quotes.reflectModule.SymbolModule.newModule",
"scala.quoted.Quotes.reflectModule.SymbolModule.freshName",
"scala.quoted.Quotes.reflectModule.SymbolMethods.info",

Expand Down
4 changes: 4 additions & 0 deletions tests/run-macros/annot-add-global-object.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
macro generated main
executed in: Test_2$package$Bar$macro$1$
macro generated main
executed in: Test_2$package$Bar$macro$2$
31 changes: 31 additions & 0 deletions tests/run-macros/annot-add-global-object/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import scala.annotation.{experimental, MacroAnnotation}
import scala.quoted._
import scala.collection.mutable

@experimental
class addClass extends MacroAnnotation:
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
import quotes.reflect._
tree match
case DefDef(name, List(TermParamClause(Nil)), tpt, Some(rhs)) =>
val parents = List(TypeTree.of[Object])
def decls(cls: Symbol): List[Symbol] =
List(Symbol.newMethod(cls, "run", MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Unit]), Flags.EmptyFlags, Symbol.noSymbol))

val mod = Symbol.newModule(Symbol.spliceOwner, Symbol.freshName("Bar"), Flags.EmptyFlags, Flags.EmptyFlags, parents.map(_.tpe), decls, Symbol.noSymbol)
val cls = mod.moduleClass

val runSym = cls.declaredMethod("run").head

val runDef = DefDef(runSym, _ => Some(rhs))

val clsDef = ClassDef(cls, parents, body = List(runDef))

val newCls = Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil)
val modVal = ValDef(mod, Some(newCls))

val newDef = DefDef.copy(tree)(name, List(TermParamClause(Nil)), tpt, Some(Apply(Select(Ref(mod), runSym), Nil)))
List(modVal, clsDef, newDef)
case _ =>
report.error("Annotation only supports `def` with one argument")
List(tree)
28 changes: 28 additions & 0 deletions tests/run-macros/annot-add-global-object/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@addClass def foo(): Unit =
println("macro generated main")
println("executed in: " + (new Throwable().getStackTrace().head.getClassName))
//> object Baz$macro$1 {
//> def run() =
//> println("macro generated main")
//> println("executed in: " + getClass.getName)
//> }
//> def foo(): Unit =
//> Baz$macro$1.run

@addClass def bar(): Unit =
println("macro generated main")
println("executed in: " + (new Throwable().getStackTrace().head.getClassName))
//> object Baz$macro$2 {
//> def run() =
//> println("macro generated main")
//> println("executed in: " + getClass.getName)
//> }
//> def foo(): Unit =
//> Baz$macro$2.run

object Bar:
def run() = ()

@main def Test(): Unit =
foo()
bar()
4 changes: 2 additions & 2 deletions tests/run-macros/annot-add-local-object.check
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
macro generated main
executed in: Test_2$package$Baz$2
executed in: Test_2$package$Baz$2$
macro generated main
executed in: Test_2$package$Baz$4
executed in: Test_2$package$Baz$4$
9 changes: 4 additions & 5 deletions tests/run-macros/annot-add-local-object/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ class addClass extends MacroAnnotation:
case DefDef(name, List(TermParamClause(Nil)), tpt, Some(rhs)) =>
val parents = List(TypeTree.of[Object])
def decls(cls: Symbol): List[Symbol] =
List(Symbol.newMethod(cls, "run", MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Unit]), Flags.Static, Symbol.noSymbol))
List(Symbol.newMethod(cls, "run", MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Unit]), Flags.EmptyFlags, Symbol.noSymbol))

val mod = Symbol.newModule(Symbol.spliceOwner, "Baz", Flags.EmptyFlags, Flags.EmptyFlags, parents.map(_.tpe), decls, Symbol.noSymbol)
val cls = mod.moduleClass

// FIXME: missing flags: Final | Module
// FIXME: how to set the self type?
val cls = Symbol.newClass(Symbol.spliceOwner, "Baz", parents = parents.map(_.tpe), decls, selfType = None)
val mod = Symbol.newVal(Symbol.spliceOwner, "Baz", cls.typeRef, Flags.Module | Flags.Lazy | Flags.Final, Symbol.noSymbol)
val runSym = cls.declaredMethod("run").head

val runDef = DefDef(runSym, _ => Some(rhs))
Expand Down
4 changes: 4 additions & 0 deletions tests/run-macros/annot-add-nested-object.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
macro generated main
executed in: Foo$Bar$macro$1$
macro generated main
executed in: Foo$Bar$macro$2$
31 changes: 31 additions & 0 deletions tests/run-macros/annot-add-nested-object/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import scala.annotation.{experimental, MacroAnnotation}
import scala.quoted._
import scala.collection.mutable

@experimental
class addClass extends MacroAnnotation:
def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
import quotes.reflect._
tree match
case DefDef(name, List(TermParamClause(Nil)), tpt, Some(rhs)) =>
val parents = List(TypeTree.of[Object])
def decls(cls: Symbol): List[Symbol] =
List(Symbol.newMethod(cls, "run", MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Unit]), Flags.EmptyFlags, Symbol.noSymbol))

val mod = Symbol.newModule(Symbol.spliceOwner, Symbol.freshName("Bar"), Flags.EmptyFlags, Flags.EmptyFlags, parents.map(_.tpe), decls, Symbol.noSymbol)
val cls = mod.moduleClass

val runSym = cls.declaredMethod("run").head

val runDef = DefDef(runSym, _ => Some(rhs))

val clsDef = ClassDef(cls, parents, body = List(runDef))

val newCls = Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil)
val modVal = ValDef(mod, Some(newCls))

val newDef = DefDef.copy(tree)(name, List(TermParamClause(Nil)), tpt, Some(Apply(Select(Ref(mod), runSym), Nil)))
List(modVal, clsDef, newDef)
case _ =>
report.error("Annotation only supports `def` with one argument")
List(tree)
26 changes: 26 additions & 0 deletions tests/run-macros/annot-add-nested-object/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class Foo():
@addClass def foo(): Unit =
println("macro generated main")
println("executed in: " + (new Throwable().getStackTrace().head.getClassName))
//> object Baz$macro$1 {
//> def run() =
//> println("macro generated main")
//> println("executed in: " + getClass.getName)
//> }
//> def foo(): Unit =
//> Baz$macro$1.run

@addClass def bar(): Unit =
println("macro generated main")
println("executed in: " + (new Throwable().getStackTrace().head.getClassName))
//> object Baz$macro$2 {
//> def run() =
//> println("macro generated main")
//> println("executed in: " + getClass.getName)
//> }
//> def foo(): Unit =
//> Baz$macro$2.run

@main def Test(): Unit =
new Foo().foo()
new Foo().bar()

0 comments on commit e488e10

Please sign in to comment.