Skip to content

Add quoted patterns #6198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 2, 2019
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]]

Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
47 changes: 21 additions & 26 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand All @@ -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
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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 ------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(_, _))
}
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1478,9 +1478,8 @@ object Parsers {

/** SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody)
* | BlockExpr
* | ‘'’ ‘{’ Block ‘}’
* | ‘'’ ‘[’ Type ‘]’
* | ‘$’ ‘{’ Block ‘}’
* | Quoted
* | quoteId
* | SimpleExpr1 [`_']
* SimpleExpr1 ::= literal
Expand All @@ -1490,6 +1489,8 @@ object Parsers {
* | SimpleExpr `.' id
* | SimpleExpr (TypeArgs | NamedTypeArgs)
* | SimpleExpr1 ArgumentExprs
* Quoted ::= ‘'’ ‘{’ Block ‘}’
* | ‘'’ ‘[’ Type ‘]’
*/
def simpleExpr(): Tree = {
var canApply = true
Expand Down Expand Up @@ -1827,6 +1828,7 @@ object Parsers {

/** SimplePattern ::= PatVar
* | Literal
* | Quoted
* | XmlPattern
* | `(' [Patterns] `)'
* | SimplePattern1 [TypeArgs] [ArgumentPatterns]
Expand All @@ -1853,6 +1855,8 @@ object Parsers {
} else wildIndent
case LPAREN =>
atSpan(in.offset) { makeTupleOrParens(inParens(patternsOpt())) }
case QUOTE =>
simpleExpr()
case XMLSTART =>
xmlLiteralPattern()
case _ =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/TypeUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Deriving.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
61 changes: 54 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand All @@ -1930,17 +1930,57 @@ 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) ::
givenReflection :: 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)

def givenReflection(implicit ctx: Context): Tree = Literal(Constant(null)) // FIXME: fill in

/** 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)
Expand All @@ -1949,7 +1989,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)
}
}

Expand Down
6 changes: 4 additions & 2 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions library/src-bootstrapped/scala/internal/Quoted.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this is the equivalent to def hole[T] in #6042, not to be confused with object patternHole { def unapply[T] = ... }.

throw new Error("Internal error: this method call should have been replaced by the compiler")
}
10 changes: 10 additions & 0 deletions library/src/scala/runtime/quoted/Matcher.scala
Original file line number Diff line number Diff line change
@@ -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] = ???
}
15 changes: 11 additions & 4 deletions library/src/scala/tasty/reflect/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, ", "))

Expand Down
12 changes: 12 additions & 0 deletions tests/neg/quotedPatterns.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
object Test {

val x = '{1 + 2}

def f(x: Int) = x
def g(x: Int, y: Int) = x * y

x match {
case '{ val a = '{ println($y) }; 0 } => ??? // error: Not found: y
case _ =>
}
}
Loading