diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 71b85d97a187..8746277d1b26 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -178,6 +178,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Splice(expr: Tree, tpe: Type)(using Context): Splice = untpd.Splice(expr).withType(tpe) + def Splice(expr: Tree)(using Context): Splice = + ta.assignType(untpd.Splice(expr), expr) + + def SplicePattern(pat: Tree, args: List[Tree], tpe: Type)(using Context): SplicePattern = + untpd.SplicePattern(pat, args).withType(tpe) + def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpe: Type)(using Context): Hole = untpd.Hole(isTerm, idx, args, content).withType(tpe) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 0a8669292a74..f8a0f725ea52 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -701,28 +701,40 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { pickleTree(alias) } case tree @ Quote(body, Nil) => - // TODO: Add QUOTE tag to TASTy assert(body.isTerm, """Quote with type should not be pickled. |Quote with type should only exists after staging phase at staging level 0.""".stripMargin) - pickleTree( - // scala.quoted.runtime.Expr.quoted[]() - ref(defn.QuotedRuntime_exprQuote) - .appliedToType(tree.bodyType) - .appliedTo(body) - .withSpan(tree.span) - ) + writeByte(QUOTE) + withLength { + pickleTree(body) + pickleType(tree.bodyType) + } case Splice(expr) => - pickleTree( // TODO: Add SPLICE tag to TASTy - // scala.quoted.runtime.Expr.splice[]() - ref(defn.QuotedRuntime_exprSplice) - .appliedToType(tree.tpe) - .appliedTo(expr) - .withSpan(tree.span) - ) - case tree: QuotePattern => - // TODO: Add QUOTEPATTERN tag to TASTy - pickleTree(QuotePatterns.encode(tree)) + writeByte(SPLICE) + withLength { + pickleTree(expr) + pickleType(tree.tpe) + } + case QuotePattern(bindings, body, quotes) => + writeByte(QUOTEPATTERN) + withLength { + if body.isType then writeByte(EXPLICITtpt) + pickleTree(body) + pickleTree(quotes) + pickleType(tree.tpe) + bindings.foreach(pickleTree) + } + case SplicePattern(pat, args) => + val targs = Nil // SplicePattern `targs` will be added with #18271 + writeByte(SPLICEPATTERN) + withLength { + pickleTree(pat) + pickleType(tree.tpe) + for targ <- targs do + writeByte(EXPLICITtpt) + pickleTree(targ) + args.foreach(pickleTree) + } case Hole(_, idx, args, _) => writeByte(HOLE) withLength { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 073edb536151..64ea2d497295 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1379,9 +1379,9 @@ class TreeUnpickler(reader: TastyReader, val fn = readTree() val args = until(end)(readTree()) if fn.symbol.isConstructor then constructorApply(fn, args) - else if fn.symbol == defn.QuotedRuntime_exprQuote then quotedExpr(fn, args) - else if fn.symbol == defn.QuotedRuntime_exprSplice then splicedExpr(fn, args) - else if fn.symbol == defn.QuotedRuntime_exprNestedSplice then nestedSpliceExpr(fn, args) + else if fn.symbol == defn.QuotedRuntime_exprQuote then quotedExpr(fn, args) // decode pre 3.5.0 encoding + else if fn.symbol == defn.QuotedRuntime_exprSplice then splicedExpr(fn, args) // decode pre 3.5.0 encoding + else if fn.symbol == defn.QuotedRuntime_exprNestedSplice then nestedSpliceExpr(fn, args) // decode pre 3.5.0 encoding else tpd.Apply(fn, args) case TYPEAPPLY => tpd.TypeApply(readTree(), until(end)(readTpt())) @@ -1503,7 +1503,7 @@ class TreeUnpickler(reader: TastyReader, val unapply = UnApply(fn, implicitArgs, argPats, patType) if fn.symbol == defn.QuoteMatching_ExprMatch_unapply || fn.symbol == defn.QuoteMatching_TypeMatch_unapply - then QuotePatterns.decode(unapply) + then QuotePatterns.decode(unapply) // decode pre 3.5.0 encoding else unapply case REFINEDtpt => val refineCls = symAtAddr.getOrElse(start, @@ -1551,6 +1551,24 @@ class TreeUnpickler(reader: TastyReader, val hi = if currentAddr == end then lo else readTpt() val alias = if currentAddr == end then EmptyTree else readTpt() createNullableTypeBoundsTree(lo, hi, alias) + case QUOTE => + Quote(readTree(), Nil).withBodyType(readType()) + case SPLICE => + Splice(readTree()).withType(readType()) + case QUOTEPATTERN => + val bodyReader = fork + skipTree() + val quotes = readTree() + val patType = readType() + val bindings = readStats(ctx.owner, end) + val body = bodyReader.readTree() // need bindings in scope, so needs to be read before + QuotePattern(bindings, body, quotes, patType) + case SPLICEPATTERN => + val pat = readTree() + val patType = readType() + val (targs, args) = until(end)(readTree()).span(_.isType) + assert(targs.isEmpty, "unexpected type arguments in SPLICEPATTERN") // `targs` will be needed for #18271. Until this fearure is added they should be empty. + SplicePattern(pat, args, patType) case HOLE => readHole(end, isTerm = true) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index c7476f5d9777..64722d51708c 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -531,6 +531,12 @@ trait TypeAssigner { def assignType(tree: untpd.UnApply, proto: Type)(using Context): UnApply = tree.withType(proto) + def assignType(tree: untpd.Splice, expr: Tree)(using Context): Splice = + val tpe = expr.tpe // Quotes ?=> Expr[T] + .baseType(defn.FunctionSymbol(1, isContextual = true)).argTypes.last // Expr[T] + .baseType(defn.QuotedExprClass).argTypes.head // T + tree.withType(tpe) + def assignType(tree: untpd.QuotePattern, proto: Type)(using Context): QuotePattern = tree.withType(proto) diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 6cd63d0d8f01..164243d3b469 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -110,10 +110,14 @@ Standard-Section: "ASTs" TopLevelStat* WHILE Length cond_Term body_Term -- while cond do body REPEATED Length elem_Type elem_Term* -- Varargs argument of type `elem` SELECTouter Length levels_Nat qual_Term underlying_Type -- Follow `levels` outer links, starting from `qual`, with given `underlying` type + QUOTE Length body_Term bodyTpe_Type -- Quoted expression `'{ body }` of a body typed as `bodyTpe` + SPLICE Length expr_Term tpe_Type -- Spliced expression `${ expr }` typed as `tpe` + SPLICEPATTEN Length pat_Term tpe_Type targs_Type* args_Term* -- Pattern splice `${pat}` or `$pat[targs*](args*)` in a quoted pattern of type `tpe`. -- patterns: BIND Length boundName_NameRef patType_Type pat_Term -- name @ pat, wherev `patType` is the type of the bound symbol ALTERNATIVE Length alt_Term* -- alt1 | ... | altn as a pattern UNAPPLY Length fun_Term ImplicitArg* pat_Type pat_Term* -- Unapply node `fun(_: pat_Type)(implicitArgs)` flowing into patterns `pat`. + QUOTEPATTERN Length body_Term quotes_Term pat_Type bindings_Term* -- Quote pattern node `'{ bindings*; body }(using quotes)` -- type trees: IDENTtpt NameRef Type -- Used for all type idents SELECTtpt NameRef qual_Term -- qual.name @@ -544,7 +548,6 @@ object TastyFormat { final val EXPLICITtpt = 103 final val ELIDED = 104 - // Tree Cat. 4: tag Nat AST final val firstNatASTTreeTag = IDENT final val IDENT = 110 @@ -610,10 +613,12 @@ object TastyFormat { final val TYPEREFin = 175 final val SELECTin = 176 final val EXPORT = 177 - // final val ??? = 178 - // final val ??? = 179 + final val QUOTE = 178 + final val SPLICE = 179 final val METHODtype = 180 final val APPLYsigpoly = 181 + final val QUOTEPATTERN = 182 + final val SPLICEPATTERN = 183 final val MATCHtype = 190 final val MATCHtpt = 191 @@ -858,6 +863,10 @@ object TastyFormat { case PROTECTEDqualified => "PROTECTEDqualified" case EXPLICITtpt => "EXPLICITtpt" case ELIDED => "ELIDED" + case QUOTE => "QUOTE" + case SPLICE => "SPLICE" + case QUOTEPATTERN => "QUOTEPATTERN" + case SPLICEPATTERN => "SPLICEPATTERN" case HOLE => "HOLE" } diff --git a/tests/pos/i13532/Bar.scala b/tests/pos-macros/i13532/Bar.scala similarity index 100% rename from tests/pos/i13532/Bar.scala rename to tests/pos-macros/i13532/Bar.scala diff --git a/tests/pos/i13532/Foo.scala b/tests/pos-macros/i13532/Foo.scala similarity index 100% rename from tests/pos/i13532/Foo.scala rename to tests/pos-macros/i13532/Foo.scala diff --git a/tests/pos/i13532/TestMacro.scala b/tests/pos-macros/i13532/TestMacro.scala similarity index 100% rename from tests/pos/i13532/TestMacro.scala rename to tests/pos-macros/i13532/TestMacro.scala diff --git a/tests/pos/i16331/Macro.scala b/tests/pos-macros/i16331/Macro.scala similarity index 100% rename from tests/pos/i16331/Macro.scala rename to tests/pos-macros/i16331/Macro.scala diff --git a/tests/pos/i16331/Main.scala b/tests/pos-macros/i16331/Main.scala similarity index 100% rename from tests/pos/i16331/Main.scala rename to tests/pos-macros/i16331/Main.scala diff --git a/tests/pos/i19604/ZSet.scala b/tests/pos-macros/i19604/ZSet.scala similarity index 100% rename from tests/pos/i19604/ZSet.scala rename to tests/pos-macros/i19604/ZSet.scala diff --git a/tests/pos/i19604/core.scala b/tests/pos-macros/i19604/core.scala similarity index 100% rename from tests/pos/i19604/core.scala rename to tests/pos-macros/i19604/core.scala diff --git a/tests/pos/i19604/macro.scala b/tests/pos-macros/i19604/macro.scala similarity index 100% rename from tests/pos/i19604/macro.scala rename to tests/pos-macros/i19604/macro.scala diff --git a/tests/pos/i19604/prelude.scala b/tests/pos-macros/i19604/prelude.scala similarity index 100% rename from tests/pos/i19604/prelude.scala rename to tests/pos-macros/i19604/prelude.scala