diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 2436ad4081f2..d21d991f65ee 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1564,7 +1564,6 @@ object Parsers { * | `(' [Patterns] `)' * | SimplePattern1 [TypeArgs] [ArgumentPatterns] * SimplePattern1 ::= Path - * | `{' Block `}' * | SimplePattern1 `.' id * PatVar ::= id * | `_' @@ -1587,8 +1586,6 @@ object Parsers { } else wildIndent case LPAREN => atPos(in.offset) { makeTupleOrParens(inParens(patternsOpt())) } - case LBRACE => - dotSelectors(blockExpr()) case XMLSTART => xmlLiteralPattern() case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 7031b7920f2a..b5ca1c651443 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -35,7 +35,8 @@ class ReTyper extends Typer { override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { assert(tree.hasType, tree) - val qual1 = typed(tree.qualifier, AnySelectionProto) + // a qualifier cannot be a pattern + val qual1 = typed(tree.qualifier, AnySelectionProto)(ctx.retractMode(Mode.Pattern)) untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 20d7369bf11f..ff40b4a61a5a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -406,9 +406,23 @@ class Typer extends Namer tree.withType(ownType) } + checkStableIdentPattern(tree1, pt) checkValue(tree1, pt) } + /** Check that a stable identifier pattern is indeed stable (SLS 8.1.5) + */ + private def checkStableIdentPattern(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { + if (ctx.mode.is(Mode.Pattern) && + !tree.isType && + !pt.isInstanceOf[ApplyingProto] && + !tree.tpe.isStable && + !isWildcardArg(tree)) + ctx.error(s"stable identifier required, but ${tree.show} found", tree.pos) + + tree + } + private def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(implicit ctx: Context): Select = checkValue(assignType(cpy.Select(tree)(qual, tree.name), qual), pt) @@ -418,7 +432,7 @@ class Typer extends Namer val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) val select = typedSelect(tree, pt, qual1) - if (select.tpe ne TryDynamicCallType) select + if (select.tpe ne TryDynamicCallType) checkStableIdentPattern(select, pt) else if (pt.isInstanceOf[PolyProto] || pt.isInstanceOf[FunProto] || pt == AssignProto) select else typedDynamicSelect(tree, Nil, pt) } diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index ddc70855ff1d..40825d221589 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -202,7 +202,7 @@ ParArgumentExprs ::= ‘(’ ExprsInParens ‘)’ | ‘(’ [ExprsInParens] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar)) ArgumentExprs ::= ParArgumentExprs | [nl] BlockExpr -BlockExpr ::= ‘{’ BlockExprContents ‘}’ +BlockExpr ::= ‘{’ BlockExprContents ‘}’ BlockExprContents ::= CaseClauses | Block Block ::= {BlockStat semi} [BlockResult] Block(stats, expr?) BlockStat ::= Import @@ -234,7 +234,6 @@ SimplePattern ::= PatVar | XmlPattern | SimplePattern1 [TypeArgs] [ArgumentPatterns] SimplePattern1 ::= Path - | ‘{’ Block ‘}’ | SimplePattern1 ‘.’ id PatVar ::= varid | ‘_’ diff --git a/tests/neg/i1846.scala b/tests/neg/i1846.scala new file mode 100644 index 000000000000..f661a86d5daf --- /dev/null +++ b/tests/neg/i1846.scala @@ -0,0 +1,19 @@ +object Test { + def main(args: Array[String]): Unit = { + val x = 42 + val Y = "42" + + x match { case { 42 } => () } // error + x match { case { 42.toString } => () } // error + x match { case { 42 }.toString => () } // error + x match { case "42".toInt => () } // error + x match { case { "42".toInt } => () } // error + x match { case { "42" }.toInt => () } // error + x match { case { "42".toInt } => () } // error + x match { case Y => () } // error + x match { case { Y.toInt } => () } // error + x match { case { Y }.toInt => () } // error + x match { case { Y }.toString => () } // error + x match { case { Y.toString } => () } // error + } +} diff --git a/tests/neg/i3812.scala b/tests/neg/i3812.scala new file mode 100644 index 000000000000..182721d65ea0 --- /dev/null +++ b/tests/neg/i3812.scala @@ -0,0 +1,13 @@ +object Test { + def main(args: Array[String]): Unit = { + val x = 42 + val Y = "42" + + x match { case { 42 } => () } // error + x match { case { "42".toInt } => () } // error + x match { case { "42" }.toInt => () } // error + x match { case { "42".toInt } => () } // error + x match { case { Y.toInt } => () } // error + x match { case { Y }.toInt => () } // error + } +} diff --git a/tests/neg/i3812b.scala b/tests/neg/i3812b.scala new file mode 100644 index 000000000000..e9b0c291ef3f --- /dev/null +++ b/tests/neg/i3812b.scala @@ -0,0 +1,27 @@ +object Test { + def main(args: Array[String]): Unit = { + case class Box(v: Int) + + val x = 42 + var Y1 = 42 + val Y2 = "42" + var Z1 = Box(4) + val Z2 = Box(4) + + x match { case Y1 => () } // error + x match { case Y2.toInt => () } // error + x match { case Y1.toString => () } // error + + x match { case Z1.v => () } // error + x match { case Z2.v => () } // ok + + Some(x) match { case Some(Z1.v) => () } // error + Some(x) match { case Some(Z2.v) => () } // ok + + Some(x) match { case Some(4) | Some(Z1.v) => () } // error + Some(x) match { case a @ Some(Z1.v) => () } // error + + Some(x) match { case Some(4) | Some(Z2.v) => () } // ok + Some(x) match { case a @ Some(Z2.v) => () } // ok + } +}