diff options
author | Florian Diebold <[email protected]> | 2020-02-07 15:24:09 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-02-07 17:28:10 +0000 |
commit | 6c70619b0126bc0e40bd9df39dcd6e711cac69c5 (patch) | |
tree | c9812b922e3f7ec753ff18e0b17b0f54f18ea737 /crates/ra_hir_ty/src | |
parent | dded90a748737c3661aad043524f2248e324c867 (diff) |
Deal better with implicit type parameters and argument lists
Diffstat (limited to 'crates/ra_hir_ty/src')
-rw-r--r-- | crates/ra_hir_ty/src/infer/expr.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lower.rs | 41 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/traits.rs | 108 | ||||
-rw-r--r-- | 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> { | |||
647 | generic_args: Option<&GenericArgs>, | 647 | generic_args: Option<&GenericArgs>, |
648 | receiver_ty: &Ty, | 648 | receiver_ty: &Ty, |
649 | ) -> Substs { | 649 | ) -> Substs { |
650 | let (total_len, _parent_len, child_len) = | 650 | let (parent_params, self_params, type_params, impl_trait_params) = |
651 | def_generics.as_ref().map_or((0, 0, 0), |g| g.len_split()); | 651 | def_generics.as_ref().map_or((0, 0, 0, 0), |g| g.provenance_split()); |
652 | assert_eq!(self_params, 0); // method shouldn't have another Self param | ||
653 | let total_len = parent_params + type_params + impl_trait_params; | ||
652 | let mut substs = Vec::with_capacity(total_len); | 654 | let mut substs = Vec::with_capacity(total_len); |
653 | // Parent arguments are unknown, except for the receiver type | 655 | // Parent arguments are unknown, except for the receiver type |
654 | if let Some(parent_generics) = def_generics.as_ref().map(|p| p.iter_parent()) { | 656 | 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> { | |||
663 | // handle provided type arguments | 665 | // handle provided type arguments |
664 | if let Some(generic_args) = generic_args { | 666 | if let Some(generic_args) = generic_args { |
665 | // if args are provided, it should be all of them, but we can't rely on that | 667 | // if args are provided, it should be all of them, but we can't rely on that |
666 | for arg in generic_args.args.iter().take(child_len) { | 668 | for arg in generic_args.args.iter().take(type_params) { |
667 | match arg { | 669 | match arg { |
668 | GenericArg::Type(type_ref) => { | 670 | GenericArg::Type(type_ref) => { |
669 | let ty = self.make_ty(type_ref); | 671 | 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 { | |||
161 | ImplTraitLoweringMode::Variable => { | 161 | ImplTraitLoweringMode::Variable => { |
162 | let idx = ctx.impl_trait_counter.get(); | 162 | let idx = ctx.impl_trait_counter.get(); |
163 | ctx.impl_trait_counter.set(idx + 1); | 163 | ctx.impl_trait_counter.set(idx + 1); |
164 | let (self_params, list_params, _impl_trait_params) = | 164 | let (parent_params, self_params, list_params, _impl_trait_params) = |
165 | if let Some(def) = ctx.resolver.generic_def() { | 165 | if let Some(def) = ctx.resolver.generic_def() { |
166 | let generics = generics(ctx.db, def); | 166 | let generics = generics(ctx.db, def); |
167 | generics.provenance_split() | 167 | generics.provenance_split() |
168 | } else { | 168 | } else { |
169 | (0, 0, 0) | 169 | (0, 0, 0, 0) |
170 | }; | 170 | }; |
171 | // assert!((idx as usize) < impl_trait_params); // TODO return position impl trait | 171 | Ty::Bound( |
172 | Ty::Bound(idx as u32 + self_params as u32 + list_params as u32) | 172 | idx as u32 |
173 | + parent_params as u32 | ||
174 | + self_params as u32 | ||
175 | + list_params as u32, | ||
176 | ) | ||
173 | } | 177 | } |
174 | ImplTraitLoweringMode::Disallowed => { | 178 | ImplTraitLoweringMode::Disallowed => { |
175 | // FIXME: report error | 179 | // FIXME: report error |
@@ -420,26 +424,23 @@ pub(super) fn substs_from_path_segment( | |||
420 | ctx: &TyLoweringContext<'_, impl HirDatabase>, | 424 | ctx: &TyLoweringContext<'_, impl HirDatabase>, |
421 | segment: PathSegment<'_>, | 425 | segment: PathSegment<'_>, |
422 | def_generic: Option<GenericDefId>, | 426 | def_generic: Option<GenericDefId>, |
423 | add_self_param: bool, | 427 | _add_self_param: bool, |
424 | ) -> Substs { | 428 | ) -> Substs { |
425 | let mut substs = Vec::new(); | 429 | let mut substs = Vec::new(); |
426 | let def_generics = def_generic.map(|def| generics(ctx.db, def.into())); | 430 | let def_generics = def_generic.map(|def| generics(ctx.db, def.into())); |
427 | 431 | ||
428 | let (total_len, parent_len, child_len) = def_generics.map_or((0, 0, 0), |g| g.len_split()); | 432 | let (parent_params, self_params, type_params, impl_trait_params) = |
429 | substs.extend(iter::repeat(Ty::Unknown).take(parent_len)); | 433 | def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); |
430 | if add_self_param { | 434 | substs.extend(iter::repeat(Ty::Unknown).take(parent_params)); |
431 | // FIXME this add_self_param argument is kind of a hack: Traits have the | ||
432 | // Self type as an implicit first type parameter, but it can't be | ||
433 | // actually provided in the type arguments | ||
434 | // (well, actually sometimes it can, in the form of type-relative paths: `<Foo as Default>::default()`) | ||
435 | // TODO handle this using type param provenance (if there's a self param, and not one provided, add unknown) | ||
436 | substs.push(Ty::Unknown); | ||
437 | } | ||
438 | if let Some(generic_args) = &segment.args_and_bindings { | 435 | if let Some(generic_args) = &segment.args_and_bindings { |
436 | if !generic_args.has_self_type { | ||
437 | substs.extend(iter::repeat(Ty::Unknown).take(self_params)); | ||
438 | } | ||
439 | let expected_num = | ||
440 | if generic_args.has_self_type { self_params + type_params } else { type_params }; | ||
441 | let skip = if generic_args.has_self_type && self_params == 0 { 1 } else { 0 }; | ||
439 | // if args are provided, it should be all of them, but we can't rely on that | 442 | // if args are provided, it should be all of them, but we can't rely on that |
440 | let self_param_correction = if add_self_param { 1 } else { 0 }; | 443 | for arg in generic_args.args.iter().skip(skip).take(expected_num) { |
441 | let child_len = child_len - self_param_correction; | ||
442 | for arg in generic_args.args.iter().take(child_len) { | ||
443 | match arg { | 444 | match arg { |
444 | GenericArg::Type(type_ref) => { | 445 | GenericArg::Type(type_ref) => { |
445 | let ty = Ty::from_hir(ctx, type_ref); | 446 | let ty = Ty::from_hir(ctx, type_ref); |
@@ -448,9 +449,9 @@ pub(super) fn substs_from_path_segment( | |||
448 | } | 449 | } |
449 | } | 450 | } |
450 | } | 451 | } |
452 | let total_len = parent_params + self_params + type_params + impl_trait_params; | ||
451 | // add placeholders for args that were not provided | 453 | // add placeholders for args that were not provided |
452 | let supplied_params = substs.len(); | 454 | for _ in substs.len()..total_len { |
453 | for _ in supplied_params..total_len { | ||
454 | substs.push(Ty::Unknown); | 455 | substs.push(Ty::Unknown); |
455 | } | 456 | } |
456 | assert_eq!(substs.len(), total_len); | 457 | 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 | |||
@@ -906,6 +906,114 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) { | |||
906 | } | 906 | } |
907 | 907 | ||
908 | #[test] | 908 | #[test] |
909 | fn argument_impl_trait_type_args_1() { | ||
910 | assert_snapshot!( | ||
911 | infer_with_mismatches(r#" | ||
912 | trait Trait {} | ||
913 | trait Foo { | ||
914 | // this function has an implicit Self param, an explicit type param, | ||
915 | // and an implicit impl Trait param! | ||
916 | fn bar<T>(x: impl Trait) -> T { loop {} } | ||
917 | } | ||
918 | fn foo<T>(x: impl Trait) -> T { loop {} } | ||
919 | struct S; | ||
920 | impl Trait for S {} | ||
921 | struct F; | ||
922 | impl Foo for F {} | ||
923 | |||
924 | fn test() { | ||
925 | Foo::bar(S); | ||
926 | <F as Foo>::bar(S); | ||
927 | F::bar(S); | ||
928 | Foo::bar::<u32>(S); | ||
929 | <F as Foo>::bar::<u32>(S); | ||
930 | |||
931 | foo(S); | ||
932 | foo::<u32>(S); | ||
933 | foo::<u32, i32>(S); // we should ignore the extraneous i32 | ||
934 | } | ||
935 | "#, true), | ||
936 | @r###" | ||
937 | [156; 157) 'x': impl Trait | ||
938 | [176; 187) '{ loop {} }': T | ||
939 | [178; 185) 'loop {}': ! | ||
940 | [183; 185) '{}': () | ||
941 | [200; 201) 'x': impl Trait | ||
942 | [220; 231) '{ loop {} }': T | ||
943 | [222; 229) 'loop {}': ! | ||
944 | [227; 229) '{}': () | ||
945 | [301; 510) '{ ... i32 }': () | ||
946 | [307; 315) 'Foo::bar': fn bar<{unknown}, {unknown}, S>(S) -> {unknown} | ||
947 | [307; 318) 'Foo::bar(S)': {unknown} | ||
948 | [316; 317) 'S': S | ||
949 | [324; 339) '<F as Foo>::bar': fn bar<F, {unknown}, S>(S) -> {unknown} | ||
950 | [324; 342) '<F as ...bar(S)': {unknown} | ||
951 | [340; 341) 'S': S | ||
952 | [348; 354) 'F::bar': fn bar<F, {unknown}, S>(S) -> {unknown} | ||
953 | [348; 357) 'F::bar(S)': {unknown} | ||
954 | [355; 356) 'S': S | ||
955 | [363; 378) 'Foo::bar::<u32>': fn bar<{unknown}, u32, S>(S) -> u32 | ||
956 | [363; 381) 'Foo::b...32>(S)': u32 | ||
957 | [379; 380) 'S': S | ||
958 | [387; 409) '<F as ...:<u32>': fn bar<F, u32, S>(S) -> u32 | ||
959 | [387; 412) '<F as ...32>(S)': u32 | ||
960 | [410; 411) 'S': S | ||
961 | [419; 422) 'foo': fn foo<{unknown}, S>(S) -> {unknown} | ||
962 | [419; 425) 'foo(S)': {unknown} | ||
963 | [423; 424) 'S': S | ||
964 | [431; 441) 'foo::<u32>': fn foo<u32, S>(S) -> u32 | ||
965 | [431; 444) 'foo::<u32>(S)': u32 | ||
966 | [442; 443) 'S': S | ||
967 | [450; 465) 'foo::<u32, i32>': fn foo<u32, S>(S) -> u32 | ||
968 | [450; 468) 'foo::<...32>(S)': u32 | ||
969 | [466; 467) 'S': S | ||
970 | "### | ||
971 | ); | ||
972 | } | ||
973 | |||
974 | #[test] | ||
975 | fn argument_impl_trait_type_args_2() { | ||
976 | assert_snapshot!( | ||
977 | infer_with_mismatches(r#" | ||
978 | trait Trait {} | ||
979 | struct S; | ||
980 | impl Trait for S {} | ||
981 | struct F<T>; | ||
982 | impl<T> F<T> { | ||
983 | fn foo<U>(self, x: impl Trait) -> (T, U) { loop {} } | ||
984 | } | ||
985 | |||
986 | fn test() { | ||
987 | F.foo(S); | ||
988 | F::<u32>.foo(S); | ||
989 | F::<u32>.foo::<i32>(S); | ||
990 | F::<u32>.foo::<i32, u32>(S); // extraneous argument should be ignored | ||
991 | } | ||
992 | "#, true), | ||
993 | @r###" | ||
994 | [88; 92) 'self': F<T> | ||
995 | [94; 95) 'x': impl Trait | ||
996 | [119; 130) '{ loop {} }': (T, U) | ||
997 | [121; 128) 'loop {}': ! | ||
998 | [126; 128) '{}': () | ||
999 | [144; 284) '{ ...ored }': () | ||
1000 | [150; 151) 'F': F<{unknown}> | ||
1001 | [150; 158) 'F.foo(S)': ({unknown}, {unknown}) | ||
1002 | [156; 157) 'S': S | ||
1003 | [164; 172) 'F::<u32>': F<u32> | ||
1004 | [164; 179) 'F::<u32>.foo(S)': (u32, {unknown}) | ||
1005 | [177; 178) 'S': S | ||
1006 | [185; 193) 'F::<u32>': F<u32> | ||
1007 | [185; 207) 'F::<u3...32>(S)': (u32, i32) | ||
1008 | [205; 206) 'S': S | ||
1009 | [213; 221) 'F::<u32>': F<u32> | ||
1010 | [213; 240) 'F::<u3...32>(S)': (u32, i32) | ||
1011 | [238; 239) 'S': S | ||
1012 | "### | ||
1013 | ); | ||
1014 | } | ||
1015 | |||
1016 | #[test] | ||
909 | #[ignore] | 1017 | #[ignore] |
910 | fn impl_trait() { | 1018 | fn impl_trait() { |
911 | assert_snapshot!( | 1019 | assert_snapshot!( |
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 { | |||
145 | (parent + child, parent, child) | 145 | (parent + child, parent, child) |
146 | } | 146 | } |
147 | 147 | ||
148 | /// (self, type param list, impl trait) | 148 | /// (parent total, self param, type param list, impl trait) |
149 | pub(crate) fn provenance_split(&self) -> (usize, usize, usize) { | 149 | pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize) { |
150 | let parent = self.parent_generics.as_ref().map_or(0, |p| p.len()); | ||
150 | let self_params = self | 151 | let self_params = self |
151 | .params | 152 | .params |
152 | .types | 153 | .types |
@@ -165,7 +166,7 @@ impl Generics { | |||
165 | .iter() | 166 | .iter() |
166 | .filter(|(_, p)| p.provenance == TypeParamProvenance::ArgumentImplTrait) | 167 | .filter(|(_, p)| p.provenance == TypeParamProvenance::ArgumentImplTrait) |
167 | .count(); | 168 | .count(); |
168 | (self_params, list_params, impl_trait_params) | 169 | (parent, self_params, list_params, impl_trait_params) |
169 | } | 170 | } |
170 | 171 | ||
171 | pub(crate) fn param_idx(&self, param: TypeParamId) -> Option<u32> { | 172 | pub(crate) fn param_idx(&self, param: TypeParamId) -> Option<u32> { |