Deal better with implicit type parameters and argument lists
This commit is contained in:
parent
dded90a748
commit
6c70619b01
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user