Repository: rust
Issue: Reconsider Rule 4E (early) for RFC 3627
Labels: T-lang,A-patterns,C-discussion

## Context

[RFC3627](https://github.com/rust-lang/rfcs/pull/3627) set out to improve some unintuitive edges of match ergonomics. The subtlest part involves fixing this case:
```rust 
let [x]: &[&T] = ...;
// x: &&T
let [&x]: &[&T] = ...;
// x: T
```
where a `&` pattern appears to remove two layers of references. T-lang agreed on the desireability of "eat-one-layer" instead, namely that a `&` pattern should only ever remove one layer of reference.

For that, the RFC proposes rule 2: "When a reference pattern matches against a reference, do not update the default binding mode". While this is arguably a straightforward change from an implementation perspective, let me show you that it does not appropriately solve the problem we set out to solve from a language perspective.

## Issue

The reason is simple: there are two references at play in `&&T`; with rule 2 we match the pattern against the _inner_ one of these. Some consequences (you can try these out in [my online tool](https://nadrieril.github.io/typing-rust-patterns/?opts1=AAEBAAABAQABAgEAAAEAAAAAAAAAAAA%3D&q=%5B%26mut+x%5D%3A+%26mut+%5B%26T%5D) which can run both [TC's](https://github.com/traviscross/match-ergonomics-formality/) and [my](https://github.com/Nadrieril/typing-rust-patterns) solvers; just note that rule4-early has bugs when combined with rule 5):

- The mutability that matters is the inner one:
```rust 
let [x]: &[&mut T] = ...;
// x: &&mut T
let [&x]: &[&mut T] = ...;
// with rule 2: Type error
// with rule 4E: x: &mut T + borrow checking error
let [&mut x]: &[&mut T] = ...;
// with rule 2: x: &T
// with rule 4E: type error

let [x]: &mut [&T] = ...;
// x: &mut &T
let [&x]: &mut [&T] = ...;
// with rule 2: `x: &mut T`, which causes a borrow-checking error
// with rule 4E: type error
let [&mut x]: &mut [&T] = ...;
// with rule 2: Type error
// with rule 4E: x: &T
```

- References are considered inherited when they shouldn't, which is visible with `mut` or `ref` bindings:
```rust
let [&x]: &[&T] = ...;
// with rule 2: x: &T
// with rule 4E: x: &T
let [&ref x]: &[&T] = ...;
// with rule 2: x: &T because the reference was considered inherited and `ref x` overrides that
// with rule 4E: x: &&T

let &[x]: &[&T] = ...;
// with rule 2: x: &T
// with rule 4E: x: &T
let &[ref x]: &[&T] = ...;
// with rule 2: x: &&T because the reference was not considered inherited
// with rule 4E: x: &&T
```

- Combined with the rest of RFC3627, we get weirdly inconsistent behaviors such as:
```rust 
let [&mut (ref x)]: &mut [&mut T] = ...;
// RFC3627: `x: &T` because we got an inherited `&mut` and `ref` overrode it
// with rule 4E: x: &&mut T
let [&mut (ref x)]: &mut [&    T] = ...;
// RFC3627: `x: &&T` because the mutability mismatch triggered rule 4 instead
// with rule 4E: x: &&T
```

In short: rule 2 does "eat-one-layer" but eats the wrong layer. The fix is simply to eat the other one. In the language of RFC3627: "when the binding mode is ref or ref mut, match the pattern against the binding mode as if it was a reference"; this has been called "rule 4-early" in our discussions.

## Edition

RFC3627 proposed to enable rules 1 and 2 over the edition. I propose to instead enable rules 1 and 4-early over the edition. Note that rule 4-early also replaces rule 4.

While I'm at it I would like to add a small additional rule, to enable fixing all the surprises:

- Rule 1.5: When the DBM (default binding mode) is not `move`, writing `ref` on a binding is an error.

We can always revert to the previous behavior (i.e. `ref x` swallows a reference if it is inherited) if we wish to later.

cc @traviscross