Deal better with implicit type parameters and argument lists

This commit is contained in:
Florian Diebold 2020-02-07 16:24:09 +01:00
parent dded90a748
commit 6c70619b01
4 changed files with 138 additions and 26 deletions

View File

@ -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);

View File

@ -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<GenericDefId>,
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: `<Foo as Default>::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);

View File

@ -905,6 +905,114 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) {
);
}
#[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<T>(x: impl Trait) -> T { loop {} }
}
fn foo<T>(x: impl Trait) -> T { loop {} }
struct S;
impl Trait for S {}
struct F;
impl Foo for F {}
fn test() {
Foo::bar(S);
<F as Foo>::bar(S);
F::bar(S);
Foo::bar::<u32>(S);
<F as Foo>::bar::<u32>(S);
foo(S);
foo::<u32>(S);
foo::<u32, i32>(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) '<F as Foo>::bar': fn bar<F, {unknown}, S>(S) -> {unknown}
[324; 342) '<F as ...bar(S)': {unknown}
[340; 341) 'S': S
[348; 354) 'F::bar': fn bar<F, {unknown}, S>(S) -> {unknown}
[348; 357) 'F::bar(S)': {unknown}
[355; 356) 'S': S
[363; 378) 'Foo::bar::<u32>': fn bar<{unknown}, u32, S>(S) -> u32
[363; 381) 'Foo::b...32>(S)': u32
[379; 380) 'S': S
[387; 409) '<F as ...:<u32>': fn bar<F, u32, S>(S) -> u32
[387; 412) '<F as ...32>(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::<u32>': fn foo<u32, S>(S) -> u32
[431; 444) 'foo::<u32>(S)': u32
[442; 443) 'S': S
[450; 465) 'foo::<u32, i32>': fn foo<u32, S>(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<T>;
impl<T> F<T> {
fn foo<U>(self, x: impl Trait) -> (T, U) { loop {} }
}
fn test() {
F.foo(S);
F::<u32>.foo(S);
F::<u32>.foo::<i32>(S);
F::<u32>.foo::<i32, u32>(S); // extraneous argument should be ignored
}
"#, true),
@r###"
[88; 92) 'self': F<T>
[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::<u32>': F<u32>
[164; 179) 'F::<u32>.foo(S)': (u32, {unknown})
[177; 178) 'S': S
[185; 193) 'F::<u32>': F<u32>
[185; 207) 'F::<u3...32>(S)': (u32, i32)
[205; 206) 'S': S
[213; 221) 'F::<u32>': F<u32>
[213; 240) 'F::<u3...32>(S)': (u32, i32)
[238; 239) 'S': S
"###
);
}
#[test]
#[ignore]
fn impl_trait() {

View File

@ -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<u32> {