From 74fdf52c722586d199f786ec35c63795e7638ccc Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 18 Nov 2019 10:33:17 +0100 Subject: [PATCH 1/2] Add quoted.util.ExprMap --- .../ReflectionCompilerInterface.scala | 7 + .../scala/quoted/util/ExprMap.scala | 158 ++++++++++++++++++ .../tasty/reflect/CompilerInterface.scala | 6 + .../src/scala/tasty/reflect/SymbolOps.scala | 10 ++ tests/run-macros/expr-map-1.check | 26 +++ tests/run-macros/expr-map-1/Macro_1.scala | 18 ++ tests/run-macros/expr-map-1/Test_2.scala | 122 ++++++++++++++ .../run-macros/flops-rewrite-2/Macro_1.scala | 36 +--- .../run-macros/flops-rewrite-3/Macro_1.scala | 36 +--- tests/run-macros/flops-rewrite/Macro_1.scala | 36 +--- 10 files changed, 362 insertions(+), 93 deletions(-) create mode 100644 library/src-bootstrapped/scala/quoted/util/ExprMap.scala create mode 100644 tests/run-macros/expr-map-1.check create mode 100644 tests/run-macros/expr-map-1/Macro_1.scala create mode 100644 tests/run-macros/expr-map-1/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala index f0ae6c92b86b..3a594cb62aa9 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala @@ -1713,6 +1713,13 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend def Symbol_noSymbol(given ctx: Context): Symbol = core.Symbols.NoSymbol + def Symbol_typeRef(symbol: Symbol)(given ctx: Context): TypeOrBounds = symbol.typeRef + + def Symbol_termRef(symbol: Symbol)(given ctx: Context): TypeOrBounds = symbol.termRef + + def Symbol_info(symbol: Symbol)(given ctx: Context): TypeOrBounds = symbol.info + + // // FLAGS // diff --git a/library/src-bootstrapped/scala/quoted/util/ExprMap.scala b/library/src-bootstrapped/scala/quoted/util/ExprMap.scala new file mode 100644 index 000000000000..8e64cedf14a9 --- /dev/null +++ b/library/src-bootstrapped/scala/quoted/util/ExprMap.scala @@ -0,0 +1,158 @@ +package scala.quoted.util + +import scala.quoted._ + +trait ExprMap { + + /** Map an expression `e` with a type `tpe` */ + def map[T](e: Expr[T])(given qctx: QuoteContext, tpe: Type[T]): Expr[T] + + /** Map subexpressions an expression `e` with a type `tpe` */ + def mapChildren[T](e: Expr[T])(given qctx: QuoteContext, tpe: Type[T]): Expr[T] = { + import qctx.tasty.{_, given} + class MapChildren() { + + def transformStatement(tree: Statement)(given ctx: Context): Statement = { + def localCtx(definition: Definition): Context = definition.symbol.localContext + tree match { + case tree: Term => + transformTerm(tree, defn.AnyType) + case tree: Definition => + transformDefinition(tree) + case tree: Import => + tree + } + } + + def transformDefinition(tree: Definition)(given ctx: Context): Definition = { + def localCtx(definition: Definition): Context = definition.symbol.localContext + tree match { + case tree: ValDef => + implicit val ctx = localCtx(tree) + val rhs1 = tree.rhs.map(x => transformTerm(x, tree.tpt.tpe)) + ValDef.copy(tree)(tree.name, tree.tpt, rhs1) + case tree: DefDef => + implicit val ctx = localCtx(tree) + DefDef.copy(tree)(tree.name, tree.typeParams, tree.paramss, tree.returnTpt, tree.rhs.map(x => transformTerm(x, tree.returnTpt.tpe))) + case tree: TypeDef => + tree + case tree: ClassDef => + ClassDef.copy(tree)(tree.name, tree.constructor, tree.parents, tree.derived, tree.self, tree.body) + } + } + + def transformTermChildren(tree: Term, tpe: Type)(given ctx: Context): Term = tree match { + case Ident(name) => + tree + case Select(qualifier, name) => + Select.copy(tree)(transformTerm(qualifier, qualifier.tpe.widen), name) + case This(qual) => + tree + case Super(qual, mix) => + tree + case tree @ Apply(fun, args) => + val MethodType(_, tpes, _) = fun.tpe.widen + Apply.copy(tree)(transformTerm(fun, defn.AnyType), transformTerms(args, tpes)) + case TypeApply(fun, args) => + TypeApply.copy(tree)(transformTerm(fun, defn.AnyType), args) + case _: Literal => + tree + case New(tpt) => + New.copy(tree)(transformTypeTree(tpt)) + case Typed(expr, tpt) => + val tp = tpt.tpe match + // TODO improve code + case AppliedType(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), ""), List(IsType(tp0))) => + type T + val a = tp0.seal.asInstanceOf[quoted.Type[T]] + '[Seq[$a]].unseal.tpe + case tp => tp + Typed.copy(tree)(transformTerm(expr, tp), transformTypeTree(tpt)) + case tree: NamedArg => + NamedArg.copy(tree)(tree.name, transformTerm(tree.value, tpe)) + case Assign(lhs, rhs) => + Assign.copy(tree)(lhs, transformTerm(rhs, lhs.tpe.widen)) + case Block(stats, expr) => + Block.copy(tree)(transformStats(stats), transformTerm(expr, tpe)) + case If(cond, thenp, elsep) => + If.copy(tree)( + transformTerm(cond, defn.BooleanType), + transformTerm(thenp, tpe), + transformTerm(elsep, tpe)) + case _: Closure => + tree + case Match(selector, cases) => + Match.copy(tree)(transformTerm(selector, selector.tpe), transformCaseDefs(cases, tpe)) + case Return(expr) => + // FIXME + // ctx.owner seems to be set to the wrong symbol + // Return.copy(tree)(transformTerm(expr, expr.tpe)) + tree + case While(cond, body) => + While.copy(tree)(transformTerm(cond, defn.BooleanType), transformTerm(body, defn.AnyType)) + case Try(block, cases, finalizer) => + Try.copy(tree)(transformTerm(block, tpe), transformCaseDefs(cases, defn.AnyType), finalizer.map(x => transformTerm(x, defn.AnyType))) + case Repeated(elems, elemtpt) => + Repeated.copy(tree)(transformTerms(elems, elemtpt.tpe), elemtpt) + case Inlined(call, bindings, expansion) => + Inlined.copy(tree)(call, transformDefinitions(bindings), transformTerm(expansion, tpe)/*()call.symbol.localContext)*/) + } + + def transformTerm(tree: Term, tpe: Type)(given ctx: Context): Term = + tree match { + case _: Closure => + tree + case _: Inlined | _: Select => + transformTermChildren(tree, tpe) + case _ => + tree.tpe.widen match { + case _: MethodType | _: PolyType => + transformTermChildren(tree, tpe) + case _ => + type X + val expr = tree.seal.asInstanceOf[Expr[X]] + val t = tpe.seal.asInstanceOf[quoted.Type[X]] + map(expr)(given qctx, t).unseal + } + } + + def transformTypeTree(tree: TypeTree)(given ctx: Context): TypeTree = tree + + def transformCaseDef(tree: CaseDef, tpe: Type)(given ctx: Context): CaseDef = + CaseDef.copy(tree)(tree.pattern, tree.guard.map(x => transformTerm(x, defn.BooleanType)), transformTerm(tree.rhs, tpe)) + + def transformTypeCaseDef(tree: TypeCaseDef)(given ctx: Context): TypeCaseDef = { + TypeCaseDef.copy(tree)(transformTypeTree(tree.pattern), transformTypeTree(tree.rhs)) + } + + def transformStats(trees: List[Statement])(given ctx: Context): List[Statement] = + trees mapConserve (transformStatement(_)) + + def transformDefinitions(trees: List[Definition])(given ctx: Context): List[Definition] = + trees mapConserve (transformDefinition(_)) + + def transformTerms(trees: List[Term], tpes: List[Type])(given ctx: Context): List[Term] = + var tpes2 = tpes // TODO use proper zipConserve + trees mapConserve { x => + val tpe :: tail = tpes2 + tpes2 = tail + transformTerm(x, tpe) + } + + def transformTerms(trees: List[Term], tpe: Type)(given ctx: Context): List[Term] = + trees.mapConserve(x => transformTerm(x, tpe)) + + def transformTypeTrees(trees: List[TypeTree])(given ctx: Context): List[TypeTree] = + trees mapConserve (transformTypeTree(_)) + + def transformCaseDefs(trees: List[CaseDef], tpe: Type)(given ctx: Context): List[CaseDef] = + trees mapConserve (x => transformCaseDef(x, tpe)) + + def transformTypeCaseDefs(trees: List[TypeCaseDef])(given ctx: Context): List[TypeCaseDef] = + trees mapConserve (transformTypeCaseDef(_)) + + } + new MapChildren().transformTermChildren(e.unseal, tpe.unseal.tpe).seal.cast[T] // Cast will only fail if this implementation has a bug + } + +} diff --git a/library/src/scala/tasty/reflect/CompilerInterface.scala b/library/src/scala/tasty/reflect/CompilerInterface.scala index 32754b1cf41a..8bb2258519da 100644 --- a/library/src/scala/tasty/reflect/CompilerInterface.scala +++ b/library/src/scala/tasty/reflect/CompilerInterface.scala @@ -1255,6 +1255,12 @@ trait CompilerInterface { def Symbol_noSymbol(given ctx: Context): Symbol + def Symbol_typeRef(symbol: Symbol)(given ctx: Context): TypeOrBounds + + def Symbol_termRef(symbol: Symbol)(given ctx: Context): TypeOrBounds + + def Symbol_info(symbol: Symbol)(given ctx: Context): TypeOrBounds + // // FLAGS // diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index cceaf785fe17..682f03741362 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -143,6 +143,16 @@ trait SymbolOps extends Core { selfSymbolOps: FlagsOps => /** The symbol of the companion module */ def companionModule(given ctx: Context): Symbol = internal.Symbol_companionModule(self) + + def typeRef(given ctx: Context): TypeOrBounds = + internal.Symbol_typeRef(self) + + def termRef(given ctx: Context): TypeOrBounds = + internal.Symbol_termRef(self) + + def info(given ctx: Context): TypeOrBounds = + internal.Symbol_info(self) + } } diff --git a/tests/run-macros/expr-map-1.check b/tests/run-macros/expr-map-1.check new file mode 100644 index 000000000000..edd7344f60f0 --- /dev/null +++ b/tests/run-macros/expr-map-1.check @@ -0,0 +1,26 @@ +oof +oofoof +ylppa +kcolb +kcolb +neht +esle +lav +vals +fed +defs +fed +rab +yrt +yllanif +hctac +elihw +wen +depyt +depyt +grAdeman +qual +adbmal +ravsgra +hctam +def diff --git a/tests/run-macros/expr-map-1/Macro_1.scala b/tests/run-macros/expr-map-1/Macro_1.scala new file mode 100644 index 000000000000..cd3f20726e3d --- /dev/null +++ b/tests/run-macros/expr-map-1/Macro_1.scala @@ -0,0 +1,18 @@ +import scala.quoted._ +import scala.quoted.matching._ + +inline def rewrite[T](x: => Any): Any = ${ stringRewriter('x) } + +private def stringRewriter(e: Expr[Any])(given QuoteContext): Expr[Any] = + StringRewriter.map(e) + +private object StringRewriter extends util.ExprMap { + + def map[T](e: Expr[T])(given QuoteContext, Type[T]): Expr[T] = e match + case Const(s: String) => + Expr(s.reverse) match + case '{ $x: T } => x + case _ => e // e had a singlton String type + case _ => mapChildren(e) + +} diff --git a/tests/run-macros/expr-map-1/Test_2.scala b/tests/run-macros/expr-map-1/Test_2.scala new file mode 100644 index 000000000000..9c8e93146569 --- /dev/null +++ b/tests/run-macros/expr-map-1/Test_2.scala @@ -0,0 +1,122 @@ +object Test { + + def main(args: Array[String]): Unit = { + println(rewrite("foo")) + println(rewrite("foo" + "foo")) + + rewrite { + println("apply") + } + + rewrite { + println("block") + println("block") + } + + val b: Boolean = true + rewrite { + if b then println("then") + else println("else") + } + + rewrite { + if !b then println("then") + else println("else") + } + + rewrite { + val s: String = "val" + println(s) + } + + rewrite { + val s: "vals" = "vals" + println(s) // prints "foo" not "oof" + } + + rewrite { + def s: String = "def" + println(s) + } + + rewrite { + def s: "defs" = "defs" + println(s) // prints "foo" not "oof" + } + + rewrite { + def s(x: String): String = x + println(s("def")) + } + + rewrite { + var s: String = "var" + s = "bar" + println(s) + } + + rewrite { + try println("try") + finally println("finally") + } + + rewrite { + try throw new Exception() + catch case x: Exception => println("catch") + } + + rewrite { + var x = true + while (x) { + println("while") + x = false + } + } + + rewrite { + val t = new Tuple1("new") + println(t._1) + } + + rewrite { + println("typed": String) + println("typed": Any) + } + + rewrite { + val f = new Foo(foo = "namedArg") + println(f.foo) + } + + rewrite { + println("qual".reverse) + } + + rewrite { + val f = () => "lambda" + println(f()) + } + + rewrite { + def f(args: String*): String = args.mkString + println(f("var", "args")) + } + + rewrite { + "match" match { + case "match" => println("match") + case x => println("x") + } + } + + // FIXME should print fed + rewrite { + def s: String = return "def" + println(s) + } + + } + +} + +class Foo(val foo: String) diff --git a/tests/run-macros/flops-rewrite-2/Macro_1.scala b/tests/run-macros/flops-rewrite-2/Macro_1.scala index 9e7424ed2bd2..a792a2da9f19 100644 --- a/tests/run-macros/flops-rewrite-2/Macro_1.scala +++ b/tests/run-macros/flops-rewrite-2/Macro_1.scala @@ -35,7 +35,7 @@ private def rewriteMacro[T: Type](x: Expr[T])(given QuoteContext): Expr[T] = { fixPoint = true ) - val x2 = rewriter.rewrite(x) + val x2 = rewriter.map(x) '{ println(${Expr(x.show)}) @@ -63,39 +63,13 @@ private object Rewriter { new Rewriter(preTransform, postTransform, fixPoint) } -private class Rewriter(preTransform: List[Transformation[_]] = Nil, postTransform: List[Transformation[_]] = Nil, fixPoint: Boolean) { - def rewrite[T](e: Expr[T])(given QuoteContext, Type[T]): Expr[T] = { +private class Rewriter(preTransform: List[Transformation[_]] = Nil, postTransform: List[Transformation[_]] = Nil, fixPoint: Boolean) extends util.ExprMap { + def map[T](e: Expr[T])(given QuoteContext, Type[T]): Expr[T] = { val e2 = preTransform.foldLeft(e)((ei, transform) => transform(ei)) - val e3 = rewriteChildren(e2) + val e3 = mapChildren(e2) val e4 = postTransform.foldLeft(e3)((ei, transform) => transform(ei)) - if fixPoint && e4 != e then rewrite(e4) + if fixPoint && e4 != e then map(e4) else e4 } - def rewriteChildren[T: Type](e: Expr[T])(given qctx: QuoteContext): Expr[T] = { - import qctx.tasty.{_, given} - class MapChildren extends TreeMap { - override def transformTerm(tree: Term)(given ctx: Context): Term = tree match { - case IsClosure(_) => - tree - case IsInlined(_) | IsSelect(_) => - transformChildrenTerm(tree) - case _ => - tree.tpe.widen match { - case IsMethodType(_) | IsPolyType(_) => - transformChildrenTerm(tree) - case _ => - tree.seal match { - case '{ $x: $t } => rewrite(x).unseal - } - } - } - def transformChildrenTerm(tree: Term)(given ctx: Context): Term = - super.transformTerm(tree) - } - (new MapChildren).transformChildrenTerm(e.unseal).seal.cast[T] // Cast will only fail if this implementation has a bug - } - } - - diff --git a/tests/run-macros/flops-rewrite-3/Macro_1.scala b/tests/run-macros/flops-rewrite-3/Macro_1.scala index 05df12a82f97..6e8738fb8824 100644 --- a/tests/run-macros/flops-rewrite-3/Macro_1.scala +++ b/tests/run-macros/flops-rewrite-3/Macro_1.scala @@ -33,7 +33,7 @@ private def rewriteMacro[T: Type](x: Expr[T])(given QuoteContext): Expr[T] = { } ) - val x2 = rewriter.rewrite(x) + val x2 = rewriter.map(x) '{ println(${Expr(x.show)}) @@ -92,7 +92,7 @@ private object Rewriter { def apply(): Rewriter = new Rewriter(Nil, Nil, false) } -private class Rewriter private (preTransform: List[Transformation] = Nil, postTransform: List[Transformation] = Nil, fixPoint: Boolean) { +private class Rewriter private (preTransform: List[Transformation] = Nil, postTransform: List[Transformation] = Nil, fixPoint: Boolean) extends util.ExprMap { def withFixPoint: Rewriter = new Rewriter(preTransform, postTransform, fixPoint = true) @@ -101,37 +101,11 @@ private class Rewriter private (preTransform: List[Transformation] = Nil, postTr def withPost(transform: Transformation): Rewriter = new Rewriter(preTransform, transform :: postTransform, fixPoint) - def rewrite[T](e: Expr[T])(given QuoteContext, Type[T]): Expr[T] = { + def map[T](e: Expr[T])(given QuoteContext, Type[T]): Expr[T] = { val e2 = preTransform.foldLeft(e)((ei, transform) => transform(ei)) - val e3 = rewriteChildren(e2) + val e3 = mapChildren(e2) val e4 = postTransform.foldLeft(e3)((ei, transform) => transform(ei)) - if fixPoint && e4 != e then rewrite(e4) else e4 - } - - def rewriteChildren[T: Type](e: Expr[T])(given qctx: QuoteContext): Expr[T] = { - import qctx.tasty.{_, given} - class MapChildren extends TreeMap { - override def transformTerm(tree: Term)(given ctx: Context): Term = tree match { - case IsClosure(_) => - tree - case IsInlined(_) | IsSelect(_) => - transformChildrenTerm(tree) - case _ => - tree.tpe.widen match { - case IsMethodType(_) | IsPolyType(_) => - transformChildrenTerm(tree) - case _ => - tree.seal match { - case '{ $x: $t } => rewrite(x).unseal - } - } - } - def transformChildrenTerm(tree: Term)(given ctx: Context): Term = - super.transformTerm(tree) - } - (new MapChildren).transformChildrenTerm(e.unseal).seal.cast[T] // Cast will only fail if this implementation has a bug + if fixPoint && e4 != e then map(e4) else e4 } } - - diff --git a/tests/run-macros/flops-rewrite/Macro_1.scala b/tests/run-macros/flops-rewrite/Macro_1.scala index 4c432283b74c..520cfdb5143a 100644 --- a/tests/run-macros/flops-rewrite/Macro_1.scala +++ b/tests/run-macros/flops-rewrite/Macro_1.scala @@ -13,7 +13,7 @@ private def rewriteMacro[T: Type](x: Expr[T])(given QuoteContext): Expr[T] = { } ) - val x2 = rewriter.rewrite(x) + val x2 = rewriter.map(x) '{ println(${Expr(x.show)}) @@ -28,12 +28,12 @@ private object Rewriter { new Rewriter(preTransform, postTransform, fixPoint) } -private class Rewriter(preTransform: Expr[Any] => Expr[Any], postTransform: Expr[Any] => Expr[Any], fixPoint: Boolean) { - def rewrite[T](e: Expr[T])(given QuoteContext, Type[T]): Expr[T] = { +private class Rewriter(preTransform: Expr[Any] => Expr[Any], postTransform: Expr[Any] => Expr[Any], fixPoint: Boolean) extends util.ExprMap { + def map[T](e: Expr[T])(given QuoteContext, Type[T]): Expr[T] = { val e2 = checkedTransform(e, preTransform) - val e3 = rewriteChildren(e2) + val e3 = mapChildren(e2) val e4 = checkedTransform(e3, postTransform) - if fixPoint && e4 != e then rewrite(e4) + if fixPoint && e4 != e then map(e4) else e4 } @@ -54,30 +54,4 @@ private class Rewriter(preTransform: Expr[Any] => Expr[Any], postTransform: Expr } } - def rewriteChildren[T: Type](e: Expr[T])(given qctx: QuoteContext): Expr[T] = { - import qctx.tasty.{_, given} - class MapChildren extends TreeMap { - override def transformTerm(tree: Term)(given ctx: Context): Term = tree match { - case IsClosure(_) => - tree - case IsInlined(_) | IsSelect(_) => - transformChildrenTerm(tree) - case _ => - tree.tpe.widen match { - case IsMethodType(_) | IsPolyType(_) => - transformChildrenTerm(tree) - case _ => - tree.seal match { - case '{ $x: $t } => rewrite(x).unseal - } - } - } - def transformChildrenTerm(tree: Term)(given ctx: Context): Term = - super.transformTerm(tree) - } - (new MapChildren).transformChildrenTerm(e.unseal).seal.cast[T] // Cast will only fail if this implementation has a bug - } - } - - From 7dd57a850ac3f7cb9d94dbc23adcac0eaa29f462 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 19 Nov 2019 13:19:02 +0100 Subject: [PATCH 2/2] Add Expr.matches --- library/src/scala/quoted/Expr.scala | 11 +++++++++++ tests/run-macros/flops-rewrite-2/Macro_1.scala | 2 +- tests/run-macros/flops-rewrite-3/Macro_1.scala | 2 +- tests/run-macros/flops-rewrite/Macro_1.scala | 2 +- tests/run-staging/expr-matches.scala | 13 +++++++++++++ 5 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 tests/run-staging/expr-matches.scala diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index ed9a35c5bea1..8d37819b107f 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -19,6 +19,17 @@ package quoted { */ final def getValue[U >: T](given qctx: QuoteContext, valueOf: ValueOfExpr[U]): Option[U] = valueOf(this) + /** Pattern matches `this` against `that`. Effectively performing a deep equality check. + * It does the equivalent of + * ``` + * this match + * case '{...} => true // where the contens of the pattern are the contents of `that` + * case _ => false + * ``` + */ + final def matches(that: Expr[Any])(given qctx: QuoteContext): Boolean = + !scala.internal.quoted.Expr.unapply[Unit, Unit](this)(given that, false, qctx).isEmpty + } object Expr { diff --git a/tests/run-macros/flops-rewrite-2/Macro_1.scala b/tests/run-macros/flops-rewrite-2/Macro_1.scala index a792a2da9f19..11e29d1fe7df 100644 --- a/tests/run-macros/flops-rewrite-2/Macro_1.scala +++ b/tests/run-macros/flops-rewrite-2/Macro_1.scala @@ -68,7 +68,7 @@ private class Rewriter(preTransform: List[Transformation[_]] = Nil, postTransfor val e2 = preTransform.foldLeft(e)((ei, transform) => transform(ei)) val e3 = mapChildren(e2) val e4 = postTransform.foldLeft(e3)((ei, transform) => transform(ei)) - if fixPoint && e4 != e then map(e4) + if fixPoint && !e4.matches(e) then map(e4) else e4 } diff --git a/tests/run-macros/flops-rewrite-3/Macro_1.scala b/tests/run-macros/flops-rewrite-3/Macro_1.scala index 6e8738fb8824..61ebff34396e 100644 --- a/tests/run-macros/flops-rewrite-3/Macro_1.scala +++ b/tests/run-macros/flops-rewrite-3/Macro_1.scala @@ -105,7 +105,7 @@ private class Rewriter private (preTransform: List[Transformation] = Nil, postTr val e2 = preTransform.foldLeft(e)((ei, transform) => transform(ei)) val e3 = mapChildren(e2) val e4 = postTransform.foldLeft(e3)((ei, transform) => transform(ei)) - if fixPoint && e4 != e then map(e4) else e4 + if fixPoint && !e4.matches(e) then map(e4) else e4 } } diff --git a/tests/run-macros/flops-rewrite/Macro_1.scala b/tests/run-macros/flops-rewrite/Macro_1.scala index 520cfdb5143a..7bc70965f5d7 100644 --- a/tests/run-macros/flops-rewrite/Macro_1.scala +++ b/tests/run-macros/flops-rewrite/Macro_1.scala @@ -33,7 +33,7 @@ private class Rewriter(preTransform: Expr[Any] => Expr[Any], postTransform: Expr val e2 = checkedTransform(e, preTransform) val e3 = mapChildren(e2) val e4 = checkedTransform(e3, postTransform) - if fixPoint && e4 != e then map(e4) + if fixPoint && !e4.matches(e) then map(e4) else e4 } diff --git a/tests/run-staging/expr-matches.scala b/tests/run-staging/expr-matches.scala new file mode 100644 index 000000000000..2f0255fbadfb --- /dev/null +++ b/tests/run-staging/expr-matches.scala @@ -0,0 +1,13 @@ +import scala.quoted._ +import scala.quoted.staging._ + + +object Test { + given Toolbox = Toolbox.make(getClass.getClassLoader) + def main(args: Array[String]): Unit = withQuoteContext { + assert('{1} matches '{1}) + assert('{println("foo")} matches '{println("foo")}) + assert('{println("foo")} matches '{println(${Expr("foo")})}) + assert('{println(Some("foo"))} matches '{println(${ val a = '{Some("foo")}; a})}) + } +}