diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 22be293c3562..4dd193c2586b 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2934,6 +2934,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def PolyFunctionClass = dotc.core.Symbols.defn.PolyFunctionClass def TupleClass(arity: Int): Symbol = dotc.core.Symbols.defn.TupleType(arity).nn.classSymbol.asClass + def NamedTupleModule: Symbol = dotc.core.Symbols.defn.NamedTupleModule def isTupleClass(sym: Symbol): Boolean = dotc.core.Symbols.defn.isTupleClass(sym) def ScalaPrimitiveValueClasses: List[Symbol] = diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 7a98d6f6f761..536eb4a693a1 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4513,6 +4513,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => */ def TupleClass(arity: Int): Symbol + /** The module symbol of module `scala.NamedTuple`. */ + @experimental def NamedTupleModule: Symbol + /** Returns `true` if `sym` is a `Tuple1`, `Tuple2`, ... `Tuple22` */ def isTupleClass(sym: Symbol): Boolean diff --git a/tests/run-macros/named-tuple.check b/tests/run-macros/named-tuple.check new file mode 100644 index 000000000000..bd37ea917253 --- /dev/null +++ b/tests/run-macros/named-tuple.check @@ -0,0 +1,2 @@ +10 +test diff --git a/tests/run-macros/named-tuple/Macro_1.scala b/tests/run-macros/named-tuple/Macro_1.scala new file mode 100644 index 000000000000..e608d2521bac --- /dev/null +++ b/tests/run-macros/named-tuple/Macro_1.scala @@ -0,0 +1,28 @@ +import language.experimental.namedTuples + +import scala.quoted._ +object Macro { + transparent inline def macr(): NamedTuple.AnyNamedTuple = ${ macrImpl() } + + def macrImpl()(using Quotes) = { + import quotes.reflect._ + + val tupleNames = TypeTree.of[scala.Tuple2["stringValue", "intValue"]] + val tupleTypes = TypeTree.of[scala.Tuple2[String, Int]] + val tupleValues = '{("test", 10)}.asTerm + + Apply( + TypeApply( + Apply( + TypeApply( + Select.unique(Ref(defn.NamedTupleModule), "build"), + List(tupleNames) + ), + List() + ), + List(tupleTypes) + ), + List(tupleValues) + ).asExprOf[NamedTuple.AnyNamedTuple] + } +} diff --git a/tests/run-macros/named-tuple/Test_2.scala b/tests/run-macros/named-tuple/Test_2.scala new file mode 100644 index 000000000000..e337795cc133 --- /dev/null +++ b/tests/run-macros/named-tuple/Test_2.scala @@ -0,0 +1,6 @@ +import language.experimental.namedTuples + +@main def Test = + val namedTple = Macro.macr() + println(namedTple.intValue) + println(namedTple.stringValue) diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 65e3a730ee7e..bdaa4aec7f24 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -74,6 +74,10 @@ val experimentalDefinitionInLibrary = Set( "scala.quoted.Quotes.reflectModule.TermParamClauseMethods.erasedArgs", "scala.quoted.Quotes.reflectModule.TermParamClauseMethods.hasErasedArgs", + // New feature: named tuples + // Need namedTuples enabled. + "scala.quoted.Quotes.reflectModule.defnModule.NamedTupleModule", + // New feature: fromNullable for explicit nulls "scala.Predef$.fromNullable",