Skip to content

Commit 0886295

Browse files
committed
Add reflect newMethodOverride and newValOverride
Closes #15035
1 parent 794818b commit 0886295

File tree

6 files changed

+107
-6
lines changed

6 files changed

+107
-6
lines changed

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2485,8 +2485,20 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
24852485
newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol)
24862486
def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
24872487
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin)
2488+
2489+
def newMethodOverride(owner: Symbol, overridden: Symbol): Symbol =
2490+
assert(isMethod(overridden), "not a method symbol: " + overridden) // TODO check that is is a member of a class
2491+
val flags = overridden.flags &~ dotc.core.Flags.Deferred | dotc.core.Flags.Override
2492+
dotc.core.Symbols.newSymbol(owner, overridden.name, flags, overridden.info, dotc.core.Symbols.NoSymbol)
2493+
24882494
def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
24892495
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin)
2496+
2497+
def newValOverride(owner: Symbol, overridden: Symbol): Symbol =
2498+
assert(isField(overridden) || isMethod(overridden), "not a method or field symbol: " + overridden) // TODO check that is is a member of a class
2499+
val flags = overridden.flags &~ dotc.core.Flags.Deferred &~ dotc.core.Flags.Method | dotc.core.Flags.Override
2500+
dotc.core.Symbols.newSymbol(owner, overridden.name, flags, overridden.info, dotc.core.Symbols.NoSymbol)
2501+
24902502
def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol =
24912503
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | Case, tpe)
24922504
def noSymbol: Symbol = dotc.core.Symbols.NoSymbol
@@ -2661,13 +2673,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26612673
private def appliedTypeRef(sym: Symbol): TypeRepr =
26622674
sym.typeRef.appliedTo(sym.typeParams.map(_.typeRef))
26632675

2664-
private def isMethod(sym: Symbol): Boolean =
2665-
sym.isTerm && sym.is(dotc.core.Flags.Method) && !sym.isConstructor
2666-
2667-
private def isField(sym: Symbol): Boolean =
2668-
sym.isTerm && !sym.is(dotc.core.Flags.Method)
26692676
end SymbolMethods
26702677

2678+
private def isMethod(sym: Symbol): Boolean =
2679+
sym.isTerm && sym.is(dotc.core.Flags.Method) && !sym.isConstructor
2680+
2681+
private def isField(sym: Symbol): Boolean =
2682+
sym.isTerm && !sym.is(dotc.core.Flags.Method)
2683+
26712684
type Signature = dotc.core.Signature
26722685

26732686
object Signature extends SignatureModule:

library/src/scala/quoted/Quotes.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3669,7 +3669,16 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
36693669
*/
36703670
def newMethod(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol
36713671

3672-
/** Generates a new val/var/lazy val symbol with the given parent, name and type.
3672+
/** Generates a new method symbol that overrides another definition.
3673+
* The signature will be the same as the overridden symbol.
3674+
*
3675+
* @param parent The owner of the method
3676+
* @param overridden The symbol being overridden
3677+
*/
3678+
@experimental
3679+
def newMethodOverride(parent: Symbol, overridden: Symbol): Symbol
3680+
3681+
/** Generates a new `val`/`var`/`lazy val` symbol with the given parent, name and type.
36733682
*
36743683
* This symbol starts without an accompanying definition.
36753684
* It is the meta-programmer's responsibility to provide exactly one corresponding definition by passing
@@ -3687,6 +3696,15 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
36873696
*/
36883697
def newVal(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol
36893698

3699+
/** Generates a new `val` or `lazy val` symbol that overrides another definition.
3700+
* The signature will be the same as the overridden symbol.
3701+
*
3702+
* @param parent The owner of the method
3703+
* @param overridden The symbol being overridden
3704+
*/
3705+
@experimental
3706+
def newValOverride(parent: Symbol, overridden: Symbol): Symbol
3707+
36903708
/** Generates a pattern bind symbol with the given parent, name and type.
36913709
*
36923710
* This symbol starts without an accompanying definition.

tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ val experimentalDefinitionInLibrary = Set(
6666
// Can be stabilized in 3.3.0 (unsure) or later
6767
"scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings",
6868
"scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation",
69+
"scala.quoted.Quotes.reflectModule.SymbolModule.newValOverride",
70+
"scala.quoted.Quotes.reflectModule.SymbolModule.newMethodgitOverride",
71+
6972
// Cant be stabilized yet.
7073
// Need newClass variant that can add constructor parameters.
7174
// Need experimental annotation macros to check that design works.

tests/run-macros/i15035.check

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
1
2+
1
3+
1
4+
1
5+
1
6+
3
7+
3
8+
3
9+
3
10+
3

tests/run-macros/i15035/Macro_1.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import scala.quoted.*
2+
3+
inline def make[T](inline value: Int): T = ${impl[T]('value)}
4+
5+
def impl[T: Type](value: Expr[Int])(using Quotes): Expr[T] = {
6+
import quotes.reflect.*
7+
8+
val className = "Foo"
9+
val parents = List(TypeTree.of[Object], TypeTree.of[T])
10+
11+
val parentMethods = TypeRepr.of[T].typeSymbol.declaredMethods
12+
13+
def decls(cls: Symbol): List[Symbol] =
14+
TypeRepr.of[T].typeSymbol.declaredFields.map(field => Symbol.newValOverride(cls, field)) ++
15+
TypeRepr.of[T].typeSymbol.declaredMethods.map(method => Symbol.newMethodOverride(cls, method))
16+
17+
val cls = Symbol.newClass(Symbol.spliceOwner, className, parents = parents.map(_.tpe), decls, selfType = None)
18+
val body =
19+
cls.declaredFields.map { method => ValDef(method, Some(value.asTerm)) } ++
20+
cls.declaredMethods.map { method => DefDef(method, argss => Some(value.asTerm)) }
21+
val clsDef = ClassDef(cls, parents, body = body)
22+
val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[T])
23+
Block(List(clsDef), newCls).asExprOf[T]
24+
}

tests/run-macros/i15035/Test_2.scala

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
trait A {
2+
val v1: Int
3+
lazy val v2: Int
4+
def f1(): Int
5+
def f2(foo: String): Int
6+
def f3: Int
7+
}
8+
9+
trait B {
10+
val v1: Int = 2
11+
lazy val v2: Int = 2
12+
def f1(): Int = 2
13+
def f2(foo: String): Int = 2
14+
def f3: Int = 2
15+
}
16+
17+
18+
@main
19+
def Test = {
20+
val a: A = make[A](1)
21+
println(a.v1)
22+
println(a.v2)
23+
println(a.f1())
24+
println(a.f2("test"))
25+
println(a.f3)
26+
27+
val b: B = make[B](3)
28+
println(b.v1)
29+
println(b.v2)
30+
println(b.f1())
31+
println(b.f2("test"))
32+
println(b.f3)
33+
}

0 commit comments

Comments
 (0)