From 72712b8a428e17d63c413522c770e8f1f0587455 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 20 Feb 2019 22:36:54 +0100 Subject: Fix handling of generics in tuple variants and refactor a bit Also make them display a tiny bit nicer. Fixes #860. --- crates/ra_hir/src/generics.rs | 13 ++++ crates/ra_hir/src/ty.rs | 83 ++++++++++++---------- .../src/ty/snapshots/tests__infer_struct.snap | 6 +- .../tests__infer_tuple_struct_generics.snap | 23 ++++++ crates/ra_hir/src/ty/tests.rs | 21 ++++++ 5 files changed, 105 insertions(+), 41 deletions(-) create mode 100644 crates/ra_hir/src/ty/snapshots/tests__infer_tuple_struct_generics.snap (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index fcc513353..c494beeb0 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -87,4 +87,17 @@ impl GenericParams { let parent_count = self.count_parent_params(); parent_count + self.params.len() } + + fn for_each_param<'a>(&'a self, f: &mut impl FnMut(&'a GenericParam)) { + if let Some(parent) = &self.parent_params { + parent.for_each_param(f); + } + self.params.iter().for_each(f); + } + + pub fn params_including_parent(&self) -> Vec<&GenericParam> { + let mut vec = Vec::with_capacity(self.count_params_including_parent()); + self.for_each_param(&mut |p| vec.push(p)); + vec + } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d4d896673..7f28a6edd 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -40,7 +40,7 @@ use crate::{ name::KnownName, expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, generics::GenericParams, - path::GenericArg, + path::{ GenericArgs, GenericArg}, adt::VariantDef, resolve::{Resolver, Resolution}, nameres::Namespace }; @@ -165,17 +165,6 @@ impl Substs { pub fn empty() -> Substs { Substs(Arc::new([])) } - - /// Replaces the end of the substitutions by other ones. - pub(crate) fn replace_tail(self, replace_by: Vec) -> Substs { - // again missing Arc::make_mut_slice... - let len = replace_by.len().min(self.0.len()); - let parent_len = self.0.len() - len; - let mut result = Vec::with_capacity(parent_len + len); - result.extend(self.0.iter().take(parent_len).cloned()); - result.extend(replace_by); - Substs(result.into()) - } } /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). @@ -639,8 +628,11 @@ impl fmt::Display for Ty { join(sig.input.iter()).surround_with("fn(", ")").separator(", ").to_fmt(f)?; write!(f, " -> {}", sig.output) } - Ty::FnDef { name, substs, sig, .. } => { - write!(f, "fn {}", name)?; + Ty::FnDef { def, name, substs, sig, .. } => { + match def { + CallableDef::Function(_) => write!(f, "fn {}", name)?, + CallableDef::Struct(_) | CallableDef::EnumVariant(_) => write!(f, "{}", name)?, + } if substs.0.len() > 0 { join(substs.0.iter()).surround_with("<", ">").separator(", ").to_fmt(f)?; } @@ -712,16 +704,18 @@ fn type_for_enum_variant_constructor(db: &impl HirDatabase, def: EnumVariant) -> .iter() .map(|(_, field)| Ty::from_hir(db, &resolver, &field.type_ref)) .collect::>(); - let output = type_for_enum(db, def.parent_enum(db)); - let sig = Arc::new(FnSig { input, output }); let substs = make_substs(&generics); + let output = type_for_enum(db, def.parent_enum(db)).apply_substs(substs.clone()); + let sig = Arc::new(FnSig { input, output }); Ty::FnDef { def: def.into(), sig, name, substs } } fn make_substs(generics: &GenericParams) -> Substs { Substs( - (0..generics.count_params_including_parent()) - .map(|_p| Ty::Unknown) + generics + .params_including_parent() + .into_iter() + .map(|p| Ty::Param { idx: p.idx, name: p.name.clone() }) .collect::>() .into(), ) @@ -736,7 +730,7 @@ fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty { } } -pub(crate) fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty { +fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Ty { let generics = s.generic_params(db); Ty::Adt { def_id: s.into(), @@ -1353,6 +1347,36 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ty } + fn substs_for_method_call( + &mut self, + def_generics: Option>, + generic_args: &Option, + ) -> Substs { + let (parent_param_count, param_count) = + def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len())); + let mut substs = Vec::with_capacity(parent_param_count + param_count); + for _ in 0..parent_param_count { + substs.push(Ty::Unknown); + } + // handle provided type arguments + if let Some(generic_args) = generic_args { + // if args are provided, it should be all of them, but we can't rely on that + for arg in generic_args.args.iter().take(param_count) { + match arg { + GenericArg::Type(type_ref) => { + let ty = self.make_ty(type_ref); + substs.push(ty); + } + } + } + }; + let supplied_params = substs.len(); + for _ in supplied_params..parent_param_count + param_count { + substs.push(Ty::Unknown); + } + Substs(substs.into()) + } + fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let body = Arc::clone(&self.body); // avoid borrow checker problem let ty = match &body[tgt_expr] { @@ -1443,25 +1467,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } None => (Ty::Unknown, receiver_ty, None), }; - // handle provided type arguments - let method_ty = if let Some(generic_args) = generic_args { - // if args are provided, it should be all of them, but we can't rely on that - let param_count = def_generics.map(|g| g.params.len()).unwrap_or(0); - let mut new_substs = Vec::with_capacity(generic_args.args.len()); - for arg in generic_args.args.iter().take(param_count) { - match arg { - GenericArg::Type(type_ref) => { - let ty = self.make_ty(type_ref); - new_substs.push(ty); - } - } - } - let substs = method_ty.substs().unwrap_or_else(Substs::empty); - let substs = substs.replace_tail(new_substs); - method_ty.apply_substs(substs) - } else { - method_ty - }; + let substs = self.substs_for_method_call(def_generics, generic_args); + let method_ty = method_ty.apply_substs(substs); let method_ty = self.insert_type_vars(method_ty); let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { Ty::FnPtr(sig) => { diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap index 294186b06..32f1fa108 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_struct.snap @@ -1,19 +1,19 @@ --- -created: "2019-02-17T16:16:58.863630956Z" +created: "2019-02-20T21:31:12.910924715Z" creator: insta@0.6.2 source: crates/ra_hir/src/ty/tests.rs expression: "&result" --- [72; 154) '{ ...a.c; }': () [82; 83) 'c': C -[86; 87) 'C': fn C(usize) -> C +[86; 87) 'C': C(usize) -> C [86; 90) 'C(1)': C [88; 89) '1': usize [96; 97) 'B': B [107; 108) 'a': A [114; 133) 'A { b:...C(1) }': A [121; 122) 'B': B -[127; 128) 'C': fn C(usize) -> C +[127; 128) 'C': C(usize) -> C [127; 131) 'C(1)': C [129; 130) '1': usize [139; 140) 'a': A diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_tuple_struct_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_tuple_struct_generics.snap new file mode 100644 index 000000000..783795cfd --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_tuple_struct_generics.snap @@ -0,0 +1,23 @@ +--- +created: "2019-02-20T21:31:12.911275141Z" +creator: insta@0.6.2 +source: crates/ra_hir/src/ty/tests.rs +expression: "&result" +--- +[77; 185) '{ ...one; }': () +[83; 84) 'A': A(T) -> A +[83; 88) 'A(42)': A +[85; 87) '42': i32 +[94; 95) 'A': A(T) -> A +[94; 103) 'A(42u128)': A +[96; 102) '42u128': u128 +[109; 113) 'Some': Some<&str>(T) -> Option +[109; 118) 'Some("x")': Option<&str> +[114; 117) '"x"': &str +[124; 136) 'Option::Some': Some<&str>(T) -> Option +[124; 141) 'Option...e("x")': Option<&str> +[137; 140) '"x"': &str +[147; 151) 'None': Option<[unknown]> +[161; 162) 'x': Option +[178; 182) 'None': Option + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 4ab442b8a..3affcb4fe 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -465,6 +465,27 @@ fn test(a1: A, i: i32) { ); } +#[test] +fn infer_tuple_struct_generics() { + check_inference( + "infer_tuple_struct_generics", + r#" +struct A(T); +enum Option { Some(T), None }; +use Option::*; + +fn test() { + A(42); + A(42u128); + Some("x"); + Option::Some("x"); + None; + let x: Option = None; +} +"#, + ); +} + #[test] fn infer_generics_in_patterns() { check_inference( -- cgit v1.2.3