Skip to content
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

Merged
merged 4 commits into from
Mar 17, 2019
Merged

Implement BindingMode for pattern matching. #982

merged 4 commits into from
Mar 17, 2019

Conversation

mjkillough
Copy link
Contributor

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.

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.
let A(n) = &mut A(1);

let v = &(1, &2);
let (_, &w) = v;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

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 :)

Copy link
Member

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

Copy link
Contributor Author

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 {
Copy link
Contributor Author

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.

Copy link
Member

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,
Copy link
Contributor Author

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.

Copy link
Member

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,
Copy link
Contributor Author

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?

Copy link
Member

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.

@matklad matklad requested a review from flodiebold March 16, 2019 19:55
default_bm = BindingMode::Move;
}

// Lose mutability.
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -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);
Copy link
Member

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 ?

Copy link
Member

@flodiebold flodiebold left a 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 {
Copy link
Member

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,
Copy link
Member

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;
Copy link
Member

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,
Copy link
Member

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.

}
BindingMode::Ref(Mutability::Mut) => {
Ty::Ref(inner_ty.clone().into(), Mutability::Mut)
}
Copy link
Member

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?

Copy link
Contributor Author

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.

let A(n) = &mut A(1);

let v = &(1, &2);
let (_, &w) = v;
Copy link
Member

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 :)

let A(n) = &mut A(1);

let v = &(1, &2);
let (_, &w) = v;
Copy link
Member

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.
@flodiebold
Copy link
Member

bors r+

bors bot added a commit that referenced this pull request Mar 17, 2019
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>
@bors
Copy link
Contributor

bors bot commented Mar 17, 2019

Build succeeded

@bors bors bot merged commit 6299ccd into rust-lang:master Mar 17, 2019
@mjkillough mjkillough deleted the binding_modes branch March 17, 2019 21:51
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