diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 7f64f2cfb941..3653be8b04f7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1132,19 +1132,14 @@ class TreeUnpickler(reader: TastyReader, val idx = readNat() val args = until(end)(readTerm()) val splice = splices(idx) - + val reifiedArgs = args.map(arg => if (arg.isTerm) new TreeExpr(arg) else new TreeType(arg)) if (isType) { - val quotedType = - if (args.isEmpty) splice.asInstanceOf[quoted.Type[_]] - else splice.asInstanceOf[Seq[Any] => quoted.Type[_]](args.map(tree => new TreeType(tree))) + val quotedType = splice.asInstanceOf[Seq[Any] => quoted.Type[_]](reifiedArgs) PickledQuotes.quotedTypeToTree(quotedType) } else { - val quotedExpr = - if (args.isEmpty) splice.asInstanceOf[quoted.Expr[_]] - else splice.asInstanceOf[Seq[Any] => quoted.Expr[_]](args.map(tree => new TreeExpr(tree))) + val quotedExpr = splice.asInstanceOf[Seq[Any] => quoted.Expr[_]](reifiedArgs) PickledQuotes.quotedExprToTree(quotedExpr) } - } // ------ Setting positions ------------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index adaec52a2917..baf59419cc4a 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -5,14 +5,13 @@ import core._ import Decorators._, Flags._, Types._, Contexts._, Symbols._, Constants._ import Flags._ import ast.Trees._ -import ast.TreeTypeMap +import ast.{TreeTypeMap, untpd} import util.Positions._ import StdNames._ -import ast.untpd import tasty.TreePickler.Hole import MegaPhase.MiniPhase import SymUtils._ -import NameKinds.OuterSelectName +import NameKinds._ import typer.Implicits.SearchFailureType import scala.collection.mutable @@ -22,6 +21,40 @@ import dotty.tools.dotc.core.quoted._ /** Translates quoted terms and types to `unpickle` method calls. * Checks that the phase consistency principle (PCP) holds. + * + * + * Transforms top level quote + * ``` + * '{ ... + * val x1 = ??? + * val x2 = ??? + * ... + * ~{ ... '{ ... x1 ... x2 ...} ... } + * ... + * } + * ``` + * to + * ``` + * unpickle( + * [[ // PICKLED TASTY + * ... + * val x1 = ??? + * val x2 = ??? + * ... + * Hole(0 | x1, x2) + * ... + * ]], + * List( + * (args: Seq[Any]) => { + * val x1$1 = args(0).asInstanceOf[Expr[T]] + * val x2$1 = args(1).asInstanceOf[Expr[T]] // can be asInstanceOf[Type[T]] + * ... + * { ... '{ ... x1$1.unary_~ ... x2$1.unary_~ ...} ... } + * } + * ) + * ) + * ``` + * and then performs the same transformation on `'{ ... x1$1.unary_~ ... x2$1.unary_~ ...}`. */ class ReifyQuotes extends MacroTransformWithImplicits { import ast.tpd._ @@ -32,73 +65,62 @@ class ReifyQuotes extends MacroTransformWithImplicits { if (ctx.compilationUnit.containsQuotesOrSplices) super.run protected def newTransformer(implicit ctx: Context): Transformer = - new Reifier(inQuote = false, null, 0, new LevelInfo) + new Reifier(inQuote = false, null, 0, new LevelInfo, new mutable.ListBuffer[Tree]) private class LevelInfo { /** A map from locally defined symbols to the staging levels of their definitions */ val levelOf = new mutable.HashMap[Symbol, Int] - /** A stack of entered symbols, to be unwound after scope exit */ - var enteredSyms: List[Symbol] = Nil - } - - /** Requiring that `paramRefs` consists of a single reference `seq` to a Seq[Any], - * a tree map that replaces each hole with index `n` with `seq(n)`, applied - * to any arguments in the hole. - */ - private def replaceHoles(paramRefs: List[Tree]) = new TreeMap { - val seq :: Nil = paramRefs - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Hole(n, args) => - val arg = - seq.select(nme.apply).appliedTo(Literal(Constant(n))).ensureConforms(tree.tpe) - if (args.isEmpty) arg - else arg.select(nme.apply).appliedTo(SeqLiteral(args, TypeTree(defn.AnyType))) - case _ => - super.transform(tree) - } + /** Register a reference defined in a quote but used in another quote nested in a splice. + * Returns a lifted version of the reference that needs to be used in its place. + * '{ + * val x = ??? + * { ... '{ ... x ... } ... }.unary_~ + * } + * Lifting the `x` in `{ ... '{ ... x ... } ... }.unary_~` will return a `x$1.unary_~` for which the `x$1` + * be created by some outer reifier. + * + * This transformation is only applied to definitions at staging level 1. + * + * See `needsLifting` + */ + val lifters = new mutable.HashMap[Symbol, RefTree => Tree] } - /** If `tree` has holes, convert it to a function taking a `Seq` of elements as arguments - * where each hole is replaced by the corresponding sequence element. - */ - private def elimHoles(tree: Tree)(implicit ctx: Context): Tree = - if (tree.existsSubTree(_.isInstanceOf[Hole])) - Lambda( - MethodType(defn.SeqType.appliedTo(defn.AnyType) :: Nil, tree.tpe), - replaceHoles(_).transform(tree)) - else tree - /** The main transformer class * @param inQuote we are within a `'(...)` context that is not shadowed by a nested `~(...)` * @param outer the next outer reifier, null is this is the topmost transformer * @param level the current level, where quotes add one and splices subtract one level * @param levels a stacked map from symbols to the levels in which they were defined + * @param embedded a list of embedded quotes (if `inSplice = true`) or splices (if `inQuote = true` */ - private class Reifier(inQuote: Boolean, val outer: Reifier, val level: Int, levels: LevelInfo) extends ImplicitsTransformer { + private class Reifier(inQuote: Boolean, val outer: Reifier, val level: Int, levels: LevelInfo, + val embedded: mutable.ListBuffer[Tree]) extends ImplicitsTransformer { import levels._ assert(level >= 0) /** A nested reifier for a quote (if `isQuote = true`) or a splice (if not) */ - def nested(isQuote: Boolean): Reifier = - new Reifier(isQuote, this, if (isQuote) level + 1 else level - 1, levels) + def nested(isQuote: Boolean): Reifier = { + val nestedEmbedded = if (level > 1 || (level == 1 && isQuote)) embedded else new mutable.ListBuffer[Tree] + new Reifier(isQuote, this, if (isQuote) level + 1 else level - 1, levels, nestedEmbedded) + } /** We are in a `~(...)` context that is not shadowed by a nested `'(...)` */ def inSplice = outer != null && !inQuote - /** A list of embedded quotes (if `inSplice = true`) or splices (if `inQuote = true`) */ - val embedded = new mutable.ListBuffer[Tree] - /** A map from type ref T to expressions of type `quoted.Type[T]`". * These will be turned into splices using `addTags` */ val importedTags = new mutable.LinkedHashMap[TypeRef, Tree]() + /** A stack of entered symbols, to be unwound after scope exit */ + var enteredSyms: List[Symbol] = Nil + /** Assuming importedTags = `Type1 -> tag1, ..., TypeN -> tagN`, the expression * * { type = .unary_~ * ... - * type = .unary.~ + * type = .unary_~ * * } * @@ -106,7 +128,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { * defined versions. As a side effect, prepend the expressions `tag1, ..., `tagN` * as splices to `embedded`. */ - def addTags(expr: Tree)(implicit ctx: Context): Tree = + private def addTags(expr: Tree)(implicit ctx: Context): Tree = if (importedTags.isEmpty) expr else { val itags = importedTags.toList @@ -151,7 +173,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { } /** Issue a "splice outside quote" error unless we ar in the body of an inline method */ - def spliceOutsideQuotes(pos: Position)(implicit ctx: Context) = + def spliceOutsideQuotes(pos: Position)(implicit ctx: Context): Unit = ctx.error(i"splice outside quotes", pos) /** Try to heal phase-inconsistent reference to type `T` using a local type definition. @@ -162,7 +184,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { def tryHeal(tp: Type, pos: Position)(implicit ctx: Context): Option[String] = tp match { case tp: TypeRef => if (level == 0) { - assert(ctx.owner.is(Macro)) + assert(ctx.owner.ownersIterator.exists(_.is(Macro))) None } else { val reqType = defn.QuotedTypeType.appliedTo(tp) @@ -258,46 +280,117 @@ class ReifyQuotes extends MacroTransformWithImplicits { * `scala.quoted.Unpickler.unpickleExpr` that matches `tpe` with * core and splices as arguments. */ - private def quotation(body: Tree, quote: Tree)(implicit ctx: Context) = { - val (body1, splices) = nested(isQuote = true).split(body) - if (inSplice) - makeHole(body1, splices, quote.tpe) + private def quotation(body: Tree, quote: Tree)(implicit ctx: Context): Tree = { + val isType = quote.symbol eq defn.typeQuoteMethod + if (level > 0) { + val body1 = nested(isQuote = true).transform(body) + // Keep quotes as trees to reduce pickled size and have a Expr.show without pickled quotes + if (isType) ref(defn.typeQuoteMethod).appliedToType(body1.tpe.widen) + else ref(defn.quoteMethod).appliedToType(body1.tpe.widen).appliedTo(body1) + } else { - def liftList(list: List[Tree], tpe: Type): Tree = { - list.foldRight[Tree](ref(defn.NilModule)) { (x, acc) => - acc.select("::".toTermName).appliedToType(tpe).appliedTo(x) - } - } - val isType = quote.tpe.isRef(defn.QuotedTypeClass) - ref(if (isType) defn.Unpickler_unpickleType else defn.Unpickler_unpickleExpr) - .appliedToType(if (isType) body1.tpe else body1.tpe.widen) - .appliedTo( + val (body1, splices) = nested(isQuote = true).split(body) + val meth = + if (isType) ref(defn.Unpickler_unpickleType).appliedToType(body1.tpe) + else ref(defn.Unpickler_unpickleExpr).appliedToType(body1.tpe.widen) + meth.appliedTo( liftList(PickledQuotes.pickleQuote(body1).map(x => Literal(Constant(x))), defn.StringType), - liftList(splices, defn.AnyType)) + liftList(splices, defn.AnyType)).withPos(quote.pos) } - }.withPos(quote.pos) + } - /** If inside a quote, split `body` into a core and a list of embedded quotes + /** If inside a quote, split the body of the splice into a core and a list of embedded quotes * and make a hole from these parts. Otherwise issue an error, unless we * are in the body of an inline method. */ - private def splice(body: Tree, splice: Tree)(implicit ctx: Context): Tree = { - if (inQuote) { - val (body1, quotes) = nested(isQuote = false).split(body) - makeHole(body1, quotes, splice.tpe) + private def splice(splice: Select)(implicit ctx: Context): Tree = { + if (level > 1) { + val body1 = nested(isQuote = false).transform(splice.qualifier) + body1.select(splice.name) } - else { + else if (!inQuote && level == 0) { spliceOutsideQuotes(splice.pos) splice } - }.withPos(splice.pos) + else { + val (body1, quotes) = nested(isQuote = false).split(splice.qualifier) + makeHole(body1, quotes, splice.tpe).withPos(splice.pos) + } + } + + /** Transforms the contents of a nested splice + * Assuming + * '{ + * val x = ??? + * val y = ??? + * { ... '{ ... x .. y ... } ... }.unary_~ + * } + * then the spliced subexpression + * { ... '{ ... x ... y ... } ... } + * will be transformed to + * (args: Seq[Any]) => { + * val x$1 = args(0).asInstanceOf[Expr[Any]] // or .asInstanceOf[Type[Any]] + * val y$1 = args(1).asInstanceOf[Expr[Any]] // or .asInstanceOf[Type[Any]] + * { ... '{ ... x$1.unary_~ ... y$1.unary_~ ... } ... } + * } + * + * See: `lift` + * + * At the same time register `embedded` trees `x` and `y` to place as arguments of the hole + * placed in the original code. + * '{ + * val x = ??? + * val y = ??? + * Hole(0 | x, y) + * } + */ + private def makeLambda(tree: Tree)(implicit ctx: Context): Tree = { + def body(arg: Tree)(implicit ctx: Context): Tree = { + var i = 0 + transformWithLifter(tree)( + (lifted: mutable.ListBuffer[Tree]) => (tree: RefTree) => { + val argTpe = + if (tree.isTerm) defn.QuotedExprType.appliedTo(tree.tpe.widen) + else defn.QuotedTypeType.appliedTo(defn.AnyType) + val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argTpe) + val liftedArg = SyntheticValDef(UniqueName.fresh(tree.name.toTermName).toTermName, selectArg) + i += 1 + embedded += tree + lifted += liftedArg + ref(liftedArg.symbol) + } + ) + } + + val lambdaOwner = ctx.owner.ownersIterator.find(o => levelOf.getOrElse(o, level) == level).get + val tpe = MethodType(defn.SeqType.appliedTo(defn.AnyType) :: Nil, tree.tpe.widen) + val meth = ctx.newSymbol(lambdaOwner, UniqueName.fresh(nme.ANON_FUN), Synthetic | Method, tpe) + Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth)) + } + + private def transformWithLifter(tree: Tree)( + lifter: mutable.ListBuffer[Tree] => RefTree => Tree)(implicit ctx: Context): Tree = { + val lifted = new mutable.ListBuffer[Tree] + val lifter2 = lifter(lifted) + outer.enteredSyms.foreach(s => lifters.put(s, lifter2)) + val tree2 = transform(tree) + lifters --= outer.enteredSyms + seq(lifted.result(), tree2) + } + + /** Returns true if this tree will be lifted by `makeLambda` */ + private def needsLifting(tree: RefTree)(implicit ctx: Context): Boolean = { + // Check phase consistency and presence of lifter + level == 1 && !tree.symbol.is(Inline) && levelOf.get(tree.symbol).contains(1) && + lifters.contains(tree.symbol) + } /** Transform `tree` and return the resulting tree and all `embedded` quotes * or splices as a pair, after performing the `addTags` transform. */ private def split(tree: Tree)(implicit ctx: Context): (Tree, List[Tree]) = { - val tree1 = addTags(transform(tree)) - (tree1, embedded.toList.map(elimHoles)) + val tree1 = if (inQuote) addTags(transform(tree)) else makeLambda(tree) + (tree1, embedded.toList) } /** Register `body` as an `embedded` quote or splice @@ -321,8 +414,11 @@ class ReifyQuotes extends MacroTransformWithImplicits { tree match { case Quoted(quotedTree) => quotation(quotedTree, tree) - case Select(body, _) if tree.symbol.isSplice => - splice(body, tree) + case tree: Select if tree.symbol.isSplice => + splice(tree) + case tree: RefTree if needsLifting(tree) => + val lift = lifters(tree.symbol) + splice(lift(tree).select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~)) case Block(stats, _) => val last = enteredSyms stats.foreach(markDef) @@ -347,7 +443,7 @@ class ReifyQuotes extends MacroTransformWithImplicits { tree case tree: DefDef if tree.symbol.is(Macro) && level == 0 => markDef(tree) - val tree1 = nested(isQuote = true).transform(tree) + nested(isQuote = true).transform(tree) // check macro code as it if appeared in a quoted context cpy.DefDef(tree)(rhs = EmptyTree) case _ => @@ -356,14 +452,19 @@ class ReifyQuotes extends MacroTransformWithImplicits { } } + private def liftList(list: List[Tree], tpe: Type)(implicit ctx: Context): Tree = { + list.foldRight[Tree](ref(defn.NilModule)) { (x, acc) => + acc.select("::".toTermName).appliedToType(tpe).appliedTo(x) + } + } + /** InlineSplice is used to detect cases where the expansion * consists of a (possibly multiple & nested) block or a sole expression. */ object InlineSplice { def unapply(tree: Tree)(implicit ctx: Context): Option[Select] = { tree match { - case expansion: Select if expansion.symbol.isSplice => - Some(expansion) + case expansion: Select if expansion.symbol.isSplice => Some(expansion) case Block(List(stat), Literal(Constant(()))) => unapply(stat) case Block(Nil, expr) => unapply(expr) case _ => None @@ -371,4 +472,4 @@ class ReifyQuotes extends MacroTransformWithImplicits { } } } -} \ No newline at end of file +} diff --git a/tests/neg/i4044a.scala b/tests/neg/i4044a.scala new file mode 100644 index 000000000000..daa64fce9fac --- /dev/null +++ b/tests/neg/i4044a.scala @@ -0,0 +1,13 @@ +import scala.quoted._ + +class Test { + + val a = '(1) + '{ + a // error + ~a + '(~a) // error + '( '(~a) ) // error + } + +} diff --git a/tests/neg/i4044b.scala b/tests/neg/i4044b.scala new file mode 100644 index 000000000000..cc44b6fd8a1e --- /dev/null +++ b/tests/neg/i4044b.scala @@ -0,0 +1,18 @@ +import scala.quoted._ + +class Test { + + '{ + + val b = '(3) + + '{ + b // error + ~(b) + ~('(b)) // error + '( '(~b) ) // error + } + + } + +} diff --git a/tests/run-with-compiler/i4044a.check b/tests/run-with-compiler/i4044a.check new file mode 100644 index 000000000000..00750edc07d6 --- /dev/null +++ b/tests/run-with-compiler/i4044a.check @@ -0,0 +1 @@ +3 diff --git a/tests/run-with-compiler/i4044a.scala b/tests/run-with-compiler/i4044a.scala new file mode 100644 index 000000000000..1eaf534cb6cc --- /dev/null +++ b/tests/run-with-compiler/i4044a.scala @@ -0,0 +1,17 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +class Foo { + def foo: Unit = { + val e: Expr[Int] = '(3) + val q = '{ ~( '{ ~e } ) } + println(q.show) + } +} + +object Test { + def main(args: Array[String]): Unit = { + val f = new Foo + f.foo + } +} diff --git a/tests/run-with-compiler/i4044b.check b/tests/run-with-compiler/i4044b.check new file mode 100644 index 000000000000..43c71a999c56 --- /dev/null +++ b/tests/run-with-compiler/i4044b.check @@ -0,0 +1,5 @@ +{ + var x: Int = 4 + x = 3 + x +} diff --git a/tests/run-with-compiler/i4044b.scala b/tests/run-with-compiler/i4044b.scala new file mode 100644 index 000000000000..c4255d7d8d7a --- /dev/null +++ b/tests/run-with-compiler/i4044b.scala @@ -0,0 +1,27 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +sealed abstract class VarRef[T] { + def update(expr: Expr[T]): Expr[Unit] + def expr: Expr[T] +} + +object VarRef { + def apply[T: Type, U](init: Expr[T])(body: VarRef[T] => Expr[U]): Expr[U] = '{ + var x = ~init + ~body( + new VarRef { + def update(e: Expr[T]): Expr[Unit] = '{ x = ~e } + def expr: Expr[T] = '(x) + } + ) + } + +} + +object Test { + def main(args: Array[String]): Unit = { + val q = VarRef(4)(varRef => '{ ~varRef.update(3); ~varRef.expr }) + println(q.show) + } +} diff --git a/tests/run-with-compiler/i4044c.check b/tests/run-with-compiler/i4044c.check new file mode 100644 index 000000000000..7ed6ff82de6b --- /dev/null +++ b/tests/run-with-compiler/i4044c.check @@ -0,0 +1 @@ +5 diff --git a/tests/run-with-compiler/i4044c.scala b/tests/run-with-compiler/i4044c.scala new file mode 100644 index 000000000000..b35fe58851bb --- /dev/null +++ b/tests/run-with-compiler/i4044c.scala @@ -0,0 +1,16 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +class Foo { + def foo: Unit = { + val q = '{ ~( '{ ~( '{ 5 } ) } ) } + println(q.show) + } +} + +object Test { + def main(args: Array[String]): Unit = { + val f = new Foo + f.foo + } +} diff --git a/tests/run-with-compiler/i4044d.check b/tests/run-with-compiler/i4044d.check new file mode 100644 index 000000000000..16281b787396 --- /dev/null +++ b/tests/run-with-compiler/i4044d.check @@ -0,0 +1,5 @@ +evaluating inner quote +{ + val b: Int = 3 + b.+(3) +} diff --git a/tests/run-with-compiler/i4044d.scala b/tests/run-with-compiler/i4044d.scala new file mode 100644 index 000000000000..6653af93d3d5 --- /dev/null +++ b/tests/run-with-compiler/i4044d.scala @@ -0,0 +1,25 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +class Foo { + def foo: Unit = { + val a: Expr[Int] = '(3) + val q: Expr[Int] = '{ + val b = 3 + ~{ + println("evaluating inner quote") + '{ + b + ~a + } + } + } + println(q.show) + } +} + +object Test { + def main(args: Array[String]): Unit = { + val f = new Foo + f.foo + } +} diff --git a/tests/run-with-compiler/i4044e.check b/tests/run-with-compiler/i4044e.check new file mode 100644 index 000000000000..af61855f9eed --- /dev/null +++ b/tests/run-with-compiler/i4044e.check @@ -0,0 +1 @@ +3.+(5).asInstanceOf[Int] diff --git a/tests/run-with-compiler/i4044e.scala b/tests/run-with-compiler/i4044e.scala new file mode 100644 index 000000000000..c3dcc631dc82 --- /dev/null +++ b/tests/run-with-compiler/i4044e.scala @@ -0,0 +1,19 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +class Foo { + def foo: Unit = { + val e: Expr[Int] = '(3) + val f: Expr[Int] = '(5) + val t: Type[Int] = '[Int] + val q = '{ ~( '{ (~e + ~f).asInstanceOf[~t] } ) } + println(q.show) + } +} + +object Test { + def main(args: Array[String]): Unit = { + val f = new Foo + f.foo + } +} diff --git a/tests/run-with-compiler/i4044f.check b/tests/run-with-compiler/i4044f.check new file mode 100644 index 000000000000..409bb72596a0 --- /dev/null +++ b/tests/run-with-compiler/i4044f.check @@ -0,0 +1,5 @@ +{ + val e1: Int = 3 + val f1: Int = 5 + e1.+(2).+(f1) +} diff --git a/tests/run-with-compiler/i4044f.scala b/tests/run-with-compiler/i4044f.scala new file mode 100644 index 000000000000..43372ff23938 --- /dev/null +++ b/tests/run-with-compiler/i4044f.scala @@ -0,0 +1,26 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +class Foo { + def foo: Unit = { + val e: Expr[Int] = '(3) + val f: Expr[Int] = '(5) + def foo(x: Expr[Int], y: Expr[Int]): Expr[Int] = '{ ~x + ~y } + val q = '{ + val e1 = ~e + val f1 = ~f + ~{ + val u = '(2) + foo('{e1 + ~u}, '(f1)) + } + } + println(q.show) + } +} + +object Test { + def main(args: Array[String]): Unit = { + val f = new Foo + f.foo + } +} diff --git a/tests/run-with-compiler/quote-lambda.scala b/tests/run-with-compiler/quote-lambda.scala new file mode 100644 index 000000000000..91a871ab1bc8 --- /dev/null +++ b/tests/run-with-compiler/quote-lambda.scala @@ -0,0 +1,7 @@ +import scala.quoted._ + +object Test { + def main(args: Array[String]): Unit = { + '{ (x: Int) => ~('(x)) } + } +} diff --git a/tests/run-with-compiler/quote-nested-1.check b/tests/run-with-compiler/quote-nested-1.check new file mode 100644 index 000000000000..2378a7f62ea6 --- /dev/null +++ b/tests/run-with-compiler/quote-nested-1.check @@ -0,0 +1 @@ +.'[Int](3) diff --git a/tests/run-with-compiler/quote-nested-1.scala b/tests/run-with-compiler/quote-nested-1.scala new file mode 100644 index 000000000000..410eaa835aae --- /dev/null +++ b/tests/run-with-compiler/quote-nested-1.scala @@ -0,0 +1,9 @@ +import quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + def main(args: Array[String]): Unit = { + val q = '{ '(3) } + println(q.show) + } +} diff --git a/tests/run-with-compiler/quote-nested-2.check b/tests/run-with-compiler/quote-nested-2.check new file mode 100644 index 000000000000..5e2472b8ba28 --- /dev/null +++ b/tests/run-with-compiler/quote-nested-2.check @@ -0,0 +1,4 @@ +{ + val a: quoted.Expr[Int] = .'[Int](4) + .'[Int](a.unary_~) +} diff --git a/tests/run-with-compiler/quote-nested-2.scala b/tests/run-with-compiler/quote-nested-2.scala new file mode 100644 index 000000000000..bdc8a01fa8b2 --- /dev/null +++ b/tests/run-with-compiler/quote-nested-2.scala @@ -0,0 +1,13 @@ +import quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + def main(args: Array[String]): Unit = { + val q = '{ + val a = '(4) + '(~a) + } + + println(q.show) + } +} diff --git a/tests/run-with-compiler/quote-nested-3.check b/tests/run-with-compiler/quote-nested-3.check new file mode 100644 index 000000000000..4b5686d7ba3f --- /dev/null +++ b/tests/run-with-compiler/quote-nested-3.check @@ -0,0 +1,7 @@ +{ + type T = String + val x: String = "foo" + val z: T = x + () + x: String +} diff --git a/tests/run-with-compiler/quote-nested-3.scala b/tests/run-with-compiler/quote-nested-3.scala new file mode 100644 index 000000000000..15ef33e2e2c9 --- /dev/null +++ b/tests/run-with-compiler/quote-nested-3.scala @@ -0,0 +1,19 @@ +import quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + def main(args: Array[String]): Unit = { + + val q = '{ + type T = String + val x = "foo" + ~{ + val y = '(x) + '{ val z: T = ~y } + } + x + } + + println(q.show) + } +} diff --git a/tests/run-with-compiler/quote-nested-4.check b/tests/run-with-compiler/quote-nested-4.check new file mode 100644 index 000000000000..ff8256f4afe7 --- /dev/null +++ b/tests/run-with-compiler/quote-nested-4.check @@ -0,0 +1,4 @@ +{ + val t: quoted.Type[String] = .type_'[String] + t: quoted.Type[String] +} diff --git a/tests/run-with-compiler/quote-nested-4.scala b/tests/run-with-compiler/quote-nested-4.scala new file mode 100644 index 000000000000..49a4a0db587e --- /dev/null +++ b/tests/run-with-compiler/quote-nested-4.scala @@ -0,0 +1,13 @@ +import quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + def main(args: Array[String]): Unit = { + val q = '{ + val t = '[String] + t + } + + println(q.show) + } +} diff --git a/tests/run-with-compiler/quote-nested-5.check b/tests/run-with-compiler/quote-nested-5.check new file mode 100644 index 000000000000..5e2472b8ba28 --- /dev/null +++ b/tests/run-with-compiler/quote-nested-5.check @@ -0,0 +1,4 @@ +{ + val a: quoted.Expr[Int] = .'[Int](4) + .'[Int](a.unary_~) +} diff --git a/tests/run-with-compiler/quote-nested-5.scala b/tests/run-with-compiler/quote-nested-5.scala new file mode 100644 index 000000000000..7d1bdd602343 --- /dev/null +++ b/tests/run-with-compiler/quote-nested-5.scala @@ -0,0 +1,16 @@ +import quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + def main(args: Array[String]): Unit = { + val q = '{ + val a = '(4) + ~('{ + '(~a) + }) + + } + + println(q.show) + } +}