`&` and `|` in type patterns

Hi,

I noticed this bug today:

The bug is that when -rewrite -source 3.4-migration is used, code like case _: Foo with Bar is rewritten to case _: Foo & Bar which, surprisingly, doesn’t parse. It needs to be written case _: (Foo & Bar) instead. The proposed fix in that ticket is to have it generate the version with the parens. But is that really the best fix? Why can’t we just accept the version without parens?

I suspect the reason is that & and | are treated the same by the parser, and case _: Foo | Bar is ambiguous. It could be parsed as case (_: Foo) | Bar (and in fact it is), or as case _: (Foo | Bar). But this ambiguity doesn’t exist for &, so that case is unambiguous afaics and we could just allow it.

Thoughts?

Everything rolls down to standard issue of every language - operators precedence (see scala3/compiler/src/dotty/tools/dotc/parsing/package.scala at 4e44c3881a4c257fb9b68290995bc4d2309d27bf · scala/scala3 · GitHub). Operators precedence creates a rules how different tokens should be applied, and how virtual parenthesis might be applied. Same as in math.

& operator has lower precedence (4) then : (7), and both of these have higher precedence then alphanumeric tokens like and (1).

We should not change these rules as it’s foundation of the language syntax. It’s applied before typing.

not disputing anything but with is also alphanumeric yet higher than :

Fixing the bug is easier than a SIP :wink:

I’m not convinced that this has anything to do with operator precedence. The error message doesn’t fit:

scala> ((): Any) match { case x: Int & String  => 0 }
-- [E040] Syntax Error: --------------------------------------------------------
1 |((): Any) match { case x: Int & String  => 0 }
  |                              ^
  |                              '=>' expected, but identifier found

That’s a parser error, not an operator precedence thing. In fact, we can simulate what the error message would look like if this was related to operator precedence by spelling out the parens that would be inferred based on operator precedence, i. e. case (x: Int) & String. If we do that, we still get an error, but not a parser error:

scala> ((): Any) match { case (x: Int) & String  => 0 }
-- [E189] Not Found Error: -----------------------------------------------------
1 |((): Any) match { case (x: Int) & String  => 0 }
  |                                ^
  |                              no pattern match extractor named & was found
  |
  | longer explanation available when compiling with `-explain`
-- [E119] Type Error: ----------------------------------------------------------
1 |((): Any) match { case (x: Int) & String  => 0 }
  |                                  ^^^^^^
  |                                  Java defined class String is not a value
2 errors found

Actually, reading that code carefully, it says firstCh match. So this isn’t about : itself, it’s about operators like :\ or :+ or ::. It’s entirely irrelevant for :, as can be seen for example in expressions like ??? : Int & String. That is parsed as ??? : (Int & String), not (??? : Int) & String.
In fact, it never occurred to me to think of : as an infix operator at all. An infix operator is something that sits between two alike things, like two expressions or two types or two patterns. That’s not the case for :.

In fact the syntax says

Pattern1          ::=  PatVar ‘:’ RefinedType

not an InfixType.

scala> type X[A, B]

scala> def x: Int X String = ???
1 warning found
-- Warning: --------------------------------------------------------------------
1 |def x: Int X String = ???
  |           ^
  |Alphanumeric type X is not declared infix; it should not be used as infix operator.
  |Instead, use prefix syntax X[...] or backticked identifier `X`.
def x: X[Int, String]

scala> def f(x: Any) = x match { case y: Int X String => }
-- [E040] Syntax Error: --------------------------------------------------------
1 |def f(x: Any) = x match { case y: Int X String => }
  |                                      ^
  |                                      '=>' expected, but identifier found