Skip to content

Remove irrefutable match pattern #10782

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1365,26 +1365,12 @@ class Typer extends Namer
case _ => false
}

val result = pt match {
pt match {
case MatchTypeInDisguise(mt) if isMatchTypeShaped(mt) =>
typedDependentMatchFinish(tree, sel1, selType, tree.cases, mt)
case _ =>
typedMatchFinish(tree, sel1, selType, tree.cases, pt)
}

result match {
case Match(sel, CaseDef(pat, _, _) :: _) =>
tree.selector.removeAttachment(desugar.CheckIrrefutable) match {
case Some(checkMode) =>
val isPatDef = checkMode == desugar.MatchCheck.IrrefutablePatDef
if (!checkIrrefutable(pat, sel.tpe, isPatDef) && sourceVersion == `3.1-migration`)
if (isPatDef) patch(Span(pat.span.end), ": @unchecked")
else patch(Span(pat.span.start), "case ")
case _ =>
}
case _ =>
}
result
}

/** Special typing of Match tree when the expected type is a MatchType,
Expand Down
44 changes: 5 additions & 39 deletions docs/docs/reference/changed-features/pattern-bindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,17 @@ layout: doc-page
title: "Pattern Bindings"
---

In Scala 2, pattern bindings in `val` definitions and `for` expressions are
loosely typed. Potentially failing matches are still accepted at compile-time,
but may influence the program's runtime behavior.
From Scala 3.1 on, type checking rules will be tightened so that errors are reported at compile-time instead.
In Scala 3 pattern definitions can carry ascriptions such as `: @unchecked`, and `for` expressions can prefix `case` when filter.

## Bindings in Pattern Definitions

```scala
val xs: List[Any] = List(1, 2, 3)
val (x: String) :: _ = xs // error: pattern's type String is more specialized
// than the right hand side expression's type Any
```
This code gives a compile-time error in Scala 3.1 (and also in Scala 3.0 under the `-source 3.1` setting) whereas it will fail at runtime with a `ClassCastException` in Scala 2. In Scala 3.1, a pattern binding is only allowed if the pattern is _irrefutable_, that is, if the right-hand side's type conforms to the pattern's type. For instance, the following is OK:
```scala
val pair = (1, true)
val (x, y) = pair
```
Sometimes one wants to decompose data anyway, even though the pattern is refutable. For instance, if at some point one knows that a list `elems` is non-empty one might
want to decompose it like this:
```scala
val first :: rest = elems // error
```
This works in Scala 2. In fact it is a typical use case for Scala 2's rules. But in Scala 3.1 it will give a type error. One can avoid the error by marking the pattern with an `@unchecked` annotation:
```scala
val first :: rest : @unchecked = elems // OK
```
This will make the compiler accept the pattern binding. It might give an error at runtime instead, if the underlying assumption that `elems` can never be empty is wrong.
## Pattern Definitions Ascriptions
Pattern definitions can have ascriptions at the end of them.
`val first :: rest : @unchecked = elems`

## Pattern Bindings in `for` Expressions

Analogous changes apply to patterns in `for` expressions. For instance:

```scala
val elems: List[Any] = List((1, 2), "hello", (3, 4))
for ((x, y) <- elems) yield (y, x) // error: pattern's type (Any, Any) is more specialized
// than the right hand side expression's type Any
```
This code gives a compile-time error in Scala 3.1 whereas in Scala 2 the list `elems`
is filtered to retain only the elements of tuple type that match the pattern `(x, y)`.
The filtering functionality can be obtained in Scala 3 by prefixing the pattern with `case`:
```scala
val elems: List[Any] = List((1, 2), "hello", (3, 4))
for (case (x, y) <- elems) yield (y, x) // returns List((2, 1), (4, 3))
```

Expand All @@ -55,7 +25,3 @@ PatDef ::= ids [‘:’ Type] ‘=’ Expr
| Pattern2 [‘:’ Type | Ascription] ‘=’ Expr
Generator ::= [‘case’] Pattern1 ‘<-’ Expr
```

## Migration

The new syntax is supported in Dotty and Scala 3.0. However, to enable smooth cross compilation between Scala 2 and Scala 3, the changed behavior and additional type checks are only enabled under the `-source 3.1` setting. They will be enabled by default in version 3.1 of the language.
32 changes: 0 additions & 32 deletions tests/neg-strict/filtering-fors.scala

This file was deleted.

25 changes: 0 additions & 25 deletions tests/neg-strict/unchecked-patterns.scala

This file was deleted.