diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 449714c6b6d6..243e4b789ba2 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1013,8 +1013,7 @@ object desugar { val arity = ts.length assert(arity <= Definitions.MaxTupleArity) def tupleTypeRef = defn.TupleType(arity) - if (arity == 1) ts.head - else if (arity == 0) + if (arity == 0) if (ctx.mode is Mode.Type) TypeTree(defn.UnitType) else unitLiteral else if (ctx.mode is Mode.Type) AppliedTypeTree(ref(tupleTypeRef), ts) else Apply(ref(tupleTypeRef.classSymbol.companionModule.termRef), ts) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 0bc50597d7b9..c9d884ac673c 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1178,6 +1178,18 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } + /** An extractor for typed splices */ + object Splice { + def apply(tree: Tree)(implicit ctx: Context): Tree = { + val argType = tree.tpe.baseType(defn.QuotedExprClass).argTypesHi.head + ref(defn.InternalQuoted_exprSplice).appliedToType(argType).appliedTo(tree) + } + def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match { + case Apply(fn, arg :: Nil) if fn.symbol == defn.InternalQuoted_exprSplice => Some(arg) + case _ => None + } + } + /** A key to be used in a context property that tracks enclosing inlined calls */ private val InlinedCalls = new Property.Key[List[Tree]] diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 4c51d26fce69..f95e4b37adce 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -93,7 +93,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def isType: Boolean = !isTerm } case class Throw(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree - case class Quote(t: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree + case class Quote(quoted: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class TypSplice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree case class DoWhile(body: Tree, cond: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree @@ -492,9 +492,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Throw if expr eq tree.expr => tree case _ => finalize(tree, untpd.Throw(expr)(tree.source)) } - def Quote(tree: Tree)(t: Tree)(implicit ctx: Context): Tree = tree match { - case tree: Quote if t eq tree.t => tree - case _ => finalize(tree, untpd.Quote(t)(tree.source)) + def Quote(tree: Tree)(quoted: Tree)(implicit ctx: Context): Tree = tree match { + case tree: Quote if quoted eq tree.quoted => tree + case _ => finalize(tree, untpd.Quote(quoted)(tree.source)) } def Splice(tree: Tree)(expr: Tree)(implicit ctx: Context): Tree = tree match { case tree: Splice if expr eq tree.expr => tree diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 9b0f5c0f69d7..5c1c678dc02c 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -705,14 +705,16 @@ class Definitions { lazy val QuotedExprType: TypeRef = ctx.requiredClassRef("scala.quoted.Expr") def QuotedExprClass(implicit ctx: Context): ClassSymbol = QuotedExprType.symbol.asClass - lazy val InternalQuotedModule: TermRef = ctx.requiredModuleRef("scala.internal.Quoted") - def InternalQuotedModuleClass: Symbol = InternalQuotedModule.symbol - lazy val InternalQuoted_exprQuoteR: TermRef = InternalQuotedModuleClass.requiredMethodRef("exprQuote".toTermName) + lazy val InternalQuotedModuleRef: TermRef = ctx.requiredModuleRef("scala.internal.Quoted") + def InternalQuotedModule: Symbol = InternalQuotedModuleRef.symbol + lazy val InternalQuoted_exprQuoteR: TermRef = InternalQuotedModule.requiredMethodRef("exprQuote") def InternalQuoted_exprQuote(implicit ctx: Context): Symbol = InternalQuoted_exprQuoteR.symbol - lazy val InternalQuoted_exprSpliceR: TermRef = InternalQuotedModuleClass.requiredMethodRef("exprSplice".toTermName) + lazy val InternalQuoted_exprSpliceR: TermRef = InternalQuotedModule.requiredMethodRef("exprSplice") def InternalQuoted_exprSplice(implicit ctx: Context): Symbol = InternalQuoted_exprSpliceR.symbol - lazy val InternalQuoted_typeQuoteR: TermRef = InternalQuotedModuleClass.requiredMethodRef("typeQuote".toTermName) + lazy val InternalQuoted_typeQuoteR: TermRef = InternalQuotedModule.requiredMethodRef("typeQuote") def InternalQuoted_typeQuote(implicit ctx: Context): Symbol = InternalQuoted_typeQuoteR.symbol + lazy val InternalQuoted_patternHoleR: TermRef = InternalQuotedModule.requiredMethodRef("patternHole") + def InternalQuoted_patternHole(implicit ctx: Context): Symbol = InternalQuoted_patternHoleR.symbol lazy val QuotedExprsModule: TermSymbol = ctx.requiredModule("scala.quoted.Exprs") def QuotedExprsClass(implicit ctx: Context): ClassSymbol = QuotedExprsModule.asClass @@ -723,24 +725,8 @@ class Definitions { lazy val QuotedType_spliceR: TypeRef = QuotedTypeClass.requiredType(tpnme.splice).typeRef def QuotedType_splice : Symbol = QuotedType_spliceR.symbol - lazy val QuotedTypeModuleType: TermRef = ctx.requiredModuleRef("scala.quoted.Type") - def QuotedTypeModule(implicit ctx: Context): Symbol = QuotedTypeModuleType.symbol - - lazy val QuotedLiftableModule: TermSymbol = ctx.requiredModule("scala.quoted.Liftable") - def QuotedLiftableModuleClass(implicit ctx: Context): ClassSymbol = QuotedLiftableModule.asClass - - def QuotedLiftable_BooleanIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("BooleanIsLiftable") - def QuotedLiftable_ByteIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("ByteIsLiftable") - def QuotedLiftable_CharIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("CharIsLiftable") - def QuotedLiftable_ShortIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("ShortIsLiftable") - def QuotedLiftable_IntIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("IntIsLiftable") - def QuotedLiftable_LongIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("LongIsLiftable") - def QuotedLiftable_FloatIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("FloatIsLiftable") - def QuotedLiftable_DoubleIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("DoubleIsLiftable") - def QuotedLiftable_StringIsLiftable: TermRef = QuotedLiftableModule.requiredMethodRef("StringIsLiftable") - - lazy val QuotedLiftableType: TypeRef = ctx.requiredClassRef("scala.quoted.Liftable") - def QuotedLiftableClass(implicit ctx: Context): ClassSymbol = QuotedLiftableType.symbol.asClass + lazy val QuotedTypeModuleRef: TermRef = ctx.requiredModuleRef("scala.quoted.Type") + def QuotedTypeModule(implicit ctx: Context): Symbol = QuotedTypeModuleRef.symbol def Unpickler_unpickleExpr: TermSymbol = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleExpr") def Unpickler_liftedExpr: TermSymbol = ctx.requiredMethod("scala.runtime.quoted.Unpickler.liftedExpr") @@ -752,6 +738,12 @@ class Definitions { lazy val TastyReflectionModule: TermSymbol = ctx.requiredModule("scala.tasty.Reflection") lazy val TastyReflection_macroContext: TermSymbol = TastyReflectionModule.requiredMethod("macroContext") + lazy val QuotedMatcherModuleRef: TermRef = ctx.requiredModuleRef("scala.runtime.quoted.Matcher") + def QuotedMatcherModule(implicit ctx: Context): Symbol = QuotedMatcherModuleRef.symbol + + lazy val QuotedMatcher_unapplyR: TermRef = QuotedMatcherModule.requiredMethodRef(nme.unapply) + def QuotedMatcher_unapply(implicit ctx: Context) = QuotedMatcher_unapplyR.symbol + lazy val EqlType: TypeRef = ctx.requiredClassRef("scala.Eql") def EqlClass(implicit ctx: Context): ClassSymbol = EqlType.symbol.asClass def EqlModule(implicit ctx: Context): Symbol = EqlClass.companionModule @@ -1178,7 +1170,9 @@ class Definitions { } def tupleType(elems: List[Type]): Type = { - TupleType(elems.size).appliedTo(elems) + val arity = elems.length + if (arity <= MaxTupleArity && TupleType(arity) != null) TupleType(arity).appliedTo(elems) + else TypeOps.nestedPairs(elems) } def isProductSubType(tp: Type)(implicit ctx: Context): Boolean = @@ -1271,8 +1265,9 @@ class Definitions { def adjustForTuple(cls: ClassSymbol, tparams: List[TypeSymbol], parents: List[Type]): List[Type] = { def syntheticParent(tparams: List[TypeSymbol]): Type = if (tparams.isEmpty) TupleTypeRef - else (tparams :\ (UnitType: Type)) ((tparam, tail) => PairType.appliedTo(tparam.typeRef, tail)) - if (isTupleClass(cls) || cls == UnitClass) parents :+ syntheticParent(tparams) else parents + else TypeOps.nestedPairs(tparams.map(_.typeRef)) + if (isTupleClass(cls) || cls == UnitClass) parents :+ syntheticParent(tparams) + else parents } // ----- primitive value class machinery ------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 1aa2c0f6f1ba..430d0b062c84 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -104,4 +104,7 @@ object Mode { /** Are we trying to find a hidden implicit? */ val FindHiddenImplicits: Mode = newMode(24, "FindHiddenImplicits") + + /** Are we in a quote in a pattern? */ + val QuotedPattern: Mode = newMode(25, "QuotedPattern") } diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 3d5482701ce8..250a7fdf7f58 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -567,4 +567,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object. object TypeOps { @sharable var track: Boolean = false // !!!DEBUG + + // TODO: Move other typeops here. It's a bit weird that they are a part of `ctx` + + def nestedPairs(ts: List[Type])(implicit ctx: Context): Type = + (ts :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 93a3a1e9c9d5..bbdf9fa97926 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1478,9 +1478,8 @@ object Parsers { /** SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody) * | BlockExpr - * | ‘'’ ‘{’ Block ‘}’ - * | ‘'’ ‘[’ Type ‘]’ * | ‘$’ ‘{’ Block ‘}’ + * | Quoted * | quoteId * | SimpleExpr1 [`_'] * SimpleExpr1 ::= literal @@ -1490,6 +1489,8 @@ object Parsers { * | SimpleExpr `.' id * | SimpleExpr (TypeArgs | NamedTypeArgs) * | SimpleExpr1 ArgumentExprs + * Quoted ::= ‘'’ ‘{’ Block ‘}’ + * | ‘'’ ‘[’ Type ‘]’ */ def simpleExpr(): Tree = { var canApply = true @@ -1827,6 +1828,7 @@ object Parsers { /** SimplePattern ::= PatVar * | Literal + * | Quoted * | XmlPattern * | `(' [Patterns] `)' * | SimplePattern1 [TypeArgs] [ArgumentPatterns] @@ -1853,6 +1855,8 @@ object Parsers { } else wildIndent case LPAREN => atSpan(in.offset) { makeTupleOrParens(inParens(patternsOpt())) } + case QUOTE => + simpleExpr() case XMLSTART => xmlLiteralPattern() case _ => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 337ece1c59e3..92033c49c9dd 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -179,7 +179,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val cls = tycon.typeSymbol if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction, cls.name.isErasedFunction) - if (tp.tupleArity >= 2) return toTextTuple(tp.tupleElementTypes) + if (tp.tupleArity >= 2 && !ctx.settings.YprintDebug.value) return toTextTuple(tp.tupleElementTypes) if (isInfixType(tp)) { val l :: r :: Nil = args val opName = tyconName(tycon) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala index 839fa32892fb..a0adbfe7ba42 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -48,8 +48,8 @@ object TypeUtils { else throw new AssertionError("not a tuple") } - /** The `*:` equivalent of an instantce of a Tuple class */ + /** The `*:` equivalent of an instance of a Tuple class */ def toNestedPairs(implicit ctx: Context): Type = - (tupleElementTypes :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) + TypeOps.nestedPairs(tupleElementTypes) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 24f20cc8c5df..79e2c9c20e41 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -78,14 +78,14 @@ trait Deriving { this: Typer => case _ => (sym.termRef, Nil) } - val elemShape = (elems :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) + val elemShape = TypeOps.nestedPairs(elems) defn.ShapeCaseType.appliedTo(constr, elemShape) } /** The shape of `cls` if `cls` is sealed */ private def sealedShape: Type = { val cases = children.map(caseShape).filter(_.exists) - val casesShape = (cases :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) + val casesShape = TypeOps.nestedPairs(cases) defn.ShapeCasesType.appliedTo(casesShape) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 48171be2327a..1c81ef0ec665 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1919,7 +1919,7 @@ class Typer extends Namer else { val elemTpes = (elems, pts).zipped.map((elem, pt) => ctx.typeComparer.widenInferred(elem.tpe, pt)) - val resTpe = (elemTpes :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) + val resTpe = TypeOps.nestedPairs(elemTpes) app1.cast(resTpe) } } @@ -1930,17 +1930,55 @@ class Typer extends Namer * while tracking the quotation level in the context. */ def typedQuote(tree: untpd.Quote, pt: Type)(implicit ctx: Context): Tree = track("typedQuote") { - tree.t match { + tree.quoted match { case untpd.Splice(innerExpr) => ctx.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.sourcePos) typed(innerExpr, pt) - case t if t.isType => - typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuoteR), List(tree.t)), pt)(quoteContext).withSpan(tree.span) - case t=> - typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuoteR), tree.t), pt)(quoteContext).withSpan(tree.span) + case quoted if quoted.isType => + typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuoteR), quoted :: Nil), pt)(quoteContext).withSpan(tree.span) + case quoted => + if (ctx.mode.is(Mode.Pattern) && level == 0) { + val exprPt = pt.baseType(defn.QuotedExprClass) + val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType + val quoted1 = typedExpr(quoted, quotedPt)(quoteContext.addMode(Mode.QuotedPattern)) + val (shape, splices) = splitQuotePattern(quoted1) + val patType = defn.tupleType(splices.tpes.map(_.widen)) + val splicePat = typed(untpd.Tuple(splices.map(untpd.TypedSplice(_))).withSpan(quoted.span), patType) + UnApply( + fun = ref(defn.QuotedMatcher_unapplyR).appliedToType(patType), + implicits = + ref(defn.InternalQuoted_exprQuoteR).appliedToType(shape.tpe).appliedTo(shape) :: + implicitArgTree(defn.TastyReflectionType, tree.span) :: Nil, + patterns = splicePat :: Nil, + proto = pt) + } + else + typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuoteR), quoted), pt)(quoteContext).withSpan(tree.span) + } + } + + def splitQuotePattern(quoted: Tree)(implicit ctx: Context): (Tree, List[Tree]) = { + object splitter extends tpd.TreeMap { + val patBuf = new mutable.ListBuffer[Tree] + override def transform(tree: Tree)(implicit ctx: Context) = tree match { + case Typed(Splice(pat), tpt) => + val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprType), tpt :: Nil) + transform(Splice(Typed(pat, exprTpt))) + case Splice(pat) => + try patternHole(tree) + finally patBuf += pat + case _ => + super.transform(tree) + } } + val result = splitter.transform(quoted) + (result, splitter.patBuf.toList) } + /** A hole the shape pattern of a quoted.Matcher.unapply, representing a splice */ + def patternHole(splice: Tree)(implicit ctx: Context): Tree = + ref(defn.InternalQuoted_patternHoleR).appliedToType(splice.tpe).withSpan(splice.span) + /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ def typedSplice(tree: untpd.Splice, pt: Type)(implicit ctx: Context): Tree = track("typedSplice") { checkSpliceOutsideQuote(tree) @@ -1949,7 +1987,14 @@ class Typer extends Namer ctx.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.sourcePos) typed(innerExpr, pt) case expr => - typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSpliceR), tree.expr), pt)(spliceContext).withSpan(tree.span) + if (ctx.mode.is(Mode.QuotedPattern) && level == 1) { + fullyDefinedType(pt, "quoted pattern selector", tree.span) + val pat = typedPattern(expr, defn.QuotedExprType.appliedTo(pt))( + spliceContext.retractMode(Mode.QuotedPattern)) + Splice(pat) + } + else + typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSpliceR), tree.expr), pt)(spliceContext).withSpan(tree.span) } } diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 9e3bf68924c9..74a9d33715d1 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -209,9 +209,8 @@ InfixExpr ::= PrefixExpr PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op) SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody) New(constr | templ) | BlockExpr - | ‘'’ ‘{’ Block ‘}’ - | ‘'’ ‘[’ Type ‘]’ | ‘$’ ‘{’ Block ‘}’ + | Quoted | quoteId // only inside splices | SimpleExpr1 [‘_’] PostfixOp(expr, _) SimpleExpr1 ::= Literal @@ -222,6 +221,8 @@ SimpleExpr1 ::= Literal | SimpleExpr (TypeArgs | NamedTypeArgs) TypeApply(expr, args) | SimpleExpr1 ArgumentExprs Apply(expr, args) | XmlExpr +Quoted ::= ‘'’ ‘{’ Block ‘}’ + | ‘'’ ‘[’ Type ‘]’ ExprsInParens ::= ExprInParens {‘,’ ExprInParens} ExprInParens ::= PostfixExpr ‘:’ Type | Expr @@ -261,6 +262,7 @@ InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar Ident(wildcard) | Literal Bind(name, Ident(wildcard)) | ‘(’ [Patterns] ‘)’ Parens(pats) Tuple(pats) + | Quoted | XmlPattern | SimplePattern1 [TypeArgs] [ArgumentPatterns] SimplePattern1 ::= Path diff --git a/library/src-bootstrapped/scala/internal/Quoted.scala b/library/src-bootstrapped/scala/internal/Quoted.scala index b3d3f41cec9e..4e6122e7006d 100644 --- a/library/src-bootstrapped/scala/internal/Quoted.scala +++ b/library/src-bootstrapped/scala/internal/Quoted.scala @@ -16,4 +16,7 @@ object Quoted { def typeQuote[T <: AnyKind]: Type[T] = throw new Error("Internal error: this method call should have been replaced by the compiler") + /** A splice in a quoted pattern is desugared by the compiler into a call to this method */ + def patternHole[T]: T = + throw new Error("Internal error: this method call should have been replaced by the compiler") } diff --git a/library/src/scala/runtime/quoted/Matcher.scala b/library/src/scala/runtime/quoted/Matcher.scala new file mode 100644 index 000000000000..b77baec24aec --- /dev/null +++ b/library/src/scala/runtime/quoted/Matcher.scala @@ -0,0 +1,10 @@ +package scala.runtime.quoted + +import scala.quoted.Expr +import scala.tasty.Reflection + +/** THIS IS A PLACEHOLDER + */ +object Matcher { + def unapply[Tup <: Tuple](scrut: Expr[_])(implicit pattern: Expr[_], reflection: Reflection): Option[Tup] = ??? +} diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index b96d82f5d92c..1680f8f5335c 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -1310,10 +1310,17 @@ trait Printers printPattern(pattern) case Pattern.Unapply(fun, implicits, patterns) => - fun match { - case Select(extractor, "unapply" | "unapplySeq") => printTree(extractor) - case TypeApply(Select(extractor, "unapply" | "unapplySeq"), _) => printTree(extractor) - case _ => throw new MatchError(fun.show) + val fun2 = fun match { + case TypeApply(fun2, _) => fun2 + case _ => fun + } + fun2 match { + case Select(extractor, "unapply" | "unapplySeq") => + printTree(extractor) + case Ident("unapply" | "unapplySeq") => + this += fun.symbol.owner.fullName.stripSuffix("$") + case _ => + throw new MatchError(fun.show) } inParens(printPatterns(patterns, ", ")) diff --git a/tests/neg/quotedPatterns.scala b/tests/neg/quotedPatterns.scala new file mode 100644 index 000000000000..cd067eb74cab --- /dev/null +++ b/tests/neg/quotedPatterns.scala @@ -0,0 +1,12 @@ +object Test { + + val x = '{1 + 2} + + def f(x: Int) = x + def g(x: Int, y: Int) = x * y + + def test given tasty.Reflection = x match { + case '{ val a = '{ println($y) }; 0 } => ??? // error: Not found: y + case _ => + } +} \ No newline at end of file diff --git a/tests/pos/quotedPatterns.scala b/tests/pos/quotedPatterns.scala new file mode 100644 index 000000000000..887104184a27 --- /dev/null +++ b/tests/pos/quotedPatterns.scala @@ -0,0 +1,19 @@ +object Test { + + val x = '{1 + 2} + + def f(x: Int) = x + def g(x: Int, y: Int) = x * y + + def res given tasty.Reflection: quoted.Expr[Int] = x match { + case '{1 + 2} => '{0} + case '{f($y)} => y + case '{g($y, $z)} => '{$y * $z} + case '{ 1 + ($y: Int)} => y + // currently gives an unreachable case warning + // but only when used in conjunction with the others. + // I believe this is because implicit arguments are not taken + // into account when checking whether we have already seen an `unapply` before. + case _ => '{1} + } +} \ No newline at end of file