mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
Auto merge of #13494 - lowr:feat/gats, r=flodiebold
feat: type inference for generic associated types This PR implements type inference for generic associated types. Basically, this PR lowers generic arguments for associated types in valid places and creates `Substitution`s for them. I focused on the inference for correct Rust programs, so there are cases where we *accidentally* manage to infer things that are actually invalid (which would then be reported by flycheck so I deem them non-fatal). See the following tests and FIXME notes on them: `gats_with_dyn`, `gats_with_impl_trait`. The added tests are rather arbitrary. Let me know if there are cases I'm missing or I should add. Closes #9673
This commit is contained in:
commit
c6e1e17a10
13 changed files with 381 additions and 98 deletions
|
@ -662,8 +662,12 @@ fn desugar_future_path(orig: TypeRef) -> Path {
|
|||
let mut generic_args: Vec<_> =
|
||||
std::iter::repeat(None).take(path.segments().len() - 1).collect();
|
||||
let mut last = GenericArgs::empty();
|
||||
let binding =
|
||||
AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
|
||||
let binding = AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
args: None,
|
||||
type_ref: Some(orig),
|
||||
bounds: Vec::new(),
|
||||
};
|
||||
last.bindings.push(binding);
|
||||
generic_args.push(Some(Interned::new(last)));
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ pub struct GenericArgs {
|
|||
pub struct AssociatedTypeBinding {
|
||||
/// The name of the associated type.
|
||||
pub name: Name,
|
||||
/// The generic arguments to the associated type. e.g. For `Trait<Assoc<'a, T> = &'a T>`, this
|
||||
/// would be `['a, T]`.
|
||||
pub args: Option<Interned<GenericArgs>>,
|
||||
/// The type bound to this associated type (in `Item = T`, this would be the
|
||||
/// `T`). This can be `None` if there are bounds instead.
|
||||
pub type_ref: Option<TypeRef>,
|
||||
|
|
|
@ -163,6 +163,10 @@ pub(super) fn lower_generic_args(
|
|||
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
|
||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||
let name = name_ref.as_name();
|
||||
let args = assoc_type_arg
|
||||
.generic_arg_list()
|
||||
.and_then(|args| lower_generic_args(lower_ctx, args))
|
||||
.map(Interned::new);
|
||||
let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
|
||||
let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
|
||||
l.bounds()
|
||||
|
@ -171,7 +175,7 @@ pub(super) fn lower_generic_args(
|
|||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
|
||||
bindings.push(AssociatedTypeBinding { name, args, type_ref, bounds });
|
||||
}
|
||||
}
|
||||
ast::GenericArg::LifetimeArg(lifetime_arg) => {
|
||||
|
@ -214,6 +218,7 @@ fn lower_generic_args_from_fn_path(
|
|||
let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty());
|
||||
bindings.push(AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
args: None,
|
||||
type_ref: Some(type_ref),
|
||||
bounds: Vec::new(),
|
||||
});
|
||||
|
@ -222,6 +227,7 @@ fn lower_generic_args_from_fn_path(
|
|||
let type_ref = TypeRef::Tuple(Vec::new());
|
||||
bindings.push(AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
args: None,
|
||||
type_ref: Some(type_ref),
|
||||
bounds: Vec::new(),
|
||||
});
|
||||
|
|
|
@ -11,9 +11,9 @@ use syntax::SmolStr;
|
|||
|
||||
use crate::{
|
||||
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
||||
from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
|
||||
CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
|
||||
Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
|
||||
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
|
||||
CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
|
||||
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
|
||||
};
|
||||
|
||||
pub trait TyExt {
|
||||
|
@ -338,10 +338,13 @@ pub trait ProjectionTyExt {
|
|||
|
||||
impl ProjectionTyExt for ProjectionTy {
|
||||
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
|
||||
TraitRef {
|
||||
trait_id: to_chalk_trait_id(self.trait_(db)),
|
||||
substitution: self.substitution.clone(),
|
||||
}
|
||||
// FIXME: something like `Split` trait from chalk-solve might be nice.
|
||||
let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into());
|
||||
let substitution = Substitution::from_iter(
|
||||
Interner,
|
||||
self.substitution.iter(Interner).skip(generics.len_self()),
|
||||
);
|
||||
TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution }
|
||||
}
|
||||
|
||||
fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
|
||||
|
|
|
@ -289,16 +289,18 @@ impl HirDisplay for ProjectionTy {
|
|||
return write!(f, "{}", TYPE_HINT_TRUNCATION);
|
||||
}
|
||||
|
||||
let trait_ = f.db.trait_data(self.trait_(f.db));
|
||||
let trait_ref = self.trait_ref(f.db);
|
||||
write!(f, "<")?;
|
||||
self.self_type_parameter(f.db).hir_fmt(f)?;
|
||||
write!(f, " as {}", trait_.name)?;
|
||||
if self.substitution.len(Interner) > 1 {
|
||||
fmt_trait_ref(&trait_ref, f, true)?;
|
||||
write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
|
||||
let proj_params_count =
|
||||
self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
|
||||
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
|
||||
if !proj_params.is_empty() {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&self.substitution.as_slice(Interner)[1..], ", ")?;
|
||||
f.write_joined(proj_params, ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -641,9 +643,12 @@ impl HirDisplay for Ty {
|
|||
// Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
|
||||
if f.display_target.is_test() {
|
||||
write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
|
||||
// Note that the generic args for the associated type come before those for the
|
||||
// trait (including the self type).
|
||||
// FIXME: reconsider the generic args order upon formatting?
|
||||
if parameters.len(Interner) > 0 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&*parameters.as_slice(Interner), ", ")?;
|
||||
f.write_joined(parameters.as_slice(Interner), ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
} else {
|
||||
|
@ -972,9 +977,20 @@ fn write_bounds_like_dyn_trait(
|
|||
angle_open = true;
|
||||
}
|
||||
if let AliasTy::Projection(proj) = alias {
|
||||
let type_alias =
|
||||
f.db.type_alias_data(from_assoc_type_id(proj.associated_ty_id));
|
||||
write!(f, "{} = ", type_alias.name)?;
|
||||
let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id);
|
||||
let type_alias = f.db.type_alias_data(assoc_ty_id);
|
||||
write!(f, "{}", type_alias.name)?;
|
||||
|
||||
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
|
||||
if proj_arg_count > 0 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(
|
||||
&proj.substitution.as_slice(Interner)[..proj_arg_count],
|
||||
", ",
|
||||
)?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
write!(f, " = ")?;
|
||||
}
|
||||
ty.hir_fmt(f)?;
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ impl<'a> InferenceContext<'a> {
|
|||
remaining_segments_for_ty,
|
||||
true,
|
||||
);
|
||||
if let TyKind::Error = ty.kind(Interner) {
|
||||
if ty.is_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
@ -124,14 +124,6 @@ pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
|
|||
pub type Guidance = chalk_solve::Guidance<Interner>;
|
||||
pub type WhereClause = chalk_ir::WhereClause<Interner>;
|
||||
|
||||
// FIXME: get rid of this
|
||||
pub fn subst_prefix(s: &Substitution, n: usize) -> Substitution {
|
||||
Substitution::from_iter(
|
||||
Interner,
|
||||
s.as_slice(Interner)[..std::cmp::min(s.len(Interner), n)].iter().cloned(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Return an index of a parameter in the generic type parameter list by it's id.
|
||||
pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
|
||||
generics(db.upcast(), id.parent).param_idx(id)
|
||||
|
@ -382,7 +374,6 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
|
|||
pub fn replace_errors_with_variables<T>(t: &T) -> Canonical<T>
|
||||
where
|
||||
T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + Clone,
|
||||
T: HasInterner<Interner = Interner>,
|
||||
{
|
||||
use chalk_ir::{
|
||||
fold::{FallibleTypeFolder, TypeSuperFoldable},
|
||||
|
|
|
@ -447,12 +447,31 @@ impl<'a> TyLoweringContext<'a> {
|
|||
.db
|
||||
.trait_data(trait_ref.hir_trait_id())
|
||||
.associated_type_by_name(segment.name);
|
||||
|
||||
match found {
|
||||
Some(associated_ty) => {
|
||||
// FIXME handle type parameters on the segment
|
||||
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
|
||||
// generic params. It's inefficient to splice the `Substitution`s, so we may want
|
||||
// that method to optionally take parent `Substitution` as we already know them at
|
||||
// this point (`trait_ref.substitution`).
|
||||
let substitution = self.substs_from_path_segment(
|
||||
segment,
|
||||
Some(associated_ty.into()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
let len_self =
|
||||
generics(self.db.upcast(), associated_ty.into()).len_self();
|
||||
let substitution = Substitution::from_iter(
|
||||
Interner,
|
||||
substitution
|
||||
.iter(Interner)
|
||||
.take(len_self)
|
||||
.chain(trait_ref.substitution.iter(Interner)),
|
||||
);
|
||||
TyKind::Alias(AliasTy::Projection(ProjectionTy {
|
||||
associated_ty_id: to_assoc_type_id(associated_ty),
|
||||
substitution: trait_ref.substitution,
|
||||
substitution,
|
||||
}))
|
||||
.intern(Interner)
|
||||
}
|
||||
|
@ -590,36 +609,48 @@ impl<'a> TyLoweringContext<'a> {
|
|||
res,
|
||||
Some(segment.name.clone()),
|
||||
move |name, t, associated_ty| {
|
||||
if name == segment.name {
|
||||
let substs = match self.type_param_mode {
|
||||
ParamLoweringMode::Placeholder => {
|
||||
// if we're lowering to placeholders, we have to put
|
||||
// them in now
|
||||
let generics = generics(
|
||||
self.db.upcast(),
|
||||
self.resolver
|
||||
.generic_def()
|
||||
.expect("there should be generics if there's a generic param"),
|
||||
);
|
||||
let s = generics.placeholder_subst(self.db);
|
||||
s.apply(t.substitution.clone(), Interner)
|
||||
}
|
||||
ParamLoweringMode::Variable => t.substitution.clone(),
|
||||
};
|
||||
// We need to shift in the bound vars, since
|
||||
// associated_type_shorthand_candidates does not do that
|
||||
let substs = substs.shifted_in_from(Interner, self.in_binders);
|
||||
// FIXME handle type parameters on the segment
|
||||
Some(
|
||||
TyKind::Alias(AliasTy::Projection(ProjectionTy {
|
||||
associated_ty_id: to_assoc_type_id(associated_ty),
|
||||
substitution: substs,
|
||||
}))
|
||||
.intern(Interner),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
if name != segment.name {
|
||||
return None;
|
||||
}
|
||||
|
||||
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
|
||||
// generic params. It's inefficient to splice the `Substitution`s, so we may want
|
||||
// that method to optionally take parent `Substitution` as we already know them at
|
||||
// this point (`t.substitution`).
|
||||
let substs = self.substs_from_path_segment(
|
||||
segment.clone(),
|
||||
Some(associated_ty.into()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
|
||||
let len_self = generics(self.db.upcast(), associated_ty.into()).len_self();
|
||||
|
||||
let substs = Substitution::from_iter(
|
||||
Interner,
|
||||
substs.iter(Interner).take(len_self).chain(t.substitution.iter(Interner)),
|
||||
);
|
||||
|
||||
let substs = match self.type_param_mode {
|
||||
ParamLoweringMode::Placeholder => {
|
||||
// if we're lowering to placeholders, we have to put
|
||||
// them in now
|
||||
let generics = generics(self.db.upcast(), def);
|
||||
let s = generics.placeholder_subst(self.db);
|
||||
s.apply(substs, Interner)
|
||||
}
|
||||
ParamLoweringMode::Variable => substs,
|
||||
};
|
||||
// We need to shift in the bound vars, since
|
||||
// associated_type_shorthand_candidates does not do that
|
||||
let substs = substs.shifted_in_from(Interner, self.in_binders);
|
||||
Some(
|
||||
TyKind::Alias(AliasTy::Projection(ProjectionTy {
|
||||
associated_ty_id: to_assoc_type_id(associated_ty),
|
||||
substitution: substs,
|
||||
}))
|
||||
.intern(Interner),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -777,7 +808,15 @@ impl<'a> TyLoweringContext<'a> {
|
|||
// handle defaults. In expression or pattern path segments without
|
||||
// explicitly specified type arguments, missing type arguments are inferred
|
||||
// (i.e. defaults aren't used).
|
||||
if !infer_args || had_explicit_args {
|
||||
// Generic parameters for associated types are not supposed to have defaults, so we just
|
||||
// ignore them.
|
||||
let is_assoc_ty = if let GenericDefId::TypeAliasId(id) = def {
|
||||
let container = id.lookup(self.db.upcast()).container;
|
||||
matches!(container, ItemContainerId::TraitId(_))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !is_assoc_ty && (!infer_args || had_explicit_args) {
|
||||
let defaults = self.db.generic_defaults(def);
|
||||
assert_eq!(total_len, defaults.len());
|
||||
let parent_from = item_len - substs.len();
|
||||
|
@ -966,9 +1005,28 @@ impl<'a> TyLoweringContext<'a> {
|
|||
None => return SmallVec::new(),
|
||||
Some(t) => t,
|
||||
};
|
||||
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
|
||||
// generic params. It's inefficient to splice the `Substitution`s, so we may want
|
||||
// that method to optionally take parent `Substitution` as we already know them at
|
||||
// this point (`super_trait_ref.substitution`).
|
||||
let substitution = self.substs_from_path_segment(
|
||||
// FIXME: This is hack. We shouldn't really build `PathSegment` directly.
|
||||
PathSegment { name: &binding.name, args_and_bindings: binding.args.as_deref() },
|
||||
Some(associated_ty.into()),
|
||||
false, // this is not relevant
|
||||
Some(super_trait_ref.self_type_parameter(Interner)),
|
||||
);
|
||||
let self_params = generics(self.db.upcast(), associated_ty.into()).len_self();
|
||||
let substitution = Substitution::from_iter(
|
||||
Interner,
|
||||
substitution
|
||||
.iter(Interner)
|
||||
.take(self_params)
|
||||
.chain(super_trait_ref.substitution.iter(Interner)),
|
||||
);
|
||||
let projection_ty = ProjectionTy {
|
||||
associated_ty_id: to_assoc_type_id(associated_ty),
|
||||
substitution: super_trait_ref.substitution,
|
||||
substitution,
|
||||
};
|
||||
let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
|
||||
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
|
||||
|
|
|
@ -196,3 +196,34 @@ fn test(
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn projection_type_correct_arguments_order() {
|
||||
check_types_source_code(
|
||||
r#"
|
||||
trait Foo<T> {
|
||||
type Assoc<U>;
|
||||
}
|
||||
fn f<T: Foo<i32>>(a: T::Assoc<usize>) {
|
||||
a;
|
||||
//^ <T as Foo<i32>>::Assoc<usize>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_associated_type_binding_in_impl_trait() {
|
||||
check_types_source_code(
|
||||
r#"
|
||||
//- minicore: sized
|
||||
trait Foo<T> {
|
||||
type Assoc<U>;
|
||||
}
|
||||
fn f(a: impl Foo<i8, Assoc<i16> = i32>) {
|
||||
a;
|
||||
//^ impl Foo<i8, Assoc<i16> = i32>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3963,3 +3963,160 @@ fn g(t: &(dyn T + Send)) {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gats_in_path() {
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
use core::ops::Deref;
|
||||
trait PointerFamily {
|
||||
type Pointer<T>: Deref<Target = T>;
|
||||
}
|
||||
|
||||
fn f<P: PointerFamily>(p: P::Pointer<i32>) {
|
||||
let a = *p;
|
||||
}
|
||||
fn g<P: PointerFamily>(p: <P as PointerFamily>::Pointer<i32>) {
|
||||
let a = *p;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
110..111 'p': PointerFamily::Pointer<i32, P>
|
||||
130..149 '{ ... *p; }': ()
|
||||
140..141 'a': i32
|
||||
144..146 '*p': i32
|
||||
145..146 'p': PointerFamily::Pointer<i32, P>
|
||||
173..174 'p': PointerFamily::Pointer<i32, P>
|
||||
212..231 '{ ... *p; }': ()
|
||||
222..223 'a': i32
|
||||
226..228 '*p': i32
|
||||
227..228 'p': PointerFamily::Pointer<i32, P>
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gats_with_impl_trait() {
|
||||
// FIXME: the last function (`fn h()`) is not valid Rust as of this writing because you cannot
|
||||
// specify the same associated type multiple times even if their arguments are different.
|
||||
// Reconsider how to treat these invalid types.
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
use core::ops::Deref;
|
||||
|
||||
trait Trait {
|
||||
type Assoc<T>: Deref<Target = T>;
|
||||
fn get<U>(&self) -> Self::Assoc<U>;
|
||||
}
|
||||
|
||||
fn f<T>(v: impl Trait) {
|
||||
v.get::<i32>().deref();
|
||||
v.get::<T>().deref();
|
||||
}
|
||||
fn g<T>(v: impl Trait<Assoc<T> = &'a T>) {
|
||||
let a = v.get::<T>();
|
||||
let a = v.get::<()>();
|
||||
}
|
||||
fn h(v: impl Trait<Assoc<i32> = &'a i32, Assoc<i64> = &'a i64> {
|
||||
let a = v.get::<i32>();
|
||||
let a = v.get::<i64>();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
90..94 'self': &Self
|
||||
126..127 'v': impl Trait
|
||||
141..198 '{ ...f(); }': ()
|
||||
147..148 'v': impl Trait
|
||||
147..161 'v.get::<i32>()': Trait::Assoc<i32, impl Trait>
|
||||
147..169 'v.get:...eref()': &i32
|
||||
175..176 'v': impl Trait
|
||||
175..187 'v.get::<T>()': Trait::Assoc<T, impl Trait>
|
||||
175..195 'v.get:...eref()': &T
|
||||
207..208 'v': impl Trait<Assoc<T> = &T>
|
||||
240..296 '{ ...>(); }': ()
|
||||
250..251 'a': &T
|
||||
254..255 'v': impl Trait<Assoc<T> = &T>
|
||||
254..266 'v.get::<T>()': &T
|
||||
276..277 'a': Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
|
||||
280..281 'v': impl Trait<Assoc<T> = &T>
|
||||
280..293 'v.get::<()>()': Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
|
||||
302..303 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
|
||||
360..419 '{ ...>(); }': ()
|
||||
370..371 'a': &i32
|
||||
374..375 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
|
||||
374..388 'v.get::<i32>()': &i32
|
||||
398..399 'a': &i64
|
||||
402..403 'v': impl Trait<Assoc<i32> = &i32, Assoc<i64> = &i64>
|
||||
402..416 'v.get::<i64>()': &i64
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gats_with_dyn() {
|
||||
// This test is here to keep track of how we infer things despite traits with GATs being not
|
||||
// object-safe currently.
|
||||
// FIXME: reconsider how to treat these invalid types.
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
use core::ops::Deref;
|
||||
|
||||
trait Trait {
|
||||
type Assoc<T>: Deref<Target = T>;
|
||||
fn get<U>(&self) -> Self::Assoc<U>;
|
||||
}
|
||||
|
||||
fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) {
|
||||
v.get::<i32>().deref();
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
90..94 'self': &Self
|
||||
127..128 'v': &(dyn Trait<Assoc<i32> = &i32>)
|
||||
164..195 '{ ...f(); }': ()
|
||||
170..171 'v': &(dyn Trait<Assoc<i32> = &i32>)
|
||||
170..184 'v.get::<i32>()': &i32
|
||||
170..192 'v.get:...eref()': &i32
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gats_in_associated_type_binding() {
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
trait Trait {
|
||||
type Assoc<T>;
|
||||
fn get<U>(&self) -> Self::Assoc<U>;
|
||||
}
|
||||
|
||||
fn f<T>(t: T)
|
||||
where
|
||||
T: Trait<Assoc<i32> = u32>,
|
||||
T: Trait<Assoc<isize> = usize>,
|
||||
{
|
||||
let a = t.get::<i32>();
|
||||
let a = t.get::<isize>();
|
||||
let a = t.get::<()>();
|
||||
}
|
||||
|
||||
"#,
|
||||
expect![[r#"
|
||||
48..52 'self': &Self
|
||||
84..85 't': T
|
||||
164..252 '{ ...>(); }': ()
|
||||
174..175 'a': u32
|
||||
178..179 't': T
|
||||
178..192 't.get::<i32>()': u32
|
||||
202..203 'a': usize
|
||||
206..207 't': T
|
||||
206..222 't.get:...ize>()': usize
|
||||
232..233 'a': Trait::Assoc<(), T>
|
||||
236..237 't': T
|
||||
236..249 't.get::<()>()': Trait::Assoc<(), T>
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use itertools::Itertools;
|
|||
|
||||
use crate::{
|
||||
chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk,
|
||||
CallableDefId, Interner,
|
||||
CallableDefId, Interner, ProjectionTyExt,
|
||||
};
|
||||
use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId};
|
||||
|
||||
|
@ -63,17 +63,31 @@ impl DebugContext<'_> {
|
|||
ItemContainerId::TraitId(t) => t,
|
||||
_ => panic!("associated type not in trait"),
|
||||
};
|
||||
let trait_data = self.0.trait_data(trait_);
|
||||
let params = projection_ty.substitution.as_slice(Interner);
|
||||
write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?;
|
||||
if params.len() > 1 {
|
||||
let trait_name = &self.0.trait_data(trait_).name;
|
||||
let trait_ref = projection_ty.trait_ref(self.0);
|
||||
let trait_params = trait_ref.substitution.as_slice(Interner);
|
||||
let self_ty = trait_ref.self_type_parameter(Interner);
|
||||
write!(fmt, "<{:?} as {}", self_ty, trait_name)?;
|
||||
if trait_params.len() > 1 {
|
||||
write!(
|
||||
fmt,
|
||||
"<{}>",
|
||||
¶ms[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
|
||||
trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
|
||||
)?;
|
||||
}
|
||||
write!(fmt, ">::{}", type_alias_data.name)
|
||||
write!(fmt, ">::{}", type_alias_data.name)?;
|
||||
|
||||
let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len();
|
||||
let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count];
|
||||
if !proj_params.is_empty() {
|
||||
write!(
|
||||
fmt,
|
||||
"<{}>",
|
||||
proj_params.iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn debug_fn_def_id(
|
||||
|
|
|
@ -51,7 +51,7 @@ TypeArg =
|
|||
Type
|
||||
|
||||
AssocTypeArg =
|
||||
NameRef GenericParamList? (':' TypeBoundList | ('=' Type | ConstArg))
|
||||
NameRef GenericArgList? (':' TypeBoundList | ('=' Type | ConstArg))
|
||||
|
||||
LifetimeArg =
|
||||
Lifetime
|
||||
|
|
|
@ -120,7 +120,7 @@ pub struct AssocTypeArg {
|
|||
impl ast::HasTypeBounds for AssocTypeArg {}
|
||||
impl AssocTypeArg {
|
||||
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
|
||||
pub fn generic_param_list(&self) -> Option<GenericParamList> { support::child(&self.syntax) }
|
||||
pub fn generic_arg_list(&self) -> Option<GenericArgList> { support::child(&self.syntax) }
|
||||
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
|
||||
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
|
||||
pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) }
|
||||
|
@ -142,16 +142,6 @@ impl ConstArg {
|
|||
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericParamList {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl GenericParamList {
|
||||
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
|
||||
pub fn generic_params(&self) -> AstChildren<GenericParam> { support::children(&self.syntax) }
|
||||
pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TypeBoundList {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
|
@ -527,6 +517,16 @@ impl Abi {
|
|||
pub fn extern_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![extern]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericParamList {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl GenericParamList {
|
||||
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
|
||||
pub fn generic_params(&self) -> AstChildren<GenericParam> { support::children(&self.syntax) }
|
||||
pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct WhereClause {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
|
@ -1834,17 +1834,6 @@ impl AstNode for ConstArg {
|
|||
}
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for GenericParamList {
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST }
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for TypeBoundList {
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND_LIST }
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
|
@ -2153,6 +2142,17 @@ impl AstNode for Abi {
|
|||
}
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for GenericParamList {
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST }
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) {
|
||||
Some(Self { syntax })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl AstNode for WhereClause {
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_CLAUSE }
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
|
@ -4263,11 +4263,6 @@ impl std::fmt::Display for ConstArg {
|
|||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for GenericParamList {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for TypeBoundList {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
|
@ -4408,6 +4403,11 @@ impl std::fmt::Display for Abi {
|
|||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for GenericParamList {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for WhereClause {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
|
|
Loading…
Reference in a new issue