-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #1846: add regression test #3810
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
Conversation
Wow! I didn't know we could do that. We should probably spec it and test it thoroughly. For instance, there are some inconsistencies: 42 match { case { 42 } => 42 } // OK
42 match { case { 42 }.toString => 42 } // OK, should error?
42 match { case { 42.toString } => 42 } // error |
@liufengyun I see you closed #1774 saying it is a bad idea. Then I think we should make this a neg test and make sure the compiler rejects block in patterns. WDYT? |
@allanrenucci It's actually consistent, I've updated the test. The following doesn't type-check because the pattern type and scrutinee type are irrelevant: 42 match { case { 42.toString } => 42 } The following code works: "h" match { case { 42.toString } => 42 } |
@allanrenucci Note that blocks are allowed in the syntax specification for Dotty: SimplePattern ::= PatVar Ident(wildcard)
| Literal Bind(name, Ident(wildcard))
| ‘(’ [Patterns] ‘)’ Parens(pats) Tuple(pats)
| XmlPattern
| SimplePattern1 [TypeArgs] [ArgumentPatterns]
SimplePattern1 ::= Path
| ‘{’ Block ‘}’
| SimplePattern1 ‘.’ id I think in #1774, I misunderstood the semantics of blocks in patterns. |
I understand why this typechecks: 42 match { case { 42.toString } => 42 } But then I don't understand why this typechecks: 42 match { case { 42 }.toString => 42 } |
The following code shows the pattern: val x = 42
val Y = "h"
x match { case { "h" }.toString => println(42) } // ok
x match { case { "h".toString } => println(42) } // error
x match { case Y => println(42) } // error
x match { case Y.toString => println(42) } // ok But arguably, all of them should be invalid. |
Can you open an issue? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more reason to reject 42 match { case { "h" }.toString => ... }
, Ycheck
is not happy about it 😄
tests/run/i1846.scala
Outdated
@@ -0,0 +1,22 @@ | |||
object Test { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think is it better to use assert
instead of a check file for simple test cases like this one. Also one more think you could test:
val foo = 42 match { case x @ { 42 } => x }
assert(foo == 42)
tests/run/i3812.scala
Outdated
x match { case { Y.toInt } => () } // ok | ||
x match { case { Y }.toInt => () } // ok | ||
|
||
// x match { case Y.toInt => () } // compiles but rejected by Ycheck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@liufengyun Ycheck
fails on this one. Should this be allowed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, I think it should be allowed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that case "42".toInt =>
does not parse. It might be confusing that one is allowed but not the other
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The syntax specification requires the prefix has to be a path
or block
.
SimplePattern ::= PatVar Ident(wildcard)
| Literal Bind(name, Ident(wildcard))
| ‘(’ [Patterns] ‘)’ Parens(pats) Tuple(pats)
| XmlPattern
| SimplePattern1 [TypeArgs] [ArgumentPatterns]
SimplePattern1 ::= Path
| ‘{’ Block ‘}’
| SimplePattern1 ‘.’ id
tests/run/i3812.scala
Outdated
val x = 42 | ||
val Y = "42" | ||
|
||
x match { case Y.toInt => () } // ok |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an error in scalac, I don't think we should support it either
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, a check for stable prefix is missing for patterns here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I'm a little unsure about Scalac behavior, as it also rejects the following code:
var X = 42
42 match { case X => () }
It seems to me it can be accepted without any issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to scala spec 2.13, the scalac behavior is correct.
@odersky This PR does the following:
Note that by removing the block syntax from patterns, following code is still supported: implicit class Regex(sc: StringContext) {
def r = new util.matching.Regex(sc.parts.mkString, sc.parts.tail.map(_ => "x"): _*)
}
scala> "123" match { case r"\d+" => true case _ => false }
res34: Boolean = true
scala> "123" match { case r"(\d+)$d" => d.toInt case _ => 0 }
res36: Int = 123 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed on leaving out blocks in patterns. But the stability checks should be justified and either be eliminated or refactored.
@@ -75,6 +75,21 @@ object RefChecks { | |||
} | |||
} | |||
|
|||
/** Check that a stable identifier pattern is indeed stable (SLS 8.1.5) | |||
*/ | |||
private def checkStableIdentPattern(tree: Tree)(implicit ctx: Context) = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it would be simpler to put this check in Typer.
Here, we could just add if (ctx.mode.is(Mode.Pattern) && !tree.tpe.isStable) error
to the checks for Ident and Select.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I am also no sure why we should demand stability? What would go wrong if we didn't?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following check seems too strong to me:
if (ctx.mode.is(Mode.Pattern) && !tree.tpe.isStable) error
As a selection of unapply
would fail this check. The check should only happen for Stable Identifier Pattern (SLS 8.1.5). I don't know what's the motivation for the Scala specification. Arguably, a var
could be allowed in a pattern, but then it complicates the specification and check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so let's keep the test for stable idents but move it to Typer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, now it's done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Fix #1846: add regression test
This PR does the following:
-Ycheck
Note that by removing the block syntax from patterns, following code is still supported: