Skip to content

Commit 30abd4f

Browse files
committed
Fix method signatures and add test for abstract classes and traits
1 parent 81cea85 commit 30abd4f

File tree

10 files changed

+174
-55
lines changed

10 files changed

+174
-55
lines changed

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

+23-24
Original file line numberDiff line numberDiff line change
@@ -2645,13 +2645,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26452645
parents: Symbol => List[TypeRepr],
26462646
decls: Symbol => List[Symbol],
26472647
selfType: Option[TypeRepr],
2648-
paramNames: List[String],
2649-
paramTypes: List[TypeRepr],
26502648
clsFlags: Flags,
2651-
clsPrivateWithin: Symbol
2649+
clsPrivateWithin: Symbol,
2650+
conParamNames: List[String],
2651+
conParamTypes: List[TypeRepr],
26522652
): Symbol =
2653-
checkValidFlags(clsFlags.toTermFlags, Flags.validClassFlags)
2654-
assert(paramNames.length == paramTypes.length, "paramNames and paramTypes must have the same length")
2653+
checkValidFlags(clsFlags, Flags.validClassFlags)
2654+
assert(conParamNames.length == conParamTypes.length, "paramNames and paramTypes must have the same length")
26552655
assert(!clsPrivateWithin.exists || clsPrivateWithin.isType, "clsPrivateWithin must be a type symbol or `Symbol.noSymbol`")
26562656
val cls = dotc.core.Symbols.newNormalizedClassSymbolUsingClassSymbolinParents(
26572657
owner,
@@ -2660,8 +2660,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26602660
parents,
26612661
selfType.getOrElse(Types.NoType),
26622662
clsPrivateWithin)
2663-
cls.enter(dotc.core.Symbols.newConstructor(cls, dotc.core.Flags.Synthetic, paramNames.map(_.toTermName), paramTypes))
2664-
for (name, tpe) <- paramNames.zip(paramTypes) do
2663+
cls.enter(dotc.core.Symbols.newConstructor(cls, dotc.core.Flags.Synthetic, conParamNames.map(_.toTermName), conParamTypes))
2664+
for (name, tpe) <- conParamNames.zip(conParamTypes) do
26652665
cls.enter(dotc.core.Symbols.newSymbol(cls, name.toTermName, Flags.ParamAccessor, tpe, Symbol.noSymbol))
26662666
for sym <- decls(cls) do cls.enter(sym)
26672667
cls
@@ -2672,39 +2672,39 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26722672
parents: Symbol => List[TypeRepr],
26732673
decls: Symbol => List[Symbol],
26742674
selfType: Option[TypeRepr],
2675-
constructorMethodType: TypeRepr => MethodOrPoly,
26762675
clsFlags: Flags,
26772676
clsPrivateWithin: Symbol,
2678-
consFlags: Flags,
2679-
consPrivateWithin: Symbol,
2680-
consParamFlags: List[List[Flags]]
2677+
conMethodType: TypeRepr => MethodOrPoly,
2678+
conFlags: Flags,
2679+
conPrivateWithin: Symbol,
2680+
conParamFlags: List[List[Flags]]
26812681
) =
26822682
assert(!clsPrivateWithin.exists || clsPrivateWithin.isType, "clsPrivateWithin must be a type symbol or `Symbol.noSymbol`")
2683-
assert(!consPrivateWithin.exists || consPrivateWithin.isType, "consPrivateWithin must be a type symbol or `Symbol.noSymbol`")
2684-
checkValidFlags(clsFlags.toTermFlags, Flags.validClassFlags)
2683+
assert(!conPrivateWithin.exists || conPrivateWithin.isType, "consPrivateWithin must be a type symbol or `Symbol.noSymbol`")
2684+
checkValidFlags(clsFlags.toTypeFlags, Flags.validClassFlags)
26852685
val cls = dotc.core.Symbols.newNormalizedClassSymbolUsingClassSymbolinParents(
26862686
owner,
26872687
name.toTypeName,
26882688
clsFlags,
26892689
parents,
26902690
selfType.getOrElse(Types.NoType),
26912691
clsPrivateWithin)
2692-
val methodType: MethodOrPoly = constructorMethodType(cls.typeRef)
2693-
def throwShapeException() = throw new Exception("Shapes of constructorMethodType and consParamFlags differ.")
2692+
val methodType: MethodOrPoly = conMethodType(cls.typeRef)
2693+
def throwShapeException() = throw new Exception("Shapes of conMethodType and conParamFlags differ.")
26942694
def checkMethodOrPolyShape(checkedMethodType: TypeRepr, clauseIdx: Int): Unit =
26952695
checkedMethodType match
26962696
case PolyType(params, _, res) if clauseIdx == 0 =>
2697-
if (consParamFlags.length < clauseIdx) throwShapeException()
2698-
if (consParamFlags(clauseIdx).length != params.length) throwShapeException()
2697+
if (conParamFlags.length < clauseIdx) throwShapeException()
2698+
if (conParamFlags(clauseIdx).length != params.length) throwShapeException()
26992699
checkMethodOrPolyShape(res, clauseIdx + 1)
27002700
case PolyType(_, _, _) => throw new Exception("Clause interleaving not supported for constructors")
27012701
case MethodType(params, _, res) =>
2702-
if (consParamFlags.length < clauseIdx) throwShapeException()
2703-
if (consParamFlags(clauseIdx).length != params.length) throwShapeException()
2702+
if (conParamFlags.length <= clauseIdx) throwShapeException()
2703+
if (conParamFlags(clauseIdx).length != params.length) throwShapeException()
27042704
checkMethodOrPolyShape(res, clauseIdx + 1)
27052705
case _ =>
27062706
checkMethodOrPolyShape(methodType, clauseIdx = 0)
2707-
cls.enter(dotc.core.Symbols.newSymbol(cls, nme.CONSTRUCTOR, Flags.Synthetic | Flags.Method | consFlags, methodType, consPrivateWithin, dotty.tools.dotc.util.Spans.NoCoord)) // constructor flags
2707+
cls.enter(dotc.core.Symbols.newSymbol(cls, nme.CONSTRUCTOR, Flags.Synthetic | Flags.Method | conFlags, methodType, conPrivateWithin, dotty.tools.dotc.util.Spans.NoCoord))
27082708
def getParamAccessors(methodType: TypeRepr, clauseIdx: Int): List[((String, TypeRepr, Boolean, Int), Int)] =
27092709
methodType match
27102710
case MethodType(paramInfosExp, resultTypeExp, res) =>
@@ -2723,17 +2723,16 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27232723
}
27242724
for ((name, tpe, isType, clauseIdx), elementIdx) <- getParamAccessors(methodType, 0) do
27252725
if isType then
2726-
val symbol = dotc.core.Symbols.newSymbol(cls, name.toTypeName, Flags.Param | Flags.Deferred | consParamFlags(clauseIdx)(elementIdx), tpe, Symbol.noSymbol)
2726+
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)
27272727
paramRefMap.addOne(elementIdx, symbol)
27282728
cls.enter(symbol)
27292729
else
27302730
val fixedType = paramRefRemapper(tpe)
2731-
cls.enter(dotc.core.Symbols.newSymbol(cls, name.toTermName, Flags.ParamAccessor | consParamFlags(clauseIdx)(elementIdx), fixedType, Symbol.noSymbol)) // add other flags (local, private, privatelocal) and set privateWithin
2731+
cls.enter(dotc.core.Symbols.newSymbol(cls, name.toTermName, Flags.ParamAccessor | conParamFlags(clauseIdx)(elementIdx), fixedType, Symbol.noSymbol)) // set privateWithin
27322732
for sym <- decls(cls) do cls.enter(sym)
27332733
cls
27342734

27352735
def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: Symbol => List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol =
2736-
// assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class")
27372736
assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`")
27382737
val mod = dotc.core.Symbols.newNormalizedModuleSymbolUsingClassSymbolInParents(
27392738
owner,
@@ -3122,7 +3121,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
31223121
private[QuotesImpl] def validTypeAliasFlags: Flags = Private | Protected | Override | Final | Infix | Local
31233122

31243123
// Keep: aligned with Quotes's `newClass`
3125-
private[QuotesImpl] def validClassFlags: Flags = Private | Protected | Final // Abstract, AbsOverride Local OPen ? PrivateLocal Protected ?
3124+
private[QuotesImpl] def validClassFlags: Flags = Private | Protected | PrivateLocal | Local | Final | Trait | Abstract // AbsOverride, Open
31263125

31273126
end Flags
31283127

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

+15-15
Original file line numberDiff line numberDiff line change
@@ -3846,10 +3846,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38463846
* @param name The name of the class
38473847
* @param parents Function returning the parent classes of the class. The first parent must not be a trait.
38483848
* Takes the constructed class symbol as an argument. Calling `cls.typeRef.asType` as part of this function will lead to cyclic reference errors.
3849-
* @param paramNames constructor parameter names.
3850-
* @param paramTypes constructor parameter types.
38513849
* @param clsFlags extra flags with which the class symbol should be constructed.
38523850
* @param clsPrivateWithin the symbol within which this new class symbol should be private. May be noSymbol.
3851+
* @param conParamNames constructor parameter names.
3852+
* @param conParamTypes constructor parameter types.
38533853
*
38543854
* Parameters can be obtained via classSymbol.memberField
38553855
*/
@@ -3858,28 +3858,28 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38583858
name: String,
38593859
parents: Symbol => List[TypeRepr],
38603860
decls: Symbol => List[Symbol], selfType: Option[TypeRepr],
3861-
paramNames: List[String],
3862-
paramTypes: List[TypeRepr],
38633861
clsFlags: Flags,
3864-
clsPrivateWithin: Symbol
3862+
clsPrivateWithin: Symbol,
3863+
conParamNames: List[String],
3864+
conParamTypes: List[TypeRepr]
38653865
): Symbol
38663866

38673867
/**
38683868
*
38693869
*
38703870
* @param owner The owner of the class
38713871
* @param name The name of the class
3872-
* @param parents Function returning the parent classes of the class. The first parent must not be a trait.
3872+
* @param parents Function returning the parent classes of the class. The first parent must not be a trait
38733873
* Takes the constructed class symbol as an argument. Calling `cls.typeRef.asType` as part of this function will lead to cyclic reference errors.
38743874
* @param decls The member declarations of the class provided the symbol of this class
38753875
* @param selfType The self type of the class if it has one
3876-
* @param constructorMethodType The MethodOrPoly type representing the type of the constructor.
3877-
* PolyType may only represent only the first clause of the constructor.
3878-
* @param clsFlags extra flags with which the class symbol should be constructed.
3876+
* @param clsFlags extra flags with which the class symbol should be constructed
38793877
* @param clsPrivateWithin the symbol within which this new class symbol should be private. May be noSymbol
3880-
* @param consFlags extra flags with which the constructor symbol should be constructed.
3881-
* @param consPrivateWithin the symbol within which the constructor for this new class symbol should be private. May be noSymbol
3882-
* @param conParamFlags extra flags with which the constructor parameter symbols should be constructed. Must match the shape of @param constructorMethodType
3878+
* @param conMethodType The MethodOrPoly type representing the type of the constructor.
3879+
* PolyType may only represent the first clause of the constructor.
3880+
* @param conFlags extra flags with which the constructor symbol should be constructed
3881+
* @param conPrivateWithin the symbol within which the constructor for this new class symbol should be private. May be noSymbol.
3882+
* @param conParamFlags extra flags with which the constructor parameter symbols should be constructed. Must match the shape of `conMethodType`.
38833883
*
38843884
*/
38853885
@experimental def newClass(
@@ -3888,11 +3888,11 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38883888
parents: Symbol => List[TypeRepr],
38893889
decls: Symbol => List[Symbol],
38903890
selfType: Option[TypeRepr],
3891-
constructorMethodType: TypeRepr => MethodOrPoly,
38923891
clsFlags: Flags,
38933892
clsPrivateWithin: Symbol,
3894-
consFlags: Flags,
3895-
consPrivateWithin: Symbol,
3893+
conMethodType: TypeRepr => MethodOrPoly,
3894+
conFlags: Flags,
3895+
conPrivateWithin: Symbol,
38963896
conParamFlags: List[List[Flags]]
38973897
): Symbol
38983898

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, Flags.EmptyFlags, Symbol.noSymbol, List("idx"), List(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])

Diff for: 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, paramNames = Nil, paramTypes = Nil, Flags.EmptyFlags, Symbol.noSymbol)
16+
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents, decls, selfType = None, Flags.EmptyFlags, Symbol.noSymbol, conParamNames = Nil, conParamTypes = Nil)
1717

1818
val parentsWithSym =
1919
cls.typeRef.asType match

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

+1-2
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, Flags.EmptyFlags, Symbol.noSymbol, List("idx", "str"), List(TypeRepr.of[Int], 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))
@@ -25,4 +25,3 @@ private def makeClassAndCallExpr(nameExpr: Expr[String], idxExpr: Expr[Int], str
2525
// new `name`(`idx`, `str`)
2626
// }
2727
}
28-

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

+1-2
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, Flags.EmptyFlags, Symbol.noSymbol, List("idx"), List(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)
@@ -27,4 +27,3 @@ private def makeClassExpr(nameExpr: Expr[String])(using Quotes): Expr[Foo] = {
2727
class Foo(i: Int) {
2828
def foo(): Unit = println(s"Calling Foo.foo with i = $i")
2929
}
30-

Diff for: tests/run-macros/newClassTraitAndAbstract.check

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class Test_2$package$anon$1
2+
{
3+
trait foo[A, B <: scala.Int](val param1: A, val param2: B) extends java.lang.Object
4+
class anon() extends java.lang.Object with foo[java.lang.String, scala.Int]("a", 1)
5+
6+
(new anon(): scala.Any)
7+
}
8+
class Test_2$package$anon$2
9+
{
10+
abstract class bar[A, B <: scala.Int](val param1: A, val param2: B) extends java.lang.Object
11+
class anon() extends bar[java.lang.String, scala.Int]("a", 1)
12+
13+
(new anon(): scala.Any)
14+
}
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//> using options -experimental
2+
3+
import scala.quoted.*
4+
5+
transparent inline def makeTrait(inline name: String): Any = ${ makeTraitExpr('name) }
6+
transparent inline def makeAbstractClass(inline name: String): Any = ${ makeAbstractClassExpr('name) }
7+
8+
private def makeTraitExpr(name: Expr[String])(using Quotes): Expr[Any] = {
9+
makeClassExpr(name, quotes.reflect.Flags.Trait, List(quotes.reflect.TypeTree.of[Object]))
10+
// '{
11+
// trait `name`[A, B <: Int](param1: A, param2: B)
12+
// class anon() extends `name`[String, Int]("a", 1)
13+
// new $anon()
14+
// }
15+
}
16+
17+
private def makeAbstractClassExpr(name: Expr[String])(using Quotes): Expr[Any] = {
18+
makeClassExpr(name, quotes.reflect.Flags.Abstract, Nil)
19+
// '{
20+
// abstract class `name`[A, B <: Int](param1: A, param2: B)
21+
// class anon() extends `name`[String, Int]("a", 1)
22+
// new $anon()
23+
// }
24+
}
25+
26+
private def makeClassExpr(using Quotes)(
27+
nameExpr: Expr[String], clsFlags: quotes.reflect.Flags, childExtraParents: List[quotes.reflect.TypeTree]
28+
): Expr[Any] = {
29+
import quotes.reflect.*
30+
31+
val name = nameExpr.valueOrAbort
32+
def decls(cls: Symbol): List[Symbol] = Nil
33+
val conMethodType =
34+
(classType: TypeRepr) => PolyType(List("A", "B"))(
35+
_ => List(TypeBounds.empty, TypeBounds.upper(TypeRepr.of[Int])),
36+
polyType => MethodType(List("param1", "param2"))((_: MethodType) => List(polyType.param(0), polyType.param(1)), (_: MethodType) => classType)
37+
)
38+
39+
val traitSymbol = Symbol.newClass(
40+
Symbol.spliceOwner,
41+
name,
42+
parents = _ => List(TypeRepr.of[Object]),
43+
decls,
44+
selfType = None,
45+
clsFlags,
46+
clsPrivateWithin = Symbol.noSymbol,
47+
conMethodType,
48+
conFlags = Flags.EmptyFlags,
49+
conPrivateWithin = Symbol.noSymbol,
50+
conParamFlags = List(List(Flags.EmptyFlags, Flags.EmptyFlags), List(Flags.EmptyFlags, Flags.EmptyFlags))
51+
)
52+
val traitDef = ClassDef(traitSymbol, List(TypeTree.of[Object]), body = Nil)
53+
54+
val traitTypeTree = Applied(TypeIdent(traitSymbol), List(TypeTree.of[String], TypeTree.of[Int]))
55+
val clsSymbol = Symbol.newClass(
56+
Symbol.spliceOwner,
57+
"anon",
58+
parents = _ => childExtraParents.map(_.tpe) ++ List(traitTypeTree.tpe),
59+
decls = _ => Nil,
60+
selfType = None,
61+
clsFlags = Flags.EmptyFlags,
62+
clsPrivateWithin = Symbol.noSymbol,
63+
conMethodType = (classType: TypeRepr) => MethodType(Nil)(_ => Nil, _ => classType),
64+
conFlags = Flags.EmptyFlags,
65+
conPrivateWithin = Symbol.noSymbol,
66+
conParamFlags = List(List())
67+
)
68+
val obj = '{new java.lang.Object()}.asTerm match
69+
case Inlined(_, _, term) => term
70+
71+
val parentsWithSym = childExtraParents ++ List(
72+
Apply(
73+
TypeApply(
74+
Select(New(traitTypeTree), traitSymbol.primaryConstructor),
75+
List(TypeTree.of[String], TypeTree.of[Int])
76+
),
77+
List(Expr("a").asTerm, Expr(1).asTerm)
78+
)
79+
)
80+
val clsDef = ClassDef(clsSymbol, parentsWithSym, body = Nil)
81+
82+
val newCls =
83+
Typed(
84+
Apply(
85+
Select(New(TypeIdent(clsSymbol)), clsSymbol.primaryConstructor),
86+
Nil
87+
),
88+
TypeTree.of[Any]
89+
)
90+
val res = Block(List(traitDef), Block(List(clsDef), newCls)).asExpr
91+
92+
Expr.ofTuple(res, Expr(res.show))
93+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//> using options -experimental
2+
3+
@main def Test: Unit = {
4+
val (cls1, show1) = makeTrait("foo")
5+
println(cls1.getClass)
6+
println(show1)
7+
8+
val (cls2, show2) = makeAbstractClass("bar")
9+
println(cls2.getClass)
10+
println(show2)
11+
}

0 commit comments

Comments
 (0)