From 6c70619b0126bc0e40bd9df39dcd6e711cac69c5 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 7 Feb 2020 16:24:09 +0100 Subject: Deal better with implicit type parameters and argument lists --- crates/ra_hir_ty/src/infer/expr.rs | 8 ++- crates/ra_hir_ty/src/lower.rs | 41 ++++++------- crates/ra_hir_ty/src/tests/traits.rs | 108 +++++++++++++++++++++++++++++++++++ crates/ra_hir_ty/src/utils.rs | 7 ++- 4 files changed, 138 insertions(+), 26 deletions(-) diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 8c360bcad..00ae35953 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -647,8 +647,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { generic_args: Option<&GenericArgs>, receiver_ty: &Ty, ) -> Substs { - let (total_len, _parent_len, child_len) = - def_generics.as_ref().map_or((0, 0, 0), |g| g.len_split()); + let (parent_params, self_params, type_params, impl_trait_params) = + def_generics.as_ref().map_or((0, 0, 0, 0), |g| g.provenance_split()); + assert_eq!(self_params, 0); // method shouldn't have another Self param + let total_len = parent_params + type_params + impl_trait_params; let mut substs = Vec::with_capacity(total_len); // Parent arguments are unknown, except for the receiver type if let Some(parent_generics) = def_generics.as_ref().map(|p| p.iter_parent()) { @@ -663,7 +665,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { // 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(child_len) { + for arg in generic_args.args.iter().take(type_params) { match arg { GenericArg::Type(type_ref) => { let ty = self.make_ty(type_ref); diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 4168e7509..d2df3fe2b 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -161,15 +161,19 @@ impl Ty { ImplTraitLoweringMode::Variable => { let idx = ctx.impl_trait_counter.get(); ctx.impl_trait_counter.set(idx + 1); - let (self_params, list_params, _impl_trait_params) = + let (parent_params, self_params, list_params, _impl_trait_params) = if let Some(def) = ctx.resolver.generic_def() { let generics = generics(ctx.db, def); generics.provenance_split() } else { - (0, 0, 0) + (0, 0, 0, 0) }; - // assert!((idx as usize) < impl_trait_params); // TODO return position impl trait - Ty::Bound(idx as u32 + self_params as u32 + list_params as u32) + Ty::Bound( + idx as u32 + + parent_params as u32 + + self_params as u32 + + list_params as u32, + ) } ImplTraitLoweringMode::Disallowed => { // FIXME: report error @@ -420,26 +424,23 @@ pub(super) fn substs_from_path_segment( ctx: &TyLoweringContext<'_, impl HirDatabase>, segment: PathSegment<'_>, def_generic: Option, - add_self_param: bool, + _add_self_param: bool, ) -> Substs { let mut substs = Vec::new(); let def_generics = def_generic.map(|def| generics(ctx.db, def.into())); - let (total_len, parent_len, child_len) = def_generics.map_or((0, 0, 0), |g| g.len_split()); - substs.extend(iter::repeat(Ty::Unknown).take(parent_len)); - if add_self_param { - // FIXME this add_self_param argument is kind of a hack: Traits have the - // Self type as an implicit first type parameter, but it can't be - // actually provided in the type arguments - // (well, actually sometimes it can, in the form of type-relative paths: `::default()`) - // TODO handle this using type param provenance (if there's a self param, and not one provided, add unknown) - substs.push(Ty::Unknown); - } + let (parent_params, self_params, type_params, impl_trait_params) = + def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); + substs.extend(iter::repeat(Ty::Unknown).take(parent_params)); if let Some(generic_args) = &segment.args_and_bindings { + if !generic_args.has_self_type { + substs.extend(iter::repeat(Ty::Unknown).take(self_params)); + } + let expected_num = + if generic_args.has_self_type { self_params + type_params } else { type_params }; + let skip = if generic_args.has_self_type && self_params == 0 { 1 } else { 0 }; // if args are provided, it should be all of them, but we can't rely on that - let self_param_correction = if add_self_param { 1 } else { 0 }; - let child_len = child_len - self_param_correction; - for arg in generic_args.args.iter().take(child_len) { + for arg in generic_args.args.iter().skip(skip).take(expected_num) { match arg { GenericArg::Type(type_ref) => { let ty = Ty::from_hir(ctx, type_ref); @@ -448,9 +449,9 @@ pub(super) fn substs_from_path_segment( } } } + let total_len = parent_params + self_params + type_params + impl_trait_params; // add placeholders for args that were not provided - let supplied_params = substs.len(); - for _ in supplied_params..total_len { + for _ in substs.len()..total_len { substs.push(Ty::Unknown); } assert_eq!(substs.len(), total_len); diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 134cea8d8..f90dadc08 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -905,6 +905,114 @@ fn test(x: impl Trait, y: &impl Trait) { ); } +#[test] +fn argument_impl_trait_type_args_1() { + assert_snapshot!( + infer_with_mismatches(r#" +trait Trait {} +trait Foo { + // this function has an implicit Self param, an explicit type param, + // and an implicit impl Trait param! + fn bar(x: impl Trait) -> T { loop {} } +} +fn foo(x: impl Trait) -> T { loop {} } +struct S; +impl Trait for S {} +struct F; +impl Foo for F {} + +fn test() { + Foo::bar(S); + ::bar(S); + F::bar(S); + Foo::bar::(S); + ::bar::(S); + + foo(S); + foo::(S); + foo::(S); // we should ignore the extraneous i32 +} +"#, true), + @r###" + [156; 157) 'x': impl Trait + [176; 187) '{ loop {} }': T + [178; 185) 'loop {}': ! + [183; 185) '{}': () + [200; 201) 'x': impl Trait + [220; 231) '{ loop {} }': T + [222; 229) 'loop {}': ! + [227; 229) '{}': () + [301; 510) '{ ... i32 }': () + [307; 315) 'Foo::bar': fn bar<{unknown}, {unknown}, S>(S) -> {unknown} + [307; 318) 'Foo::bar(S)': {unknown} + [316; 317) 'S': S + [324; 339) '::bar': fn bar(S) -> {unknown} + [324; 342) '(S) -> {unknown} + [348; 357) 'F::bar(S)': {unknown} + [355; 356) 'S': S + [363; 378) 'Foo::bar::': fn bar<{unknown}, u32, S>(S) -> u32 + [363; 381) 'Foo::b...32>(S)': u32 + [379; 380) 'S': S + [387; 409) '': fn bar(S) -> u32 + [387; 412) '(S)': u32 + [410; 411) 'S': S + [419; 422) 'foo': fn foo<{unknown}, S>(S) -> {unknown} + [419; 425) 'foo(S)': {unknown} + [423; 424) 'S': S + [431; 441) 'foo::': fn foo(S) -> u32 + [431; 444) 'foo::(S)': u32 + [442; 443) 'S': S + [450; 465) 'foo::': fn foo(S) -> u32 + [450; 468) 'foo::<...32>(S)': u32 + [466; 467) 'S': S + "### + ); +} + +#[test] +fn argument_impl_trait_type_args_2() { + assert_snapshot!( + infer_with_mismatches(r#" +trait Trait {} +struct S; +impl Trait for S {} +struct F; +impl F { + fn foo(self, x: impl Trait) -> (T, U) { loop {} } +} + +fn test() { + F.foo(S); + F::.foo(S); + F::.foo::(S); + F::.foo::(S); // extraneous argument should be ignored +} +"#, true), + @r###" + [88; 92) 'self': F + [94; 95) 'x': impl Trait + [119; 130) '{ loop {} }': (T, U) + [121; 128) 'loop {}': ! + [126; 128) '{}': () + [144; 284) '{ ...ored }': () + [150; 151) 'F': F<{unknown}> + [150; 158) 'F.foo(S)': ({unknown}, {unknown}) + [156; 157) 'S': S + [164; 172) 'F::': F + [164; 179) 'F::.foo(S)': (u32, {unknown}) + [177; 178) 'S': S + [185; 193) 'F::': F + [185; 207) 'F::(S)': (u32, i32) + [205; 206) 'S': S + [213; 221) 'F::': F + [213; 240) 'F::(S)': (u32, i32) + [238; 239) 'S': S + "### + ); +} + #[test] #[ignore] fn impl_trait() { diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs index e307d958d..508ae9046 100644 --- a/crates/ra_hir_ty/src/utils.rs +++ b/crates/ra_hir_ty/src/utils.rs @@ -145,8 +145,9 @@ impl Generics { (parent + child, parent, child) } - /// (self, type param list, impl trait) - pub(crate) fn provenance_split(&self) -> (usize, usize, usize) { + /// (parent total, self param, type param list, impl trait) + pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize) { + let parent = self.parent_generics.as_ref().map_or(0, |p| p.len()); let self_params = self .params .types @@ -165,7 +166,7 @@ impl Generics { .iter() .filter(|(_, p)| p.provenance == TypeParamProvenance::ArgumentImplTrait) .count(); - (self_params, list_params, impl_trait_params) + (parent, self_params, list_params, impl_trait_params) } pub(crate) fn param_idx(&self, param: TypeParamId) -> Option { -- cgit v1.2.3