From b42c5ced68c019108e079dc01d0bd29606efc10c Mon Sep 17 00:00:00 2001 From: Michael Killough Date: Sat, 16 Mar 2019 18:13:13 +0000 Subject: 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. --- crates/ra_hir/src/ty/infer.rs | 95 +++++++++++++++++++++++++++------ crates/ra_hir/src/ty/tests.rs | 121 ++++++++++++++++++++++++++---------------- 2 files changed, 154 insertions(+), 62 deletions(-) (limited to 'crates/ra_hir/src/ty') 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 { impl_froms!(ExprOrPatId: ExprId, PatId); +/// Binding modes inferred for patterns. +/// https://doc.rust-lang.org/reference/patterns.html#binding-modes +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum BindingMode { + Move, + Ref(Mutability), +} + +impl BindingMode { + pub fn convert(annotation: &BindingAnnotation) -> BindingMode { + match annotation { + BindingAnnotation::Unannotated | BindingAnnotation::Mutable => BindingMode::Move, + BindingAnnotation::Ref => BindingMode::Ref(Mutability::Shared), + BindingAnnotation::RefMut => BindingMode::Ref(Mutability::Mut), + } + } +} + /// The result of type inference: A mapping from expressions and patterns to types. #[derive(Clone, PartialEq, Eq, Debug)] pub struct InferenceResult { @@ -530,6 +548,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { path: Option<&Path>, subpats: &[PatId], expected: &Ty, + default_bm: BindingMode, ) -> Ty { let (ty, def) = self.resolve_variant(path); @@ -542,13 +561,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .and_then(|d| d.field(self.db, &Name::tuple_field_name(i))) .map_or(Ty::Unknown, |field| field.ty(self.db)) .subst(&substs); - self.infer_pat(subpat, &expected_ty); + self.infer_pat(subpat, &expected_ty, default_bm); } ty } - fn infer_struct_pat(&mut self, path: Option<&Path>, subpats: &[FieldPat], expected: &Ty) -> Ty { + fn infer_struct_pat( + &mut self, + path: Option<&Path>, + subpats: &[FieldPat], + expected: &Ty, + default_bm: BindingMode, + ) -> Ty { let (ty, def) = self.resolve_variant(path); self.unify(&ty, expected); @@ -559,15 +584,42 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let matching_field = def.and_then(|it| it.field(self.db, &subpat.name)); let expected_ty = matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs); - self.infer_pat(subpat.pat, &expected_ty); + self.infer_pat(subpat.pat, &expected_ty, default_bm); } ty } - fn infer_pat(&mut self, pat: PatId, expected: &Ty) -> Ty { + fn infer_pat(&mut self, pat: PatId, mut expected: &Ty, mut default_bm: BindingMode) -> Ty { let body = Arc::clone(&self.body); // avoid borrow checker problem + let is_non_ref_pat = match &body[pat] { + Pat::Tuple(..) + | Pat::TupleStruct { .. } + | Pat::Struct { .. } + | Pat::Range { .. } + | Pat::Slice { .. } => true, + // TODO: Path/Lit might actually evaluate to ref, but inference is unimplemented. + Pat::Path(..) | Pat::Lit(..) => true, + Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Missing => false, + }; + if is_non_ref_pat { + while let Ty::Ref(inner, mutability) = expected { + expected = inner; + default_bm = match default_bm { + BindingMode::Move => BindingMode::Ref(*mutability), + BindingMode::Ref(Mutability::Shared) => BindingMode::Ref(Mutability::Shared), + BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(*mutability), + } + } + } else if let Pat::Ref { .. } = &body[pat] { + default_bm = BindingMode::Move; + } + + // Lose mutability. + let default_bm = default_bm; + let expected = expected; + let ty = match &body[pat] { Pat::Tuple(ref args) => { let expectations = match *expected { @@ -579,7 +631,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let inner_tys = args .iter() .zip(expectations_iter) - .map(|(&pat, ty)| self.infer_pat(pat, ty)) + .map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm)) .collect::>() .into(); @@ -595,14 +647,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } _ => &Ty::Unknown, }; - let subty = self.infer_pat(*pat, expectation); + let subty = self.infer_pat(*pat, expectation, default_bm); Ty::Ref(subty.into(), *mutability) } Pat::TupleStruct { path: ref p, args: ref subpats } => { - self.infer_tuple_struct_pat(p.as_ref(), subpats, expected) + self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm) } Pat::Struct { path: ref p, args: ref fields } => { - self.infer_struct_pat(p.as_ref(), fields, expected) + self.infer_struct_pat(p.as_ref(), fields, expected, default_bm) } Pat::Path(path) => { // TODO use correct resolver for the surrounding expression @@ -610,17 +662,26 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.infer_path_expr(&resolver, &path, pat.into()).unwrap_or(Ty::Unknown) } Pat::Bind { mode, name: _name, subpat } => { + let mode = if mode == &BindingAnnotation::Unannotated { + default_bm + } else { + BindingMode::convert(mode) + }; let inner_ty = if let Some(subpat) = subpat { - self.infer_pat(*subpat, expected) + self.infer_pat(*subpat, expected, default_bm) } else { expected.clone() }; let inner_ty = self.insert_type_vars_shallow(inner_ty); let bound_ty = match mode { - BindingAnnotation::Ref => Ty::Ref(inner_ty.clone().into(), Mutability::Shared), - BindingAnnotation::RefMut => Ty::Ref(inner_ty.clone().into(), Mutability::Mut), - BindingAnnotation::Mutable | BindingAnnotation::Unannotated => inner_ty.clone(), + BindingMode::Ref(Mutability::Shared) => { + Ty::Ref(inner_ty.clone().into(), Mutability::Shared) + } + BindingMode::Ref(Mutability::Mut) => { + Ty::Ref(inner_ty.clone().into(), Mutability::Mut) + } + BindingMode::Move => inner_ty.clone(), }; let bound_ty = self.resolve_ty_as_possible(&mut vec![], bound_ty); self.write_pat_ty(pat, bound_ty); @@ -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); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); Ty::unit() } @@ -714,7 +775,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } else { Ty::Unknown }; - self.infer_pat(*arg_pat, &expected); + self.infer_pat(*arg_pat, &expected, BindingMode::Move); } // TODO: infer lambda type etc. @@ -804,7 +865,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for arm in arms { for &pat in &arm.pats { - let _pat_ty = self.infer_pat(pat, &input_ty); + let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::Move); } if let Some(guard_expr) = arm.guard { self.infer_expr(guard_expr, &Expectation::has_type(Ty::Bool)); @@ -1004,7 +1065,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { decl_ty }; - self.infer_pat(*pat, &ty); + self.infer_pat(*pat, &ty, BindingMode::Move); } Statement::Expr(expr) => { self.infer_expr(*expr, &Expectation::none()); @@ -1020,7 +1081,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for (type_ref, pat) in signature.params().iter().zip(body.params()) { let ty = self.make_ty(type_ref); - self.infer_pat(*pat, &ty); + self.infer_pat(*pat, &ty, BindingMode::Move); } self.return_ty = self.make_ty(signature.ret_type()); } 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) { fn infer_pattern() { assert_snapshot_matches!( infer(r#" +struct A(T); + fn test(x: &i32) { let y = x; let &z = x; @@ -772,6 +774,12 @@ fn test(x: &i32) { let lambda = |a: u64, b, c: i32| { a + b; c }; + let A(n) = &A(1); + let A(n) = &mut A(1); + + let v = &(1, &2); + let (_, &w) = v; + let ref ref_to_x = x; let mut mut_x = x; let ref mut mut_ref_to_x = x; @@ -779,53 +787,76 @@ fn test(x: &i32) { } "#), @r###" -[9; 10) 'x': &i32 -[18; 369) '{ ...o_x; }': () -[28; 29) 'y': &i32 -[32; 33) 'x': &i32 -[43; 45) '&z': &i32 -[44; 45) 'z': i32 -[48; 49) 'x': &i32 -[59; 60) 'a': i32 -[63; 64) 'z': i32 -[74; 80) '(c, d)': (i32, &str) -[75; 76) 'c': i32 -[78; 79) 'd': &str -[83; 95) '(1, "hello")': (i32, &str) -[84; 85) '1': i32 -[87; 94) '"hello"': &str -[102; 152) 'for (e... }': () -[106; 112) '(e, f)': ({unknown}, {unknown}) -[107; 108) 'e': {unknown} -[110; 111) 'f': {unknown} -[116; 125) 'some_iter': {unknown} -[126; 152) '{ ... }': () -[140; 141) 'g': {unknown} -[144; 145) 'e': {unknown} -[158; 205) 'if let... }': () -[165; 170) '[val]': {unknown} -[173; 176) 'opt': {unknown} -[177; 205) '{ ... }': () -[191; 192) 'h': {unknown} -[195; 198) 'val': {unknown} -[215; 221) 'lambda': {unknown} -[224; 256) '|a: u6...b; c }': {unknown} -[225; 226) 'a': u64 -[233; 234) 'b': u64 -[236; 237) 'c': i32 -[244; 256) '{ a + b; c }': i32 -[246; 247) 'a': u64 -[246; 251) 'a + b': u64 +[26; 27) 'x': &i32 +[35; 479) '{ ...o_x; }': () +[45; 46) 'y': &i32 +[49; 50) 'x': &i32 +[60; 62) '&z': &i32 +[61; 62) 'z': i32 +[65; 66) 'x': &i32 +[76; 77) 'a': i32 +[80; 81) 'z': i32 +[91; 97) '(c, d)': (i32, &str) +[92; 93) 'c': i32 +[95; 96) 'd': &str +[100; 112) '(1, "hello")': (i32, &str) +[101; 102) '1': i32 +[104; 111) '"hello"': &str +[119; 169) 'for (e... }': () +[123; 129) '(e, f)': ({unknown}, {unknown}) +[124; 125) 'e': {unknown} +[127; 128) 'f': {unknown} +[133; 142) 'some_iter': {unknown} +[143; 169) '{ ... }': () +[157; 158) 'g': {unknown} +[161; 162) 'e': {unknown} +[175; 222) 'if let... }': () +[182; 187) '[val]': {unknown} +[190; 193) 'opt': {unknown} +[194; 222) '{ ... }': () +[208; 209) 'h': {unknown} +[212; 215) 'val': {unknown} +[232; 238) 'lambda': {unknown} +[241; 273) '|a: u6...b; c }': {unknown} +[242; 243) 'a': u64 [250; 251) 'b': u64 [253; 254) 'c': i32 -[267; 279) 'ref ref_to_x': &&i32 -[282; 283) 'x': &i32 -[293; 302) 'mut mut_x': &i32 -[305; 306) 'x': &i32 -[316; 336) 'ref mu...f_to_x': &mut &i32 -[339; 340) 'x': &i32 -[350; 351) 'k': &mut &i32 -[354; 366) 'mut_ref_to_x': &mut &i32"### +[261; 273) '{ a + b; c }': i32 +[263; 264) 'a': u64 +[263; 268) 'a + b': u64 +[267; 268) 'b': u64 +[270; 271) 'c': i32 +[284; 288) 'A(n)': A +[286; 287) 'n': &i32 +[291; 296) '&A(1)': &A +[292; 293) 'A': A(T) -> A +[292; 296) 'A(1)': A +[294; 295) '1': i32 +[306; 310) 'A(n)': A +[308; 309) 'n': &mut i32 +[313; 322) '&mut A(1)': &mut A +[318; 319) 'A': A(T) -> A +[318; 322) 'A(1)': A +[320; 321) '1': i32 +[333; 334) 'v': &(i32, &i32) +[337; 345) '&(1, &2)': &(i32, &i32) +[338; 345) '(1, &2)': (i32, &i32) +[339; 340) '1': i32 +[342; 344) '&2': &i32 +[343; 344) '2': i32 +[355; 362) '(_, &w)': (i32, &i32) +[356; 357) '_': i32 +[359; 361) '&w': &i32 +[360; 361) 'w': i32 +[365; 366) 'v': &(i32, &i32) +[377; 389) 'ref ref_to_x': &&i32 +[392; 393) 'x': &i32 +[403; 412) 'mut mut_x': &i32 +[415; 416) 'x': &i32 +[426; 446) 'ref mu...f_to_x': &mut &i32 +[449; 450) 'x': &i32 +[460; 461) 'k': &mut &i32 +[464; 476) 'mut_ref_to_x': &mut &i32"### ); } -- cgit v1.2.3