Skip to content

Commit 2ca2fc3

Browse files
committed
Improve api, documentation and add conParamsPrivateWithin
1 parent 0f020f8 commit 2ca2fc3

File tree

15 files changed

+177
-32
lines changed

15 files changed

+177
-32
lines changed

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

+10-10
Original file line numberDiff line numberDiff line change
@@ -2690,10 +2690,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26902690
selfType: Option[TypeRepr],
26912691
clsFlags: Flags,
26922692
clsPrivateWithin: Symbol,
2693-
conParamNames: List[String],
2694-
conParamTypes: List[TypeRepr],
2693+
conParams: List[(String, TypeRepr)]
26952694
): Symbol =
2696-
assert(conParamNames.length == conParamTypes.length, "Lengths of conParamNames and conParamTypes must be equal")
2695+
val (conParamNames, conParamTypes) = conParams.unzip()
26972696
newClass(
26982697
owner,
26992698
name,
@@ -2706,7 +2705,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27062705
conMethodType = res => MethodType(conParamNames)(_ => conParamTypes, _ => res),
27072706
conFlags = Flags.EmptyFlags,
27082707
conPrivateWithin = Symbol.noSymbol,
2709-
conParamFlags = List(for i <- conParamNames yield Flags.EmptyFlags)
2708+
conParamFlags = List(for i <- conParamNames yield Flags.EmptyFlags),
2709+
conParamPrivateWithins = List(for i <- conParamNames yield Symbol.noSymbol)
27102710
)
27112711

27122712
def newClass(
@@ -2721,7 +2721,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27212721
conMethodType: TypeRepr => MethodOrPoly,
27222722
conFlags: Flags,
27232723
conPrivateWithin: Symbol,
2724-
conParamFlags: List[List[Flags]]
2724+
conParamFlags: List[List[Flags]],
2725+
conParamPrivateWithins: List[List[Symbol]]
27252726
) =
27262727
assert(!clsPrivateWithin.exists || clsPrivateWithin.isType, "clsPrivateWithin must be a type symbol or `Symbol.noSymbol`")
27272728
assert(!conPrivateWithin.exists || conPrivateWithin.isType, "consPrivateWithin must be a type symbol or `Symbol.noSymbol`")
@@ -2760,7 +2761,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27602761
paramNames.zip(paramBounds).map(_ :* true :* clauseIdx).zipWithIndex ++ getParamAccessors(res, clauseIdx + 1)
27612762
case result =>
27622763
List()
2763-
// Maps PolyType indexes to type symbols
2764+
// Maps PolyType indexes to type parameter symbols
27642765
val paramRefMap = collection.mutable.HashMap[Int, Symbol]()
27652766
val paramRefRemapper = new Types.TypeMap {
27662767
def apply(tp: Types.Type) = tp match {
@@ -2771,13 +2772,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27712772
for ((name, tpe, isType, clauseIdx), elementIdx) <- getParamAccessors(methodType, 0) do
27722773
if isType then
27732774
checkValidFlags(conParamFlags(clauseIdx)(elementIdx), Flags.validClassTypeParamFlags)
2774-
val symbol = dotc.core.Symbols.newSymbol(cls, name.toTypeName, Flags.Param | Flags.Deferred | Flags.Private | Flags.PrivateLocal | Flags.Local | conParamFlags(clauseIdx)(elementIdx), tpe, Symbol.noSymbol)
2775+
val symbol = dotc.core.Symbols.newSymbol(cls, name.toTypeName, Flags.Param | Flags.Deferred | Flags.Private | Flags.PrivateLocal | Flags.Local | conParamFlags(clauseIdx)(elementIdx), tpe, conParamPrivateWithins(clauseIdx)(elementIdx))
27752776
paramRefMap.addOne(elementIdx, symbol)
27762777
cls.enter(symbol)
27772778
else
27782779
checkValidFlags(conParamFlags(clauseIdx)(elementIdx), Flags.validClassTermParamFlags)
27792780
val fixedType = paramRefRemapper(tpe)
2780-
cls.enter(dotc.core.Symbols.newSymbol(cls, name.toTermName, Flags.ParamAccessor | conParamFlags(clauseIdx)(elementIdx), fixedType, Symbol.noSymbol)) // TODO set privateWithin?
2781+
cls.enter(dotc.core.Symbols.newSymbol(cls, name.toTermName, Flags.ParamAccessor | conParamFlags(clauseIdx)(elementIdx), fixedType, conParamPrivateWithins(clauseIdx)(elementIdx)))
27812782
for sym <- decls(cls) do cls.enter(sym)
27822783
cls
27832784

@@ -3189,10 +3190,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
31893190
private[QuotesImpl] def validTypeAliasFlags: Flags = Private | Protected | Override | Final | Infix | Local
31903191

31913192
// Keep: aligned with Quotes's `newClass`
3192-
private[QuotesImpl] def validClassFlags: Flags = Private | Protected | PrivateLocal | Local | Final | Trait | Abstract // AbsOverride, Open
3193+
private[QuotesImpl] def validClassFlags: Flags = Private | Protected | PrivateLocal | Local | Final | Trait | Abstract | Open
31933194

31943195
// Keep: aligned with Quote's 'newClass'
3195-
// Private constructor would be currently useless, but if we decide to add a way to register companions in the future it might be useful
31963196
private[QuotesImpl] def validClassConstructorFlags: Flags = Synthetic | Method | Private | Protected | PrivateLocal | Local
31973197

31983198
// Keep: aligned with Quotes's `newClass`

library/src/scala/quoted/Quotes.scala

+73-9
Original file line numberDiff line numberDiff line change
@@ -3862,8 +3862,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38623862
* Takes the constructed class symbol as an argument. Calling `cls.typeRef.asType` as part of this function will lead to cyclic reference errors.
38633863
* @param clsFlags extra flags with which the class symbol should be constructed.
38643864
* @param clsPrivateWithin the symbol within which this new class symbol should be private. May be noSymbol.
3865-
* @param conParamNames constructor parameter names.
3866-
* @param conParamTypes constructor parameter types.
3865+
* @param conParams constructor parameter pairs of names and types.
38673866
*
38683867
* Parameters assigned by the constructor can be obtained via `classSymbol.memberField`.
38693868
* This symbol starts without an accompanying definition.
@@ -3877,31 +3876,93 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38773876
owner: Symbol,
38783877
name: String,
38793878
parents: Symbol => List[TypeRepr],
3880-
decls: Symbol => List[Symbol], selfType: Option[TypeRepr],
3879+
decls: Symbol => List[Symbol],
3880+
selfType: Option[TypeRepr],
38813881
clsFlags: Flags,
38823882
clsPrivateWithin: Symbol,
3883-
conParamNames: List[String],
3884-
conParamTypes: List[TypeRepr]
3883+
conParams: List[(String, TypeRepr)]
38853884
): Symbol
38863885

38873886
/** Generates a new class symbol with a constructor of the shape signified by a passed PolyOrMethod parameter.
3888-
* TODO example with PolyType
3887+
*
3888+
* Example usage:
3889+
* ```
3890+
* val name = "myClass"
3891+
* def decls(cls: Symbol): List[Symbol] =
3892+
* List(Symbol.newMethod(cls, "getParam", MethodType(Nil)(_ => Nil, _ => cls.typeMember("T").typeRef)))
3893+
* val conMethodType =
3894+
* (classType: TypeRepr) => PolyType(List("T"))(_ => List(TypeBounds.empty), polyType =>
3895+
* MethodType(List("param"))((_: MethodType) => List(polyType.param(0)), (_: MethodType) =>
3896+
* classType
3897+
* )
3898+
* )
3899+
* val cls = Symbol.newClass(
3900+
* Symbol.spliceOwner,
3901+
* name,
3902+
* parents = _ => List(TypeRepr.of[Object]),
3903+
* decls,
3904+
* selfType = None,
3905+
* clsFlags = Flags.EmptyFlags,
3906+
* clsPrivateWithin = Symbol.noSymbol,
3907+
* clsAnnotations = Nil,
3908+
* conMethodType,
3909+
* conFlags = Flags.EmptyFlags,
3910+
* conPrivateWithin = Symbol.noSymbol,
3911+
* conParamFlags = List(List(Flags.EmptyFlags), List(Flags.EmptyFlags)),
3912+
* conParamPrivateWithins = List(List(Symbol.noSymbol), List(Symbol.noSymbol))
3913+
* )
3914+
*
3915+
* val getParamSym = cls.declaredMethod("getParam").head
3916+
* def getParamRhs(): Option[Term] =
3917+
* val paramValue = This(cls).select(cls.fieldMember("param")).asExpr
3918+
* Some('{ println("Calling getParam"); $paramValue }.asTerm)
3919+
* val getParamDef = DefDef(getParamSym, _ => getParamRhs())
3920+
*
3921+
* val clsDef = ClassDef(cls, List(TypeTree.of[Object]), body = List(getParamDef))
3922+
* val newCls =
3923+
* Apply(
3924+
* Select(
3925+
* Apply(
3926+
* TypeApply(Select(New(TypeIdent(cls)), cls.primaryConstructor), List(TypeTree.of[String])),
3927+
* List(Expr("test").asTerm)
3928+
* ),
3929+
* cls.methodMember("getParam").head
3930+
* ),
3931+
* Nil
3932+
* )
3933+
*
3934+
* Block(List(clsDef), newCls).asExpr
3935+
* ```
3936+
* constructs the equivalent to
3937+
* ```
3938+
* '{
3939+
* class myClass[T](val param: T) {
3940+
* def getParam: T =
3941+
* println("Calling getParam")
3942+
* param
3943+
* }
3944+
* new myClass[String]("test").getParam()
3945+
* }
3946+
* ```
38893947
*
38903948
* @param owner The owner of the class
38913949
* @param name The name of the class
38923950
* @param parents Function returning the parent classes of the class. The first parent must not be a trait
38933951
* Takes the constructed class symbol as an argument. Calling `cls.typeRef.asType` as part of this function will lead to cyclic reference errors.
38943952
* @param decls The member declarations of the class provided the symbol of this class
38953953
* @param selfType The self type of the class if it has one
3896-
* @param clsFlags extra flags with which the class symbol should be constructed
3954+
* @param clsFlags extra flags with which the class symbol should be constructed. Can be `Private` | `Protected` | `PrivateLocal` | `Local` | `Final` | `Trait` | `Abstract` | `Open`
38973955
* @param clsPrivateWithin the symbol within which this new class symbol should be private. May be noSymbol
38983956
* @param clsAnnotations annotations of the class
38993957
* @param conMethodType Function returning MethodOrPoly type representing the type of the constructor.
39003958
* Takes the result type as parameter which must be returned from the innermost MethodOrPoly.
39013959
* PolyType may only represent the first clause of the constructor.
3902-
* @param conFlags extra flags with which the constructor symbol should be constructed
3960+
* @param conFlags extra flags with which the constructor symbol should be constructed. Can be `Synthetic` | `Method` | `Private` | `Protected` | `PrivateLocal` | `Local`
39033961
* @param conPrivateWithin the symbol within which the constructor for this new class symbol should be private. May be noSymbol.
39043962
* @param conParamFlags extra flags with which the constructor parameter symbols should be constructed. Must match the shape of `conMethodType`.
3963+
* For type parameters those can be `Param` | `Deferred` | `Private` | `PrivateLocal` | `Local`.
3964+
* For term parameters those can be `ParamAccessor` | `Private` | `Protected` | `PrivateLocal` | `Local`
3965+
* @param conParamPrivateWithins the symbols within which the constructor parameters should be private. Must match the shape of `conMethodType`. Can consist of noSymbol.
39053966
*
39063967
* Term and type parameters assigned by the constructor can be obtained via `classSymbol.memberField`/`classSymbol.memberType`.
39073968
* This symbol starts without an accompanying definition.
@@ -3911,6 +3972,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
39113972
* @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be
39123973
* direct or indirect children of the reflection context's owner.
39133974
*/
3975+
// Keep doc aligned with QuotesImpl's validFlags: `clsFlags` with `validClassFlags`, `conFlags` with `validClassConstructorFlags`,
3976+
// conParamFlags with `validClassTypeParamFlags` and `validClassTermParamFlags`
39143977
@experimental def newClass(
39153978
owner: Symbol,
39163979
name: String,
@@ -3923,7 +3986,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
39233986
conMethodType: TypeRepr => MethodOrPoly,
39243987
conFlags: Flags,
39253988
conPrivateWithin: Symbol,
3926-
conParamFlags: List[List[Flags]]
3989+
conParamFlags: List[List[Flags]],
3990+
conParamPrivateWithins: List[List[Symbol]]
39273991
): Symbol
39283992

39293993
/** Generates a new module symbol with an associated module class symbol,

tests/neg-macros/i19842-a.check

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
|
1010
| at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
1111
| at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:210)
12-
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:278)
13-
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:277)
12+
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:284)
13+
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:283)
1414
| at Macros$.makeSerializer(Macro.scala:25)
1515
|
1616
|---------------------------------------------------------------------------------------------------------------------

tests/neg-macros/i19842-b.check

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
|
1010
| at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
1111
| at dotty.tools.dotc.transform.TreeChecker$.checkParents(TreeChecker.scala:210)
12-
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:278)
13-
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:277)
12+
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:284)
13+
| at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:283)
1414
| at Macros$.makeSerializer(Macro.scala:27)
1515
|
1616
|---------------------------------------------------------------------------------------------------------------------

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

1515
val clsDef = ClassDef(cls, parents, body = Nil)
1616
val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[Object])

tests/pos-macros/newClassExtendsWithSymbolInParent/Macro_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Foo[_]] =
1313
List(AppliedType(TypeRepr.typeConstructorOf(Class.forName("Foo")), List(TypeIdent(cls).tpe)))
1414
def decls(cls: Symbol): List[Symbol] = Nil
1515

16-
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents, decls, selfType = None, Flags.EmptyFlags, Symbol.noSymbol, conParamNames = Nil, conParamTypes = Nil)
16+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents, decls, selfType = None, Flags.EmptyFlags, Symbol.noSymbol, conParams = Nil)
1717

1818
val parentsWithSym =
1919
cls.typeRef.asType match

tests/run-macros/newClassAnnotation/Macro_1.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Any] = {
3131
conMethodType,
3232
conFlags = Flags.EmptyFlags,
3333
conPrivateWithin = Symbol.noSymbol,
34-
conParamFlags = List(List())
34+
conParamFlags = List(List()),
35+
conParamPrivateWithins = List(List())
3536
)
3637

3738
val clsDef = ClassDef(cls, List(TypeTree.of[Object]), body = Nil)

tests/run-macros/newClassExtendsJavaClass/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[JavaClass[
1010
val parents = List(TypeTree.of[JavaClass[Int]])
1111
def decls(cls: Symbol): List[Symbol] = Nil
1212

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

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

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, Flags.EmptyFlags, Symbol.noSymbol, List("idx", "str"), List(TypeRepr.of[Int], TypeRepr.of[String]))
13+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = _ => parents.map(_.tpe), decls, selfType = None, Flags.EmptyFlags, Symbol.noSymbol, List(("idx", TypeRepr.of[Int]), ("str", TypeRepr.of[String])))
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))

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

tests/run-macros/newClassTraitAndAbstract/Macro_1.scala

+6-2
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ private def makeClassExpr(using Quotes)(
4444
selfType = None,
4545
clsFlags,
4646
clsPrivateWithin = Symbol.noSymbol,
47+
clsAnnotations = Nil,
4748
conMethodType,
4849
conFlags = Flags.EmptyFlags,
4950
conPrivateWithin = Symbol.noSymbol,
50-
conParamFlags = List(List(Flags.EmptyFlags, Flags.EmptyFlags), List(Flags.EmptyFlags, Flags.EmptyFlags))
51+
conParamFlags = List(List(Flags.EmptyFlags, Flags.EmptyFlags), List(Flags.EmptyFlags, Flags.EmptyFlags)),
52+
conParamPrivateWithins = List(List(Symbol.noSymbol, Symbol.noSymbol), List(Symbol.noSymbol, Symbol.noSymbol))
5153
)
5254
val traitDef = ClassDef(traitSymbol, List(TypeTree.of[Object]), body = Nil)
5355

@@ -60,10 +62,12 @@ private def makeClassExpr(using Quotes)(
6062
selfType = None,
6163
clsFlags = Flags.EmptyFlags,
6264
clsPrivateWithin = Symbol.noSymbol,
65+
clsAnnotations = Nil,
6366
conMethodType = (classType: TypeRepr) => MethodType(Nil)(_ => Nil, _ => classType),
6467
conFlags = Flags.EmptyFlags,
6568
conPrivateWithin = Symbol.noSymbol,
66-
conParamFlags = List(List())
69+
conParamFlags = List(List()),
70+
conParamPrivateWithins = List(List(Symbol.noSymbol, Symbol.noSymbol), List(Symbol.noSymbol, Symbol.noSymbol))
6771
)
6872
val obj = '{new java.lang.Object()}.asTerm match
6973
case Inlined(_, _, term) => term
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Calling getParam
2+
test

0 commit comments

Comments
 (0)