From a88648462b4f4ed93c5ff8accc2a4fbf8f8b26fe Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 23 Apr 2021 10:51:44 +0200 Subject: [PATCH] Fix Reflection wildcard abstraction Currently, we are missing in the API the Wildcard concept for `case _ =>` patterns. These are encoded as term `Ident` with name `_`. This tree can be inconsistently matched by `Ident`, `TypeIdent` or `WildcardTypeTree`. There is also no way to create an `Ident(_)`. The typed expression can contain non-Term trees: Wildcard, Alternatives, Bind and Unapply. The solution is to add a TypedTree supertype of Typed that contains a Tree. Changes * `Ident` does not match `Ident(_)` * `TypeIdent` does not match `Ident(_)` * `WildcardTypeTree` does not match `Ident(_)` if it is a term * Add `WildcardPattern` type that matches a term `Ident(_)` * `Typed` only matched if the expr is a `Term` * Add `TypedTree` Fixes #12188 --- community-build/community-projects/protoquill | 2 +- .../quoted/runtime/impl/QuotesImpl.scala | 58 ++++++++++++- .../runtime/impl/printers/Extractors.scala | 4 + .../runtime/impl/printers/SourceCode.scala | 27 ++++-- library/src/scala/quoted/Quotes.scala | 82 ++++++++++++++++--- project/MiMaFilters.scala | 5 ++ tests/pos-macros/i11401/X_1.scala | 18 ++-- tests/pos-macros/i12188b/Macro_1.scala | 13 +++ tests/pos-macros/i12188b/Test_2.scala | 6 ++ tests/pos-macros/i12188c/Macro_1.scala | 15 ++++ tests/pos-macros/i12188c/Test_2.scala | 2 + .../Yretain-trees/tasty-definitions-2.check | 2 +- .../Yretain-trees/tasty-definitions-3.check | 2 +- tests/run-macros/i12188.check | 3 + tests/run-macros/i12188/Macro_1.scala | 23 ++++++ tests/run-macros/i12188/Test_2.scala | 8 ++ tests/run-macros/tasty-extractors-1.check | 20 ++--- tests/run-macros/tasty-extractors-2.check | 2 +- tests/run-staging/i5161.check | 2 +- 19 files changed, 248 insertions(+), 46 deletions(-) create mode 100644 tests/pos-macros/i12188b/Macro_1.scala create mode 100644 tests/pos-macros/i12188b/Test_2.scala create mode 100644 tests/pos-macros/i12188c/Macro_1.scala create mode 100644 tests/pos-macros/i12188c/Test_2.scala create mode 100644 tests/run-macros/i12188.check create mode 100644 tests/run-macros/i12188/Macro_1.scala create mode 100644 tests/run-macros/i12188/Test_2.scala diff --git a/community-build/community-projects/protoquill b/community-build/community-projects/protoquill index 41532f2b193e..ef5524bb9fab 160000 --- a/community-build/community-projects/protoquill +++ b/community-build/community-projects/protoquill @@ -1 +1 @@ -Subproject commit 41532f2b193e00aec49dd2fa46247d6cb0a6b64d +Subproject commit ef5524bb9fabbbed237f343b57c1a78a38c3dced diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index f94eac90f037..d45f39388403 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -341,11 +341,19 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object TermTypeTest extends TypeTest[Tree, Term]: def unapply(x: Tree): Option[Term & x.type] = x match - case x: tpd.PatternTree => None + case x: (tpd.Ident & x.type) => + if x.isTerm && x.name != nme.WILDCARD then Some(x) + else None + case x: (tpd.Typed & x.type) => + // Matches `Typed` but not `TypedTree` + TypedTypeTest.unapply(x) case x: (tpd.SeqLiteral & x.type) => Some(x) case x: (tpd.Inlined & x.type) => Some(x) case x: (tpd.NamedArg & x.type) => Some(x) - case _ => if x.isTerm then Some(x) else None + case x: tpd.PatternTree => None + case _ => + if x.isTerm then Some(x) + else None end TermTypeTest object Term extends TermModule: @@ -429,7 +437,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object IdentTypeTest extends TypeTest[Tree, Ident]: def unapply(x: Tree): Option[Ident & x.type] = x match - case x: (tpd.Ident & x.type) if x.isTerm => Some(x) + case x: (tpd.Ident & x.type) if x.isTerm && x.name != nme.WILDCARD => Some(x) case _ => None end IdentTypeTest @@ -655,7 +663,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object TypedTypeTest extends TypeTest[Tree, Typed]: def unapply(x: Tree): Option[Typed & x.type] = x match - case x: (tpd.Typed & x.type) => Some(x) + case x: (tpd.Typed & x.type) => + x.expr match + case TermTypeTest(_) => Some(x) + case _ => None case _ => None end TypedTypeTest @@ -1406,6 +1417,45 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension end TypeCaseDefMethods + + type WildcardPattern = tpd.Ident + + object WildcardPatternTypeTest extends TypeTest[Tree, WildcardPattern]: + def unapply(x: Tree): Option[WildcardPattern & x.type] = x match + case x: (tpd.Ident & x.type) if x.name == nme.WILDCARD => Some(x) + case _ => None + end WildcardPatternTypeTest + + object WildcardPattern extends WildcardPatternModule: + def apply(): WildcardPattern = + withDefaultPos(untpd.Ident(nme.WILDCARD).withType(dotc.core.Symbols.defn.AnyType)) + def unapply(pattern: WildcardPattern): true = true + end WildcardPattern + + type TypedTree = tpd.Typed + + object TypedTreeTypeTest extends TypeTest[Tree, TypedTree]: + def unapply(x: Tree): Option[TypedTree & x.type] = x match + case x: (tpd.Typed & x.type) => Some(x) + case _ => None + end TypedTreeTypeTest + + object TypedTree extends TypedTreeModule: + def apply(expr: Term, tpt: TypeTree): Typed = + withDefaultPos(tpd.Typed(xCheckMacroValidExpr(expr), tpt)) + def copy(original: Tree)(expr: Term, tpt: TypeTree): Typed = + tpd.cpy.Typed(original)(xCheckMacroValidExpr(expr), tpt) + def unapply(x: Typed): (Term, TypeTree) = + (x.expr, x.tpt) + end TypedTree + + given TypedTreeMethods: TypedTreeMethods with + extension (self: Typed) + def tree: Tree = self.expr + def tpt: TypeTree = self.tpt + end extension + end TypedTreeMethods + type Bind = tpd.Bind object BindTypeTest extends TypeTest[Tree, Bind]: diff --git a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala index 361727bc8490..0502d9c9c477 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala @@ -166,12 +166,16 @@ object Extractors { this += "CaseDef(" += pat += ", " += guard += ", " += body += ")" case TypeCaseDef(pat, body) => this += "TypeCaseDef(" += pat += ", " += body += ")" + case WildcardPattern() => + this += "WildcardPattern()" case Bind(name, body) => this += "Bind(\"" += name += "\", " += body += ")" case Unapply(fun, implicits, patterns) => this += "Unapply(" += fun += ", " ++= implicits += ", " ++= patterns += ")" case Alternatives(patterns) => this += "Alternatives(" ++= patterns += ")" + case TypedTree(tree, tpt) => + this += "TypedTree(" += tree += ", " += tpt += ")" } def visitConstant(x: Constant): this.type = x match { diff --git a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala index 86f80a6ccba0..1d4afe9e22be 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala @@ -328,7 +328,7 @@ object SourceCode { } this - case Ident("_") => + case WildcardPattern() => this += "_" case tree: Ident => @@ -453,6 +453,15 @@ object SourceCode { printTypeOrAnnots(tpt.tpe) } } + case TypedTree(tree1, tpt) => + printPattern(tree1) + tree1 match + case WildcardPattern() => + this += ":" + printType(tpt.tpe) + case _ => // Alternatives, Unapply, Bind + this + case Assign(lhs, rhs) => printTree(lhs) @@ -896,13 +905,13 @@ object SourceCode { } private def printPattern(pattern: Tree): this.type = pattern match { - case Ident("_") => + case WildcardPattern() => this += "_" - case Bind(name, Ident("_")) => + case Bind(name, WildcardPattern()) => this += name - case Bind(name, Typed(Ident("_"), tpt)) => + case Bind(name, TypedTree(WildcardPattern(), tpt)) => this += highlightValDef(name) += ": " printTypeTree(tpt) @@ -928,9 +937,13 @@ object SourceCode { case Alternatives(trees) => inParens(printPatterns(trees, " | ")) - case Typed(Ident("_"), tpt) => - this += "_: " - printTypeTree(tpt) + case TypedTree(tree1, tpt) => + tree1 match + case WildcardPattern() => + this += "_: " + printTypeTree(tpt) + case _ => + printPattern(tree1) case v: Term => printTree(v) diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index d8f1c431a651..0333a22c98d5 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -133,7 +133,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * | +- Apply * | +- TypeApply * | +- Super - * | +- Typed * | +- Assign * | +- Block * | +- Closure @@ -146,7 +145,16 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * | +- Inlined * | +- SelectOuter * | +- While + * | +---+- Typed + * | / + * +- TypedTree +------------------ยท + * +- WildcardPattern + * +- Bind + * +- Unapply + * +- Alternatives * | + * +- CaseDef + * +- TypeCaseDef * | * +- TypeTree ----+- Inferred * | +- TypeIdent @@ -164,13 +172,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * | * +- TypeBoundsTree * +- WildcardTypeTree - * | - * +- CaseDef - * | - * +- TypeCaseDef - * +- Bind - * +- Unapply - * +- Alternatives * * +- ParamClause -+- TypeParamClause * +- TermParamClause @@ -1120,8 +1121,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `Typed` */ given TypedTypeTest: TypeTest[Tree, Typed] - /** Tree representing a type ascription `x: T` in the source code */ - type Typed <: Term + /** Tree representing a type ascription `x: T` in the source code. + * + * Also represents a pattern that contains a term `x`. + * Other `: T` patterns use the more general `TypedTree`. + */ + type Typed <: Term & TypedTree /** Module object of `type Typed` */ val Typed: TypedModule @@ -2049,6 +2054,56 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => // ----- Patterns ------------------------------------------------ + /** Pattern representing a `_` wildcard. */ + type WildcardPattern <: Tree + + /** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `WildcardPattern` */ + given WildcardPatternTypeTest: TypeTest[Tree, WildcardPattern] + + /** Module object of `type WildcardPattern` */ + val WildcardPattern: WildcardPatternModule + + /** Methods of the module object `val WildcardPattern` */ + trait WildcardPatternModule { this: WildcardPattern.type => + def apply(): WildcardPattern + def unapply(pattern: WildcardPattern): true + } + + /** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `TypedTree` */ + given TypedTreeTypeTest: TypeTest[Tree, TypedTree] + + /** Tree representing a type ascription or pattern `x: T` in the source code + * + * The tree `x` may contain a `Constant`, `Ref`, `Wildcard`, `Bind`, `Unapply` or `Alternatives`. + */ + type TypedTree <: Tree + + /** Module object of `type TypedTree` */ + val TypedTree: TypedTreeModule + + /** Methods of the module object `val TypedTree` */ + trait TypedTreeModule { this: TypedTree.type => + + /** Create a type ascription `: ` */ + def apply(expr: Tree, tpt: TypeTree): TypedTree + + def copy(original: Tree)(expr: Tree, tpt: TypeTree): TypedTree + + /** Matches `: ` */ + def unapply(x: TypedTree): (Tree, TypeTree) + } + + /** Makes extension methods on `TypedTree` available without any imports */ + given TypedTreeMethods: TypedTreeMethods + + /** Extension methods of `TypedTree` */ + trait TypedTreeMethods: + extension (self: TypedTree) + def tree: Tree + def tpt: TypeTree + end extension + end TypedTreeMethods + /** Pattern representing a `_ @ _` binding. */ type Bind <: Tree @@ -4341,9 +4396,11 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => case TypeBoundsTree(lo, hi) => foldTree(foldTree(x, lo)(owner), hi)(owner) case CaseDef(pat, guard, body) => foldTree(foldTrees(foldTree(x, pat)(owner), guard)(owner), body)(owner) case TypeCaseDef(pat, body) => foldTree(foldTree(x, pat)(owner), body)(owner) + case WildcardPattern() => x case Bind(_, body) => foldTree(x, body)(owner) case Unapply(fun, implicits, patterns) => foldTrees(foldTrees(foldTree(x, fun)(owner), implicits)(owner), patterns)(owner) case Alternatives(patterns) => foldTrees(x, patterns)(owner) + case TypedTree(tree1, tpt) => foldTree(foldTree(x, tree1)(owner), tpt)(owner) } } end TreeAccumulator @@ -4401,12 +4458,15 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => transformCaseDef(tree)(owner) case tree: TypeCaseDef => transformTypeCaseDef(tree)(owner) + case WildcardPattern() => tree case pattern: Bind => Bind.copy(pattern)(pattern.name, pattern.pattern) case pattern: Unapply => Unapply.copy(pattern)(transformTerm(pattern.fun)(owner), transformSubTrees(pattern.implicits)(owner), transformTrees(pattern.patterns)(owner)) case pattern: Alternatives => Alternatives.copy(pattern)(transformTrees(pattern.patterns)(owner)) + case TypedTree(expr, tpt) => + TypedTree.copy(tree)(transformTree(expr)(owner), transformTypeTree(tpt)(owner)) } } @@ -4457,7 +4517,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => case New(tpt) => New.copy(tree)(transformTypeTree(tpt)(owner)) case Typed(expr, tpt) => - Typed.copy(tree)(/*FIXME #12222: transformTerm(expr)(owner)*/transformTree(expr)(owner).asInstanceOf[Term], transformTypeTree(tpt)(owner)) + Typed.copy(tree)(transformTerm(expr)(owner), transformTypeTree(tpt)(owner)) case tree: NamedArg => NamedArg.copy(tree)(tree.name, transformTerm(tree.value)(owner)) case Assign(lhs, rhs) => diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index c4d52097eba8..7fd778d65323 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -11,5 +11,10 @@ object MiMaFilters { exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SourceFileMethods.getJPath"), exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SourceFileMethods.name"), exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SourceFileMethods.path"), + exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.WildcardPatternTypeTest"), + exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.WildcardPattern"), + exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedTreeTypeTest"), + exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedTree"), + exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedTreeMethods"), ) } diff --git a/tests/pos-macros/i11401/X_1.scala b/tests/pos-macros/i11401/X_1.scala index 92f868d996a4..b8a5d033ec58 100644 --- a/tests/pos-macros/i11401/X_1.scala +++ b/tests/pos-macros/i11401/X_1.scala @@ -16,7 +16,7 @@ class SLSelect[S]: def fold[S](s0:S)(step: (S,SLSelect[S])=> S): S = { ??? - } + } def fold_async[S](s0:S)(step: (S,SLSelect[S])=> Future[S]): Future[S] = { ??? @@ -27,7 +27,7 @@ class SLSelect[S]: await(s0.onRead(ch)(f).runAsync()) def runAsync(): Future[S] = ??? - + object X: @@ -36,21 +36,21 @@ object X: processImpl[T]('f) } - def processImpl[T:Type](t:Expr[T])(using Quotes):Expr[Future[T]] = + def processImpl[T:Type](t:Expr[T])(using Quotes):Expr[Future[T]] = import quotes.reflect._ val r = processTree[T](t.asTerm) r.asExprOf[Future[T]] - - def processTree[T:Type](using Quotes)(t: quotes.reflect.Term):quotes.reflect.Term = + + def processTree[T:Type](using Quotes)(t: quotes.reflect.Term):quotes.reflect.Term = import quotes.reflect._ val r: Term = t match case Inlined(_,List(),body) => processTree(body) - case Inlined(d,bindings,body) => + case Inlined(d,bindings,body) => Inlined(d,bindings,processTree[T](body)) case Block(stats,expr) => Block(stats,processTree(expr)) case Apply(Apply(TypeApply(Select(x,"fold"),targs),List(state)),List(fun)) => - val nFun = processLambda[T](fun) + val nFun = processLambda[T](fun) Apply(Apply(TypeApply(Select.unique(x,"fold_async"),targs),List(state)),List(nFun)) case Apply(TypeApply(Ident("await"),targs),List(body)) => body case Typed(x,tp) => Typed(processTree(x), Inferred(TypeRepr.of[Future].appliedTo(tp.tpe)) ) @@ -58,8 +58,8 @@ object X: val checker = new TreeMap() {} checker.transformTerm(r)(Symbol.spliceOwner) r - - def processLambda[T:Type](using Quotes)(fun: quotes.reflect.Term):quotes.reflect.Term = + + def processLambda[T:Type](using Quotes)(fun: quotes.reflect.Term):quotes.reflect.Term = import quotes.reflect._ def changeArgs(oldArgs:List[Tree], newArgs:List[Tree], body:Term, owner: Symbol):Term = diff --git a/tests/pos-macros/i12188b/Macro_1.scala b/tests/pos-macros/i12188b/Macro_1.scala new file mode 100644 index 000000000000..6af8c9c1a1d8 --- /dev/null +++ b/tests/pos-macros/i12188b/Macro_1.scala @@ -0,0 +1,13 @@ +import scala.quoted.* + +object MatchTest { + inline def test[T](inline obj: Any): Unit = ${testImpl('obj)} + + def testImpl[T](objExpr: Expr[T])(using Quotes): Expr[Unit] = { + import quotes.reflect.* + // test that the extractors work + val Inlined(None, Nil, Block(Nil, Match(param @ Ident("a"), List(CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))), CaseDef(WildcardPattern(), None, Block(Nil, Literal(UnitConstant()))))))) = objExpr.asTerm + // test that the constructors work + Block(Nil, Match(param, List(CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))), CaseDef(WildcardPattern(), None, Block(Nil, Literal(UnitConstant())))))).asExprOf[Unit] + } +} diff --git a/tests/pos-macros/i12188b/Test_2.scala b/tests/pos-macros/i12188b/Test_2.scala new file mode 100644 index 000000000000..f9abca65a1f8 --- /dev/null +++ b/tests/pos-macros/i12188b/Test_2.scala @@ -0,0 +1,6 @@ + +def test(a: Int) = MatchTest.test { + a match + case 1 => + case _ => +} diff --git a/tests/pos-macros/i12188c/Macro_1.scala b/tests/pos-macros/i12188c/Macro_1.scala new file mode 100644 index 000000000000..06891e79c933 --- /dev/null +++ b/tests/pos-macros/i12188c/Macro_1.scala @@ -0,0 +1,15 @@ +import scala.quoted.* + +object MatchTest { + inline def test(a: Int): Unit = ${testImpl('a)} + + def testImpl(a: Expr[Any])(using Quotes): Expr[Unit] = { + import quotes.reflect.* + val matchTree = Match(a.asTerm, List( + CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))), + CaseDef(Alternatives(List(Literal(IntConstant(2)), Literal(IntConstant(3)), Literal(IntConstant(4)))), None, Block(Nil, Literal(UnitConstant()))), + CaseDef(TypedTree(Alternatives(List(Literal(IntConstant(4)), Literal(IntConstant(5)))), TypeIdent(defn.IntClass)), None, Block(Nil, Literal(UnitConstant()))), + CaseDef(TypedTree(WildcardPattern(), TypeIdent(defn.IntClass)), None, Block(Nil, Literal(UnitConstant()))))) + matchTree.asExprOf[Unit] + } +} diff --git a/tests/pos-macros/i12188c/Test_2.scala b/tests/pos-macros/i12188c/Test_2.scala new file mode 100644 index 000000000000..90391314a3a7 --- /dev/null +++ b/tests/pos-macros/i12188c/Test_2.scala @@ -0,0 +1,2 @@ + +def test(a: Int) = MatchTest.test(a) \ No newline at end of file diff --git a/tests/run-custom-args/Yretain-trees/tasty-definitions-2.check b/tests/run-custom-args/Yretain-trees/tasty-definitions-2.check index 3c2b67edfde0..f5aa2c0fff49 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-definitions-2.check +++ b/tests/run-custom-args/Yretain-trees/tasty-definitions-2.check @@ -1,3 +1,3 @@ DefDef("foo", Nil, TypeIdent("Int"), Some(Apply(Select(Literal(IntConstant(1)), "+"), List(Literal(IntConstant(2)))))) ValDef("bar", TypeIdent("Int"), Some(Apply(Select(Literal(IntConstant(2)), "+"), List(Literal(IntConstant(3)))))) -Bind("x", Ident("_")) +Bind("x", WildcardPattern()) diff --git a/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check b/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check index 3c2b67edfde0..f5aa2c0fff49 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check +++ b/tests/run-custom-args/Yretain-trees/tasty-definitions-3.check @@ -1,3 +1,3 @@ DefDef("foo", Nil, TypeIdent("Int"), Some(Apply(Select(Literal(IntConstant(1)), "+"), List(Literal(IntConstant(2)))))) ValDef("bar", TypeIdent("Int"), Some(Apply(Select(Literal(IntConstant(2)), "+"), List(Literal(IntConstant(3)))))) -Bind("x", Ident("_")) +Bind("x", WildcardPattern()) diff --git a/tests/run-macros/i12188.check b/tests/run-macros/i12188.check new file mode 100644 index 000000000000..922215bbeac5 --- /dev/null +++ b/tests/run-macros/i12188.check @@ -0,0 +1,3 @@ +PC1 +PC2 +default diff --git a/tests/run-macros/i12188/Macro_1.scala b/tests/run-macros/i12188/Macro_1.scala new file mode 100644 index 000000000000..7f3858d93b56 --- /dev/null +++ b/tests/run-macros/i12188/Macro_1.scala @@ -0,0 +1,23 @@ +import scala.quoted.* + +object MatchTest { + inline def test[T](inline obj: T): String = ${testImpl('obj)} + + def testImpl[T](objExpr: Expr[T])(using qctx: Quotes, t: Type[T]): Expr[String] = { + import qctx.reflect.* + + val obj = objExpr.asTerm + val cases = obj.tpe.typeSymbol.children.map { child => + val subtype = TypeIdent(child) + val bind = Symbol.newBind(Symbol.spliceOwner, "c", Flags.EmptyFlags, subtype.tpe) + CaseDef(Bind(bind, Typed(Ref(bind), subtype)), None, Literal(StringConstant(subtype.show))) + } ::: { + CaseDef(WildcardPattern(), None, Literal(StringConstant("default"))) + } :: Nil + val bind = Symbol.newBind(Symbol.spliceOwner, "o", Flags.EmptyFlags, obj.tpe) + val result = Match(obj, cases) + val code = result.show(using Printer.TreeAnsiCode) + // println(code) + result.asExprOf[String] + } +} diff --git a/tests/run-macros/i12188/Test_2.scala b/tests/run-macros/i12188/Test_2.scala new file mode 100644 index 000000000000..a3d0f0de58fb --- /dev/null +++ b/tests/run-macros/i12188/Test_2.scala @@ -0,0 +1,8 @@ +sealed trait P +case class PC1(a: String) extends P +case class PC2(b: Int) extends P + +@main def Test = + println(MatchTest.test(PC1("ab"): P)) + println(MatchTest.test(PC2(10): P)) + println(MatchTest.test(null: P)) diff --git a/tests/run-macros/tasty-extractors-1.check b/tests/run-macros/tasty-extractors-1.check index 5e7a330779eb..fdd41b61638d 100644 --- a/tests/run-macros/tasty-extractors-1.check +++ b/tests/run-macros/tasty-extractors-1.check @@ -40,37 +40,37 @@ OrType(ConstantType(IntConstant(1)), ConstantType(IntConstant(2))) Inlined(None, Nil, Match(Literal(StringConstant("a")), List(CaseDef(Literal(StringConstant("a")), None, Block(Nil, Literal(UnitConstant())))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Match(Literal(StringConstant("b")), List(CaseDef(Bind("n", Ident("_")), None, Block(Nil, Literal(UnitConstant())))))) +Inlined(None, Nil, Match(Literal(StringConstant("b")), List(CaseDef(Bind("n", WildcardPattern()), None, Block(Nil, Literal(UnitConstant())))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Match(Literal(StringConstant("c")), List(CaseDef(Bind("n", Typed(Ident("_"), TypeIdent("String"))), None, Block(Nil, Literal(UnitConstant())))))) +Inlined(None, Nil, Match(Literal(StringConstant("c")), List(CaseDef(Bind("n", TypedTree(WildcardPattern(), TypeIdent("String"))), None, Block(Nil, Literal(UnitConstant())))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Match(Literal(StringConstant("e")), List(CaseDef(Ident("_"), None, Block(Nil, Literal(UnitConstant())))))) +Inlined(None, Nil, Match(Literal(StringConstant("e")), List(CaseDef(WildcardPattern(), None, Block(Nil, Literal(UnitConstant())))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Match(Literal(StringConstant("f")), List(CaseDef(Typed(Ident("_"), TypeIdent("String")), None, Block(Nil, Literal(UnitConstant())))))) +Inlined(None, Nil, Match(Literal(StringConstant("f")), List(CaseDef(TypedTree(WildcardPattern(), TypeIdent("String")), None, Block(Nil, Literal(UnitConstant())))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Match(Typed(Literal(StringConstant("g")), TypeIdent("Any")), List(CaseDef(Alternatives(List(Typed(Ident("_"), TypeIdent("String")), Typed(Ident("_"), TypeIdent("Int")))), None, Block(Nil, Literal(UnitConstant())))))) +Inlined(None, Nil, Match(Typed(Literal(StringConstant("g")), TypeIdent("Any")), List(CaseDef(Alternatives(List(TypedTree(WildcardPattern(), TypeIdent("String")), TypedTree(WildcardPattern(), TypeIdent("Int")))), None, Block(Nil, Literal(UnitConstant())))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Match(Literal(StringConstant("h")), List(CaseDef(Ident("_"), Some(Literal(BooleanConstant(false))), Block(Nil, Literal(UnitConstant())))))) +Inlined(None, Nil, Match(Literal(StringConstant("h")), List(CaseDef(WildcardPattern(), Some(Literal(BooleanConstant(false))), Block(Nil, Literal(UnitConstant())))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Block(List(ValDef("a", Inferred(), Some(Literal(StringConstant("o"))))), Match(Literal(StringConstant("i")), List(CaseDef(Bind("a", Ident("_")), None, Block(Nil, Literal(UnitConstant()))))))) +Inlined(None, Nil, Block(List(ValDef("a", Inferred(), Some(Literal(StringConstant("o"))))), Match(Literal(StringConstant("i")), List(CaseDef(Bind("a", WildcardPattern()), None, Block(Nil, Literal(UnitConstant()))))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Match(Ident("Nil"), List(CaseDef(Unapply(TypeApply(Select(Ident("List"), "unapplySeq"), List(Inferred())), Nil, List(Bind("a", Ident("_")), Bind("b", Ident("_")), Bind("c", Ident("_")))), None, Block(Nil, Literal(UnitConstant())))))) +Inlined(None, Nil, Match(Ident("Nil"), List(CaseDef(Unapply(TypeApply(Select(Ident("List"), "unapplySeq"), List(Inferred())), Nil, List(Bind("a", WildcardPattern()), Bind("b", WildcardPattern()), Bind("c", WildcardPattern()))), None, Block(Nil, Literal(UnitConstant())))))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Try(Literal(IntConstant(1)), List(CaseDef(Ident("_"), None, Block(Nil, Literal(UnitConstant())))), None)) +Inlined(None, Nil, Try(Literal(IntConstant(1)), List(CaseDef(WildcardPattern(), None, Block(Nil, Literal(UnitConstant())))), None)) OrType(ConstantType(IntConstant(1)), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")) Inlined(None, Nil, Try(Literal(IntConstant(2)), Nil, Some(Literal(UnitConstant())))) ConstantType(IntConstant(2)) -Inlined(None, Nil, Try(Literal(IntConstant(3)), List(CaseDef(Ident("_"), None, Block(Nil, Literal(UnitConstant())))), Some(Literal(UnitConstant())))) +Inlined(None, Nil, Try(Literal(IntConstant(3)), List(CaseDef(WildcardPattern(), None, Block(Nil, Literal(UnitConstant())))), Some(Literal(UnitConstant())))) OrType(ConstantType(IntConstant(3)), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")) Inlined(None, Nil, Literal(BooleanConstant(false))) diff --git a/tests/run-macros/tasty-extractors-2.check b/tests/run-macros/tasty-extractors-2.check index 1223a1fb71d9..4357cfa89b97 100644 --- a/tests/run-macros/tasty-extractors-2.check +++ b/tests/run-macros/tasty-extractors-2.check @@ -49,7 +49,7 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), None, List(DefDef("a", Nil, Inferred(), Some(Literal(IntConstant(0))))))), Literal(UnitConstant()))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Product"), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), None, List(DefDef("hashCode", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_hashCode"), List(This(Some("Foo")))))), DefDef("equals", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(Apply(Select(This(Some("Foo")), "eq"), List(TypeApply(Select(Ident("x$0"), "$asInstanceOf$"), List(Inferred())))), "||"), List(Match(Ident("x$0"), List(CaseDef(Bind("x$0", Typed(Ident("_"), Inferred())), None, Apply(Select(Literal(BooleanConstant(true)), "&&"), List(Apply(Select(Ident("x$0"), "canEqual"), List(This(Some("Foo"))))))), CaseDef(Ident("_"), None, Literal(BooleanConstant(false))))))))), DefDef("toString", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_toString"), List(This(Some("Foo")))))), DefDef("canEqual", List(TermParamClause(List(ValDef("that", Inferred(), None)))), Inferred(), Some(TypeApply(Select(Ident("that"), "isInstanceOf"), List(Inferred())))), DefDef("productArity", Nil, Inferred(), Some(Literal(IntConstant(0)))), DefDef("productPrefix", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), DefDef("productElement", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(Ident("_"), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("productElementName", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(Ident("_"), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("copy", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), Inferred()), Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))), DefDef("unapply", List(TermParamClause(List(ValDef("x$1", Inferred(), None)))), Singleton(Literal(BooleanConstant(true))), Some(Literal(BooleanConstant(true)))), DefDef("toString", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), TypeDef("MirroredMonoType", TypeBoundsTree(Inferred(), Inferred())), DefDef("fromProduct", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil)))))), Literal(UnitConstant()))) +Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Product"), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), None, List(DefDef("hashCode", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_hashCode"), List(This(Some("Foo")))))), DefDef("equals", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(Apply(Select(This(Some("Foo")), "eq"), List(TypeApply(Select(Ident("x$0"), "$asInstanceOf$"), List(Inferred())))), "||"), List(Match(Ident("x$0"), List(CaseDef(Bind("x$0", TypedTree(WildcardPattern(), Inferred())), None, Apply(Select(Literal(BooleanConstant(true)), "&&"), List(Apply(Select(Ident("x$0"), "canEqual"), List(This(Some("Foo"))))))), CaseDef(WildcardPattern(), None, Literal(BooleanConstant(false))))))))), DefDef("toString", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_toString"), List(This(Some("Foo")))))), DefDef("canEqual", List(TermParamClause(List(ValDef("that", Inferred(), None)))), Inferred(), Some(TypeApply(Select(Ident("that"), "isInstanceOf"), List(Inferred())))), DefDef("productArity", Nil, Inferred(), Some(Literal(IntConstant(0)))), DefDef("productPrefix", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), DefDef("productElement", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(WildcardPattern(), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("productElementName", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(WildcardPattern(), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("copy", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), Inferred()), Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))), DefDef("unapply", List(TermParamClause(List(ValDef("x$1", Inferred(), None)))), Singleton(Literal(BooleanConstant(true))), Some(Literal(BooleanConstant(true)))), DefDef("toString", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), TypeDef("MirroredMonoType", TypeBoundsTree(Inferred(), Inferred())), DefDef("fromProduct", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil)))))), Literal(UnitConstant()))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(ClassDef("Foo1", DefDef("", List(TermParamClause(List(ValDef("a", TypeIdent("Int"), None)))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), None, List(ValDef("a", Inferred(), None)))), Literal(UnitConstant()))) diff --git a/tests/run-staging/i5161.check b/tests/run-staging/i5161.check index a178c827d633..27c72498d7f1 100644 --- a/tests/run-staging/i5161.check +++ b/tests/run-staging/i5161.check @@ -1,6 +1,6 @@ run : Some(2) show : scala.Tuple2.apply[scala.Option[scala.Int], scala.Option[scala.Int]](scala.Some.apply[scala.Int](1), scala.Some.apply[scala.Int](1)) match { - case scala.Tuple2((scala.Some(x): scala.Some[scala.Int]), (scala.Some(y): scala.Some[scala.Int])) => + case scala.Tuple2(scala.Some(x), scala.Some(y)) => scala.Some.apply[scala.Int](x.+(y)) case _ => scala.None