-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add reflect newMethodOverride and newValOverride #15041
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3669,7 +3669,16 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => | |
*/ | ||
def newMethod(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol | ||
|
||
/** Generates a new val/var/lazy val symbol with the given parent, name and type. | ||
/** Generates a new method symbol that overrides another definition. | ||
* The signature will be the same as the overridden symbol. | ||
* | ||
* @param parent The owner of the method | ||
* @param overridden The symbol being overridden | ||
*/ | ||
@experimental | ||
def newMethodOverride(parent: Symbol, overridden: Symbol): Symbol | ||
|
||
/** Generates a new `val`/`var`/`lazy val` symbol with the given parent, name and type. | ||
* | ||
* This symbol starts without an accompanying definition. | ||
* 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 => | |
*/ | ||
def newVal(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol | ||
|
||
/** Generates a new `val` or `lazy val` symbol that overrides another definition. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it also work with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. This design does not compose well. It might be better to have an API to compute the methodic type of the override signature and use the other |
||
* The signature will be the same as the overridden symbol. | ||
* | ||
* @param parent The owner of the method | ||
* @param overridden The symbol being overridden | ||
*/ | ||
@experimental | ||
def newValOverride(parent: Symbol, overridden: Symbol): Symbol | ||
|
||
/** Generates a pattern bind symbol with the given parent, name and type. | ||
* | ||
* This symbol starts without an accompanying definition. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
1 | ||
1 | ||
1 | ||
1 | ||
1 | ||
3 | ||
3 | ||
3 | ||
3 | ||
3 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import scala.quoted.* | ||
|
||
inline def make[T](inline value: Int): T = ${impl[T]('value)} | ||
|
||
def impl[T: Type](value: Expr[Int])(using Quotes): Expr[T] = { | ||
import quotes.reflect.* | ||
|
||
val className = "Foo" | ||
val parents = List(TypeTree.of[Object], TypeTree.of[T]) | ||
|
||
val parentMethods = TypeRepr.of[T].typeSymbol.declaredMethods | ||
|
||
def decls(cls: Symbol): List[Symbol] = | ||
TypeRepr.of[T].typeSymbol.declaredFields.map(field => Symbol.newValOverride(cls, field)) ++ | ||
TypeRepr.of[T].typeSymbol.declaredMethods.map(method => Symbol.newMethodOverride(cls, method)) | ||
|
||
val cls = Symbol.newClass(Symbol.spliceOwner, className, parents = parents.map(_.tpe), decls, selfType = None) | ||
val body = | ||
cls.declaredFields.map { method => ValDef(method, Some(value.asTerm)) } ++ | ||
cls.declaredMethods.map { method => DefDef(method, argss => Some(value.asTerm)) } | ||
val clsDef = ClassDef(cls, parents, body = body) | ||
val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[T]) | ||
Block(List(clsDef), newCls).asExprOf[T] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
trait A { | ||
val v1: Int | ||
lazy val v2: Int | ||
def f1(): Int | ||
def f2(foo: String): Int | ||
def f3: Int | ||
} | ||
|
||
trait B { | ||
val v1: Int = 2 | ||
lazy val v2: Int = 2 | ||
def f1(): Int = 2 | ||
def f2(foo: String): Int = 2 | ||
def f3: Int = 2 | ||
} | ||
|
||
|
||
@main | ||
def Test = { | ||
val a: A = make[A](1) | ||
println(a.v1) | ||
println(a.v2) | ||
println(a.f1()) | ||
println(a.f2("test")) | ||
println(a.f3) | ||
|
||
val b: B = make[B](3) | ||
println(b.v1) | ||
println(b.v2) | ||
println(b.f1()) | ||
println(b.f2("test")) | ||
println(b.f3) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This API isn't flexible enough to handle situations where you need to override two methods at once. For example:
In C, if I try to override only
A#foo
or onlyB#foo
, I will get a type error, I'm forced to override both at once.