mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
⬆️ rust-analyzer
This commit is contained in:
parent
8807fc4cc3
commit
c60b1f6414
40 changed files with 827 additions and 407 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -340,8 +340,8 @@ impl<'a> InferenceTable<'a> {
|
|||
self.resolve_with_fallback(t, &|_, _, d, _| d)
|
||||
}
|
||||
|
||||
/// Unify two types and register new trait goals that arise from that.
|
||||
pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
|
||||
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
|
||||
let result = match self.try_unify(ty1, ty2) {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
|
@ -350,9 +350,13 @@ impl<'a> InferenceTable<'a> {
|
|||
true
|
||||
}
|
||||
|
||||
/// Unify two types and return new trait goals arising from it, so the
|
||||
/// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the
|
||||
/// caller needs to deal with them.
|
||||
pub(crate) fn try_unify<T: Zip<Interner>>(&mut self, t1: &T, t2: &T) -> InferResult<()> {
|
||||
pub(crate) fn try_unify<T: ?Sized + Zip<Interner>>(
|
||||
&mut self,
|
||||
t1: &T,
|
||||
t2: &T,
|
||||
) -> InferResult<()> {
|
||||
match self.var_unification_table.relate(
|
||||
Interner,
|
||||
&self.db,
|
||||
|
|
|
@ -81,7 +81,20 @@ pub type PlaceholderIndex = chalk_ir::PlaceholderIndex;
|
|||
pub type VariableKind = chalk_ir::VariableKind<Interner>;
|
||||
pub type VariableKinds = chalk_ir::VariableKinds<Interner>;
|
||||
pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>;
|
||||
/// Represents generic parameters and an item bound by them. When the item has parent, the binders
|
||||
/// also contain the generic parameters for its parent. See chalk's documentation for details.
|
||||
///
|
||||
/// One thing to keep in mind when working with `Binders` (and `Substitution`s, which represent
|
||||
/// generic arguments) in rust-analyzer is that the ordering within *is* significant - the generic
|
||||
/// parameters/arguments for an item MUST come before those for its parent. This is to facilitate
|
||||
/// the integration with chalk-solve, which mildly puts constraints as such. See #13335 for its
|
||||
/// motivation in detail.
|
||||
pub type Binders<T> = chalk_ir::Binders<T>;
|
||||
/// Interned list of generic arguments for an item. When an item has parent, the `Substitution` for
|
||||
/// it contains generic arguments for both its parent and itself. See chalk's documentation for
|
||||
/// details.
|
||||
///
|
||||
/// See `Binders` for the constraint on the ordering.
|
||||
pub type Substitution = chalk_ir::Substitution<Interner>;
|
||||
pub type GenericArg = chalk_ir::GenericArg<Interner>;
|
||||
pub type GenericArgData = chalk_ir::GenericArgData<Interner>;
|
||||
|
@ -124,14 +137,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 +387,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(),
|
||||
|
|
|
@ -22,10 +22,10 @@ use crate::{
|
|||
from_foreign_def_id,
|
||||
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
|
||||
primitive::{FloatTy, IntTy, UintTy},
|
||||
static_lifetime,
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
utils::all_super_traits,
|
||||
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
|
||||
Scalar, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
|
||||
Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
/// This is used as a key for indexing impls.
|
||||
|
@ -624,52 +624,76 @@ pub(crate) fn iterate_method_candidates<T>(
|
|||
slot
|
||||
}
|
||||
|
||||
/// Looks up the impl method that actually runs for the trait method `func`.
|
||||
///
|
||||
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
|
||||
pub fn lookup_impl_method(
|
||||
self_ty: &Ty,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
trait_: TraitId,
|
||||
func: FunctionId,
|
||||
fn_subst: Substitution,
|
||||
) -> FunctionId {
|
||||
let trait_id = match func.lookup(db.upcast()).container {
|
||||
ItemContainerId::TraitId(id) => id,
|
||||
_ => return func,
|
||||
};
|
||||
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
|
||||
let fn_params = fn_subst.len(Interner) - trait_params;
|
||||
let trait_ref = TraitRef {
|
||||
trait_id: to_chalk_trait_id(trait_id),
|
||||
substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)),
|
||||
};
|
||||
|
||||
let name = &db.function_data(func).name;
|
||||
lookup_impl_method_for_trait_ref(trait_ref, db, env, name).unwrap_or(func)
|
||||
}
|
||||
|
||||
fn lookup_impl_method_for_trait_ref(
|
||||
trait_ref: TraitRef,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
name: &Name,
|
||||
) -> Option<FunctionId> {
|
||||
let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?;
|
||||
let trait_impls = db.trait_impls_in_deps(env.krate);
|
||||
let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp);
|
||||
let mut table = InferenceTable::new(db, env.clone());
|
||||
find_matching_impl(impls, &mut table, &self_ty).and_then(|data| {
|
||||
data.items.iter().find_map(|it| match it {
|
||||
AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
|
||||
_ => None,
|
||||
})
|
||||
let self_ty = trait_ref.self_type_parameter(Interner);
|
||||
let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
|
||||
let impls = db.trait_impls_in_deps(env.krate);
|
||||
let impls = impls.for_trait_and_self_ty(trait_ref.hir_trait_id(), self_ty_fp);
|
||||
|
||||
let table = InferenceTable::new(db, env);
|
||||
|
||||
let impl_data = find_matching_impl(impls, table, trait_ref)?;
|
||||
impl_data.items.iter().find_map(|it| match it {
|
||||
AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn find_matching_impl(
|
||||
mut impls: impl Iterator<Item = ImplId>,
|
||||
table: &mut InferenceTable<'_>,
|
||||
self_ty: &Ty,
|
||||
mut table: InferenceTable<'_>,
|
||||
actual_trait_ref: TraitRef,
|
||||
) -> Option<Arc<ImplData>> {
|
||||
let db = table.db;
|
||||
loop {
|
||||
let impl_ = impls.next()?;
|
||||
let r = table.run_in_snapshot(|table| {
|
||||
let impl_data = db.impl_data(impl_);
|
||||
let substs =
|
||||
let impl_substs =
|
||||
TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
|
||||
let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
|
||||
let trait_ref = db
|
||||
.impl_trait(impl_)
|
||||
.expect("non-trait method in find_matching_impl")
|
||||
.substitute(Interner, &impl_substs);
|
||||
|
||||
table
|
||||
.unify(self_ty, &impl_ty)
|
||||
.then(|| {
|
||||
let wh_goals =
|
||||
crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
|
||||
.into_iter()
|
||||
.map(|b| b.cast(Interner));
|
||||
if !table.unify(&trait_ref, &actual_trait_ref) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let goal = crate::Goal::all(Interner, wh_goals);
|
||||
|
||||
table.try_obligation(goal).map(|_| impl_data)
|
||||
})
|
||||
.flatten()
|
||||
let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs)
|
||||
.into_iter()
|
||||
.map(|b| b.cast(Interner));
|
||||
let goal = crate::Goal::all(Interner, wcs);
|
||||
table.try_obligation(goal).map(|_| impl_data)
|
||||
});
|
||||
if r.is_some() {
|
||||
break r;
|
||||
|
@ -1214,7 +1238,7 @@ fn is_valid_fn_candidate(
|
|||
let expected_receiver =
|
||||
sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
|
||||
|
||||
check_that!(table.unify(&receiver_ty, &expected_receiver));
|
||||
check_that!(table.unify(receiver_ty, &expected_receiver));
|
||||
}
|
||||
|
||||
if let ItemContainerId::ImplId(impl_id) = container {
|
||||
|
|
|
@ -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,124 @@ fn g(t: &(dyn T + Send)) {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gats_in_path() {
|
||||
check_types(
|
||||
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;
|
||||
//^ i32
|
||||
}
|
||||
fn g<P: PointerFamily>(p: <P as PointerFamily>::Pointer<i32>) {
|
||||
let a = *p;
|
||||
//^ i32
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gats_with_impl_trait() {
|
||||
// FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot
|
||||
// specify the same associated type multiple times even if their arguments are different (c.f.
|
||||
// `fn h()`, which is valid). Reconsider how to treat these invalid types.
|
||||
check_types(
|
||||
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) {
|
||||
let a = v.get::<i32>().deref();
|
||||
//^ &i32
|
||||
let a = v.get::<T>().deref();
|
||||
//^ &T
|
||||
}
|
||||
fn g<'a, T: 'a>(v: impl Trait<Assoc<T> = &'a T>) {
|
||||
let a = v.get::<T>();
|
||||
//^ &T
|
||||
let a = v.get::<()>();
|
||||
//^ Trait::Assoc<(), impl Trait<Assoc<T> = &T>>
|
||||
}
|
||||
fn h<'a>(v: impl Trait<Assoc<i32> = &'a i32> + Trait<Assoc<i64> = &'a i64>) {
|
||||
let a = v.get::<i32>();
|
||||
//^ &i32
|
||||
let a = v.get::<i64>();
|
||||
//^ &i64
|
||||
}
|
||||
fn i<'a>(v: impl Trait<Assoc<i32> = &'a i32, Assoc<i64> = &'a i64>) {
|
||||
let a = v.get::<i32>();
|
||||
//^ &i32
|
||||
let a = 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_types(
|
||||
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>();
|
||||
//^ u32
|
||||
let a = t.get::<isize>();
|
||||
//^ usize
|
||||
let a = 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(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue