From 61764dd0f349911cb9e9a7a67f6d95dc1a260477 Mon Sep 17 00:00:00 2001 From: Hamza REMMAL Date: Mon, 6 May 2024 20:58:14 +0200 Subject: [PATCH 1/4] Add the possibility to create a typeSymbol in the Quotes API --- .../scala/quoted/runtime/impl/QuotesImpl.scala | 8 ++++++++ library/src/scala/quoted/Quotes.scala | 18 ++++++++++++++++++ .../quoted-sym-newtype/Macro_1.scala | 10 ++++++++++ .../pos-macros/quoted-sym-newtype/Test_2.scala | 2 ++ 4 files changed, 38 insertions(+) create mode 100644 tests/pos-macros/quoted-sym-newtype/Macro_1.scala create mode 100644 tests/pos-macros/quoted-sym-newtype/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index ce8d19aae46a..a81539fba2e8 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2649,6 +2649,11 @@ 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 noSymbol: Symbol = dotc.core.Symbols.NoSymbol private inline def checkValidFlags(inline flags: Flags, inline valid: Flags): Unit = @@ -2989,6 +2994,9 @@ 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 + + private[QuotesImpl] def validTypeFlags: Flags = Private | Protected | Override | Deferred | Final | Infix | Local + end Flags given FlagsMethods: FlagsMethods with diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index fad769793bb7..3b762b73ba00 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3963,6 +3963,24 @@ 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 + * + * 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 or 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 + /** Definition not available */ def noSymbol: Symbol diff --git a/tests/pos-macros/quoted-sym-newtype/Macro_1.scala b/tests/pos-macros/quoted-sym-newtype/Macro_1.scala new file mode 100644 index 000000000000..bb41c82553d6 --- /dev/null +++ b/tests/pos-macros/quoted-sym-newtype/Macro_1.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +inline def testMacro = ${ testImpl } + +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") + '{ () } +} \ No newline at end of file diff --git a/tests/pos-macros/quoted-sym-newtype/Test_2.scala b/tests/pos-macros/quoted-sym-newtype/Test_2.scala new file mode 100644 index 000000000000..506f836ab4a5 --- /dev/null +++ b/tests/pos-macros/quoted-sym-newtype/Test_2.scala @@ -0,0 +1,2 @@ + +def test = testMacro \ No newline at end of file From 56893de676677292a09925b3fdece60b985caa42 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Wed, 21 Aug 2024 10:15:58 +0200 Subject: [PATCH 2/4] Split Symbol.newType into 2 methods and add tests --- .../quoted/runtime/impl/QuotesImpl.scala | 11 +++++--- library/src/scala/quoted/Quotes.scala | 26 ++++++++++++++++--- .../quote-sym-newboundedtype/Macro_1.scala | 22 ++++++++++++++++ .../quote-sym-newboundedtype/Test_2.scala | 4 +++ .../quote-sym-newtype-in-trait/Macro_1.scala | 18 +++++++++++++ .../quote-sym-newtype-in-trait/Test_2.scala | 4 +++ .../quote-sym-newtype/Macro_1.scala | 13 ++++++++++ .../pos-macros/quote-sym-newtype/Test_2.scala | 2 ++ .../quoted-sym-newtype/Macro_1.scala | 10 ------- .../quoted-sym-newtype/Test_2.scala | 2 -- .../stdlibExperimentalDefinitions.scala | 3 +++ 11 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala create mode 100644 tests/pos-macros/quote-sym-newboundedtype/Test_2.scala create mode 100644 tests/pos-macros/quote-sym-newtype-in-trait/Macro_1.scala create mode 100644 tests/pos-macros/quote-sym-newtype-in-trait/Test_2.scala create mode 100644 tests/pos-macros/quote-sym-newtype/Macro_1.scala create mode 100644 tests/pos-macros/quote-sym-newtype/Test_2.scala delete mode 100644 tests/pos-macros/quoted-sym-newtype/Macro_1.scala delete mode 100644 tests/pos-macros/quoted-sym-newtype/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index a81539fba2e8..ba6a6bb56253 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2649,11 +2649,15 @@ 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 = + + 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 = @@ -2995,6 +2999,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 3b762b73ba00..99cf04ec423c 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3963,8 +3963,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. @@ -3972,14 +3990,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..b38a4304b9d2 --- /dev/null +++ b/tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala @@ -0,0 +1,22 @@ +//> using options -experimental -Yno-experimental +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..2d479a09695a --- /dev/null +++ b/tests/pos-macros/quote-sym-newboundedtype/Test_2.scala @@ -0,0 +1,4 @@ +//> using options -experimental -Yno-experimental +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..1d07c5080e26 --- /dev/null +++ b/tests/pos-macros/quote-sym-newtype-in-trait/Macro_1.scala @@ -0,0 +1,18 @@ +//> using options -experimental -Yno-experimental +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..2d479a09695a --- /dev/null +++ b/tests/pos-macros/quote-sym-newtype-in-trait/Test_2.scala @@ -0,0 +1,4 @@ +//> using options -experimental -Yno-experimental +def test = + testMacro + transparentTestMacro diff --git a/tests/pos-macros/quote-sym-newtype/Macro_1.scala b/tests/pos-macros/quote-sym-newtype/Macro_1.scala new file mode 100644 index 000000000000..9973ba1e047e --- /dev/null +++ b/tests/pos-macros/quote-sym-newtype/Macro_1.scala @@ -0,0 +1,13 @@ +//> using options -experimental -Yno-experimental +import scala.quoted.* + +inline def testMacro = ${ testImpl } + +def testImpl(using Quotes): Expr[Unit] = { + import quotes.reflect.* + 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), '{()}.asTerm).asExprOf[Unit] +} diff --git a/tests/pos-macros/quote-sym-newtype/Test_2.scala b/tests/pos-macros/quote-sym-newtype/Test_2.scala new file mode 100644 index 000000000000..5a272acbdda4 --- /dev/null +++ b/tests/pos-macros/quote-sym-newtype/Test_2.scala @@ -0,0 +1,2 @@ +//> using options -experimental -Yno-experimental +def test = testMacro diff --git a/tests/pos-macros/quoted-sym-newtype/Macro_1.scala b/tests/pos-macros/quoted-sym-newtype/Macro_1.scala deleted file mode 100644 index bb41c82553d6..000000000000 --- a/tests/pos-macros/quoted-sym-newtype/Macro_1.scala +++ /dev/null @@ -1,10 +0,0 @@ -import scala.quoted.* - -inline def testMacro = ${ testImpl } - -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") - '{ () } -} \ No newline at end of file diff --git a/tests/pos-macros/quoted-sym-newtype/Test_2.scala b/tests/pos-macros/quoted-sym-newtype/Test_2.scala deleted file mode 100644 index 506f836ab4a5..000000000000 --- a/tests/pos-macros/quoted-sym-newtype/Test_2.scala +++ /dev/null @@ -1,2 +0,0 @@ - -def test = testMacro \ No newline at end of file diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 15ccd38f860c..bbabc376b07b 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -63,6 +63,9 @@ val experimentalDefinitionInLibrary = Set( "scala.quoted.Quotes.reflectModule.SymbolModule.newModule", "scala.quoted.Quotes.reflectModule.SymbolModule.freshName", "scala.quoted.Quotes.reflectModule.SymbolMethods.info", + // Added for 3.6.0, stabilize after feedback. + "scala.quoted.Quotes.reflectModule.SymbolModule.newBoundedType", + "scala.quoted.Quotes.reflectModule.SymbolModule.newTypeAlias", // New feature: functions with erased parameters. // Need erasedDefinitions enabled. From 9db0a00023dac264edf3f17fc9bef6aa048f06c2 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 3 Oct 2024 13:20:22 +0200 Subject: [PATCH 3/4] Address review comments --- .../src/scala/quoted/runtime/impl/QuotesImpl.scala | 14 +++++++++----- library/src/scala/quoted/Quotes.scala | 10 +++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index ba6a6bb56253..901e0038efd5 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2651,12 +2651,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Case, tpe) def newTypeAlias(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr, privateWithin: Symbol): Symbol = - checkValidFlags(flags.toTypeFlags, Flags.validTypeFlags) + checkValidFlags(flags.toTypeFlags, Flags.validTypeAliasFlags) + assert(!tpe.isInstanceOf[Types.TypeBounds], "Passed `tpe` into newTypeAlias should not represent TypeBounds") 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) + checkValidFlags(flags.toTypeFlags, Flags.validBoundedTypeFlags) + dotc.core.Symbols.newSymbol(owner, name.toTypeName, flags, tpe, privateWithin) def noSymbol: Symbol = dotc.core.Symbols.NoSymbol @@ -2999,8 +3000,11 @@ 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 + // Keep: aligned with Quotes's 'newBoundedType' doc + private[QuotesImpl] def validBoundedTypeFlags: Flags = Private | Protected | Override | Deferred | Final | Infix | Local + + // Keep: aligned with Quotes's `newTypeAlias` doc + private[QuotesImpl] def validTypeAliasFlags: Flags = Private | Protected | Override | Final | Infix | Local end Flags diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 99cf04ec423c..7a98d6f6f761 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3971,14 +3971,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 flags extra flags to with which symbol can be constructed. Can be `Private` | `Protected` | `Override` | `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. + * @param privateWithin the symbol within which this new type 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` + // Keep: `flags` doc aligned with QuotesImpl's `validTypeAliasFlags` 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 @@ -3991,12 +3991,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @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 bounds of the type - * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. + * @param privateWithin the symbol within which this new type 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` + // Keep: `flags` doc aligned with QuotesImpl's `validBoundedTypeFlags` def newBoundedType(parent: Symbol, name: String, flags: Flags, tpe: TypeBounds, privateWithin: Symbol): Symbol /** Definition not available */ From 5d06f9676517f18d7827316e58fc4b78dcab538c Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Tue, 8 Oct 2024 01:29:29 +0200 Subject: [PATCH 4/4] Address some review comments --- .../quoted/runtime/impl/QuotesImpl.scala | 4 +- .../quote-sym-newtype/Macro_1.scala | 47 +++++++++++++++++++ .../neg-macros/quote-sym-newtype/Test_2.scala | 6 +++ .../quote-sym-newboundedtype/Macro_1.scala | 39 ++++++++++++--- .../quote-sym-newtype-in-trait/Macro_1.scala | 25 ++++++++-- 5 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 tests/neg-macros/quote-sym-newtype/Macro_1.scala create mode 100644 tests/neg-macros/quote-sym-newtype/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 901e0038efd5..e8524a193e5a 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2653,11 +2653,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def newTypeAlias(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr, privateWithin: Symbol): Symbol = checkValidFlags(flags.toTypeFlags, Flags.validTypeAliasFlags) assert(!tpe.isInstanceOf[Types.TypeBounds], "Passed `tpe` into newTypeAlias should not represent TypeBounds") - dotc.core.Symbols.newSymbol(owner, name.toTypeName, flags | dotc.core.Flags.Deferred, dotc.core.Types.TypeAlias(tpe), privateWithin) + dotc.core.Symbols.newSymbol(owner, name.toTypeName, flags, dotc.core.Types.TypeAlias(tpe), privateWithin) def newBoundedType(owner: Symbol, name: String, flags: Flags, tpe: TypeBounds, privateWithin: Symbol): Symbol = checkValidFlags(flags.toTypeFlags, Flags.validBoundedTypeFlags) - dotc.core.Symbols.newSymbol(owner, name.toTypeName, flags, tpe, privateWithin) + dotc.core.Symbols.newSymbol(owner, name.toTypeName, flags | dotc.core.Flags.Deferred, tpe, privateWithin) def noSymbol: Symbol = dotc.core.Symbols.NoSymbol diff --git a/tests/neg-macros/quote-sym-newtype/Macro_1.scala b/tests/neg-macros/quote-sym-newtype/Macro_1.scala new file mode 100644 index 000000000000..953be0d5497b --- /dev/null +++ b/tests/neg-macros/quote-sym-newtype/Macro_1.scala @@ -0,0 +1,47 @@ +//> using options -experimental -Yno-experimental +import scala.quoted.* + +inline def testConflictingBounds = ${ testConflictingBoundsImpl } +inline def testConflictingBoundsWithTypeLambda = ${ testConflictingBoundsWithTypeLambdaImpl } + +transparent inline def transparentTestConflictingBounds = ${ testConflictingBoundsImpl } +transparent inline def transparentTestConflictingBoundsWithTypeLambda = ${ testConflictingBoundsWithTypeLambdaImpl } + + +def testConflictingBoundsImpl(using Quotes): Expr[Object] = { + import quotes.reflect.* + + def makeType(owner: Symbol): Symbol = + // type Foo >: Int <: String + Symbol.newBoundedType( + owner, + "Foo", + Flags.EmptyFlags, + TypeBounds(TypeRepr.of[Int], TypeRepr.of[String]), + Symbol.noSymbol + ) + makeClass(makeType) +} + +def testConflictingBoundsWithTypeLambdaImpl(using Quotes): Expr[Object] = { + import quotes.reflect.* + def makeType(owner: Symbol): Symbol = + // type Foo >: [X] =>> Int <: Any + Symbol.newBoundedType( + owner, + "Foo", + Flags.EmptyFlags, + TypeBounds(TypeLambda.apply(List("X"), _ => List(TypeBounds.empty), _ => TypeRepr.of[Int]), TypeRepr.of[Any]), + Symbol.noSymbol + ) + makeClass(makeType) +} + +def makeClass(using quotes: Quotes)(typeCons: quotes.reflect.Symbol => quotes.reflect.Symbol) = { + import quotes.reflect.* + val clsSymbol = Symbol.newClass(Symbol.spliceOwner, "CLS", List(TypeRepr.of[Object]), sym => List(typeCons(sym)), None) + val classDef: ClassDef = ClassDef(clsSymbol, List(TypeTree.of[Object]), List(TypeDef(clsSymbol.typeMember("Foo")))) + + Block(List(classDef), Apply(Select(New(TypeIdent(clsSymbol)), clsSymbol.primaryConstructor), List.empty)).asExprOf[Object] +} + diff --git a/tests/neg-macros/quote-sym-newtype/Test_2.scala b/tests/neg-macros/quote-sym-newtype/Test_2.scala new file mode 100644 index 000000000000..60fef3cb7322 --- /dev/null +++ b/tests/neg-macros/quote-sym-newtype/Test_2.scala @@ -0,0 +1,6 @@ +//> using options -experimental -Yno-experimental +def test = + transparentTestConflictingBounds // error + transparentTestConflictingBoundsWithTypeLambda // error + // testConflictingBounds // should throw an error here also, to be implemented before stabilisation + // testConflictingBoundsWithTypeLambda // should throw an error here also, to be implemented before stabilisation diff --git a/tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala b/tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala index b38a4304b9d2..97b7d7566e9a 100644 --- a/tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala +++ b/tests/pos-macros/quote-sym-newboundedtype/Macro_1.scala @@ -8,15 +8,42 @@ 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) + def makeBasicType(owner: Symbol): Symbol = + Symbol.newBoundedType(owner, "tpe", Flags.EmptyFlags, TypeBounds.lower(TypeRepr.of[String]), Symbol.noSymbol) - val typeDef = TypeDef(makeType(Symbol.spliceOwner)) + def makeTypesForClass(owner: Symbol): List[Symbol] = + val typeLambda = TypeLambda.apply(List("X"), _ => List(TypeBounds.empty), _ => TypeRepr.of[Int]) + List( + makeBasicType(owner), + // type Bla >: Nothing <: [X] =>> Int + Symbol.newBoundedType( + owner, + "tpe1", + Flags.EmptyFlags, + TypeBounds.upper(typeLambda), + Symbol.noSymbol + ), + // type Bar >: [X] =>> Int <: [X] =>> Int + Symbol.newBoundedType( + owner, + "tpe2", + Flags.EmptyFlags, + TypeBounds(typeLambda, typeLambda), + Symbol.noSymbol + ) + ) + + val typeDef = TypeDef(makeBasicType(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))])") + println(typeDef.toString) + assert(typeDef.toString == "TypeDef(tpe,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")))) + val clsSymbol = Symbol.newClass(Symbol.spliceOwner, "CLS", List(TypeRepr.of[Object]), sym => makeTypesForClass(sym), None) + val classDef: ClassDef = ClassDef(clsSymbol, List(TypeTree.of[Object]), List( + TypeDef(clsSymbol.typeMember("tpe")), + TypeDef(clsSymbol.typeMember("tpe1")), + TypeDef(clsSymbol.typeMember("tpe2")), + )) 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/Macro_1.scala b/tests/pos-macros/quote-sym-newtype-in-trait/Macro_1.scala index 1d07c5080e26..60f0587b85a7 100644 --- a/tests/pos-macros/quote-sym-newtype-in-trait/Macro_1.scala +++ b/tests/pos-macros/quote-sym-newtype-in-trait/Macro_1.scala @@ -8,11 +8,28 @@ 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) + def makeBasicType(owner: Symbol): Symbol = + Symbol.newTypeAlias(owner, "tpe", 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")))) + def makeTypesForClass(owner: Symbol): List[Symbol] = + val typeLambda = TypeLambda.apply(List("X"), _ => List(TypeBounds.empty), _ => TypeRepr.of[Int]) + List( + makeBasicType(owner), + // type Foo = [X] =>> Int + Symbol.newTypeAlias( + owner, + "tpe1", + Flags.EmptyFlags, + typeLambda, + Symbol.noSymbol + ), + ) + + val clsSymbol = Symbol.newClass(Symbol.spliceOwner, "CLS", List(TypeRepr.of[Object]), sym => makeTypesForClass(sym), None) + val classDef: ClassDef = ClassDef(clsSymbol, List(TypeTree.of[Object]), List( + TypeDef(clsSymbol.typeMember("tpe")), + TypeDef(clsSymbol.typeMember("tpe1")), + )) Block(List(classDef), Apply(Select(New(TypeIdent(clsSymbol)), clsSymbol.primaryConstructor), List.empty)).asExprOf[Object] }