diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index ab763fbf47e5..1c8f190a70d4 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2613,11 +2613,19 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol = checkValidFlags(flags.toTermFlags, Flags.validBindFlags) dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Case, tpe) - + def newType(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr, privateWithin: Symbol): Symbol = checkValidFlags(flags.toTypeFlags, Flags.validTypeFlags) dotc.core.Symbols.newSymbol(owner, name.toTypeName, flags | dotc.core.Flags.Deferred, tpe, privateWithin) - + + def newTypeAlias(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr, privateWithin: Symbol): Symbol = + checkValidFlags(flags.toTypeFlags, Flags.validTypeFlags) + dotc.core.Symbols.newSymbol(owner, name.toTypeName, flags | dotc.core.Flags.Deferred, dotc.core.Types.TypeAlias(tpe), privateWithin) + + def newBoundedType(owner: Symbol, name: String, flags: Flags, tpe: TypeBounds, privateWithin: Symbol): Symbol = + checkValidFlags(flags.toTypeFlags, Flags.validTypeFlags) + dotc.core.Symbols.newSymbol(owner, name.toTypeName, flags | dotc.core.Flags.Deferred, tpe, privateWithin) + def noSymbol: Symbol = dotc.core.Symbols.NoSymbol private inline def checkValidFlags(inline flags: Flags, inline valid: Flags): Unit = @@ -2959,6 +2967,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler // Keep: aligned with Quotes's `newBind` doc private[QuotesImpl] def validBindFlags: Flags = Case // Flags that could be allowed: Implicit | Given | Erased + // Keep: aligned with Quotes's 'newType' doc private[QuotesImpl] def validTypeFlags: Flags = Private | Protected | Override | Deferred | Final | Infix | Local end Flags diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index c35569e8e2c0..63f1f503ee41 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3915,8 +3915,26 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => // Keep: `flags` doc aligned with QuotesImpl's `validBindFlags` def newBind(parent: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol - /** Generate a new type symbol with the given parent, name and type - * + /** Generate a new type symbol for a type alias 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 + * this symbol to the TypeDef constructor. + * + * @param parent The owner of the type + * @param name The name of the type + * @param flags extra flags to with which symbol can be constructed. `Deferred` flag will be added. Can be `Private` | `Protected` | `Override` | `Deferred` | `Final` | `Infix` | `Local` + * @param tpe The rhs the type alias + * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. + * @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be + * direct or indirect children of the reflection context's owner. + */ + @experimental + // Keep: `flags` doc aligned with QuotesImpl's `validTypeFlags` + def newTypeAlias(parent: Symbol, name: String, flags: Flags, tpe: TypeRepr, privateWithin: Symbol): Symbol + + /** Generate a new type symbol for a type bounds 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 * this symbol to the TypeDef constructor. @@ -3924,14 +3942,14 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @param parent The owner of the type * @param name The name of the type * @param flags extra flags to with which symbol can be constructed. `Deferred` flag will be added. Can be `Private` | `Protected` | `Override` | `Deferred` | `Final` | `Infix` | `Local` - * @param tpe The rhs or bounds of the type + * @param tpe The bounds of the type * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. * @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be * direct or indirect children of the reflection context's owner. */ @experimental // Keep: `flags` doc aligned with QuotesImpl's `validTypeFlags` - def newType(parent: Symbol, name: String, flags: Flags, tpe: TypeRepr, privateWithin: Symbol): Symbol + def newBoundedType(parent: Symbol, name: String, flags: Flags, tpe: TypeBounds, privateWithin: Symbol): Symbol /** Definition not available */ def noSymbol: Symbol diff --git a/tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala b/tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala new file mode 100644 index 000000000000..5fcdbb9f1cfc --- /dev/null +++ b/tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala @@ -0,0 +1,21 @@ +import scala.quoted.* + +inline def testMacro = ${ testImpl } + +transparent inline def transparentTestMacro = ${ testImpl } + +def testImpl(using Quotes): Expr[Object] = { + import quotes.reflect.* + + def makeType(owner: Symbol): Symbol = + Symbol.newBoundedType(owner, "mytype", Flags.EmptyFlags, TypeBounds.lower(TypeRepr.of[String]), Symbol.noSymbol) + + val typeDef = TypeDef(makeType(Symbol.spliceOwner)) + // Expr printer does not work here, see comment: + // https://github.com/scala/scala3/pull/20347#issuecomment-2096824617 + assert(typeDef.toString == "TypeDef(mytype,TypeTree[TypeBounds(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class java)),object lang),String),TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Any))])") + + val clsSymbol = Symbol.newClass(Symbol.spliceOwner, "CLS", List(TypeRepr.of[Object]), sym => List(makeType(sym)), None) + val classDef: ClassDef = ClassDef(clsSymbol, List(TypeTree.of[Object]), List(TypeDef(clsSymbol.typeMember("mytype")))) + Block(List(classDef), Apply(Select(New(TypeIdent(clsSymbol)), clsSymbol.primaryConstructor), List.empty)).asExprOf[Object] +} diff --git a/tests/pos-macros/quote-sym-newboundedtype/Test_2.scala b/tests/pos-macros/quote-sym-newboundedtype/Test_2.scala new file mode 100644 index 000000000000..775cef4569cf --- /dev/null +++ b/tests/pos-macros/quote-sym-newboundedtype/Test_2.scala @@ -0,0 +1,3 @@ +def test = + testMacro + transparentTestMacro diff --git a/tests/pos-macros/quote-sym-newtype-in-trait/Macro_1.scala b/tests/pos-macros/quote-sym-newtype-in-trait/Macro_1.scala new file mode 100644 index 000000000000..21de39d9821e --- /dev/null +++ b/tests/pos-macros/quote-sym-newtype-in-trait/Macro_1.scala @@ -0,0 +1,17 @@ +import scala.quoted.* + +inline def testMacro = ${ testImpl } + +transparent inline def transparentTestMacro = ${ testImpl } + +def testImpl(using Quotes): Expr[Object] = { + import quotes.reflect.* + + def makeType(owner: Symbol): Symbol = + Symbol.newTypeAlias(owner, "mytype", Flags.EmptyFlags, TypeRepr.of[String], Symbol.noSymbol) + + val clsSymbol = Symbol.newClass(Symbol.spliceOwner, "CLS", List(TypeRepr.of[Object]), sym => List(makeType(sym)), None) + val classDef: ClassDef = ClassDef(clsSymbol, List(TypeTree.of[Object]), List(TypeDef(clsSymbol.typeMember("mytype")))) + + Block(List(classDef), Apply(Select(New(TypeIdent(clsSymbol)), clsSymbol.primaryConstructor), List.empty)).asExprOf[Object] +} diff --git a/tests/pos-macros/quote-sym-newtype-in-trait/Test_2.scala b/tests/pos-macros/quote-sym-newtype-in-trait/Test_2.scala new file mode 100644 index 000000000000..775cef4569cf --- /dev/null +++ b/tests/pos-macros/quote-sym-newtype-in-trait/Test_2.scala @@ -0,0 +1,3 @@ +def test = + testMacro + transparentTestMacro diff --git a/tests/pos-macros/quoted-sym-newtype/Macro_1.scala b/tests/pos-macros/quoted-sym-newtype/Macro_1.scala index bb41c82553d6..308884153042 100644 --- a/tests/pos-macros/quoted-sym-newtype/Macro_1.scala +++ b/tests/pos-macros/quoted-sym-newtype/Macro_1.scala @@ -1,10 +1,12 @@ import scala.quoted.* - + inline def testMacro = ${ testImpl } -def testImpl(using Quotes): Expr[Unit] = { +def testImpl(using Quotes): Expr[Unit] = { import quotes.reflect.* - val sym = Symbol.newType(Symbol.spliceOwner, "mytype", Flags.EmptyFlags, TypeRepr.of[String], Symbol.noSymbol) - assert(TypeDef(sym).show == "type mytype = java.lang.String") - '{ () } + val sym = Symbol.newTypeAlias(Symbol.spliceOwner, "mytype", Flags.EmptyFlags, TypeRepr.of[String], Symbol.noSymbol) + val typeDef = TypeDef(sym) + assert(typeDef.show == "type mytype = java.lang.String") + + Block(List(typeDef), '{()}).asExprOf[Unit] } \ No newline at end of file