-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Implement BindingMode for pattern matching. #982
Conversation
Implement `BindingMode` for pattern matching, so that types can be correctly inferred using match ergonomics. The binding mode defaults to `Move` (referred to as 'BindingMode::BindByValue` in rustc), and is updated by automatic dereferencing of the value being matched.
crates/ra_hir/src/ty/tests.rs
Outdated
let A(n) = &mut A(1); | ||
|
||
let v = &(1, &2); | ||
let (_, &w) = v; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tests the else if let Pat::Ref { .. }
, which is rust-lang/rust#46688.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe a separate test for that, plus a marker (tested_by!
), would be nice :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, in general, I'd prefer adding the new test cases in a separate test -- it's already not so easy what's what here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hadn't come across tested_by!
. Very neat! 🙂
I separated this out into two test-cases, as I think it makes the snapshots easier to read, and makes it clear what is being tested.
/// Binding modes inferred for patterns. | ||
/// https://doc.rust-lang.org/reference/patterns.html#binding-modes | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
enum BindingMode { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure where to put this new enum
. As it is only used by inference, I left it here for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, if it's only used here I think it's the right place.
/// https://doc.rust-lang.org/reference/patterns.html#binding-modes | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
enum BindingMode { | ||
Move, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rustc
calls this BindByValue
(and takes a mutability argument).
I chose to call it Move
to match the reference documentation, and the match ergonomics RFC which refer to it that way. I'm very happy to change it if consistency with rustc
is more important.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Move
is good :)
| Pat::Range { .. } | ||
| Pat::Slice { .. } => true, | ||
// TODO: Path/Lit might actually evaluate to ref, but inference is unimplemented. | ||
Pat::Path(..) | Pat::Lit(..) => true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the relevant bits of the rustc
implementation. Though I am not sure we're able to implement those here yet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm. somehow I was sure we supported literal patterns, but apparently we don't yet 😄 those should be easy to add, though. Path patterns to consts are supported. We'll probably need to extract the 'resolving' part from infer_path_expr
to do the check. I think it'd be good to leave that for a separate PR though.
default_bm = BindingMode::Move; | ||
} | ||
|
||
// Lose mutability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
crates/ra_hir/src/ty/infer.rs
Outdated
@@ -700,7 +761,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
} | |||
Expr::For { iterable, body, pat } => { | |||
let _iterable_ty = self.infer_expr(*iterable, &Expectation::none()); | |||
self.infer_pat(*pat, &Ty::Unknown); | |||
self.infer_pat(*pat, &Ty::Unknown, BindingMode::Move); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Micro nit, but this seems like a good place for Default ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM apart from a few nits (mainly, separating the test would be nicer), thanks!
/// Binding modes inferred for patterns. | ||
/// https://doc.rust-lang.org/reference/patterns.html#binding-modes | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
enum BindingMode { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, if it's only used here I think it's the right place.
/// https://doc.rust-lang.org/reference/patterns.html#binding-modes | ||
#[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
enum BindingMode { | ||
Move, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Move
is good :)
} | ||
} | ||
} else if let Pat::Ref { .. } = &body[pat] { | ||
default_bm = BindingMode::Move; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a similar comment as in rustc might be appropriate here?
| Pat::Range { .. } | ||
| Pat::Slice { .. } => true, | ||
// TODO: Path/Lit might actually evaluate to ref, but inference is unimplemented. | ||
Pat::Path(..) | Pat::Lit(..) => true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm. somehow I was sure we supported literal patterns, but apparently we don't yet 😄 those should be easy to add, though. Path patterns to consts are supported. We'll probably need to extract the 'resolving' part from infer_path_expr
to do the check. I think it'd be good to leave that for a separate PR though.
crates/ra_hir/src/ty/infer.rs
Outdated
} | ||
BindingMode::Ref(Mutability::Mut) => { | ||
Ty::Ref(inner_ty.clone().into(), Mutability::Mut) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these two could just be written as
BindingMode::Ref(m) => Ty::ref(inner_ty.clone().into(), m)
or am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right! That'd be much simpler.
crates/ra_hir/src/ty/tests.rs
Outdated
let A(n) = &mut A(1); | ||
|
||
let v = &(1, &2); | ||
let (_, &w) = v; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe a separate test for that, plus a marker (tested_by!
), would be nice :)
crates/ra_hir/src/ty/tests.rs
Outdated
let A(n) = &mut A(1); | ||
|
||
let v = &(1, &2); | ||
let (_, &w) = v; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, in general, I'd prefer adding the new test cases in a separate test -- it's already not so easy what's what here
This decouples callers from knowing what the default binding mode of pattern matching is.
bors r+ |
982: Implement BindingMode for pattern matching. r=flodiebold a=mjkillough Implement `BindingMode` for pattern matching, so that types can be correctly inferred using match ergonomics. The binding mode defaults to `Move` (referred to as 'BindingMode::BindByValue` in rustc), and is updated by automatic dereferencing of the value being matched. Fixes #888. - [Binding modes in The Reference](https://doc.rust-lang.org/reference/patterns.html#binding-modes) - [`rustc` implementation](https://github.com/rust-lang/rust/blob/e17c48e2f21eefd59748e364234efc7037a3ec96/src/librustc_typeck/check/_match.rs#L77) (and [definition of `BindingMode`](https://github.com/rust-lang/rust/blob/e957ed9d10ec589bdd523b88b4b44c41b1ecf763/src/librustc/ty/binding.rs)) - [Match Ergonomics RFC](https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#binding-mode-rules) Co-authored-by: Michael Killough <michaeljkillough@gmail.com>
Build succeeded |
Implement
BindingMode
for pattern matching, so that types can becorrectly inferred using match ergonomics. The binding mode defaults to
Move
(referred to as 'BindingMode::BindByValue` in rustc), and isupdated by automatic dereferencing of the value being matched.
Fixes #888.
rustc
implementation (and definition ofBindingMode
)