aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-03-17 21:41:37 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-03-17 21:41:37 +0000
commit40c6dd1f4c57d6a8ec26c1bdef24753c884c38aa (patch)
treecf9959b252a7c9e25c4e186b57f92ff9d6bec61d /crates/ra_hir/src
parent91e7a3b6f2a9f1a09f874e9644491898338a842f (diff)
parent6299ccd350c190003c51aa68f48b1edfb1a497b1 (diff)
Merge #982
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 <[email protected]>
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r--crates/ra_hir/src/marks.rs1
-rw-r--r--crates/ra_hir/src/ty/infer.rs99
-rw-r--r--crates/ra_hir/src/ty/tests.rs54
3 files changed, 137 insertions, 17 deletions
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index bbf57004d..5b6400042 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -8,4 +8,5 @@ test_utils::marks!(
8 glob_enum 8 glob_enum
9 glob_across_crates 9 glob_across_crates
10 std_prelude 10 std_prelude
11 match_ergonomics_ref
11); 12);
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index c9a5bc7a1..735cdecb9 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -63,6 +63,30 @@ enum ExprOrPatId {
63 63
64impl_froms!(ExprOrPatId: ExprId, PatId); 64impl_froms!(ExprOrPatId: ExprId, PatId);
65 65
66/// Binding modes inferred for patterns.
67/// https://doc.rust-lang.org/reference/patterns.html#binding-modes
68#[derive(Copy, Clone, Debug, Eq, PartialEq)]
69enum BindingMode {
70 Move,
71 Ref(Mutability),
72}
73
74impl BindingMode {
75 pub fn convert(annotation: &BindingAnnotation) -> BindingMode {
76 match annotation {
77 BindingAnnotation::Unannotated | BindingAnnotation::Mutable => BindingMode::Move,
78 BindingAnnotation::Ref => BindingMode::Ref(Mutability::Shared),
79 BindingAnnotation::RefMut => BindingMode::Ref(Mutability::Mut),
80 }
81 }
82}
83
84impl Default for BindingMode {
85 fn default() -> Self {
86 BindingMode::Move
87 }
88}
89
66/// The result of type inference: A mapping from expressions and patterns to types. 90/// The result of type inference: A mapping from expressions and patterns to types.
67#[derive(Clone, PartialEq, Eq, Debug)] 91#[derive(Clone, PartialEq, Eq, Debug)]
68pub struct InferenceResult { 92pub struct InferenceResult {
@@ -530,6 +554,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
530 path: Option<&Path>, 554 path: Option<&Path>,
531 subpats: &[PatId], 555 subpats: &[PatId],
532 expected: &Ty, 556 expected: &Ty,
557 default_bm: BindingMode,
533 ) -> Ty { 558 ) -> Ty {
534 let (ty, def) = self.resolve_variant(path); 559 let (ty, def) = self.resolve_variant(path);
535 560
@@ -542,13 +567,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
542 .and_then(|d| d.field(self.db, &Name::tuple_field_name(i))) 567 .and_then(|d| d.field(self.db, &Name::tuple_field_name(i)))
543 .map_or(Ty::Unknown, |field| field.ty(self.db)) 568 .map_or(Ty::Unknown, |field| field.ty(self.db))
544 .subst(&substs); 569 .subst(&substs);
545 self.infer_pat(subpat, &expected_ty); 570 self.infer_pat(subpat, &expected_ty, default_bm);
546 } 571 }
547 572
548 ty 573 ty
549 } 574 }
550 575
551 fn infer_struct_pat(&mut self, path: Option<&Path>, subpats: &[FieldPat], expected: &Ty) -> Ty { 576 fn infer_struct_pat(
577 &mut self,
578 path: Option<&Path>,
579 subpats: &[FieldPat],
580 expected: &Ty,
581 default_bm: BindingMode,
582 ) -> Ty {
552 let (ty, def) = self.resolve_variant(path); 583 let (ty, def) = self.resolve_variant(path);
553 584
554 self.unify(&ty, expected); 585 self.unify(&ty, expected);
@@ -559,15 +590,45 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
559 let matching_field = def.and_then(|it| it.field(self.db, &subpat.name)); 590 let matching_field = def.and_then(|it| it.field(self.db, &subpat.name));
560 let expected_ty = 591 let expected_ty =
561 matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs); 592 matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs);
562 self.infer_pat(subpat.pat, &expected_ty); 593 self.infer_pat(subpat.pat, &expected_ty, default_bm);
563 } 594 }
564 595
565 ty 596 ty
566 } 597 }
567 598
568 fn infer_pat(&mut self, pat: PatId, expected: &Ty) -> Ty { 599 fn infer_pat(&mut self, pat: PatId, mut expected: &Ty, mut default_bm: BindingMode) -> Ty {
569 let body = Arc::clone(&self.body); // avoid borrow checker problem 600 let body = Arc::clone(&self.body); // avoid borrow checker problem
570 601
602 let is_non_ref_pat = match &body[pat] {
603 Pat::Tuple(..)
604 | Pat::TupleStruct { .. }
605 | Pat::Struct { .. }
606 | Pat::Range { .. }
607 | Pat::Slice { .. } => true,
608 // TODO: Path/Lit might actually evaluate to ref, but inference is unimplemented.
609 Pat::Path(..) | Pat::Lit(..) => true,
610 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Missing => false,
611 };
612 if is_non_ref_pat {
613 while let Ty::Ref(inner, mutability) = expected {
614 expected = inner;
615 default_bm = match default_bm {
616 BindingMode::Move => BindingMode::Ref(*mutability),
617 BindingMode::Ref(Mutability::Shared) => BindingMode::Ref(Mutability::Shared),
618 BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(*mutability),
619 }
620 }
621 } else if let Pat::Ref { .. } = &body[pat] {
622 tested_by!(match_ergonomics_ref);
623 // When you encounter a `&pat` pattern, reset to Move.
624 // This is so that `w` is by value: `let (_, &w) = &(1, &2);`
625 default_bm = BindingMode::Move;
626 }
627
628 // Lose mutability.
629 let default_bm = default_bm;
630 let expected = expected;
631
571 let ty = match &body[pat] { 632 let ty = match &body[pat] {
572 Pat::Tuple(ref args) => { 633 Pat::Tuple(ref args) => {
573 let expectations = match *expected { 634 let expectations = match *expected {
@@ -579,7 +640,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
579 let inner_tys = args 640 let inner_tys = args
580 .iter() 641 .iter()
581 .zip(expectations_iter) 642 .zip(expectations_iter)
582 .map(|(&pat, ty)| self.infer_pat(pat, ty)) 643 .map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm))
583 .collect::<Vec<_>>() 644 .collect::<Vec<_>>()
584 .into(); 645 .into();
585 646
@@ -595,14 +656,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
595 } 656 }
596 _ => &Ty::Unknown, 657 _ => &Ty::Unknown,
597 }; 658 };
598 let subty = self.infer_pat(*pat, expectation); 659 let subty = self.infer_pat(*pat, expectation, default_bm);
599 Ty::Ref(subty.into(), *mutability) 660 Ty::Ref(subty.into(), *mutability)
600 } 661 }
601 Pat::TupleStruct { path: ref p, args: ref subpats } => { 662 Pat::TupleStruct { path: ref p, args: ref subpats } => {
602 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected) 663 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm)
603 } 664 }
604 Pat::Struct { path: ref p, args: ref fields } => { 665 Pat::Struct { path: ref p, args: ref fields } => {
605 self.infer_struct_pat(p.as_ref(), fields, expected) 666 self.infer_struct_pat(p.as_ref(), fields, expected, default_bm)
606 } 667 }
607 Pat::Path(path) => { 668 Pat::Path(path) => {
608 // TODO use correct resolver for the surrounding expression 669 // TODO use correct resolver for the surrounding expression
@@ -610,17 +671,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
610 self.infer_path_expr(&resolver, &path, pat.into()).unwrap_or(Ty::Unknown) 671 self.infer_path_expr(&resolver, &path, pat.into()).unwrap_or(Ty::Unknown)
611 } 672 }
612 Pat::Bind { mode, name: _name, subpat } => { 673 Pat::Bind { mode, name: _name, subpat } => {
674 let mode = if mode == &BindingAnnotation::Unannotated {
675 default_bm
676 } else {
677 BindingMode::convert(mode)
678 };
613 let inner_ty = if let Some(subpat) = subpat { 679 let inner_ty = if let Some(subpat) = subpat {
614 self.infer_pat(*subpat, expected) 680 self.infer_pat(*subpat, expected, default_bm)
615 } else { 681 } else {
616 expected.clone() 682 expected.clone()
617 }; 683 };
618 let inner_ty = self.insert_type_vars_shallow(inner_ty); 684 let inner_ty = self.insert_type_vars_shallow(inner_ty);
619 685
620 let bound_ty = match mode { 686 let bound_ty = match mode {
621 BindingAnnotation::Ref => Ty::Ref(inner_ty.clone().into(), Mutability::Shared), 687 BindingMode::Ref(mutability) => Ty::Ref(inner_ty.clone().into(), mutability),
622 BindingAnnotation::RefMut => Ty::Ref(inner_ty.clone().into(), Mutability::Mut), 688 BindingMode::Move => inner_ty.clone(),
623 BindingAnnotation::Mutable | BindingAnnotation::Unannotated => inner_ty.clone(),
624 }; 689 };
625 let bound_ty = self.resolve_ty_as_possible(&mut vec![], bound_ty); 690 let bound_ty = self.resolve_ty_as_possible(&mut vec![], bound_ty);
626 self.write_pat_ty(pat, bound_ty); 691 self.write_pat_ty(pat, bound_ty);
@@ -700,7 +765,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
700 } 765 }
701 Expr::For { iterable, body, pat } => { 766 Expr::For { iterable, body, pat } => {
702 let _iterable_ty = self.infer_expr(*iterable, &Expectation::none()); 767 let _iterable_ty = self.infer_expr(*iterable, &Expectation::none());
703 self.infer_pat(*pat, &Ty::Unknown); 768 self.infer_pat(*pat, &Ty::Unknown, BindingMode::default());
704 self.infer_expr(*body, &Expectation::has_type(Ty::unit())); 769 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
705 Ty::unit() 770 Ty::unit()
706 } 771 }
@@ -714,7 +779,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
714 } else { 779 } else {
715 Ty::Unknown 780 Ty::Unknown
716 }; 781 };
717 self.infer_pat(*arg_pat, &expected); 782 self.infer_pat(*arg_pat, &expected, BindingMode::default());
718 } 783 }
719 784
720 // TODO: infer lambda type etc. 785 // TODO: infer lambda type etc.
@@ -807,7 +872,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
807 872
808 for arm in arms { 873 for arm in arms {
809 for &pat in &arm.pats { 874 for &pat in &arm.pats {
810 let _pat_ty = self.infer_pat(pat, &input_ty); 875 let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::default());
811 } 876 }
812 if let Some(guard_expr) = arm.guard { 877 if let Some(guard_expr) = arm.guard {
813 self.infer_expr(guard_expr, &Expectation::has_type(Ty::Bool)); 878 self.infer_expr(guard_expr, &Expectation::has_type(Ty::Bool));
@@ -1007,7 +1072,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1007 decl_ty 1072 decl_ty
1008 }; 1073 };
1009 1074
1010 self.infer_pat(*pat, &ty); 1075 self.infer_pat(*pat, &ty, BindingMode::default());
1011 } 1076 }
1012 Statement::Expr(expr) => { 1077 Statement::Expr(expr) => {
1013 self.infer_expr(*expr, &Expectation::none()); 1078 self.infer_expr(*expr, &Expectation::none());
@@ -1023,7 +1088,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1023 for (type_ref, pat) in signature.params().iter().zip(body.params()) { 1088 for (type_ref, pat) in signature.params().iter().zip(body.params()) {
1024 let ty = self.make_ty(type_ref); 1089 let ty = self.make_ty(type_ref);
1025 1090
1026 self.infer_pat(*pat, &ty); 1091 self.infer_pat(*pat, &ty, BindingMode::default());
1027 } 1092 }
1028 self.return_ty = self.make_ty(signature.ret_type()); 1093 self.return_ty = self.make_ty(signature.ret_type());
1029 } 1094 }
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index acae71c26..0f2172ddf 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -831,6 +831,60 @@ fn test(x: &i32) {
831} 831}
832 832
833#[test] 833#[test]
834fn infer_pattern_match_ergonomics() {
835 assert_snapshot_matches!(
836 infer(r#"
837struct A<T>(T);
838
839fn test() {
840 let A(n) = &A(1);
841 let A(n) = &mut A(1);
842}
843"#),
844 @r###"
845[28; 79) '{ ...(1); }': ()
846[38; 42) 'A(n)': A<i32>
847[40; 41) 'n': &i32
848[45; 50) '&A(1)': &A<i32>
849[46; 47) 'A': A<i32>(T) -> A<T>
850[46; 50) 'A(1)': A<i32>
851[48; 49) '1': i32
852[60; 64) 'A(n)': A<i32>
853[62; 63) 'n': &mut i32
854[67; 76) '&mut A(1)': &mut A<i32>
855[72; 73) 'A': A<i32>(T) -> A<T>
856[72; 76) 'A(1)': A<i32>
857[74; 75) '1': i32"###
858 );
859}
860
861#[test]
862fn infer_pattern_match_ergonomics_ref() {
863 covers!(match_ergonomics_ref);
864 assert_snapshot_matches!(
865 infer(r#"
866fn test() {
867 let v = &(1, &2);
868 let (_, &w) = v;
869}
870"#),
871 @r###"
872[11; 57) '{ ...= v; }': ()
873[21; 22) 'v': &(i32, &i32)
874[25; 33) '&(1, &2)': &(i32, &i32)
875[26; 33) '(1, &2)': (i32, &i32)
876[27; 28) '1': i32
877[30; 32) '&2': &i32
878[31; 32) '2': i32
879[43; 50) '(_, &w)': (i32, &i32)
880[44; 45) '_': i32
881[47; 49) '&w': &i32
882[48; 49) 'w': i32
883[53; 54) 'v': &(i32, &i32)"###
884 );
885}
886
887#[test]
834fn infer_adt_pattern() { 888fn infer_adt_pattern() {
835 assert_snapshot_matches!( 889 assert_snapshot_matches!(
836 infer(r#" 890 infer(r#"