Skip to content

Fix #3332: Disallow bound variables in pattern alternatives #3429

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 4 commits into from
Nov 11, 2017

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Nov 1, 2017

Disallow bound variables where the binder appears as a pattern alternative.

Fixes #3332.

There's nothing we can do about the parsing. If we parsed

case x: A | B 

as

case x: (A | B)

then we would run into an error for

case _: A | _: B

But the latter is a common idiom.

Disallow bound variables where the binder appears as a pattern alternative.
@odersky odersky changed the title Fix #3332: Disallow bound variable sin pattern alternatives Fix #3332: Better handling of |-types and alternatives in patterns Nov 1, 2017
@odersky odersky changed the title Fix #3332: Better handling of |-types and alternatives in patterns Fix #3332: Disallow bound variables in pattern alternatives Nov 1, 2017
@odersky odersky requested a review from allanrenucci November 1, 2017 19:59
@smarter
Copy link
Member

smarter commented Nov 1, 2017

There's nothing we can do about the parsing.

Maybe when we report this error we could add "did you mean case x : (A | B) ?" if A | B is a valid type

@smarter
Copy link
Member

smarter commented Nov 1, 2017

Or maybe we need more backtracking in the parser :)

@smarter
Copy link
Member

smarter commented Nov 1, 2017

we would run into an error for
case _: A | _: B
But the latter is a common idiom.

One compromise could be: do backtracking under Scala2 mode but warn people when we backtrack that they should write (_: A) | (_: B) (for Scala 2 compatiblity) or _: A | B (which is more natural but incompatible).

@odersky
Copy link
Contributor Author

odersky commented Nov 2, 2017

@smarter It's actually worse than that. Here's some code that is legal now, but would mean something different if we changed the parsing scheme:

case class A
class B

case _: B | A

@smarter
Copy link
Member

smarter commented Nov 2, 2017

Actually, case class A isn't valid, you have to write case object A instead, and in that case case _: (B | A) wouldn't compile, it would have to be case _: (B | A.type)

@odersky
Copy link
Contributor Author

odersky commented Nov 2, 2017

Here's a more complete transscript of what goes wrong:

scala> case class A()
defined class A

scala> class B
defined class B

scala> (A(): Any) match { case _: B | A => "ok" case _ => "no" }
res1: String = no

That's the current behavior. If we change the parsing, we'd get "ok" instead.

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.

It doesn't work in this case (_ seems to be treated as a variable):

class Test {
  case class A()
  def test(x: Any) = x match {
    case _: String | _ @ A() => 1
  }
}
15 |    case _: String | _ @ A() => 1
   |                         ^^^
   |                         Illegal variable in pattern alternative

@@ -87,7 +90,7 @@ object Mode {
/** Don't suppress exceptions thrown during show */
val PrintShowExceptions = newMode(18, "PrintShowExceptions")

val PatternOrType = Pattern | Type
val PatternOrTypeBits = Pattern | Type | InPatternAlternative
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did you rename this value? I think the old name is more consistent with the other mode's name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's because otherwise we'd risk that someone thinks it's Pattern | Type only. And PatternOrTypeorInPatternAlternative was too long.

I thought that `_` never appears as a bound variable in a bind but was wrong.
We now rewrite `(_ @ P)` to `P`.
@odersky
Copy link
Contributor Author

odersky commented Nov 2, 2017

@allanrenucci The failure should be fixed now.

@smarter
Copy link
Member

smarter commented Nov 3, 2017

scala> case class A()
defined class A
scala> class B
defined class B
scala> (A(): Any) match { case _: B | A => "ok" case _ => "no" }
res1: String = no

This is a bit off-topic at this point, but I would argue that we're not doing anyone any favour by accepting this without at least a warning: it's extremely rare to want to pattern match on the companion object of a case class and is most likely to happen when trying to pattern match on the case class itself. I would rewrite A as _: A.type to make the meaning obvious.

@odersky odersky merged commit a15a7f6 into scala:master Nov 11, 2017
@allanrenucci allanrenucci deleted the fix-3332 branch December 14, 2017 19:19
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.

3 participants