Skip to content

Commit bcc08aa

Browse files
committed
Add ability to use the class symbol in classdef parents
1 parent 33fbd5f commit bcc08aa

File tree

8 files changed

+116
-15
lines changed

8 files changed

+116
-15
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/Symbols.scala

+52
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,30 @@ object Symbols extends SymUtils {
629629
newClassSymbol(owner, name, flags, completer, privateWithin, coord, compUnitInfo)
630630
}
631631

632+
/** Same as `newNormalizedClassSymbol` except that `parents` can be a function returning a list of arbitrary
633+
* types which get normalized into type refs and parameter bindings.
634+
*/
635+
def newNormalizedClassSymbolUsingClassSymbolinParents(
636+
owner: Symbol,
637+
name: TypeName,
638+
flags: FlagSet,
639+
parentTypes: Symbol => List[Type],
640+
selfInfo: Type = NoType,
641+
privateWithin: Symbol = NoSymbol,
642+
coord: Coord = NoCoord,
643+
compUnitInfo: CompilationUnitInfo | Null = null)(using Context): ClassSymbol = {
644+
def completer = new LazyType {
645+
def complete(denot: SymDenotation)(using Context): Unit = {
646+
val cls = denot.asClass.classSymbol
647+
val decls = newScope
648+
val parents = parentTypes(cls).map(_.dealias)
649+
assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class")
650+
denot.info = ClassInfo(owner.thisType, cls, parents, decls, selfInfo)
651+
}
652+
}
653+
newClassSymbol(owner, name, flags, completer, privateWithin, coord, compUnitInfo)
654+
}
655+
632656
def newRefinedClassSymbol(coord: Coord = NoCoord)(using Context): ClassSymbol =
633657
newCompleteClassSymbol(ctx.owner, tpnme.REFINE_CLASS, NonMember, parents = Nil, newScope, coord = coord)
634658

@@ -706,6 +730,34 @@ object Symbols extends SymUtils {
706730
privateWithin, coord, compUnitInfo)
707731
}
708732

733+
/** Same as `newNormalizedModuleSymbol` except that `parents` can be a function returning a list of arbitrary
734+
* types which get normalized into type refs and parameter bindings.
735+
*/
736+
def newNormalizedModuleSymbolUsingClassSymbolInParents(
737+
owner: Symbol,
738+
name: TermName,
739+
modFlags: FlagSet,
740+
clsFlags: FlagSet,
741+
parentTypes: ClassSymbol => List[Type],
742+
decls: Scope,
743+
privateWithin: Symbol = NoSymbol,
744+
coord: Coord = NoCoord,
745+
compUnitInfo: CompilationUnitInfo | Null = null)(using Context): TermSymbol = {
746+
def completer(module: Symbol) = new LazyType {
747+
def complete(denot: SymDenotation)(using Context): Unit = {
748+
val cls = denot.asClass.classSymbol
749+
val decls = newScope
750+
val parents = parentTypes(cls)
751+
assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class")
752+
denot.info = ClassInfo(owner.thisType, cls, parents.map(_.dealias), decls, TermRef(owner.thisType, module))
753+
}
754+
}
755+
newModuleSymbol(
756+
owner, name, modFlags, clsFlags,
757+
(module, modcls) => completer(module),
758+
privateWithin, coord, compUnitInfo)
759+
}
760+
709761
/** Create a package symbol with associated package class
710762
* from its non-info fields and a lazy type for loading the package's members.
711763
*/

Diff for: compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

+7-6
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
406406
case x: (tpd.NamedArg & x.type) => Some(x)
407407
case x: (tpd.Typed & x.type) =>
408408
TypedTypeTest.unapply(x) // Matches `Typed` but not `TypedOrTest`
409+
case x: (tpd.TypeDef & x.type) => Some(x)
409410
case _ => if x.isTerm then Some(x) else None
410411
end TermTypeTest
411412

@@ -2632,10 +2633,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26322633
for sym <- decls(cls) do cls.enter(sym)
26332634
cls
26342635

2635-
def newClass(parent: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr], paramNames: List[String], paramTypes: List[TypeRepr], flags: Flags, privateWithin: Symbol): Symbol =
2636-
assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class")
2636+
def newClass(parent: Symbol, name: String, parents: Symbol => List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr], paramNames: List[String], paramTypes: List[TypeRepr], flags: Flags, privateWithin: Symbol): Symbol =
26372637
checkValidFlags(flags.toTermFlags, Flags.validClassFlags)
2638-
val cls = dotc.core.Symbols.newNormalizedClassSymbol(
2638+
assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
2639+
val cls = dotc.core.Symbols.newNormalizedClassSymbolUsingClassSymbolinParents(
26392640
parent,
26402641
name.toTypeName,
26412642
flags,
@@ -2648,10 +2649,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26482649
for sym <- decls(cls) do cls.enter(sym)
26492650
cls
26502651

2651-
def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol =
2652-
assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class")
2652+
def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: Symbol => List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol =
2653+
// assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class")
26532654
assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
2654-
val mod = dotc.core.Symbols.newNormalizedModuleSymbol(
2655+
val mod = dotc.core.Symbols.newNormalizedModuleSymbolUsingClassSymbolInParents(
26552656
owner,
26562657
name.toTermName,
26572658
modFlags | dotc.core.Flags.ModuleValCreationFlags,

Diff for: library/src/scala/quoted/Quotes.scala

+8-6
Original file line numberDiff line numberDiff line change
@@ -3838,17 +3838,19 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38383838
* direct or indirect children of the reflection context's owner.
38393839
*/
38403840
// TODO: add flags and privateWithin
3841-
@experimental def newClass(parent: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr]): Symbol
3841+
@experimental def newClass(owner: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr]): Symbol
38423842

3843-
/*
3843+
/**
3844+
* @param parent declerations of this class provided the symbol of this class.
3845+
* Calling `cls.typeRef.asType` as part of this function will lead to cyclic reference errors.
38443846
* @param paramNames constructor parameter names.
38453847
* @param paramTypes constructor parameter types.
38463848
* @param flags extra flags with which the class symbol should be constructed.
38473849
* @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol.
38483850
*
38493851
* Parameters can be obtained via classSymbol.memberField
38503852
*/
3851-
@experimental def newClass(parent: Symbol, name: String, parents: List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr], paramNames: List[String], paramTypes: List[TypeRepr], flags: Flags, privateWithin: Symbol): Symbol
3853+
@experimental def newClass(owner: Symbol, name: String, parents: Symbol => List[TypeRepr], decls: Symbol => List[Symbol], selfType: Option[TypeRepr], paramNames: List[String], paramTypes: List[TypeRepr], flags: Flags, privateWithin: Symbol): Symbol
38523854

38533855
/** Generates a new module symbol with an associated module class symbol,
38543856
* this is equivalent to an `object` declaration in source code.
@@ -3865,7 +3867,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38653867
* def decls(cls: Symbol): List[Symbol] =
38663868
* List(Symbol.newMethod(cls, "run", MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Unit]), Flags.EmptyFlags, Symbol.noSymbol))
38673869
*
3868-
* val mod = Symbol.newModule(Symbol.spliceOwner, moduleName, Flags.EmptyFlags, Flags.EmptyFlags, parents.map(_.tpe), decls, Symbol.noSymbol)
3870+
* val mod = Symbol.newModule(Symbol.spliceOwner, moduleName, Flags.EmptyFlags, Flags.EmptyFlags, _ => parents.map(_.tpe), decls, Symbol.noSymbol)
38693871
* val cls = mod.moduleClass
38703872
* val runSym = cls.declaredMethod("run").head
38713873
*
@@ -3893,7 +3895,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38933895
* @param name The name of the class
38943896
* @param modFlags extra flags with which the module symbol should be constructed
38953897
* @param clsFlags extra flags with which the module class symbol should be constructed
3896-
* @param parents The parent classes of the class. The first parent must not be a trait.
3898+
* @param parents A function that takes the symbol of the module class as input and returns the parent classes of the class. The first parent must not be a trait.
38973899
* @param decls A function that takes the symbol of the module class as input and return the symbols of its declared members
38983900
* @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol.
38993901
*
@@ -3906,7 +3908,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
39063908
*
39073909
* @syntax markdown
39083910
*/
3909-
@experimental def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol
3911+
@experimental def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: Symbol => List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol
39103912

39113913
/** Generates a new method symbol with the given parent, name and type.
39123914
*

Diff for: tests/neg-macros/newClassParamsMissingArgument/Macro_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Object] =
1010
val parents = List(TypeTree.of[Object])
1111
def decls(cls: Symbol): List[Symbol] = Nil
1212

13-
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None, List("idx"), List(TypeRepr.of[Int]), Flags.EmptyFlags, Symbol.noSymbol)
13+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = _ => parents.map(_.tpe), decls, selfType = None, List("idx"), List(TypeRepr.of[Int]), Flags.EmptyFlags, Symbol.noSymbol)
1414

1515
val clsDef = ClassDef(cls, parents, body = Nil)
1616
val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[Object])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//> using options -experimental
2+
3+
import scala.quoted.*
4+
5+
transparent inline def makeClass(inline name: String): Foo[_] = ${ makeClassExpr('name) }
6+
private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Foo[_]] = {
7+
import quotes.reflect.*
8+
9+
val name = nameExpr.valueOrAbort
10+
11+
// using asType on the passed Symbol leads to cyclic reference errors
12+
def parents(cls: Symbol) =
13+
List(AppliedType(TypeRepr.typeConstructorOf(Class.forName("Foo")), List(TypeIdent(cls).tpe)))
14+
def decls(cls: Symbol): List[Symbol] = Nil
15+
16+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents, decls, selfType = None, paramNames = Nil, paramTypes = Nil, Flags.EmptyFlags, Symbol.noSymbol)
17+
18+
val parentsWithSym =
19+
cls.typeRef.asType match
20+
case '[t] =>
21+
List(Apply(TypeApply(Select(New(TypeTree.of[Foo[t]]), TypeRepr.of[Foo[t]].typeSymbol.primaryConstructor), List(TypeTree.of[t])), List()))
22+
val clsDef = ClassDef(cls, parentsWithSym, body = Nil)
23+
24+
val newCls = cls.typeRef.asType match
25+
case '[t] =>
26+
Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[Foo[t]])
27+
28+
cls.typeRef.asType match
29+
case '[t] =>
30+
Block(List(clsDef), newCls).asExprOf[Foo[t]]
31+
32+
// '{
33+
// class Name() extends Foo[Name.type]()
34+
// new Name()
35+
// }
36+
}
37+
38+
class Foo[X]() { self: X =>
39+
def getSelf: X = self
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//> using options -experimental
2+
3+
@main def Test: Unit = {
4+
val foo = makeClass("Bar")
5+
foo.getSelf
6+
}

Diff for: tests/run-macros/newClassParams/Macro_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private def makeClassAndCallExpr(nameExpr: Expr[String], idxExpr: Expr[Int], str
1010

1111
def decls(cls: Symbol): List[Symbol] = List(Symbol.newMethod(cls, "foo", MethodType(Nil)(_ => Nil, _ => TypeRepr.of[Unit])))
1212
val parents = List(TypeTree.of[Object])
13-
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None, List("idx", "str"), List(TypeRepr.of[Int], TypeRepr.of[String]), Flags.EmptyFlags, Symbol.noSymbol)
13+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = _ => parents.map(_.tpe), decls, selfType = None, List("idx", "str"), List(TypeRepr.of[Int], TypeRepr.of[String]), Flags.EmptyFlags, Symbol.noSymbol)
1414

1515
val fooDef = DefDef(cls.methodMember("foo")(0), argss => Some('{println(s"Foo method call with (${${Ref(cls.fieldMember("idx")).asExpr}}, ${${Ref(cls.fieldMember("str")).asExpr}})")}.asTerm))
1616
val clsDef = ClassDef(cls, parents, body = List(fooDef))

Diff for: tests/run-macros/newClassParamsExtendsClassParams/Macro_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Foo] = {
1010
val parents = List('{ new Foo(1) }.asTerm)
1111
def decls(cls: Symbol): List[Symbol] = Nil
1212

13-
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None, List("idx"), List(TypeRepr.of[Int]), Flags.EmptyFlags, Symbol.noSymbol)
13+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = _ => parents.map(_.tpe), decls, selfType = None, List("idx"), List(TypeRepr.of[Int]), Flags.EmptyFlags, Symbol.noSymbol)
1414

1515
val parentsWithSym = List(Apply(Select(New(TypeTree.of[Foo]), TypeRepr.of[Foo].typeSymbol.primaryConstructor), List(Ref(cls.fieldMember("idx")))))
1616
val clsDef = ClassDef(cls, parentsWithSym, body = Nil)

0 commit comments

Comments
 (0)