Skip to content

Commit 1fbda34

Browse files
committed
Add reflect ClassDef.apply
1 parent d466f9f commit 1fbda34

File tree

9 files changed

+101
-1
lines changed

9 files changed

+101
-1
lines changed

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
228228
end ClassDefTypeTest
229229

230230
object ClassDef extends ClassDefModule:
231+
def apply(cls: Symbol, parents: List[Tree], body: List[Statement]): ClassDef =
232+
val constr = ctx.typeAssigner.assignType(untpd.DefDef(nme.CONSTRUCTOR, Nil, tpd.TypeTree(dotc.core.Symbols.defn.UnitClass.typeRef), untpd.EmptyTree), cls.primaryConstructor)
233+
tpd.ClassDefWithParents(cls.asClass, constr, parents, body)
234+
231235
def copy(original: Tree)(name: String, constr: DefDef, parents: List[Tree], selfOpt: Option[ValDef], body: List[Statement]): ClassDef = {
232236
val dotc.ast.Trees.TypeDef(_, originalImpl: tpd.Template) = original
233237
tpd.cpy.TypeDef(original)(name.toTypeName, tpd.cpy.Template(originalImpl)(constr, parents, derived = Nil, selfOpt.getOrElse(tpd.EmptyValDef), body))
@@ -260,6 +264,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
260264

261265
object DefDef extends DefDefModule:
262266
def apply(symbol: Symbol, rhsFn: List[List[Tree]] => Option[Term]): DefDef =
267+
assert(symbol.isTerm, s"expected a term symbol but received $symbol")
263268
withDefaultPos(tpd.DefDef(symbol.asTerm, prefss =>
264269
xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree)
265270
))
@@ -1804,7 +1809,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
18041809
(x.prefix, x.name.toString)
18051810
end TermRef
18061811

1807-
type TypeRef = dotc.core.Types.NamedType
1812+
type TypeRef = dotc.core.Types.TypeRef
18081813

18091814
object TypeRefTypeTest extends TypeTest[TypeRepr, TypeRef]:
18101815
def unapply(x: TypeRepr): Option[TypeRef & x.type] = x match
@@ -2454,6 +2459,20 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
24542459
def requiredModule(path: String): Symbol = dotc.core.Symbols.requiredModule(path)
24552460
def requiredMethod(path: String): Symbol = dotc.core.Symbols.requiredMethod(path)
24562461
def classSymbol(fullName: String): Symbol = dotc.core.Symbols.requiredClass(fullName)
2462+
2463+
def newClass(owner: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfInfo: Option[TypeRepr]): Symbol =
2464+
val cls = dotc.core.Symbols.newNormalizedClassSymbol(
2465+
owner,
2466+
name.toTypeName,
2467+
Flags.EmptyFlags,
2468+
parents,
2469+
dotc.core.Scopes.newScope, // TODO remove from parameters of newNormalizedClassSymbol
2470+
selfInfo.getOrElse(Types.NoType),
2471+
dotc.core.Symbols.NoSymbol)
2472+
cls.enter(dotc.core.Symbols.newConstructor(cls, dotc.core.Flags.Synthetic, Nil, Nil))
2473+
for sym <- decls(cls) do cls.enter(sym)
2474+
cls
2475+
24572476
def newMethod(owner: Symbol, name: String, tpe: TypeRepr): Symbol =
24582477
newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol)
24592478
def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =

library/src/scala/quoted/Quotes.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
464464

465465
/** Methods of the module object `val ClassDef` */
466466
trait ClassDefModule { this: ClassDef.type =>
467+
@experimental def apply(cls: Symbol, parents: List[Tree /* Term | TypeTree */], body: List[Statement]): ClassDef
467468
def copy(original: Tree)(name: String, constr: DefDef, parents: List[Tree /* Term | TypeTree */], selfOpt: Option[ValDef], body: List[Statement]): ClassDef
468469
def unapply(cdef: ClassDef): (String, DefDef, List[Tree /* Term | TypeTree */], Option[ValDef], List[Statement])
469470
}
@@ -3533,6 +3534,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
35333534
/** The class Symbol of a global class definition */
35343535
def classSymbol(fullName: String): Symbol
35353536

3537+
@experimental def newClass(owner: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfInfo: Option[TypeRepr]): Symbol
3538+
35363539
/** Generates a new method symbol with the given parent, name and type.
35373540
*
35383541
* This symbol starts without an accompanying definition.

project/MiMaFilters.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ object MiMaFilters {
1717
ProblemFilters.exclude[MissingClassProblem]("scala.compiletime.ops.long$"),
1818
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#CompilationInfoModule.XmacroSettings"),
1919
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#CompilationInfoModule.XmacroSettings"),
20+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#ClassDefModule.apply"),
21+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#ClassDefModule.apply"),
22+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolModule.newClass"),
23+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolModule.newClass"),
2024

2125
// Private to the compiler - needed for forward binary compatibility
2226
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.since")

tests/run-macros/newClass.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Constructing foo
2+
class Test_2$package$foo$1
3+
Constructing bar
4+
class Test_2$package$bar$1
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import scala.quoted.*
2+
3+
inline def makeClass(inline name: String): Any = ${ makeClassExpr('name) }
4+
private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Any] = {
5+
import quotes.reflect.*
6+
7+
val name = nameExpr.valueOrAbort
8+
val parents = List(TypeTree.of[Object])
9+
def decls(cls: Symbol): List[Symbol] = Nil
10+
11+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfInfo = None)
12+
13+
val clsDef = ClassDef(cls, parents, body = List('{println(s"Constructing ${$nameExpr}")}.asTerm))
14+
val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[Object])
15+
16+
Block(List(clsDef), newCls).asExpr
17+
// '{
18+
// class `name`() { println("Constructing `name`") }
19+
// new `name`()
20+
// }
21+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@main def Test: Unit = {
2+
val foo = makeClass("foo")
3+
println(foo.getClass)
4+
val bar = makeClass("bar")
5+
println(bar.getClass)
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Calling foo.foo
2+
class Test_2$package$foo$1
3+
Calling bar.foo
4+
class Test_2$package$bar$1
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import scala.quoted.*
2+
3+
inline def makeClass(inline name: String): Foo = ${ makeClassExpr('name) }
4+
private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Foo] = {
5+
import quotes.reflect.*
6+
7+
val name = nameExpr.valueOrAbort
8+
val parents = List(TypeTree.of[Object], TypeTree.of[Foo])
9+
def decls(cls: Symbol): List[Symbol] =
10+
List(Symbol.newMethod(cls, "foo", MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Unit])))
11+
12+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfInfo = None)
13+
val fooSym = cls.declaredMethod("foo").head
14+
15+
val fooDef = DefDef(fooSym, argss => Some('{println(s"Calling ${$nameExpr}.foo")}.asTerm))
16+
val clsDef = ClassDef(cls, parents, body = List(fooDef))
17+
val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[Foo])
18+
19+
Block(List(clsDef), newCls).asExprOf[Foo]
20+
21+
// '{
22+
// class `name`() extends Foo {
23+
// def foo(): Unit = println("Calling `name`.foo")
24+
// }
25+
// new `name`()
26+
// }
27+
}
28+
29+
trait Foo {
30+
def foo(): Unit
31+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@main def Test: Unit = {
2+
val foo: Foo = makeClass("foo")
3+
foo.foo()
4+
println(foo.getClass)
5+
val bar: Foo = makeClass("bar")
6+
bar.foo()
7+
println(bar.getClass)
8+
}

0 commit comments

Comments
 (0)