diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index c27a470b3778..73d0b78d30ee 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2485,8 +2485,20 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol) def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol = dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin) + + def newMethodOverride(owner: Symbol, overridden: Symbol): Symbol = + assert(isMethod(overridden), "not a method symbol: " + overridden) // TODO check that is is a member of a class + val flags = overridden.flags &~ dotc.core.Flags.Deferred | dotc.core.Flags.Override + dotc.core.Symbols.newSymbol(owner, overridden.name, flags, overridden.info, dotc.core.Symbols.NoSymbol) + def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol = dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin) + + def newValOverride(owner: Symbol, overridden: Symbol): Symbol = + assert(isField(overridden) || isMethod(overridden), "not a method or field symbol: " + overridden) // TODO check that is is a member of a class + val flags = overridden.flags &~ dotc.core.Flags.Deferred &~ dotc.core.Flags.Method | dotc.core.Flags.Override + dotc.core.Symbols.newSymbol(owner, overridden.name, flags, overridden.info, dotc.core.Symbols.NoSymbol) + def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol = dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | Case, tpe) def noSymbol: Symbol = dotc.core.Symbols.NoSymbol @@ -2663,13 +2675,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler private def appliedTypeRef(sym: Symbol): TypeRepr = sym.typeRef.appliedTo(sym.typeParams.map(_.typeRef)) - private def isMethod(sym: Symbol): Boolean = - sym.isTerm && sym.is(dotc.core.Flags.Method) && !sym.isConstructor - - private def isField(sym: Symbol): Boolean = - sym.isTerm && !sym.is(dotc.core.Flags.Method) end SymbolMethods + private def isMethod(sym: Symbol): Boolean = + sym.isTerm && sym.is(dotc.core.Flags.Method) && !sym.isConstructor + + private def isField(sym: Symbol): Boolean = + sym.isTerm && !sym.is(dotc.core.Flags.Method) + type Signature = dotc.core.Signature object Signature extends SignatureModule: diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 2f4439d44a83..2c30ba394b78 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -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. + * 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. diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index e46d73c70b2b..01875d6432f3 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -66,6 +66,9 @@ val experimentalDefinitionInLibrary = Set( // Can be stabilized in 3.3.0 (unsure) or later "scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings", "scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation", + "scala.quoted.Quotes.reflectModule.SymbolModule.newValOverride", + "scala.quoted.Quotes.reflectModule.SymbolModule.newMethodOverride", + // Cant be stabilized yet. // Need newClass variant that can add constructor parameters. // Need experimental annotation macros to check that design works. diff --git a/tests/run-macros/i15035.check b/tests/run-macros/i15035.check new file mode 100644 index 000000000000..035910b7ac2d --- /dev/null +++ b/tests/run-macros/i15035.check @@ -0,0 +1,10 @@ +1 +1 +1 +1 +1 +3 +3 +3 +3 +3 diff --git a/tests/run-macros/i15035/Macro_1.scala b/tests/run-macros/i15035/Macro_1.scala new file mode 100644 index 000000000000..1793ffb60c00 --- /dev/null +++ b/tests/run-macros/i15035/Macro_1.scala @@ -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] +} diff --git a/tests/run-macros/i15035/Test_2.scala b/tests/run-macros/i15035/Test_2.scala new file mode 100644 index 000000000000..7d04025dacb6 --- /dev/null +++ b/tests/run-macros/i15035/Test_2.scala @@ -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) +}