Skip to content

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

Merged
merged 10 commits into from
Jan 27, 2018
Merged

Fix #1846: add regression test #3810

merged 10 commits into from
Jan 27, 2018

Conversation

liufengyun
Copy link
Contributor

@liufengyun liufengyun commented Jan 11, 2018

Fix #1846: add regression test

This PR does the following:

  • removes the block syntax in patterns
  • fix a crash under -Ycheck
  • check that stable identifier patterns are really stable -- scalac compatible

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

@allanrenucci
Copy link
Contributor

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

@allanrenucci
Copy link
Contributor

@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?

@liufengyun
Copy link
Contributor Author

@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 }

@liufengyun
Copy link
Contributor Author

@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.

@allanrenucci
Copy link
Contributor

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 }

@liufengyun
Copy link
Contributor Author

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.

@allanrenucci
Copy link
Contributor

But arguably, all of them should be invalid.

Can you open an issue?

Copy link
Contributor

@allanrenucci allanrenucci left a 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 😄

@@ -0,0 +1,22 @@
object Test {
Copy link
Contributor

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)

x match { case { Y.toInt } => () } // ok
x match { case { Y }.toInt => () } // ok

// x match { case Y.toInt => () } // compiles but rejected by Ycheck
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor Author

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

@allanrenucci allanrenucci removed their assignment Jan 12, 2018
val x = 42
val Y = "42"

x match { case Y.toInt => () } // ok
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

@liufengyun liufengyun Jan 18, 2018

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@liufengyun
Copy link
Contributor Author

@odersky This PR does the following:

  • removes the block syntax in patterns
  • fix a crash under -Ycheck
  • check that stable identifier patterns are really stable -- scalac compatible

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

Copy link
Contributor

@odersky odersky left a 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) = {
Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

@odersky odersky assigned liufengyun and unassigned odersky Jan 21, 2018
@liufengyun liufengyun assigned odersky and unassigned liufengyun Jan 22, 2018
@odersky odersky assigned liufengyun and unassigned odersky Jan 22, 2018
@liufengyun liufengyun assigned odersky and unassigned liufengyun Jan 22, 2018
Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

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

LGTM

@odersky odersky merged commit 46956b0 into scala:master Jan 27, 2018
@allanrenucci allanrenucci deleted the fix-1846 branch January 27, 2018 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expanding a pattern match leads to "head of empty list"
3 participants