mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
Deal better with implicit type parameters and argument lists
This commit is contained in:
parent
dded90a748
commit
6c70619b01
4 changed files with 138 additions and 26 deletions
|
@ -647,8 +647,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
generic_args: Option<&GenericArgs>,
|
generic_args: Option<&GenericArgs>,
|
||||||
receiver_ty: &Ty,
|
receiver_ty: &Ty,
|
||||||
) -> Substs {
|
) -> Substs {
|
||||||
let (total_len, _parent_len, child_len) =
|
let (parent_params, self_params, type_params, impl_trait_params) =
|
||||||
def_generics.as_ref().map_or((0, 0, 0), |g| g.len_split());
|
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);
|
let mut substs = Vec::with_capacity(total_len);
|
||||||
// Parent arguments are unknown, except for the receiver type
|
// Parent arguments are unknown, except for the receiver type
|
||||||
if let Some(parent_generics) = def_generics.as_ref().map(|p| p.iter_parent()) {
|
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
|
// handle provided type arguments
|
||||||
if let Some(generic_args) = generic_args {
|
if let Some(generic_args) = generic_args {
|
||||||
// if args are provided, it should be all of them, but we can't rely on that
|
// 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 {
|
match arg {
|
||||||
GenericArg::Type(type_ref) => {
|
GenericArg::Type(type_ref) => {
|
||||||
let ty = self.make_ty(type_ref);
|
let ty = self.make_ty(type_ref);
|
||||||
|
|
|
@ -161,15 +161,19 @@ impl Ty {
|
||||||
ImplTraitLoweringMode::Variable => {
|
ImplTraitLoweringMode::Variable => {
|
||||||
let idx = ctx.impl_trait_counter.get();
|
let idx = ctx.impl_trait_counter.get();
|
||||||
ctx.impl_trait_counter.set(idx + 1);
|
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() {
|
if let Some(def) = ctx.resolver.generic_def() {
|
||||||
let generics = generics(ctx.db, def);
|
let generics = generics(ctx.db, def);
|
||||||
generics.provenance_split()
|
generics.provenance_split()
|
||||||
} else {
|
} else {
|
||||||
(0, 0, 0)
|
(0, 0, 0, 0)
|
||||||
};
|
};
|
||||||
// assert!((idx as usize) < impl_trait_params); // TODO return position impl trait
|
Ty::Bound(
|
||||||
Ty::Bound(idx as u32 + self_params as u32 + list_params as u32)
|
idx as u32
|
||||||
|
+ parent_params as u32
|
||||||
|
+ self_params as u32
|
||||||
|
+ list_params as u32,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ImplTraitLoweringMode::Disallowed => {
|
ImplTraitLoweringMode::Disallowed => {
|
||||||
// FIXME: report error
|
// FIXME: report error
|
||||||
|
@ -420,26 +424,23 @@ pub(super) fn substs_from_path_segment(
|
||||||
ctx: &TyLoweringContext<'_, impl HirDatabase>,
|
ctx: &TyLoweringContext<'_, impl HirDatabase>,
|
||||||
segment: PathSegment<'_>,
|
segment: PathSegment<'_>,
|
||||||
def_generic: Option<GenericDefId>,
|
def_generic: Option<GenericDefId>,
|
||||||
add_self_param: bool,
|
_add_self_param: bool,
|
||||||
) -> Substs {
|
) -> Substs {
|
||||||
let mut substs = Vec::new();
|
let mut substs = Vec::new();
|
||||||
let def_generics = def_generic.map(|def| generics(ctx.db, def.into()));
|
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());
|
let (parent_params, self_params, type_params, impl_trait_params) =
|
||||||
substs.extend(iter::repeat(Ty::Unknown).take(parent_len));
|
def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split());
|
||||||
if add_self_param {
|
substs.extend(iter::repeat(Ty::Unknown).take(parent_params));
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
if let Some(generic_args) = &segment.args_and_bindings {
|
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
|
// 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 };
|
for arg in generic_args.args.iter().skip(skip).take(expected_num) {
|
||||||
let child_len = child_len - self_param_correction;
|
|
||||||
for arg in generic_args.args.iter().take(child_len) {
|
|
||||||
match arg {
|
match arg {
|
||||||
GenericArg::Type(type_ref) => {
|
GenericArg::Type(type_ref) => {
|
||||||
let ty = Ty::from_hir(ctx, 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
|
// add placeholders for args that were not provided
|
||||||
let supplied_params = substs.len();
|
for _ in substs.len()..total_len {
|
||||||
for _ in supplied_params..total_len {
|
|
||||||
substs.push(Ty::Unknown);
|
substs.push(Ty::Unknown);
|
||||||
}
|
}
|
||||||
assert_eq!(substs.len(), total_len);
|
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]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn impl_trait() {
|
fn impl_trait() {
|
||||||
|
|
|
@ -145,8 +145,9 @@ impl Generics {
|
||||||
(parent + child, parent, child)
|
(parent + child, parent, child)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (self, type param list, impl trait)
|
/// (parent total, self param, type param list, impl trait)
|
||||||
pub(crate) fn provenance_split(&self) -> (usize, usize, usize) {
|
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
|
let self_params = self
|
||||||
.params
|
.params
|
||||||
.types
|
.types
|
||||||
|
@ -165,7 +166,7 @@ impl Generics {
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, p)| p.provenance == TypeParamProvenance::ArgumentImplTrait)
|
.filter(|(_, p)| p.provenance == TypeParamProvenance::ArgumentImplTrait)
|
||||||
.count();
|
.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> {
|
pub(crate) fn param_idx(&self, param: TypeParamId) -> Option<u32> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue