Skip to content

Commit

Permalink
Add reflect ClassDef.module
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Jan 5, 2023
1 parent 9f4042a commit 1acd745
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 18 deletions.
8 changes: 8 additions & 0 deletions compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
def unapply(cdef: ClassDef): (String, DefDef, List[Tree /* Term | TypeTree */], Option[ValDef], List[Statement]) =
val rhs = cdef.rhs.asInstanceOf[tpd.Template]
(cdef.name.toString, cdef.constructor, cdef.parents, cdef.self, rhs.body)

def module(module: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): (ValDef, ClassDef) = {
val cls = module.moduleClass
val clsDef = ClassDef(cls, parents, body)
val newCls = Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil)
val modVal = ValDef(module, Some(newCls))
(modVal, clsDef)
}
end ClassDef

given ClassDefMethods: ClassDefMethods with
Expand Down
31 changes: 26 additions & 5 deletions library/src/scala/quoted/Quotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -467,9 +467,33 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
* otherwise the can be `Term` containing the `New` applied to the parameters of the extended class.
* @param body List of members of the class. The members must align with the members of `cls`.
*/
// TODO add selfOpt: Option[ValDef]?
@experimental def apply(cls: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): ClassDef
def copy(original: Tree)(name: String, constr: DefDef, parents: List[Tree /* Term | TypeTree */], selfOpt: Option[ValDef], body: List[Statement]): ClassDef
def unapply(cdef: ClassDef): (String, DefDef, List[Tree /* Term | TypeTree */], Option[ValDef], List[Statement])


/** Create the ValDef and ClassDef of a module.
*
* Equivalent to
* ```
* def module(module: Symbol, parents: List[Tree], body: List[Statement]): (ValDef, ClassDef) =
* val modCls = module.moduleClass
* val modClassDef = ClassDef(modCls, parents, body)
* val modValDef = ValDef(module, Some(Apply(Select(New(TypeIdent(modCls)), cls.primaryConstructor), Nil)))
* List(modValDef, modClassDef)
* ```
*
* @param module the module symbol (of the module lazy val)
* @param parents parents of the module class
* @param body body of the module class
* @return The module lazy val definition and module class definition.
* These should be added one after the other (in that order) in the body of a class or statements of a block.
*
* @syntax markdown
*/
// TODO add selfOpt: Option[ValDef]?
@experimental def module(module: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): (ValDef, ClassDef)
}

/** Makes extension methods on `ClassDef` available without any imports */
Expand Down Expand Up @@ -3660,14 +3684,11 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
* val runSym = cls.declaredMethod("run").head
*
* val runDef = DefDef(runSym, _ => Some('{ println("run") }.asTerm))
* 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 modDef = List(modVal, clsDef)
* val modDef = ClassDef.module(mod, parents, body = List(runDef))
*
* val callRun = Apply(Select(Ref(mod), runSym), Nil)
*
* Block(modDef, callRun)
* Block(modDef.toList, callRun)
* ```
* constructs the equivalent to
* ```scala
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ val experimentalDefinitionInLibrary = Set(
// Need newClass variant that can add constructor parameters.
// Need experimental annotation macros to check that design works.
"scala.quoted.Quotes.reflectModule.ClassDefModule.apply",
"scala.quoted.Quotes.reflectModule.ClassDefModule.module",
"scala.quoted.Quotes.reflectModule.SymbolModule.newClass",
"scala.quoted.Quotes.reflectModule.SymbolModule.newModule",
"scala.quoted.Quotes.reflectModule.SymbolModule.freshName",
Expand Down
7 changes: 2 additions & 5 deletions tests/run-macros/annot-add-global-object/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,10 @@ class addClass extends MacroAnnotation:

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 modDef = ClassDef.module(mod, parents, body = List(runDef))

val newDef = DefDef.copy(tree)(name, List(TermParamClause(Nil)), tpt, Some(Apply(Select(Ref(mod), runSym), Nil)))
List(modVal, clsDef, newDef)
modDef.toList ::: newDef :: Nil
case _ =>
report.error("Annotation only supports `def` with one argument")
List(tree)
5 changes: 1 addition & 4 deletions tests/run-macros/annot-add-local-object/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ class addClass extends MacroAnnotation:

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 (modVal, clsDef) = ClassDef.module(mod, parents, body = List(runDef))

val newDef = DefDef.copy(tree)(name, List(TermParamClause(Nil)), tpt, Some(Apply(Select(Ref(mod), runSym), Nil)))
List(modVal, clsDef, newDef)
Expand Down
5 changes: 1 addition & 4 deletions tests/run-macros/annot-add-nested-object/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ class addClass extends MacroAnnotation:

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 (modVal, clsDef) = ClassDef.module(mod, parents, body = List(runDef))

val newDef = DefDef.copy(tree)(name, List(TermParamClause(Nil)), tpt, Some(Apply(Select(Ref(mod), runSym), Nil)))
List(modVal, clsDef, newDef)
Expand Down

0 comments on commit 1acd745

Please sign in to comment.