diff options
author | Michael Killough <[email protected]> | 2019-03-16 18:13:13 +0000 |
---|---|---|
committer | Michael Killough <[email protected]> | 2019-03-16 18:13:13 +0000 |
commit | b42c5ced68c019108e079dc01d0bd29606efc10c (patch) | |
tree | 340dd45df03b6841103cad9e3980dfe261f8b4e7 /crates/ra_hir | |
parent | 97a87bf3a68338b1acc2b7a02dfa43096bf47e05 (diff) |
Implement BindingMode for pattern matching.
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.
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/ty/infer.rs | 95 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 121 |
2 files changed, 154 insertions, 62 deletions
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 268d2c110..7200446ba 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs | |||
@@ -63,6 +63,24 @@ enum ExprOrPatId { | |||
63 | 63 | ||
64 | impl_froms!(ExprOrPatId: ExprId, PatId); | 64 | impl_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)] | ||
69 | enum BindingMode { | ||
70 | Move, | ||
71 | Ref(Mutability), | ||
72 | } | ||
73 | |||
74 | impl 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 | |||
66 | /// The result of type inference: A mapping from expressions and patterns to types. | 84 | /// The result of type inference: A mapping from expressions and patterns to types. |
67 | #[derive(Clone, PartialEq, Eq, Debug)] | 85 | #[derive(Clone, PartialEq, Eq, Debug)] |
68 | pub struct InferenceResult { | 86 | pub struct InferenceResult { |
@@ -530,6 +548,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
530 | path: Option<&Path>, | 548 | path: Option<&Path>, |
531 | subpats: &[PatId], | 549 | subpats: &[PatId], |
532 | expected: &Ty, | 550 | expected: &Ty, |
551 | default_bm: BindingMode, | ||
533 | ) -> Ty { | 552 | ) -> Ty { |
534 | let (ty, def) = self.resolve_variant(path); | 553 | let (ty, def) = self.resolve_variant(path); |
535 | 554 | ||
@@ -542,13 +561,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
542 | .and_then(|d| d.field(self.db, &Name::tuple_field_name(i))) | 561 | .and_then(|d| d.field(self.db, &Name::tuple_field_name(i))) |
543 | .map_or(Ty::Unknown, |field| field.ty(self.db)) | 562 | .map_or(Ty::Unknown, |field| field.ty(self.db)) |
544 | .subst(&substs); | 563 | .subst(&substs); |
545 | self.infer_pat(subpat, &expected_ty); | 564 | self.infer_pat(subpat, &expected_ty, default_bm); |
546 | } | 565 | } |
547 | 566 | ||
548 | ty | 567 | ty |
549 | } | 568 | } |
550 | 569 | ||
551 | fn infer_struct_pat(&mut self, path: Option<&Path>, subpats: &[FieldPat], expected: &Ty) -> Ty { | 570 | fn infer_struct_pat( |
571 | &mut self, | ||
572 | path: Option<&Path>, | ||
573 | subpats: &[FieldPat], | ||
574 | expected: &Ty, | ||
575 | default_bm: BindingMode, | ||
576 | ) -> Ty { | ||
552 | let (ty, def) = self.resolve_variant(path); | 577 | let (ty, def) = self.resolve_variant(path); |
553 | 578 | ||
554 | self.unify(&ty, expected); | 579 | self.unify(&ty, expected); |
@@ -559,15 +584,42 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
559 | let matching_field = def.and_then(|it| it.field(self.db, &subpat.name)); | 584 | let matching_field = def.and_then(|it| it.field(self.db, &subpat.name)); |
560 | let expected_ty = | 585 | let expected_ty = |
561 | matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs); | 586 | matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs); |
562 | self.infer_pat(subpat.pat, &expected_ty); | 587 | self.infer_pat(subpat.pat, &expected_ty, default_bm); |
563 | } | 588 | } |
564 | 589 | ||
565 | ty | 590 | ty |
566 | } | 591 | } |
567 | 592 | ||
568 | fn infer_pat(&mut self, pat: PatId, expected: &Ty) -> Ty { | 593 | 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 | 594 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
570 | 595 | ||
596 | let is_non_ref_pat = match &body[pat] { | ||
597 | Pat::Tuple(..) | ||
598 | | Pat::TupleStruct { .. } | ||
599 | | Pat::Struct { .. } | ||
600 | | Pat::Range { .. } | ||
601 | | Pat::Slice { .. } => true, | ||
602 | // TODO: Path/Lit might actually evaluate to ref, but inference is unimplemented. | ||
603 | Pat::Path(..) | Pat::Lit(..) => true, | ||
604 | Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Missing => false, | ||
605 | }; | ||
606 | if is_non_ref_pat { | ||
607 | while let Ty::Ref(inner, mutability) = expected { | ||
608 | expected = inner; | ||
609 | default_bm = match default_bm { | ||
610 | BindingMode::Move => BindingMode::Ref(*mutability), | ||
611 | BindingMode::Ref(Mutability::Shared) => BindingMode::Ref(Mutability::Shared), | ||
612 | BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(*mutability), | ||
613 | } | ||
614 | } | ||
615 | } else if let Pat::Ref { .. } = &body[pat] { | ||
616 | default_bm = BindingMode::Move; | ||
617 | } | ||
618 | |||
619 | // Lose mutability. | ||
620 | let default_bm = default_bm; | ||
621 | let expected = expected; | ||
622 | |||
571 | let ty = match &body[pat] { | 623 | let ty = match &body[pat] { |
572 | Pat::Tuple(ref args) => { | 624 | Pat::Tuple(ref args) => { |
573 | let expectations = match *expected { | 625 | let expectations = match *expected { |
@@ -579,7 +631,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
579 | let inner_tys = args | 631 | let inner_tys = args |
580 | .iter() | 632 | .iter() |
581 | .zip(expectations_iter) | 633 | .zip(expectations_iter) |
582 | .map(|(&pat, ty)| self.infer_pat(pat, ty)) | 634 | .map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm)) |
583 | .collect::<Vec<_>>() | 635 | .collect::<Vec<_>>() |
584 | .into(); | 636 | .into(); |
585 | 637 | ||
@@ -595,14 +647,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
595 | } | 647 | } |
596 | _ => &Ty::Unknown, | 648 | _ => &Ty::Unknown, |
597 | }; | 649 | }; |
598 | let subty = self.infer_pat(*pat, expectation); | 650 | let subty = self.infer_pat(*pat, expectation, default_bm); |
599 | Ty::Ref(subty.into(), *mutability) | 651 | Ty::Ref(subty.into(), *mutability) |
600 | } | 652 | } |
601 | Pat::TupleStruct { path: ref p, args: ref subpats } => { | 653 | Pat::TupleStruct { path: ref p, args: ref subpats } => { |
602 | self.infer_tuple_struct_pat(p.as_ref(), subpats, expected) | 654 | self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm) |
603 | } | 655 | } |
604 | Pat::Struct { path: ref p, args: ref fields } => { | 656 | Pat::Struct { path: ref p, args: ref fields } => { |
605 | self.infer_struct_pat(p.as_ref(), fields, expected) | 657 | self.infer_struct_pat(p.as_ref(), fields, expected, default_bm) |
606 | } | 658 | } |
607 | Pat::Path(path) => { | 659 | Pat::Path(path) => { |
608 | // TODO use correct resolver for the surrounding expression | 660 | // TODO use correct resolver for the surrounding expression |
@@ -610,17 +662,26 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
610 | self.infer_path_expr(&resolver, &path, pat.into()).unwrap_or(Ty::Unknown) | 662 | self.infer_path_expr(&resolver, &path, pat.into()).unwrap_or(Ty::Unknown) |
611 | } | 663 | } |
612 | Pat::Bind { mode, name: _name, subpat } => { | 664 | Pat::Bind { mode, name: _name, subpat } => { |
665 | let mode = if mode == &BindingAnnotation::Unannotated { | ||
666 | default_bm | ||
667 | } else { | ||
668 | BindingMode::convert(mode) | ||
669 | }; | ||
613 | let inner_ty = if let Some(subpat) = subpat { | 670 | let inner_ty = if let Some(subpat) = subpat { |
614 | self.infer_pat(*subpat, expected) | 671 | self.infer_pat(*subpat, expected, default_bm) |
615 | } else { | 672 | } else { |
616 | expected.clone() | 673 | expected.clone() |
617 | }; | 674 | }; |
618 | let inner_ty = self.insert_type_vars_shallow(inner_ty); | 675 | let inner_ty = self.insert_type_vars_shallow(inner_ty); |
619 | 676 | ||
620 | let bound_ty = match mode { | 677 | let bound_ty = match mode { |
621 | BindingAnnotation::Ref => Ty::Ref(inner_ty.clone().into(), Mutability::Shared), | 678 | BindingMode::Ref(Mutability::Shared) => { |
622 | BindingAnnotation::RefMut => Ty::Ref(inner_ty.clone().into(), Mutability::Mut), | 679 | Ty::Ref(inner_ty.clone().into(), Mutability::Shared) |
623 | BindingAnnotation::Mutable | BindingAnnotation::Unannotated => inner_ty.clone(), | 680 | } |
681 | BindingMode::Ref(Mutability::Mut) => { | ||
682 | Ty::Ref(inner_ty.clone().into(), Mutability::Mut) | ||
683 | } | ||
684 | BindingMode::Move => inner_ty.clone(), | ||
624 | }; | 685 | }; |
625 | let bound_ty = self.resolve_ty_as_possible(&mut vec![], bound_ty); | 686 | let bound_ty = self.resolve_ty_as_possible(&mut vec![], bound_ty); |
626 | self.write_pat_ty(pat, bound_ty); | 687 | self.write_pat_ty(pat, bound_ty); |
@@ -700,7 +761,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
700 | } | 761 | } |
701 | Expr::For { iterable, body, pat } => { | 762 | Expr::For { iterable, body, pat } => { |
702 | let _iterable_ty = self.infer_expr(*iterable, &Expectation::none()); | 763 | let _iterable_ty = self.infer_expr(*iterable, &Expectation::none()); |
703 | self.infer_pat(*pat, &Ty::Unknown); | 764 | self.infer_pat(*pat, &Ty::Unknown, BindingMode::Move); |
704 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 765 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
705 | Ty::unit() | 766 | Ty::unit() |
706 | } | 767 | } |
@@ -714,7 +775,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
714 | } else { | 775 | } else { |
715 | Ty::Unknown | 776 | Ty::Unknown |
716 | }; | 777 | }; |
717 | self.infer_pat(*arg_pat, &expected); | 778 | self.infer_pat(*arg_pat, &expected, BindingMode::Move); |
718 | } | 779 | } |
719 | 780 | ||
720 | // TODO: infer lambda type etc. | 781 | // TODO: infer lambda type etc. |
@@ -804,7 +865,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
804 | 865 | ||
805 | for arm in arms { | 866 | for arm in arms { |
806 | for &pat in &arm.pats { | 867 | for &pat in &arm.pats { |
807 | let _pat_ty = self.infer_pat(pat, &input_ty); | 868 | let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::Move); |
808 | } | 869 | } |
809 | if let Some(guard_expr) = arm.guard { | 870 | if let Some(guard_expr) = arm.guard { |
810 | self.infer_expr(guard_expr, &Expectation::has_type(Ty::Bool)); | 871 | self.infer_expr(guard_expr, &Expectation::has_type(Ty::Bool)); |
@@ -1004,7 +1065,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1004 | decl_ty | 1065 | decl_ty |
1005 | }; | 1066 | }; |
1006 | 1067 | ||
1007 | self.infer_pat(*pat, &ty); | 1068 | self.infer_pat(*pat, &ty, BindingMode::Move); |
1008 | } | 1069 | } |
1009 | Statement::Expr(expr) => { | 1070 | Statement::Expr(expr) => { |
1010 | self.infer_expr(*expr, &Expectation::none()); | 1071 | self.infer_expr(*expr, &Expectation::none()); |
@@ -1020,7 +1081,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
1020 | for (type_ref, pat) in signature.params().iter().zip(body.params()) { | 1081 | for (type_ref, pat) in signature.params().iter().zip(body.params()) { |
1021 | let ty = self.make_ty(type_ref); | 1082 | let ty = self.make_ty(type_ref); |
1022 | 1083 | ||
1023 | self.infer_pat(*pat, &ty); | 1084 | self.infer_pat(*pat, &ty, BindingMode::Move); |
1024 | } | 1085 | } |
1025 | self.return_ty = self.make_ty(signature.ret_type()); | 1086 | self.return_ty = self.make_ty(signature.ret_type()); |
1026 | } | 1087 | } |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c7d409e6d..b12ea4334 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -756,6 +756,8 @@ fn test(x: &str, y: isize) { | |||
756 | fn infer_pattern() { | 756 | fn infer_pattern() { |
757 | assert_snapshot_matches!( | 757 | assert_snapshot_matches!( |
758 | infer(r#" | 758 | infer(r#" |
759 | struct A<T>(T); | ||
760 | |||
759 | fn test(x: &i32) { | 761 | fn test(x: &i32) { |
760 | let y = x; | 762 | let y = x; |
761 | let &z = x; | 763 | let &z = x; |
@@ -772,6 +774,12 @@ fn test(x: &i32) { | |||
772 | 774 | ||
773 | let lambda = |a: u64, b, c: i32| { a + b; c }; | 775 | let lambda = |a: u64, b, c: i32| { a + b; c }; |
774 | 776 | ||
777 | let A(n) = &A(1); | ||
778 | let A(n) = &mut A(1); | ||
779 | |||
780 | let v = &(1, &2); | ||
781 | let (_, &w) = v; | ||
782 | |||
775 | let ref ref_to_x = x; | 783 | let ref ref_to_x = x; |
776 | let mut mut_x = x; | 784 | let mut mut_x = x; |
777 | let ref mut mut_ref_to_x = x; | 785 | let ref mut mut_ref_to_x = x; |
@@ -779,53 +787,76 @@ fn test(x: &i32) { | |||
779 | } | 787 | } |
780 | "#), | 788 | "#), |
781 | @r###" | 789 | @r###" |
782 | [9; 10) 'x': &i32 | 790 | [26; 27) 'x': &i32 |
783 | [18; 369) '{ ...o_x; }': () | 791 | [35; 479) '{ ...o_x; }': () |
784 | [28; 29) 'y': &i32 | 792 | [45; 46) 'y': &i32 |
785 | [32; 33) 'x': &i32 | 793 | [49; 50) 'x': &i32 |
786 | [43; 45) '&z': &i32 | 794 | [60; 62) '&z': &i32 |
787 | [44; 45) 'z': i32 | 795 | [61; 62) 'z': i32 |
788 | [48; 49) 'x': &i32 | 796 | [65; 66) 'x': &i32 |
789 | [59; 60) 'a': i32 | 797 | [76; 77) 'a': i32 |
790 | [63; 64) 'z': i32 | 798 | [80; 81) 'z': i32 |
791 | [74; 80) '(c, d)': (i32, &str) | 799 | [91; 97) '(c, d)': (i32, &str) |
792 | [75; 76) 'c': i32 | 800 | [92; 93) 'c': i32 |
793 | [78; 79) 'd': &str | 801 | [95; 96) 'd': &str |
794 | [83; 95) '(1, "hello")': (i32, &str) | 802 | [100; 112) '(1, "hello")': (i32, &str) |
795 | [84; 85) '1': i32 | 803 | [101; 102) '1': i32 |
796 | [87; 94) '"hello"': &str | 804 | [104; 111) '"hello"': &str |
797 | [102; 152) 'for (e... }': () | 805 | [119; 169) 'for (e... }': () |
798 | [106; 112) '(e, f)': ({unknown}, {unknown}) | 806 | [123; 129) '(e, f)': ({unknown}, {unknown}) |
799 | [107; 108) 'e': {unknown} | 807 | [124; 125) 'e': {unknown} |
800 | [110; 111) 'f': {unknown} | 808 | [127; 128) 'f': {unknown} |
801 | [116; 125) 'some_iter': {unknown} | 809 | [133; 142) 'some_iter': {unknown} |
802 | [126; 152) '{ ... }': () | 810 | [143; 169) '{ ... }': () |
803 | [140; 141) 'g': {unknown} | 811 | [157; 158) 'g': {unknown} |
804 | [144; 145) 'e': {unknown} | 812 | [161; 162) 'e': {unknown} |
805 | [158; 205) 'if let... }': () | 813 | [175; 222) 'if let... }': () |
806 | [165; 170) '[val]': {unknown} | 814 | [182; 187) '[val]': {unknown} |
807 | [173; 176) 'opt': {unknown} | 815 | [190; 193) 'opt': {unknown} |
808 | [177; 205) '{ ... }': () | 816 | [194; 222) '{ ... }': () |
809 | [191; 192) 'h': {unknown} | 817 | [208; 209) 'h': {unknown} |
810 | [195; 198) 'val': {unknown} | 818 | [212; 215) 'val': {unknown} |
811 | [215; 221) 'lambda': {unknown} | 819 | [232; 238) 'lambda': {unknown} |
812 | [224; 256) '|a: u6...b; c }': {unknown} | 820 | [241; 273) '|a: u6...b; c }': {unknown} |
813 | [225; 226) 'a': u64 | 821 | [242; 243) 'a': u64 |
814 | [233; 234) 'b': u64 | ||
815 | [236; 237) 'c': i32 | ||
816 | [244; 256) '{ a + b; c }': i32 | ||
817 | [246; 247) 'a': u64 | ||
818 | [246; 251) 'a + b': u64 | ||
819 | [250; 251) 'b': u64 | 822 | [250; 251) 'b': u64 |
820 | [253; 254) 'c': i32 | 823 | [253; 254) 'c': i32 |
821 | [267; 279) 'ref ref_to_x': &&i32 | 824 | [261; 273) '{ a + b; c }': i32 |
822 | [282; 283) 'x': &i32 | 825 | [263; 264) 'a': u64 |
823 | [293; 302) 'mut mut_x': &i32 | 826 | [263; 268) 'a + b': u64 |
824 | [305; 306) 'x': &i32 | 827 | [267; 268) 'b': u64 |
825 | [316; 336) 'ref mu...f_to_x': &mut &i32 | 828 | [270; 271) 'c': i32 |
826 | [339; 340) 'x': &i32 | 829 | [284; 288) 'A(n)': A<i32> |
827 | [350; 351) 'k': &mut &i32 | 830 | [286; 287) 'n': &i32 |
828 | [354; 366) 'mut_ref_to_x': &mut &i32"### | 831 | [291; 296) '&A(1)': &A<i32> |
832 | [292; 293) 'A': A<i32>(T) -> A<T> | ||
833 | [292; 296) 'A(1)': A<i32> | ||
834 | [294; 295) '1': i32 | ||
835 | [306; 310) 'A(n)': A<i32> | ||
836 | [308; 309) 'n': &mut i32 | ||
837 | [313; 322) '&mut A(1)': &mut A<i32> | ||
838 | [318; 319) 'A': A<i32>(T) -> A<T> | ||
839 | [318; 322) 'A(1)': A<i32> | ||
840 | [320; 321) '1': i32 | ||
841 | [333; 334) 'v': &(i32, &i32) | ||
842 | [337; 345) '&(1, &2)': &(i32, &i32) | ||
843 | [338; 345) '(1, &2)': (i32, &i32) | ||
844 | [339; 340) '1': i32 | ||
845 | [342; 344) '&2': &i32 | ||
846 | [343; 344) '2': i32 | ||
847 | [355; 362) '(_, &w)': (i32, &i32) | ||
848 | [356; 357) '_': i32 | ||
849 | [359; 361) '&w': &i32 | ||
850 | [360; 361) 'w': i32 | ||
851 | [365; 366) 'v': &(i32, &i32) | ||
852 | [377; 389) 'ref ref_to_x': &&i32 | ||
853 | [392; 393) 'x': &i32 | ||
854 | [403; 412) 'mut mut_x': &i32 | ||
855 | [415; 416) 'x': &i32 | ||
856 | [426; 446) 'ref mu...f_to_x': &mut &i32 | ||
857 | [449; 450) 'x': &i32 | ||
858 | [460; 461) 'k': &mut &i32 | ||
859 | [464; 476) 'mut_ref_to_x': &mut &i32"### | ||
829 | ); | 860 | ); |
830 | } | 861 | } |
831 | 862 | ||