mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Merge #11550
11550: Refactor autoderef/method resolution r=flodiebold a=flodiebold - don't return the receiver type from method resolution; instead just return the autorefs/autoderefs that happened and repeat them. This ensures all the effects like trait obligations and whatever we learned about type variables from derefing them are actually applied. Also, it allows us to get rid of `decanonicalize_ty`, which was just wrong in principle. - Autoderef itself now directly works with an inference table. Sadly this has the effect of making it harder to use as an iterator, often requiring manual `while let` loops. (rustc works around this by using inner mutability in the inference context, so that things like unifying types don't require a unique reference.) - We now record the adjustments (autoref/deref) for method receivers and index expressions, which we didn't before. - Removed the redundant crate parameter from method resolution, since the trait_env contains the crate as well. - in the HIR API, the methods now take a scope to determine the trait env. `Type` carries a trait env, but I think that's probably a bad decision because it's easy to create it with the wrong env, e.g. by using `Adt::ty`. This mostly didn't matter so far because `iterate_method_candidates` took a crate parameter and ignored `self.krate`, but the trait env would still have been wrong in those cases, which I think would give some wrong results in some edge cases. Fixes #10058. Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
ab896e38e1
@ -2440,7 +2440,7 @@ impl Impl {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Type {
|
||||
krate: CrateId,
|
||||
krate: CrateId, // FIXME this is probably redundant with the TraitEnvironment
|
||||
env: Arc<TraitEnvironment>,
|
||||
ty: Ty,
|
||||
}
|
||||
@ -2533,12 +2533,9 @@ impl Type {
|
||||
/// Checks that particular type `ty` implements `std::future::Future`.
|
||||
/// This function is used in `.await` syntax completion.
|
||||
pub fn impls_future(&self, db: &dyn HirDatabase) -> bool {
|
||||
// No special case for the type of async block, since Chalk can figure it out.
|
||||
|
||||
let krate = self.krate;
|
||||
|
||||
let std_future_trait =
|
||||
db.lang_item(krate, SmolStr::new_inline("future_trait")).and_then(|it| it.as_trait());
|
||||
let std_future_trait = db
|
||||
.lang_item(self.krate, SmolStr::new_inline("future_trait"))
|
||||
.and_then(|it| it.as_trait());
|
||||
let std_future_trait = match std_future_trait {
|
||||
Some(it) => it,
|
||||
None => return false,
|
||||
@ -2546,13 +2543,7 @@ impl Type {
|
||||
|
||||
let canonical_ty =
|
||||
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
|
||||
method_resolution::implements_trait(
|
||||
&canonical_ty,
|
||||
db,
|
||||
self.env.clone(),
|
||||
krate,
|
||||
std_future_trait,
|
||||
)
|
||||
method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), std_future_trait)
|
||||
}
|
||||
|
||||
/// Checks that particular type `ty` implements `std::ops::FnOnce`.
|
||||
@ -2560,9 +2551,7 @@ impl Type {
|
||||
/// This function can be used to check if a particular type is callable, since FnOnce is a
|
||||
/// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
|
||||
pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
|
||||
let krate = self.krate;
|
||||
|
||||
let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) {
|
||||
let fnonce_trait = match FnTrait::FnOnce.get_id(db, self.krate) {
|
||||
Some(it) => it,
|
||||
None => return false,
|
||||
};
|
||||
@ -2573,7 +2562,6 @@ impl Type {
|
||||
&canonical_ty,
|
||||
db,
|
||||
self.env.clone(),
|
||||
krate,
|
||||
fnonce_trait,
|
||||
)
|
||||
}
|
||||
@ -2744,9 +2732,8 @@ impl Type {
|
||||
pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
|
||||
// There should be no inference vars in types passed here
|
||||
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
|
||||
let environment = self.env.env.clone();
|
||||
let ty = InEnvironment { goal: canonical, environment };
|
||||
autoderef(db, Some(self.krate), ty).map(|canonical| canonical.value)
|
||||
let environment = self.env.clone();
|
||||
autoderef(db, environment, canonical).map(|canonical| canonical.value)
|
||||
}
|
||||
|
||||
// This would be nicer if it just returned an iterator, but that runs into
|
||||
@ -2801,24 +2788,26 @@ impl Type {
|
||||
pub fn iterate_method_candidates<T>(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
krate: Crate,
|
||||
scope: &SemanticsScope,
|
||||
// FIXME this can be retrieved from `scope`, except autoimport uses this
|
||||
// to specify a different set, so the method needs to be split
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
with_local_impls: Option<Module>,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(Type, Function) -> Option<T>,
|
||||
mut callback: impl FnMut(Function) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let _p = profile::span("iterate_method_candidates");
|
||||
let mut slot = None;
|
||||
|
||||
self.iterate_method_candidates_dyn(
|
||||
db,
|
||||
krate,
|
||||
scope,
|
||||
traits_in_scope,
|
||||
with_local_impls,
|
||||
name,
|
||||
&mut |ty, assoc_item_id| {
|
||||
&mut |assoc_item_id| {
|
||||
if let AssocItemId::FunctionId(func) = assoc_item_id {
|
||||
if let Some(res) = callback(self.derived(ty.clone()), func.into()) {
|
||||
if let Some(res) = callback(func.into()) {
|
||||
slot = Some(res);
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
@ -2832,50 +2821,55 @@ impl Type {
|
||||
fn iterate_method_candidates_dyn(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
krate: Crate,
|
||||
scope: &SemanticsScope,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
with_local_impls: Option<Module>,
|
||||
name: Option<&Name>,
|
||||
callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>,
|
||||
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
|
||||
) {
|
||||
// There should be no inference vars in types passed here
|
||||
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
|
||||
|
||||
let env = self.env.clone();
|
||||
let krate = krate.id;
|
||||
let krate = match scope.krate() {
|
||||
Some(k) => k,
|
||||
None => return,
|
||||
};
|
||||
let environment = scope.resolver().generic_def().map_or_else(
|
||||
|| Arc::new(TraitEnvironment::empty(krate.id)),
|
||||
|d| db.trait_environment(d),
|
||||
);
|
||||
|
||||
method_resolution::iterate_method_candidates_dyn(
|
||||
&canonical,
|
||||
db,
|
||||
env,
|
||||
krate,
|
||||
environment,
|
||||
traits_in_scope,
|
||||
with_local_impls.and_then(|b| b.id.containing_block()).into(),
|
||||
name,
|
||||
method_resolution::LookupMode::MethodCall,
|
||||
&mut |ty, id| callback(&ty.value, id),
|
||||
&mut |_adj, id| callback(id),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn iterate_path_candidates<T>(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
krate: Crate,
|
||||
scope: &SemanticsScope,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
with_local_impls: Option<Module>,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(Type, AssocItem) -> Option<T>,
|
||||
mut callback: impl FnMut(AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let _p = profile::span("iterate_path_candidates");
|
||||
let mut slot = None;
|
||||
self.iterate_path_candidates_dyn(
|
||||
db,
|
||||
krate,
|
||||
scope,
|
||||
traits_in_scope,
|
||||
with_local_impls,
|
||||
name,
|
||||
&mut |ty, assoc_item_id| {
|
||||
if let Some(res) = callback(self.derived(ty.clone()), assoc_item_id.into()) {
|
||||
&mut |assoc_item_id| {
|
||||
if let Some(res) = callback(assoc_item_id.into()) {
|
||||
slot = Some(res);
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
@ -2888,27 +2882,31 @@ impl Type {
|
||||
fn iterate_path_candidates_dyn(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
krate: Crate,
|
||||
scope: &SemanticsScope,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
with_local_impls: Option<Module>,
|
||||
name: Option<&Name>,
|
||||
callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>,
|
||||
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
|
||||
) {
|
||||
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
|
||||
|
||||
let env = self.env.clone();
|
||||
let krate = krate.id;
|
||||
let krate = match scope.krate() {
|
||||
Some(k) => k,
|
||||
None => return,
|
||||
};
|
||||
let environment = scope.resolver().generic_def().map_or_else(
|
||||
|| Arc::new(TraitEnvironment::empty(krate.id)),
|
||||
|d| db.trait_environment(d),
|
||||
);
|
||||
|
||||
method_resolution::iterate_method_candidates_dyn(
|
||||
method_resolution::iterate_path_candidates(
|
||||
&canonical,
|
||||
db,
|
||||
env,
|
||||
krate,
|
||||
environment,
|
||||
traits_in_scope,
|
||||
with_local_impls.and_then(|b| b.id.containing_block()).into(),
|
||||
name,
|
||||
method_resolution::LookupMode::Path,
|
||||
&mut |ty, id| callback(&ty.value, id),
|
||||
&mut |id| callback(id),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1230,6 +1230,10 @@ impl<'a> SemanticsScope<'a> {
|
||||
Some(Crate { id: self.resolver.krate()? })
|
||||
}
|
||||
|
||||
pub(crate) fn resolver(&self) -> &Resolver {
|
||||
&self.resolver
|
||||
}
|
||||
|
||||
/// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
|
||||
pub fn visible_traits(&self) -> FxHashSet<TraitId> {
|
||||
let resolver = &self.resolver;
|
||||
|
@ -3,20 +3,16 @@
|
||||
//! reference to a type with the field `bar`. This is an approximation of the
|
||||
//! logic in rustc (which lives in librustc_typeck/check/autoderef.rs).
|
||||
|
||||
use std::iter::successors;
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind};
|
||||
use hir_def::lang_item::LangItemTarget;
|
||||
use chalk_ir::cast::Cast;
|
||||
use hir_expand::name::name;
|
||||
use limit::Limit;
|
||||
use syntax::SmolStr;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
|
||||
ConstrainedSubst, DebruijnIndex, Environment, Guidance, InEnvironment, Interner,
|
||||
ProjectionTyExt, Solution, Substitution, Ty, TyBuilder, TyKind,
|
||||
db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
|
||||
TraitEnvironment, Ty, TyBuilder, TyKind,
|
||||
};
|
||||
|
||||
static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
|
||||
@ -26,40 +22,34 @@ pub(crate) enum AutoderefKind {
|
||||
Overloaded,
|
||||
}
|
||||
|
||||
pub(crate) struct Autoderef<'db> {
|
||||
db: &'db dyn HirDatabase,
|
||||
ty: Canonical<Ty>,
|
||||
pub(crate) struct Autoderef<'a, 'db> {
|
||||
pub(crate) table: &'a mut InferenceTable<'db>,
|
||||
ty: Ty,
|
||||
at_start: bool,
|
||||
krate: Option<CrateId>,
|
||||
environment: Environment,
|
||||
steps: Vec<(AutoderefKind, Ty)>,
|
||||
}
|
||||
|
||||
impl<'db> Autoderef<'db> {
|
||||
pub(crate) fn new(
|
||||
db: &'db dyn HirDatabase,
|
||||
krate: Option<CrateId>,
|
||||
ty: InEnvironment<Canonical<Ty>>,
|
||||
) -> Self {
|
||||
let InEnvironment { goal: ty, environment } = ty;
|
||||
Autoderef { db, ty, at_start: true, environment, krate, steps: Vec::new() }
|
||||
impl<'a, 'db> Autoderef<'a, 'db> {
|
||||
pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty) -> Self {
|
||||
let ty = table.resolve_ty_shallow(&ty);
|
||||
Autoderef { table, ty, at_start: true, steps: Vec::new() }
|
||||
}
|
||||
|
||||
pub(crate) fn step_count(&self) -> usize {
|
||||
self.steps.len()
|
||||
}
|
||||
|
||||
pub(crate) fn steps(&self) -> &[(AutoderefKind, chalk_ir::Ty<Interner>)] {
|
||||
pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
|
||||
&self.steps
|
||||
}
|
||||
|
||||
pub(crate) fn final_ty(&self) -> Ty {
|
||||
self.ty.value.clone()
|
||||
self.ty.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Autoderef<'_> {
|
||||
type Item = (Canonical<Ty>, usize);
|
||||
impl Iterator for Autoderef<'_, '_> {
|
||||
type Item = (Ty, usize);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.at_start {
|
||||
@ -71,54 +61,42 @@ impl Iterator for Autoderef<'_> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) {
|
||||
(
|
||||
AutoderefKind::Builtin,
|
||||
Canonical { value: derefed.clone(), binders: self.ty.binders.clone() },
|
||||
)
|
||||
} else {
|
||||
(
|
||||
AutoderefKind::Overloaded,
|
||||
deref_by_trait(
|
||||
self.db,
|
||||
self.krate?,
|
||||
InEnvironment { goal: &self.ty, environment: self.environment.clone() },
|
||||
)?,
|
||||
)
|
||||
};
|
||||
let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?;
|
||||
|
||||
self.steps.push((kind, self.ty.value.clone()));
|
||||
self.steps.push((kind, self.ty.clone()));
|
||||
self.ty = new_ty;
|
||||
|
||||
Some((self.ty.clone(), self.step_count()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn autoderef_step(table: &mut InferenceTable, ty: Ty) -> Option<(AutoderefKind, Ty)> {
|
||||
if let Some(derefed) = builtin_deref(&ty) {
|
||||
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
|
||||
} else {
|
||||
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: replace uses of this with Autoderef above
|
||||
pub fn autoderef<'a>(
|
||||
db: &'a dyn HirDatabase,
|
||||
krate: Option<CrateId>,
|
||||
ty: InEnvironment<Canonical<Ty>>,
|
||||
env: Arc<TraitEnvironment>,
|
||||
ty: Canonical<Ty>,
|
||||
) -> impl Iterator<Item = Canonical<Ty>> + 'a {
|
||||
let InEnvironment { goal: ty, environment } = ty;
|
||||
successors(Some(ty), move |ty| {
|
||||
deref(db, krate?, InEnvironment { goal: ty, environment: environment.clone() })
|
||||
})
|
||||
.take(AUTODEREF_RECURSION_LIMIT.inner())
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let mut autoderef = Autoderef::new(&mut table, ty);
|
||||
let mut v = Vec::new();
|
||||
while let Some((ty, _steps)) = autoderef.next() {
|
||||
v.push(autoderef.table.canonicalize(ty).value);
|
||||
}
|
||||
v.into_iter()
|
||||
}
|
||||
|
||||
pub(crate) fn deref(
|
||||
db: &dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
ty: InEnvironment<&Canonical<Ty>>,
|
||||
) -> Option<Canonical<Ty>> {
|
||||
pub(crate) fn deref(table: &mut InferenceTable, ty: Ty) -> Option<Ty> {
|
||||
let _p = profile::span("deref");
|
||||
match builtin_deref(&ty.goal.value) {
|
||||
Some(derefed) => {
|
||||
Some(Canonical { value: derefed.clone(), binders: ty.goal.binders.clone() })
|
||||
}
|
||||
None => deref_by_trait(db, krate, ty),
|
||||
}
|
||||
autoderef_step(table, ty).map(|(_, ty)| ty)
|
||||
}
|
||||
|
||||
fn builtin_deref(ty: &Ty) -> Option<&Ty> {
|
||||
@ -129,16 +107,12 @@ fn builtin_deref(ty: &Ty) -> Option<&Ty> {
|
||||
}
|
||||
}
|
||||
|
||||
fn deref_by_trait(
|
||||
db: &dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
ty: InEnvironment<&Canonical<Ty>>,
|
||||
) -> Option<Canonical<Ty>> {
|
||||
fn deref_by_trait(table: &mut InferenceTable, ty: Ty) -> Option<Ty> {
|
||||
let db = table.db;
|
||||
let _p = profile::span("deref_by_trait");
|
||||
let deref_trait = match db.lang_item(krate, SmolStr::new_inline("deref"))? {
|
||||
LangItemTarget::TraitId(it) => it,
|
||||
_ => return None,
|
||||
};
|
||||
let deref_trait = db
|
||||
.lang_item(table.trait_env.krate, SmolStr::new_inline("deref"))
|
||||
.and_then(|l| l.as_trait())?;
|
||||
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
|
||||
|
||||
let projection = {
|
||||
@ -148,114 +122,16 @@ fn deref_by_trait(
|
||||
// namely Deref's Self type
|
||||
return None;
|
||||
}
|
||||
b.push(ty.goal.value.clone()).build()
|
||||
b.push(ty).build()
|
||||
};
|
||||
|
||||
// FIXME make the Canonical / bound var handling nicer
|
||||
|
||||
// Check that the type implements Deref at all
|
||||
let trait_ref = projection.trait_ref(db);
|
||||
let implements_goal = Canonical {
|
||||
binders: ty.goal.binders.clone(),
|
||||
value: InEnvironment {
|
||||
goal: trait_ref.cast(Interner),
|
||||
environment: ty.environment.clone(),
|
||||
},
|
||||
};
|
||||
if db.trait_solve(krate, implements_goal).is_none() {
|
||||
return None;
|
||||
}
|
||||
let implements_goal: Goal = trait_ref.cast(Interner);
|
||||
table.try_obligation(implements_goal.clone())?;
|
||||
|
||||
// Now do the assoc type projection
|
||||
let alias_eq = AliasEq {
|
||||
alias: AliasTy::Projection(projection),
|
||||
ty: TyKind::BoundVar(BoundVar::new(
|
||||
DebruijnIndex::INNERMOST,
|
||||
ty.goal.binders.len(Interner),
|
||||
))
|
||||
.intern(Interner),
|
||||
};
|
||||
table.register_obligation(implements_goal);
|
||||
|
||||
let in_env = InEnvironment { goal: alias_eq.cast(Interner), environment: ty.environment };
|
||||
|
||||
let canonical = Canonical {
|
||||
value: in_env,
|
||||
binders: CanonicalVarKinds::from_iter(
|
||||
Interner,
|
||||
ty.goal.binders.iter(Interner).cloned().chain(Some(chalk_ir::WithKind::new(
|
||||
VariableKind::Ty(chalk_ir::TyVariableKind::General),
|
||||
chalk_ir::UniverseIndex::ROOT,
|
||||
))),
|
||||
),
|
||||
};
|
||||
|
||||
let solution = db.trait_solve(krate, canonical)?;
|
||||
|
||||
match &solution {
|
||||
Solution::Unique(Canonical { value: ConstrainedSubst { subst, .. }, binders })
|
||||
| Solution::Ambig(Guidance::Definite(Canonical { value: subst, binders })) => {
|
||||
// FIXME: vars may contain solutions for any inference variables
|
||||
// that happened to be inside ty. To correctly handle these, we
|
||||
// would have to pass the solution up to the inference context, but
|
||||
// that requires a larger refactoring (especially if the deref
|
||||
// happens during method resolution). So for the moment, we just
|
||||
// check that we're not in the situation where we would actually
|
||||
// need to handle the values of the additional variables, i.e.
|
||||
// they're just being 'passed through'. In the 'standard' case where
|
||||
// we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
|
||||
// the case.
|
||||
|
||||
// FIXME: if the trait solver decides to truncate the type, these
|
||||
// assumptions will be broken. We would need to properly introduce
|
||||
// new variables in that case
|
||||
|
||||
for i in 1..binders.len(Interner) {
|
||||
if subst.at(Interner, i - 1).assert_ty_ref(Interner).kind(Interner)
|
||||
!= &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
|
||||
{
|
||||
warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
// FIXME: we remove lifetime variables here since they can confuse
|
||||
// the method resolution code later
|
||||
Some(fixup_lifetime_variables(Canonical {
|
||||
value: subst.at(Interner, subst.len(Interner) - 1).assert_ty_ref(Interner).clone(),
|
||||
binders: binders.clone(),
|
||||
}))
|
||||
}
|
||||
Solution::Ambig(_) => {
|
||||
info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fixup_lifetime_variables<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>(
|
||||
c: Canonical<T>,
|
||||
) -> Canonical<T> {
|
||||
// Removes lifetime variables from the Canonical, replacing them by static lifetimes.
|
||||
let mut i = 0;
|
||||
let subst = Substitution::from_iter(
|
||||
Interner,
|
||||
c.binders.iter(Interner).map(|vk| match vk.kind {
|
||||
VariableKind::Ty(_) => {
|
||||
let index = i;
|
||||
i += 1;
|
||||
BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(Interner).cast(Interner)
|
||||
}
|
||||
VariableKind::Lifetime => static_lifetime().cast(Interner),
|
||||
VariableKind::Const(_) => unimplemented!(),
|
||||
}),
|
||||
);
|
||||
let binders = CanonicalVarKinds::from_iter(
|
||||
Interner,
|
||||
c.binders.iter(Interner).filter(|vk| match vk.kind {
|
||||
VariableKind::Ty(_) => true,
|
||||
VariableKind::Lifetime => false,
|
||||
VariableKind::Const(_) => true,
|
||||
}),
|
||||
);
|
||||
let value = subst.apply(c.value, Interner);
|
||||
Canonical { binders, value }
|
||||
let result = table.normalize_projection_ty(projection);
|
||||
Some(table.resolve_ty_shallow(&result))
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
//! to certain types. To record this, we use the union-find implementation from
|
||||
//! the `ena` crate, which is extracted from rustc.
|
||||
|
||||
use std::ops::Index;
|
||||
use std::sync::Arc;
|
||||
use std::{collections::hash_map::Entry, ops::Index};
|
||||
|
||||
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
|
||||
use hir_def::{
|
||||
@ -46,7 +46,7 @@ use crate::{
|
||||
pub use unify::could_unify;
|
||||
pub(crate) use unify::unify;
|
||||
|
||||
mod unify;
|
||||
pub(crate) mod unify;
|
||||
mod path;
|
||||
mod expr;
|
||||
mod pat;
|
||||
@ -228,7 +228,7 @@ pub enum Adjust {
|
||||
/// The target type is `U` in both cases, with the region and mutability
|
||||
/// being those shared by both the receiver and the returned reference.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct OverloadedDeref(Mutability);
|
||||
pub struct OverloadedDeref(pub Mutability);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum AutoBorrow {
|
||||
@ -455,16 +455,6 @@ impl<'a> InferenceContext<'a> {
|
||||
self.result.method_resolutions.insert(expr, (func, subst));
|
||||
}
|
||||
|
||||
fn write_field_resolution(&mut self, expr: ExprId, field: FieldId) {
|
||||
self.result.field_resolutions.insert(expr, field);
|
||||
}
|
||||
|
||||
fn write_field_resolution_if_empty(&mut self, expr: ExprId, field: FieldId) {
|
||||
if let Entry::Vacant(entry) = self.result.field_resolutions.entry(expr) {
|
||||
entry.insert(field);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_variant_resolution(&mut self, id: ExprOrPatId, variant: VariantId) {
|
||||
self.result.variant_resolutions.insert(id, variant);
|
||||
}
|
||||
|
@ -259,27 +259,19 @@ impl<'a> InferenceContext<'a> {
|
||||
// details of coercion errors though, so I think it's useful to leave
|
||||
// the structure like it is.
|
||||
|
||||
let canonicalized = self.canonicalize(from_ty.clone());
|
||||
let mut autoderef = Autoderef::new(
|
||||
self.db,
|
||||
self.resolver.krate(),
|
||||
InEnvironment {
|
||||
goal: canonicalized.value.clone(),
|
||||
environment: self.trait_env.env.clone(),
|
||||
},
|
||||
);
|
||||
let snapshot = self.table.snapshot();
|
||||
|
||||
let mut autoderef = Autoderef::new(&mut self.table, from_ty.clone());
|
||||
let mut first_error = None;
|
||||
let mut found = None;
|
||||
|
||||
for (referent_ty, autoderefs) in autoderef.by_ref() {
|
||||
while let Some((referent_ty, autoderefs)) = autoderef.next() {
|
||||
if autoderefs == 0 {
|
||||
// Don't let this pass, otherwise it would cause
|
||||
// &T to autoref to &&T.
|
||||
continue;
|
||||
}
|
||||
|
||||
let referent_ty = canonicalized.decanonicalize_ty(&mut self.table, referent_ty);
|
||||
|
||||
// At this point, we have deref'd `a` to `referent_ty`. So
|
||||
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
|
||||
// In the autoderef loop for `&'a mut Vec<T>`, we would get
|
||||
@ -304,7 +296,7 @@ impl<'a> InferenceContext<'a> {
|
||||
// from `&mut T` to `&U`.
|
||||
let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc
|
||||
let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner);
|
||||
match self.table.try_unify(&derefd_from_ty, to_ty) {
|
||||
match autoderef.table.try_unify(&derefd_from_ty, to_ty) {
|
||||
Ok(result) => {
|
||||
found = Some(result.map(|()| derefd_from_ty));
|
||||
break;
|
||||
@ -325,6 +317,7 @@ impl<'a> InferenceContext<'a> {
|
||||
let InferOk { value: ty, goals } = match found {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
self.table.rollback_to(snapshot);
|
||||
let err = first_error.expect("coerce_borrowed_pointer had no error");
|
||||
return Err(err);
|
||||
}
|
||||
@ -345,29 +338,13 @@ impl<'a> InferenceContext<'a> {
|
||||
return success(vec![], ty, goals);
|
||||
}
|
||||
|
||||
let mut adjustments = self.auto_deref_adjust_steps(&autoderef);
|
||||
let mut adjustments = auto_deref_adjust_steps(&autoderef);
|
||||
adjustments
|
||||
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() });
|
||||
|
||||
success(adjustments, ty, goals)
|
||||
}
|
||||
|
||||
pub(super) fn auto_deref_adjust_steps(&self, autoderef: &Autoderef<'_>) -> Vec<Adjustment> {
|
||||
let steps = autoderef.steps();
|
||||
let targets =
|
||||
steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty()));
|
||||
steps
|
||||
.iter()
|
||||
.map(|(kind, _source)| match kind {
|
||||
// We do not know what kind of deref we require at this point yet
|
||||
AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
|
||||
AutoderefKind::Builtin => None,
|
||||
})
|
||||
.zip(targets)
|
||||
.map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target })
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Attempts to coerce from the type of a Rust function item into a function pointer.
|
||||
fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult {
|
||||
match to_ty.kind(Interner) {
|
||||
@ -620,3 +597,19 @@ fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError
|
||||
(Mutability::Not, Mutability::Mut) => Err(TypeError),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec<Adjustment> {
|
||||
let steps = autoderef.steps();
|
||||
let targets =
|
||||
steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty()));
|
||||
steps
|
||||
.iter()
|
||||
.map(|(kind, _source)| match kind {
|
||||
// We do not know what kind of deref we require at this point yet
|
||||
AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
|
||||
AutoderefKind::Builtin => None,
|
||||
})
|
||||
.zip(targets)
|
||||
.map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target })
|
||||
.collect()
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Type inference for expressions.
|
||||
|
||||
use std::{
|
||||
collections::hash_map::Entry,
|
||||
iter::{repeat, repeat_with},
|
||||
mem,
|
||||
sync::Arc,
|
||||
@ -26,15 +27,14 @@ use crate::{
|
||||
method_resolution,
|
||||
primitive::{self, UintTy},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
utils::{generics, Generics},
|
||||
AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
|
||||
ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar,
|
||||
Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
use super::{
|
||||
find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext,
|
||||
InferenceDiagnostic, TypeMismatch,
|
||||
coerce::auto_deref_adjust_steps, find_breakable, BindingMode, BreakableContext, Diverges,
|
||||
Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
|
||||
};
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
@ -77,51 +77,6 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
|
||||
let krate = self.resolver.krate()?;
|
||||
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
||||
let output_assoc_type =
|
||||
self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
||||
|
||||
let mut arg_tys = vec![];
|
||||
let arg_ty = TyBuilder::tuple(num_args)
|
||||
.fill(repeat_with(|| {
|
||||
let arg = self.table.new_type_var();
|
||||
arg_tys.push(arg.clone());
|
||||
arg
|
||||
}))
|
||||
.build();
|
||||
|
||||
let projection = {
|
||||
let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type);
|
||||
if b.remaining() != 2 {
|
||||
return None;
|
||||
}
|
||||
b.push(ty.clone()).push(arg_ty).build()
|
||||
};
|
||||
|
||||
let trait_env = self.trait_env.env.clone();
|
||||
let obligation = InEnvironment {
|
||||
goal: projection.trait_ref(self.db).cast(Interner),
|
||||
environment: trait_env,
|
||||
};
|
||||
let canonical = self.canonicalize(obligation.clone());
|
||||
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
||||
self.push_obligation(obligation.goal);
|
||||
let return_ty = self.table.normalize_projection_ty(projection);
|
||||
Some((arg_tys, return_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
|
||||
match ty.callable_sig(self.db) {
|
||||
Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())),
|
||||
None => self.callable_sig_from_fn_trait(ty, num_args),
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
self.db.unwind_if_cancelled();
|
||||
|
||||
@ -319,22 +274,19 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
Expr::Call { callee, args } => {
|
||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
||||
let canonicalized = self.canonicalize(callee_ty.clone());
|
||||
let mut derefs = Autoderef::new(
|
||||
self.db,
|
||||
self.resolver.krate(),
|
||||
InEnvironment {
|
||||
goal: canonicalized.value.clone(),
|
||||
environment: self.table.trait_env.env.clone(),
|
||||
},
|
||||
);
|
||||
let res = derefs.by_ref().find_map(|(callee_deref_ty, _)| {
|
||||
let ty = &canonicalized.decanonicalize_ty(&mut self.table, callee_deref_ty);
|
||||
self.callable_sig(ty, args.len())
|
||||
});
|
||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone());
|
||||
let mut res = None;
|
||||
// manual loop to be able to access `derefs.table`
|
||||
while let Some((callee_deref_ty, _)) = derefs.next() {
|
||||
res = derefs.table.callable_sig(&callee_deref_ty, args.len());
|
||||
if res.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let (param_tys, ret_ty): (Vec<Ty>, Ty) = match res {
|
||||
Some(res) => {
|
||||
self.write_expr_adj(*callee, self.auto_deref_adjust_steps(&derefs));
|
||||
let adjustments = auto_deref_adjust_steps(&derefs);
|
||||
self.write_expr_adj(*callee, adjustments);
|
||||
res
|
||||
}
|
||||
None => (Vec::new(), self.err_ty()),
|
||||
@ -489,88 +441,67 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
Expr::Field { expr, name } => {
|
||||
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let canonicalized = self.canonicalize(receiver_ty);
|
||||
|
||||
let mut autoderef = Autoderef::new(
|
||||
self.db,
|
||||
self.resolver.krate(),
|
||||
InEnvironment {
|
||||
goal: canonicalized.value.clone(),
|
||||
environment: self.trait_env.env.clone(),
|
||||
},
|
||||
);
|
||||
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty);
|
||||
let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
|
||||
let module = self.resolver.module();
|
||||
let db = self.db;
|
||||
let is_visible = |field_id: &FieldId| {
|
||||
module
|
||||
.map(|mod_id| {
|
||||
db.field_visibilities(field_id.parent)[field_id.local_id]
|
||||
.is_visible_from(db.upcast(), mod_id)
|
||||
})
|
||||
.unwrap_or(true)
|
||||
};
|
||||
match canonicalized
|
||||
.decanonicalize_ty(&mut self.table, derefed_ty)
|
||||
.kind(Interner)
|
||||
{
|
||||
TyKind::Tuple(_, substs) => name.as_tuple_index().and_then(|idx| {
|
||||
substs
|
||||
.as_slice(Interner)
|
||||
.get(idx)
|
||||
.map(|a| a.assert_ty_ref(Interner))
|
||||
.cloned()
|
||||
}),
|
||||
let (field_id, parameters) = match derefed_ty.kind(Interner) {
|
||||
TyKind::Tuple(_, substs) => {
|
||||
return name.as_tuple_index().and_then(|idx| {
|
||||
substs
|
||||
.as_slice(Interner)
|
||||
.get(idx)
|
||||
.map(|a| a.assert_ty_ref(Interner))
|
||||
.cloned()
|
||||
});
|
||||
}
|
||||
TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => {
|
||||
let local_id = self.db.struct_data(*s).variant_data.field(name)?;
|
||||
let field = FieldId { parent: (*s).into(), local_id };
|
||||
if is_visible(&field) {
|
||||
self.write_field_resolution(tgt_expr, field);
|
||||
Some(
|
||||
self.db.field_types((*s).into())[field.local_id]
|
||||
.clone()
|
||||
.substitute(Interner, ¶meters),
|
||||
)
|
||||
} else {
|
||||
// Write down the first field resolution even if it is not visible
|
||||
// This aids IDE features for private fields like goto def and in
|
||||
// case of autoderef finding an applicable field, this will be
|
||||
// overwritten in a following cycle
|
||||
self.write_field_resolution_if_empty(tgt_expr, field);
|
||||
None
|
||||
}
|
||||
(field, parameters.clone())
|
||||
}
|
||||
TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => {
|
||||
let local_id = self.db.union_data(*u).variant_data.field(name)?;
|
||||
let field = FieldId { parent: (*u).into(), local_id };
|
||||
if is_visible(&field) {
|
||||
self.write_field_resolution(tgt_expr, field);
|
||||
Some(
|
||||
self.db.field_types((*u).into())[field.local_id]
|
||||
.clone()
|
||||
.substitute(Interner, ¶meters),
|
||||
)
|
||||
} else {
|
||||
// Write down the first field resolution even if it is not visible
|
||||
// This aids IDE features for private fields like goto def and in
|
||||
// case of autoderef finding an applicable field, this will be
|
||||
// overwritten in a following cycle
|
||||
self.write_field_resolution_if_empty(tgt_expr, field);
|
||||
None
|
||||
}
|
||||
(field, parameters.clone())
|
||||
}
|
||||
_ => None,
|
||||
_ => return None,
|
||||
};
|
||||
let module = self.resolver.module();
|
||||
let is_visible = module
|
||||
.map(|mod_id| {
|
||||
self.db.field_visibilities(field_id.parent)[field_id.local_id]
|
||||
.is_visible_from(self.db.upcast(), mod_id)
|
||||
})
|
||||
.unwrap_or(true);
|
||||
if !is_visible {
|
||||
// Write down the first field resolution even if it is not visible
|
||||
// This aids IDE features for private fields like goto def and in
|
||||
// case of autoderef finding an applicable field, this will be
|
||||
// overwritten in a following cycle
|
||||
if let Entry::Vacant(entry) = self.result.field_resolutions.entry(tgt_expr)
|
||||
{
|
||||
entry.insert(field_id);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
// can't have `write_field_resolution` here because `self.table` is borrowed :(
|
||||
self.result.field_resolutions.insert(tgt_expr, field_id);
|
||||
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
|
||||
.clone()
|
||||
.substitute(Interner, ¶meters);
|
||||
Some(ty)
|
||||
});
|
||||
let ty = match ty {
|
||||
Some(ty) => {
|
||||
self.write_expr_adj(*expr, self.auto_deref_adjust_steps(&autoderef));
|
||||
let adjustments = auto_deref_adjust_steps(&autoderef);
|
||||
self.write_expr_adj(*expr, adjustments);
|
||||
let ty = self.insert_type_vars(ty);
|
||||
let ty = self.normalize_associated_types_in(ty);
|
||||
ty
|
||||
}
|
||||
None => self.err_ty(),
|
||||
_ => self.err_ty(),
|
||||
};
|
||||
let ty = self.insert_type_vars(ty);
|
||||
self.normalize_associated_types_in(ty)
|
||||
ty
|
||||
}
|
||||
Expr::Await { expr } => {
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
@ -628,25 +559,9 @@ impl<'a> InferenceContext<'a> {
|
||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let inner_ty = self.resolve_ty_shallow(&inner_ty);
|
||||
match op {
|
||||
UnaryOp::Deref => match self.resolver.krate() {
|
||||
Some(krate) => {
|
||||
let canonicalized = self.canonicalize(inner_ty);
|
||||
match autoderef::deref(
|
||||
self.db,
|
||||
krate,
|
||||
InEnvironment {
|
||||
goal: &canonicalized.value,
|
||||
environment: self.trait_env.env.clone(),
|
||||
},
|
||||
) {
|
||||
Some(derefed_ty) => {
|
||||
canonicalized.decanonicalize_ty(&mut self.table, derefed_ty)
|
||||
}
|
||||
None => self.err_ty(),
|
||||
}
|
||||
}
|
||||
None => self.err_ty(),
|
||||
},
|
||||
UnaryOp::Deref => {
|
||||
autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
|
||||
}
|
||||
UnaryOp::Neg => {
|
||||
match inner_ty.kind(Interner) {
|
||||
// Fast path for builtins
|
||||
@ -732,20 +647,19 @@ impl<'a> InferenceContext<'a> {
|
||||
let base_ty = self.infer_expr_inner(*base, &Expectation::none());
|
||||
let index_ty = self.infer_expr(*index, &Expectation::none());
|
||||
|
||||
if let (Some(index_trait), Some(krate)) =
|
||||
(self.resolve_ops_index(), self.resolver.krate())
|
||||
{
|
||||
let canonicalized = self.canonicalize(base_ty);
|
||||
let self_ty = method_resolution::resolve_indexing_op(
|
||||
if let Some(index_trait) = self.resolve_ops_index() {
|
||||
let canonicalized = self.canonicalize(base_ty.clone());
|
||||
let receiver_adjustments = method_resolution::resolve_indexing_op(
|
||||
self.db,
|
||||
&canonicalized.value,
|
||||
self.trait_env.clone(),
|
||||
krate,
|
||||
canonicalized.value,
|
||||
index_trait,
|
||||
);
|
||||
let self_ty = self_ty.map_or(self.err_ty(), |t| {
|
||||
canonicalized.decanonicalize_ty(&mut self.table, t)
|
||||
});
|
||||
let (self_ty, adj) = receiver_adjustments
|
||||
.map_or((self.err_ty(), Vec::new()), |adj| {
|
||||
adj.apply(&mut self.table, base_ty)
|
||||
});
|
||||
self.write_expr_adj(*base, adj);
|
||||
self.resolve_associated_type_with_params(
|
||||
self_ty,
|
||||
self.resolve_ops_index_output(),
|
||||
@ -992,22 +906,20 @@ impl<'a> InferenceContext<'a> {
|
||||
|
||||
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
|
||||
|
||||
let resolved = self.resolver.krate().and_then(|krate| {
|
||||
method_resolution::lookup_method(
|
||||
&canonicalized_receiver.value,
|
||||
self.db,
|
||||
self.trait_env.clone(),
|
||||
krate,
|
||||
&traits_in_scope,
|
||||
self.resolver.module().into(),
|
||||
method_name,
|
||||
)
|
||||
});
|
||||
let resolved = method_resolution::lookup_method(
|
||||
&canonicalized_receiver.value,
|
||||
self.db,
|
||||
self.trait_env.clone(),
|
||||
&traits_in_scope,
|
||||
self.resolver.module().into(),
|
||||
method_name,
|
||||
);
|
||||
let (receiver_ty, method_ty, substs) = match resolved {
|
||||
Some((ty, func)) => {
|
||||
let ty = canonicalized_receiver.decanonicalize_ty(&mut self.table, ty);
|
||||
Some((adjust, func)) => {
|
||||
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
|
||||
let generics = generics(self.db.upcast(), func.into());
|
||||
let substs = self.substs_for_method_call(generics, generic_args, &ty);
|
||||
let substs = self.substs_for_method_call(generics, generic_args);
|
||||
self.write_expr_adj(receiver, adjustments);
|
||||
self.write_method_resolution(tgt_expr, func, substs.clone());
|
||||
(ty, self.db.value_ty(func.into()), substs)
|
||||
}
|
||||
@ -1120,20 +1032,15 @@ impl<'a> InferenceContext<'a> {
|
||||
&mut self,
|
||||
def_generics: Generics,
|
||||
generic_args: Option<&GenericArgs>,
|
||||
receiver_ty: &Ty,
|
||||
) -> Substitution {
|
||||
let (parent_params, self_params, type_params, impl_trait_params) =
|
||||
def_generics.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
|
||||
for (_id, param) in def_generics.iter_parent() {
|
||||
if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf {
|
||||
substs.push(receiver_ty.clone());
|
||||
} else {
|
||||
substs.push(self.table.new_type_var());
|
||||
}
|
||||
// Parent arguments are unknown
|
||||
for _ in def_generics.iter_parent() {
|
||||
substs.push(self.table.new_type_var());
|
||||
}
|
||||
// handle provided type arguments
|
||||
if let Some(generic_args) = generic_args {
|
||||
|
@ -218,14 +218,12 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
|
||||
let canonical_ty = self.canonicalize(ty.clone());
|
||||
let krate = self.resolver.krate()?;
|
||||
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
|
||||
|
||||
method_resolution::iterate_method_candidates(
|
||||
&canonical_ty.value,
|
||||
self.db,
|
||||
self.table.trait_env.clone(),
|
||||
krate,
|
||||
&traits_in_scope,
|
||||
self.resolver.module().into(),
|
||||
Some(name),
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Unification and canonicalization logic.
|
||||
|
||||
use std::{fmt, mem, sync::Arc};
|
||||
use std::{fmt, iter, mem, sync::Arc};
|
||||
|
||||
use chalk_ir::{
|
||||
cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution,
|
||||
@ -8,12 +8,14 @@ use chalk_ir::{
|
||||
};
|
||||
use chalk_solve::infer::ParameterEnaVariableExt;
|
||||
use ena::unify::UnifyKey;
|
||||
use hir_expand::name;
|
||||
|
||||
use super::{InferOk, InferResult, InferenceContext, TypeError};
|
||||
use crate::{
|
||||
db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, Const,
|
||||
DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime,
|
||||
ProjectionTy, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind,
|
||||
db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
|
||||
Canonical, Const, DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar,
|
||||
Interner, Lifetime, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution,
|
||||
TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
|
||||
};
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
@ -24,32 +26,20 @@ impl<'a> InferenceContext<'a> {
|
||||
where
|
||||
T::Result: HasInterner<Interner = Interner>,
|
||||
{
|
||||
// try to resolve obligations before canonicalizing, since this might
|
||||
// result in new knowledge about variables
|
||||
self.resolve_obligations_as_possible();
|
||||
self.table.canonicalize(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct Canonicalized<T>
|
||||
pub(crate) struct Canonicalized<T>
|
||||
where
|
||||
T: HasInterner<Interner = Interner>,
|
||||
{
|
||||
pub(super) value: Canonical<T>,
|
||||
pub(crate) value: Canonical<T>,
|
||||
free_vars: Vec<GenericArg>,
|
||||
}
|
||||
|
||||
impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
|
||||
/// this method is wrong and shouldn't exist
|
||||
pub(super) fn decanonicalize_ty(&self, table: &mut InferenceTable, ty: Canonical<Ty>) -> Ty {
|
||||
let mut vars = self.free_vars.clone();
|
||||
while ty.binders.len(Interner) > vars.len() {
|
||||
vars.push(table.new_type_var().cast(Interner));
|
||||
}
|
||||
chalk_ir::Substitute::apply(&vars, ty.value, Interner)
|
||||
}
|
||||
|
||||
pub(super) fn apply_solution(
|
||||
&self,
|
||||
ctx: &mut InferenceTable,
|
||||
@ -203,13 +193,16 @@ impl<'a> InferenceTable<'a> {
|
||||
.intern(Interner)
|
||||
}
|
||||
|
||||
pub(super) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>(
|
||||
pub(crate) fn canonicalize<T: Fold<Interner> + HasInterner<Interner = Interner>>(
|
||||
&mut self,
|
||||
t: T,
|
||||
) -> Canonicalized<T::Result>
|
||||
where
|
||||
T::Result: HasInterner<Interner = Interner>,
|
||||
{
|
||||
// try to resolve obligations before canonicalizing, since this might
|
||||
// result in new knowledge about variables
|
||||
self.resolve_obligations_as_possible();
|
||||
let result = self.var_unification_table.canonicalize(Interner, t);
|
||||
let free_vars = result
|
||||
.free_vars
|
||||
@ -225,7 +218,7 @@ impl<'a> InferenceTable<'a> {
|
||||
/// type annotation (e.g. from a let type annotation, field type or function
|
||||
/// call). `make_ty` handles this already, but e.g. for field types we need
|
||||
/// to do it as well.
|
||||
pub(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
|
||||
pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
|
||||
fold_tys(
|
||||
ty,
|
||||
|ty, _| match ty.kind(Interner) {
|
||||
@ -238,7 +231,7 @@ impl<'a> InferenceTable<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
|
||||
pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
|
||||
let var = self.new_type_var();
|
||||
let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
|
||||
let obligation = alias_eq.cast(Interner);
|
||||
@ -299,6 +292,13 @@ impl<'a> InferenceTable<'a> {
|
||||
self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback)
|
||||
}
|
||||
|
||||
pub(crate) fn instantiate_canonical<T>(&mut self, canonical: Canonical<T>) -> T::Result
|
||||
where
|
||||
T: HasInterner<Interner = Interner> + Fold<Interner> + std::fmt::Debug,
|
||||
{
|
||||
self.var_unification_table.instantiate_canonical(Interner, canonical)
|
||||
}
|
||||
|
||||
fn resolve_with_fallback_inner<T>(
|
||||
&mut self,
|
||||
var_stack: &mut Vec<InferenceVar>,
|
||||
@ -351,6 +351,7 @@ impl<'a> InferenceTable<'a> {
|
||||
/// If `ty` is a type variable with known type, returns that type;
|
||||
/// otherwise, return ty.
|
||||
pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
|
||||
self.resolve_obligations_as_possible();
|
||||
self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone())
|
||||
}
|
||||
|
||||
@ -363,6 +364,16 @@ impl<'a> InferenceTable<'a> {
|
||||
self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
|
||||
}
|
||||
|
||||
/// Checks an obligation without registering it. Useful mostly to check
|
||||
/// whether a trait *might* be implemented before deciding to 'lock in' the
|
||||
/// choice (during e.g. method resolution or deref).
|
||||
pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option<Solution> {
|
||||
let in_env = InEnvironment::new(&self.trait_env.env, goal);
|
||||
let canonicalized = self.canonicalize(in_env);
|
||||
let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value);
|
||||
solution
|
||||
}
|
||||
|
||||
pub(crate) fn register_obligation(&mut self, goal: Goal) {
|
||||
let in_env = InEnvironment::new(&self.trait_env.env, goal);
|
||||
self.register_obligation_in_env(in_env)
|
||||
@ -522,6 +533,51 @@ impl<'a> InferenceTable<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
|
||||
match ty.callable_sig(self.db) {
|
||||
Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())),
|
||||
None => self.callable_sig_from_fn_trait(ty, num_args),
|
||||
}
|
||||
}
|
||||
|
||||
fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
|
||||
let krate = self.trait_env.krate;
|
||||
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
|
||||
let output_assoc_type =
|
||||
self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
|
||||
|
||||
let mut arg_tys = vec![];
|
||||
let arg_ty = TyBuilder::tuple(num_args)
|
||||
.fill(iter::repeat_with(|| {
|
||||
let arg = self.new_type_var();
|
||||
arg_tys.push(arg.clone());
|
||||
arg
|
||||
}))
|
||||
.build();
|
||||
|
||||
let projection = {
|
||||
let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type);
|
||||
if b.remaining() != 2 {
|
||||
return None;
|
||||
}
|
||||
b.push(ty.clone()).push(arg_ty).build()
|
||||
};
|
||||
|
||||
let trait_env = self.trait_env.env.clone();
|
||||
let obligation = InEnvironment {
|
||||
goal: projection.trait_ref(self.db).cast(Interner),
|
||||
environment: trait_env,
|
||||
};
|
||||
let canonical = self.canonicalize(obligation.clone());
|
||||
if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
|
||||
self.register_obligation(obligation.goal);
|
||||
let return_ty = self.normalize_projection_ty(projection);
|
||||
Some((arg_tys, return_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for InferenceTable<'a> {
|
||||
|
@ -17,10 +17,11 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
autoderef,
|
||||
autoderef::{self, AutoderefKind},
|
||||
consteval::{self, ConstExt},
|
||||
db::HirDatabase,
|
||||
from_foreign_def_id,
|
||||
infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
|
||||
primitive::{self, FloatTy, IntTy, UintTy},
|
||||
static_lifetime,
|
||||
utils::all_super_traits,
|
||||
@ -429,28 +430,25 @@ pub fn def_crates(
|
||||
Some(res)
|
||||
}
|
||||
|
||||
/// Look up the method with the given name, returning the actual autoderefed
|
||||
/// receiver type (but without autoref applied yet).
|
||||
/// Look up the method with the given name.
|
||||
pub(crate) fn lookup_method(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: &Name,
|
||||
) -> Option<(Canonical<Ty>, FunctionId)> {
|
||||
) -> Option<(ReceiverAdjustments, FunctionId)> {
|
||||
iterate_method_candidates(
|
||||
ty,
|
||||
db,
|
||||
env,
|
||||
krate,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
Some(name),
|
||||
LookupMode::MethodCall,
|
||||
|ty, f| match f {
|
||||
AssocItemId::FunctionId(f) => Some((ty.clone(), f)),
|
||||
|adjustments, f| match f {
|
||||
AssocItemId::FunctionId(f) => Some((adjustments, f)),
|
||||
_ => None,
|
||||
},
|
||||
)
|
||||
@ -496,33 +494,89 @@ impl From<Option<BlockId>> for VisibleFromModule {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ReceiverAdjustments {
|
||||
autoref: Option<Mutability>,
|
||||
autoderefs: usize,
|
||||
unsize_array: bool,
|
||||
}
|
||||
|
||||
impl ReceiverAdjustments {
|
||||
pub(crate) fn apply(&self, table: &mut InferenceTable, ty: Ty) -> (Ty, Vec<Adjustment>) {
|
||||
let mut ty = ty;
|
||||
let mut adjust = Vec::new();
|
||||
for _ in 0..self.autoderefs {
|
||||
match autoderef::autoderef_step(table, ty.clone()) {
|
||||
None => {
|
||||
never!("autoderef not possible for {:?}", ty);
|
||||
ty = TyKind::Error.intern(Interner);
|
||||
break;
|
||||
}
|
||||
Some((kind, new_ty)) => {
|
||||
ty = new_ty.clone();
|
||||
adjust.push(Adjustment {
|
||||
kind: Adjust::Deref(match kind {
|
||||
// FIXME should we know the mutability here?
|
||||
AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
|
||||
AutoderefKind::Builtin => None,
|
||||
}),
|
||||
target: new_ty,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.unsize_array {
|
||||
ty = match ty.kind(Interner) {
|
||||
TyKind::Array(inner, _) => TyKind::Slice(inner.clone()).intern(Interner),
|
||||
_ => {
|
||||
never!("unsize_array with non-array {:?}", ty);
|
||||
ty
|
||||
}
|
||||
};
|
||||
// FIXME this is kind of wrong since the unsize needs to happen to a pointer/reference
|
||||
adjust.push(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCast::Unsize),
|
||||
target: ty.clone(),
|
||||
});
|
||||
}
|
||||
if let Some(m) = self.autoref {
|
||||
ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
|
||||
adjust
|
||||
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
|
||||
}
|
||||
(ty, adjust)
|
||||
}
|
||||
|
||||
fn with_autoref(&self, m: Mutability) -> ReceiverAdjustments {
|
||||
Self { autoref: Some(m), ..*self }
|
||||
}
|
||||
}
|
||||
|
||||
// This would be nicer if it just returned an iterator, but that runs into
|
||||
// lifetime problems, because we need to borrow temp `CrateImplDefs`.
|
||||
// FIXME add a context type here?
|
||||
pub fn iterate_method_candidates<T>(
|
||||
pub(crate) fn iterate_method_candidates<T>(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
mut callback: impl FnMut(&Canonical<Ty>, AssocItemId) -> Option<T>,
|
||||
mut callback: impl FnMut(ReceiverAdjustments, AssocItemId) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let mut slot = None;
|
||||
iterate_method_candidates_dyn(
|
||||
ty,
|
||||
db,
|
||||
env,
|
||||
krate,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
mode,
|
||||
&mut |ty, item| {
|
||||
&mut |adj, item| {
|
||||
assert!(slot.is_none());
|
||||
if let Some(it) = callback(ty, item) {
|
||||
if let Some(it) = callback(adj, item) {
|
||||
slot = Some(it);
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
@ -532,28 +586,45 @@ pub fn iterate_method_candidates<T>(
|
||||
slot
|
||||
}
|
||||
|
||||
pub fn iterate_path_candidates(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
iterate_method_candidates_dyn(
|
||||
ty,
|
||||
db,
|
||||
env,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
LookupMode::Path,
|
||||
// the adjustments are not relevant for path lookup
|
||||
&mut |_, id| callback(id),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn iterate_method_candidates_dyn(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
|
||||
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
match mode {
|
||||
LookupMode::MethodCall => {
|
||||
// For method calls, rust first does any number of autoderef, and then one
|
||||
// autoref (i.e. when the method takes &self or &mut self). We just ignore
|
||||
// the autoref currently -- when we find a method matching the given name,
|
||||
// we assume it fits.
|
||||
|
||||
// Also note that when we've got a receiver like &S, even if the method we
|
||||
// find in the end takes &self, we still do the autoderef step (just as
|
||||
// rustc does an autoderef and then autoref again).
|
||||
let ty = InEnvironment { goal: ty.clone(), environment: env.env.clone() };
|
||||
// For method calls, rust first does any number of autoderef, and
|
||||
// then one autoref (i.e. when the method takes &self or &mut self).
|
||||
// Note that when we've got a receiver like &S, even if the method
|
||||
// we find in the end takes &self, we still do the autoderef step
|
||||
// (just as rustc does an autoderef and then autoref again).
|
||||
|
||||
// We have to be careful about the order we're looking at candidates
|
||||
// in here. Consider the case where we're resolving `x.clone()`
|
||||
@ -568,29 +639,31 @@ pub fn iterate_method_candidates_dyn(
|
||||
// the methods by autoderef order of *receiver types*, not *self
|
||||
// types*.
|
||||
|
||||
let deref_chain = autoderef_method_receiver(db, krate, ty);
|
||||
let mut deref_chains = stdx::slice_tails(&deref_chain);
|
||||
let mut table = InferenceTable::new(db, env.clone());
|
||||
let ty = table.instantiate_canonical(ty.clone());
|
||||
let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty);
|
||||
let deref_chains = stdx::slice_tails(&deref_chain);
|
||||
|
||||
deref_chains.try_for_each(|deref_chain| {
|
||||
let result = deref_chains.zip(adj).try_for_each(|(deref_chain, adj)| {
|
||||
iterate_method_candidates_with_autoref(
|
||||
deref_chain,
|
||||
adj,
|
||||
db,
|
||||
env.clone(),
|
||||
krate,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
callback,
|
||||
)
|
||||
})
|
||||
});
|
||||
result
|
||||
}
|
||||
LookupMode::Path => {
|
||||
// No autoderef for path lookups
|
||||
iterate_method_candidates_for_self_ty(
|
||||
ty,
|
||||
db,
|
||||
env,
|
||||
krate,
|
||||
env.clone(),
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
@ -602,27 +675,27 @@ pub fn iterate_method_candidates_dyn(
|
||||
|
||||
fn iterate_method_candidates_with_autoref(
|
||||
deref_chain: &[Canonical<Ty>],
|
||||
first_adjustment: ReceiverAdjustments,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
|
||||
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
let (receiver_ty, rest) = match deref_chain.split_first() {
|
||||
Some((rec, rest)) => (rec.clone(), rest),
|
||||
Some((rec, rest)) => (rec, rest),
|
||||
None => {
|
||||
never!("received empty deref-chain");
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
};
|
||||
iterate_method_candidates_by_receiver(
|
||||
&receiver_ty,
|
||||
receiver_ty,
|
||||
first_adjustment.clone(),
|
||||
&rest,
|
||||
db,
|
||||
env.clone(),
|
||||
krate,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
@ -630,17 +703,17 @@ fn iterate_method_candidates_with_autoref(
|
||||
)?;
|
||||
|
||||
let refed = Canonical {
|
||||
binders: receiver_ty.binders.clone(),
|
||||
value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
|
||||
.intern(Interner),
|
||||
binders: receiver_ty.binders.clone(),
|
||||
};
|
||||
|
||||
iterate_method_candidates_by_receiver(
|
||||
&refed,
|
||||
first_adjustment.with_autoref(Mutability::Not),
|
||||
deref_chain,
|
||||
db,
|
||||
env.clone(),
|
||||
krate,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
@ -648,16 +721,17 @@ fn iterate_method_candidates_with_autoref(
|
||||
)?;
|
||||
|
||||
let ref_muted = Canonical {
|
||||
binders: receiver_ty.binders,
|
||||
value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value).intern(Interner),
|
||||
value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone())
|
||||
.intern(Interner),
|
||||
binders: receiver_ty.binders.clone(),
|
||||
};
|
||||
|
||||
iterate_method_candidates_by_receiver(
|
||||
&ref_muted,
|
||||
first_adjustment.with_autoref(Mutability::Mut),
|
||||
deref_chain,
|
||||
db,
|
||||
env,
|
||||
krate,
|
||||
env.clone(),
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
@ -667,14 +741,14 @@ fn iterate_method_candidates_with_autoref(
|
||||
|
||||
fn iterate_method_candidates_by_receiver(
|
||||
receiver_ty: &Canonical<Ty>,
|
||||
receiver_adjustments: ReceiverAdjustments,
|
||||
rest_of_deref_chain: &[Canonical<Ty>],
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
|
||||
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
// We're looking for methods with *receiver* type receiver_ty. These could
|
||||
// be found in any of the derefs of receiver_ty, so we have to go through
|
||||
@ -686,7 +760,7 @@ fn iterate_method_candidates_by_receiver(
|
||||
env.clone(),
|
||||
name,
|
||||
Some(receiver_ty),
|
||||
krate,
|
||||
Some(receiver_adjustments.clone()),
|
||||
visible_from_module,
|
||||
&mut callback,
|
||||
)?
|
||||
@ -697,10 +771,10 @@ fn iterate_method_candidates_by_receiver(
|
||||
self_ty,
|
||||
db,
|
||||
env.clone(),
|
||||
krate,
|
||||
traits_in_scope,
|
||||
name,
|
||||
Some(receiver_ty),
|
||||
Some(receiver_adjustments.clone()),
|
||||
&mut callback,
|
||||
)?
|
||||
}
|
||||
@ -712,11 +786,10 @@ fn iterate_method_candidates_for_self_ty(
|
||||
self_ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
|
||||
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
iterate_inherent_methods(
|
||||
self_ty,
|
||||
@ -724,24 +797,24 @@ fn iterate_method_candidates_for_self_ty(
|
||||
env.clone(),
|
||||
name,
|
||||
None,
|
||||
krate,
|
||||
None,
|
||||
visible_from_module,
|
||||
&mut callback,
|
||||
)?;
|
||||
iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback)
|
||||
iterate_trait_method_candidates(self_ty, db, env, traits_in_scope, name, None, None, callback)
|
||||
}
|
||||
|
||||
fn iterate_trait_method_candidates(
|
||||
self_ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
name: Option<&Name>,
|
||||
receiver_ty: Option<&Canonical<Ty>>,
|
||||
callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
|
||||
receiver_adjustments: Option<ReceiverAdjustments>,
|
||||
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
let receiver_is_array = matches!(self_ty.value.kind(Interner), chalk_ir::TyKind::Array(..));
|
||||
let self_is_array = matches!(self_ty.value.kind(Interner), chalk_ir::TyKind::Array(..));
|
||||
// if ty is `dyn Trait`, the trait doesn't need to be in scope
|
||||
let inherent_trait =
|
||||
self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
|
||||
@ -763,10 +836,10 @@ fn iterate_trait_method_candidates(
|
||||
// 2021.
|
||||
// This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for
|
||||
// arrays.
|
||||
if data.skip_array_during_method_dispatch && receiver_is_array {
|
||||
if data.skip_array_during_method_dispatch && self_is_array {
|
||||
// FIXME: this should really be using the edition of the method name's span, in case it
|
||||
// comes from a macro
|
||||
if db.crate_graph()[krate].edition < Edition::Edition2021 {
|
||||
if db.crate_graph()[env.krate].edition < Edition::Edition2021 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -782,14 +855,13 @@ fn iterate_trait_method_candidates(
|
||||
continue;
|
||||
}
|
||||
if !known_implemented {
|
||||
let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone());
|
||||
if db.trait_solve(krate, goal.cast(Interner)).is_none() {
|
||||
let goal = generic_implements_goal(db, env.clone(), t, self_ty);
|
||||
if db.trait_solve(env.krate, goal.cast(Interner)).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
known_implemented = true;
|
||||
// FIXME: we shouldn't be ignoring the binders here
|
||||
callback(self_ty, item)?
|
||||
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
@ -824,11 +896,11 @@ fn iterate_inherent_methods(
|
||||
env: Arc<TraitEnvironment>,
|
||||
name: Option<&Name>,
|
||||
receiver_ty: Option<&Canonical<Ty>>,
|
||||
krate: CrateId,
|
||||
receiver_adjustments: Option<ReceiverAdjustments>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
|
||||
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
let def_crates = match def_crates(db, &self_ty.value, krate) {
|
||||
let def_crates = match def_crates(db, &self_ty.value, env.krate) {
|
||||
Some(k) => k,
|
||||
None => return ControlFlow::Continue(()),
|
||||
};
|
||||
@ -848,6 +920,7 @@ fn iterate_inherent_methods(
|
||||
env.clone(),
|
||||
name,
|
||||
receiver_ty,
|
||||
receiver_adjustments.clone(),
|
||||
module,
|
||||
callback,
|
||||
)?;
|
||||
@ -856,7 +929,17 @@ fn iterate_inherent_methods(
|
||||
|
||||
for krate in def_crates {
|
||||
let impls = db.inherent_impls_in_crate(krate);
|
||||
impls_for_self_ty(&impls, self_ty, db, env.clone(), name, receiver_ty, module, callback)?;
|
||||
impls_for_self_ty(
|
||||
&impls,
|
||||
self_ty,
|
||||
db,
|
||||
env.clone(),
|
||||
name,
|
||||
receiver_ty,
|
||||
receiver_adjustments.clone(),
|
||||
module,
|
||||
callback,
|
||||
)?;
|
||||
}
|
||||
return ControlFlow::Continue(());
|
||||
|
||||
@ -867,8 +950,9 @@ fn iterate_inherent_methods(
|
||||
env: Arc<TraitEnvironment>,
|
||||
name: Option<&Name>,
|
||||
receiver_ty: Option<&Canonical<Ty>>,
|
||||
receiver_adjustments: Option<ReceiverAdjustments>,
|
||||
visible_from_module: Option<ModuleId>,
|
||||
callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
|
||||
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
|
||||
) -> ControlFlow<()> {
|
||||
let impls_for_self_ty = filter_inherent_impls_for_self_ty(impls, &self_ty.value);
|
||||
for &impl_def in impls_for_self_ty {
|
||||
@ -889,33 +973,32 @@ fn iterate_inherent_methods(
|
||||
// already happens in `is_valid_candidate` above; if not, we
|
||||
// check it here
|
||||
if receiver_ty.is_none()
|
||||
&& inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none()
|
||||
&& inherent_impl_substs(db, env.clone(), impl_def, &self_ty).is_none()
|
||||
{
|
||||
cov_mark::hit!(impl_self_type_match_without_receiver);
|
||||
continue;
|
||||
}
|
||||
let receiver_ty = receiver_ty.unwrap_or(self_ty);
|
||||
callback(receiver_ty, item)?;
|
||||
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the self type for the index trait call.
|
||||
/// Returns the receiver type for the index trait call.
|
||||
pub fn resolve_indexing_op(
|
||||
db: &dyn HirDatabase,
|
||||
ty: &Canonical<Ty>,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
ty: Canonical<Ty>,
|
||||
index_trait: TraitId,
|
||||
) -> Option<Canonical<Ty>> {
|
||||
let ty = InEnvironment { goal: ty.clone(), environment: env.env.clone() };
|
||||
let deref_chain = autoderef_method_receiver(db, krate, ty);
|
||||
for ty in deref_chain {
|
||||
let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone());
|
||||
if db.trait_solve(krate, goal.cast(Interner)).is_some() {
|
||||
return Some(ty);
|
||||
) -> Option<ReceiverAdjustments> {
|
||||
let mut table = InferenceTable::new(db, env.clone());
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty);
|
||||
for (ty, adj) in deref_chain.into_iter().zip(adj) {
|
||||
let goal = generic_implements_goal(db, env.clone(), index_trait, &ty);
|
||||
if db.trait_solve(env.krate, goal.cast(Interner)).is_some() {
|
||||
return Some(adj);
|
||||
}
|
||||
}
|
||||
None
|
||||
@ -1067,11 +1150,10 @@ pub fn implements_trait(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
trait_: TraitId,
|
||||
) -> bool {
|
||||
let goal = generic_implements_goal(db, env, trait_, ty.clone());
|
||||
let solution = db.trait_solve(krate, goal.cast(Interner));
|
||||
let goal = generic_implements_goal(db, env.clone(), trait_, &ty);
|
||||
let solution = db.trait_solve(env.krate, goal.cast(Interner));
|
||||
|
||||
solution.is_some()
|
||||
}
|
||||
@ -1080,11 +1162,10 @@ pub fn implements_trait_unique(
|
||||
ty: &Canonical<Ty>,
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
krate: CrateId,
|
||||
trait_: TraitId,
|
||||
) -> bool {
|
||||
let goal = generic_implements_goal(db, env, trait_, ty.clone());
|
||||
let solution = db.trait_solve(krate, goal.cast(Interner));
|
||||
let goal = generic_implements_goal(db, env.clone(), trait_, &ty);
|
||||
let solution = db.trait_solve(env.krate, goal.cast(Interner));
|
||||
|
||||
matches!(solution, Some(crate::Solution::Unique(_)))
|
||||
}
|
||||
@ -1095,11 +1176,11 @@ fn generic_implements_goal(
|
||||
db: &dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
trait_: TraitId,
|
||||
self_ty: Canonical<Ty>,
|
||||
self_ty: &Canonical<Ty>,
|
||||
) -> Canonical<InEnvironment<super::DomainGoal>> {
|
||||
let mut kinds = self_ty.binders.interned().to_vec();
|
||||
let trait_ref = TyBuilder::trait_ref(db, trait_)
|
||||
.push(self_ty.value)
|
||||
.push(self_ty.value.clone())
|
||||
.fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
|
||||
.build();
|
||||
kinds.extend(
|
||||
@ -1117,17 +1198,27 @@ fn generic_implements_goal(
|
||||
}
|
||||
|
||||
fn autoderef_method_receiver(
|
||||
db: &dyn HirDatabase,
|
||||
krate: CrateId,
|
||||
ty: InEnvironment<Canonical<Ty>>,
|
||||
) -> Vec<Canonical<Ty>> {
|
||||
let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect();
|
||||
// As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
|
||||
if let Some(TyKind::Array(parameters, _)) = deref_chain.last().map(|ty| ty.value.kind(Interner))
|
||||
{
|
||||
let kinds = deref_chain.last().unwrap().binders.clone();
|
||||
let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner);
|
||||
deref_chain.push(Canonical { value: unsized_ty, binders: kinds })
|
||||
table: &mut InferenceTable,
|
||||
ty: Ty,
|
||||
) -> (Vec<Canonical<Ty>>, Vec<ReceiverAdjustments>) {
|
||||
let (mut deref_chain, mut adjustments): (Vec<_>, Vec<_>) = (Vec::new(), Vec::new());
|
||||
let mut autoderef = autoderef::Autoderef::new(table, ty);
|
||||
while let Some((ty, derefs)) = autoderef.next() {
|
||||
deref_chain.push(autoderef.table.canonicalize(ty).value);
|
||||
adjustments.push(ReceiverAdjustments {
|
||||
autoref: None,
|
||||
autoderefs: derefs,
|
||||
unsize_array: false,
|
||||
});
|
||||
}
|
||||
deref_chain
|
||||
// As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
|
||||
if let (Some((TyKind::Array(parameters, _), binders)), Some(adj)) = (
|
||||
deref_chain.last().map(|ty| (ty.value.kind(Interner), ty.binders.clone())),
|
||||
adjustments.last().cloned(),
|
||||
) {
|
||||
let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner);
|
||||
deref_chain.push(Canonical { value: unsized_ty, binders });
|
||||
adjustments.push(ReceiverAdjustments { unsize_array: true, ..adj });
|
||||
}
|
||||
(deref_chain, adjustments)
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
||||
.trim_start_matches("adjustments: ")
|
||||
.split(',')
|
||||
.map(|it| it.trim().to_string())
|
||||
.filter(|it| !it.is_empty())
|
||||
.collect(),
|
||||
);
|
||||
} else {
|
||||
|
@ -242,6 +242,45 @@ fn test() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_autoderef_implication_1() {
|
||||
check_no_mismatches(
|
||||
r"
|
||||
//- minicore: deref
|
||||
struct Foo<T>;
|
||||
impl core::ops::Deref for Foo<u32> { type Target = (); }
|
||||
|
||||
fn takes_ref_foo<T>(x: &Foo<T>) {}
|
||||
fn test() {
|
||||
let foo = Foo;
|
||||
//^^^ type: Foo<{unknown}>
|
||||
takes_ref_foo(&foo);
|
||||
|
||||
let foo = Foo;
|
||||
//^^^ type: Foo<u32>
|
||||
let _: &() = &foo;
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_autoderef_implication_2() {
|
||||
check(
|
||||
r"
|
||||
//- minicore: deref
|
||||
struct Foo<T>;
|
||||
impl core::ops::Deref for Foo<u32> { type Target = (); }
|
||||
|
||||
fn takes_ref_foo<T>(x: &Foo<T>) {}
|
||||
fn test() {
|
||||
let foo = Foo;
|
||||
//^^^ type: Foo<{unknown}>
|
||||
let _: &u32 = &Foo;
|
||||
//^^^^ expected &u32, got &Foo<{unknown}>
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closure_return_coerce() {
|
||||
check_no_mismatches(
|
||||
|
@ -1460,3 +1460,121 @@ fn main() {
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deref_fun_1() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
|
||||
struct A<T, U>(T, U);
|
||||
struct B<T>(T);
|
||||
struct C<T>(T);
|
||||
|
||||
impl<T> core::ops::Deref for A<B<T>, u32> {
|
||||
type Target = B<T>;
|
||||
fn deref(&self) -> &B<T> { &self.0 }
|
||||
}
|
||||
impl core::ops::Deref for B<isize> {
|
||||
type Target = C<isize>;
|
||||
fn deref(&self) -> &C<isize> { loop {} }
|
||||
}
|
||||
|
||||
impl<T: Copy> C<T> {
|
||||
fn thing(&self) -> T { self.0 }
|
||||
}
|
||||
|
||||
fn make<T>() -> T { loop {} }
|
||||
|
||||
fn test() {
|
||||
let a1 = A(make(), make());
|
||||
let _: usize = (*a1).0;
|
||||
a1;
|
||||
//^^ A<B<usize>, u32>
|
||||
|
||||
let a2 = A(make(), make());
|
||||
a2.thing();
|
||||
//^^^^^^^^^^ isize
|
||||
a2;
|
||||
//^^ A<B<isize>, u32>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deref_fun_2() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
|
||||
struct A<T, U>(T, U);
|
||||
struct B<T>(T);
|
||||
struct C<T>(T);
|
||||
|
||||
impl<T> core::ops::Deref for A<B<T>, u32> {
|
||||
type Target = B<T>;
|
||||
fn deref(&self) -> &B<T> { &self.0 }
|
||||
}
|
||||
impl core::ops::Deref for B<isize> {
|
||||
type Target = C<isize>;
|
||||
fn deref(&self) -> &C<isize> { loop {} }
|
||||
}
|
||||
|
||||
impl<T> core::ops::Deref for A<C<T>, i32> {
|
||||
type Target = C<T>;
|
||||
fn deref(&self) -> &C<T> { &self.0 }
|
||||
}
|
||||
|
||||
impl<T: Copy> C<T> {
|
||||
fn thing(&self) -> T { self.0 }
|
||||
}
|
||||
|
||||
fn make<T>() -> T { loop {} }
|
||||
|
||||
fn test() {
|
||||
let a1 = A(make(), 1u32);
|
||||
a1.thing();
|
||||
a1;
|
||||
//^^ A<B<isize>, u32>
|
||||
|
||||
let a2 = A(make(), 1i32);
|
||||
let _: &str = a2.thing();
|
||||
a2;
|
||||
//^^ A<C<&str>, i32>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receiver_adjustment_autoref() {
|
||||
check(
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn foo(&self) {}
|
||||
}
|
||||
fn test() {
|
||||
Foo.foo();
|
||||
//^^^ adjustments: Borrow(Ref(Not))
|
||||
(&Foo).foo();
|
||||
// ^^^^ adjustments: ,
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receiver_adjustment_unsize_array() {
|
||||
// FIXME not quite correct
|
||||
check(
|
||||
r#"
|
||||
//- minicore: slice
|
||||
fn test() {
|
||||
let a = [1, 2, 3];
|
||||
a.len();
|
||||
} //^ adjustments: Pointer(Unsize), Borrow(Ref(Not))
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
@ -540,6 +540,52 @@ fn test() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_ops_index_field() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: index
|
||||
struct Bar;
|
||||
struct Foo {
|
||||
field: u32;
|
||||
}
|
||||
|
||||
impl core::ops::Index<u32> for Bar {
|
||||
type Output = Foo;
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let a = Bar;
|
||||
let b = a[1u32].field;
|
||||
b;
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_ops_index_field_autoderef() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: index
|
||||
struct Bar;
|
||||
struct Foo {
|
||||
field: u32;
|
||||
}
|
||||
|
||||
impl core::ops::Index<u32> for Bar {
|
||||
type Output = Foo;
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let a = Bar;
|
||||
let b = (&a[1u32]).field;
|
||||
b;
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_ops_index_int() {
|
||||
check_types(
|
||||
|
@ -40,8 +40,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TraitEnvironment {
|
||||
pub krate: CrateId,
|
||||
// When we're using Chalk's Ty we can make this a BTreeMap since it's Ord,
|
||||
// but for now it's too annoying...
|
||||
// FIXME make this a BTreeMap
|
||||
pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>,
|
||||
pub env: chalk_ir::Environment<Interner>,
|
||||
}
|
||||
|
@ -154,11 +154,11 @@ fn is_ref_and_impls_iter_method(
|
||||
let has_wanted_method = ty
|
||||
.iterate_method_candidates(
|
||||
sema.db,
|
||||
krate,
|
||||
&scope,
|
||||
&traits_in_scope,
|
||||
None,
|
||||
Some(&wanted_method),
|
||||
|_, func| {
|
||||
|func| {
|
||||
if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
|
||||
return Some(());
|
||||
}
|
||||
|
@ -371,7 +371,7 @@ fn handle_ref_field_usage(ctx: &AssistContext, field_expr: &FieldExpr) -> RefDat
|
||||
fn is_auto_ref(ctx: &AssistContext, call_expr: &MethodCallExpr) -> bool {
|
||||
fn impl_(ctx: &AssistContext, call_expr: &MethodCallExpr) -> Option<bool> {
|
||||
let rec = call_expr.receiver()?;
|
||||
let rec_ty = ctx.sema.type_of_expr(&rec)?.adjusted();
|
||||
let rec_ty = ctx.sema.type_of_expr(&rec)?.original();
|
||||
// input must be actual value
|
||||
if rec_ty.is_reference() {
|
||||
return Some(false);
|
||||
|
@ -90,10 +90,9 @@ fn get_impl_method(
|
||||
let impl_def: hir::Impl = ctx.sema.to_def(impl_)?;
|
||||
|
||||
let scope = ctx.sema.scope(impl_.syntax());
|
||||
let krate = impl_def.module(db).krate();
|
||||
let ty = impl_def.self_ty(db);
|
||||
let traits_in_scope = scope.visible_traits();
|
||||
ty.iterate_method_candidates(db, krate, &traits_in_scope, None, Some(fn_name), |_, func| {
|
||||
ty.iterate_method_candidates(db, &scope, &traits_in_scope, None, Some(fn_name), |func| {
|
||||
Some(func)
|
||||
})
|
||||
}
|
||||
|
@ -74,30 +74,28 @@ fn complete_methods(
|
||||
receiver: &hir::Type,
|
||||
mut f: impl FnMut(hir::Function),
|
||||
) {
|
||||
if let Some(krate) = ctx.krate {
|
||||
let mut seen_methods = FxHashSet::default();
|
||||
let mut traits_in_scope = ctx.scope.visible_traits();
|
||||
let mut seen_methods = FxHashSet::default();
|
||||
let mut traits_in_scope = ctx.scope.visible_traits();
|
||||
|
||||
// Remove drop from the environment as calling `Drop::drop` is not allowed
|
||||
if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
|
||||
cov_mark::hit!(dot_remove_drop_trait);
|
||||
traits_in_scope.remove(&drop_trait.into());
|
||||
}
|
||||
|
||||
receiver.iterate_method_candidates(
|
||||
ctx.db,
|
||||
krate,
|
||||
&traits_in_scope,
|
||||
ctx.module,
|
||||
None,
|
||||
|_ty, func| {
|
||||
if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) {
|
||||
f(func);
|
||||
}
|
||||
None::<()>
|
||||
},
|
||||
);
|
||||
// Remove drop from the environment as calling `Drop::drop` is not allowed
|
||||
if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() {
|
||||
cov_mark::hit!(dot_remove_drop_trait);
|
||||
traits_in_scope.remove(&drop_trait.into());
|
||||
}
|
||||
|
||||
receiver.iterate_method_candidates(
|
||||
ctx.db,
|
||||
&ctx.scope,
|
||||
&traits_in_scope,
|
||||
ctx.module,
|
||||
None,
|
||||
|func| {
|
||||
if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) {
|
||||
f(func);
|
||||
}
|
||||
None::<()>
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -134,39 +134,37 @@ fn pattern_path_completion(
|
||||
.for_each(|variant| acc.add_enum_variant(ctx, variant, None));
|
||||
}
|
||||
res @ (hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_)) => {
|
||||
if let Some(krate) = ctx.krate {
|
||||
let ty = match res {
|
||||
hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
|
||||
hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
|
||||
_ => return,
|
||||
};
|
||||
let ty = match res {
|
||||
hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
|
||||
hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
|
||||
e.variants(ctx.db)
|
||||
.into_iter()
|
||||
.for_each(|variant| acc.add_enum_variant(ctx, variant, None));
|
||||
}
|
||||
|
||||
let traits_in_scope = ctx.scope.visible_traits();
|
||||
let mut seen = FxHashSet::default();
|
||||
ty.iterate_path_candidates(
|
||||
ctx.db,
|
||||
krate,
|
||||
&traits_in_scope,
|
||||
ctx.module,
|
||||
None,
|
||||
|_ty, item| {
|
||||
// Note associated consts cannot be referenced in patterns
|
||||
if let AssocItem::TypeAlias(ta) = item {
|
||||
// We might iterate candidates of a trait multiple times here, so deduplicate them.
|
||||
if seen.insert(item) {
|
||||
acc.add_type_alias(ctx, ta);
|
||||
}
|
||||
}
|
||||
None::<()>
|
||||
},
|
||||
);
|
||||
if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
|
||||
e.variants(ctx.db)
|
||||
.into_iter()
|
||||
.for_each(|variant| acc.add_enum_variant(ctx, variant, None));
|
||||
}
|
||||
|
||||
let traits_in_scope = ctx.scope.visible_traits();
|
||||
let mut seen = FxHashSet::default();
|
||||
ty.iterate_path_candidates(
|
||||
ctx.db,
|
||||
&ctx.scope,
|
||||
&traits_in_scope,
|
||||
ctx.module,
|
||||
None,
|
||||
|item| {
|
||||
// Note associated consts cannot be referenced in patterns
|
||||
if let AssocItem::TypeAlias(ta) = item {
|
||||
// We might iterate candidates of a trait multiple times here, so deduplicate them.
|
||||
if seen.insert(item) {
|
||||
acc.add_type_alias(ctx, ta);
|
||||
}
|
||||
}
|
||||
None::<()>
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -138,11 +138,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||
let traits_in_scope = ctx.scope.visible_traits();
|
||||
ty.iterate_path_candidates(
|
||||
ctx.db,
|
||||
krate,
|
||||
&ctx.scope,
|
||||
&traits_in_scope,
|
||||
ctx.module,
|
||||
None,
|
||||
|_ty, item| {
|
||||
|item| {
|
||||
add_assoc_item(acc, ctx, item);
|
||||
None::<()>
|
||||
},
|
||||
@ -164,35 +164,33 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
|
||||
}
|
||||
}
|
||||
hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => {
|
||||
if let Some(krate) = ctx.krate {
|
||||
let ty = match resolution {
|
||||
hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
|
||||
hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
|
||||
_ => return,
|
||||
};
|
||||
let ty = match resolution {
|
||||
hir::PathResolution::TypeParam(param) => param.ty(ctx.db),
|
||||
hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
|
||||
add_enum_variants(acc, ctx, e);
|
||||
}
|
||||
|
||||
let traits_in_scope = ctx.scope.visible_traits();
|
||||
let mut seen = FxHashSet::default();
|
||||
ty.iterate_path_candidates(
|
||||
ctx.db,
|
||||
krate,
|
||||
&traits_in_scope,
|
||||
ctx.module,
|
||||
None,
|
||||
|_ty, item| {
|
||||
// We might iterate candidates of a trait multiple times here, so deduplicate
|
||||
// them.
|
||||
if seen.insert(item) {
|
||||
add_assoc_item(acc, ctx, item);
|
||||
}
|
||||
None::<()>
|
||||
},
|
||||
);
|
||||
if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
|
||||
add_enum_variants(acc, ctx, e);
|
||||
}
|
||||
|
||||
let traits_in_scope = ctx.scope.visible_traits();
|
||||
let mut seen = FxHashSet::default();
|
||||
ty.iterate_path_candidates(
|
||||
ctx.db,
|
||||
&ctx.scope,
|
||||
&traits_in_scope,
|
||||
ctx.module,
|
||||
None,
|
||||
|item| {
|
||||
// We might iterate candidates of a trait multiple times here, so deduplicate
|
||||
// them.
|
||||
if seen.insert(item) {
|
||||
add_assoc_item(acc, ctx, item);
|
||||
}
|
||||
None::<()>
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Look up accessible paths for items.
|
||||
use hir::{
|
||||
AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
|
||||
ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type,
|
||||
ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
@ -239,7 +239,6 @@ impl ImportAssets {
|
||||
let _p = profile::span("import_assets::search_for");
|
||||
|
||||
let scope_definitions = self.scope_definitions(sema);
|
||||
let current_crate = self.module_with_candidate.krate();
|
||||
let mod_path = |item| {
|
||||
get_mod_path(
|
||||
sema.db,
|
||||
@ -249,15 +248,18 @@ impl ImportAssets {
|
||||
)
|
||||
};
|
||||
|
||||
let krate = self.module_with_candidate.krate();
|
||||
let scope = sema.scope(&self.candidate_node);
|
||||
|
||||
match &self.import_candidate {
|
||||
ImportCandidate::Path(path_candidate) => {
|
||||
path_applicable_imports(sema, current_crate, path_candidate, mod_path)
|
||||
path_applicable_imports(sema, krate, path_candidate, mod_path)
|
||||
}
|
||||
ImportCandidate::TraitAssocItem(trait_candidate) => {
|
||||
trait_applicable_items(sema, current_crate, trait_candidate, true, mod_path)
|
||||
trait_applicable_items(sema, krate, &scope, trait_candidate, true, mod_path)
|
||||
}
|
||||
ImportCandidate::TraitMethod(trait_candidate) => {
|
||||
trait_applicable_items(sema, current_crate, trait_candidate, false, mod_path)
|
||||
trait_applicable_items(sema, krate, &scope, trait_candidate, false, mod_path)
|
||||
}
|
||||
}
|
||||
.into_iter()
|
||||
@ -447,6 +449,7 @@ fn module_with_segment_name(
|
||||
fn trait_applicable_items(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
current_crate: Crate,
|
||||
scope: &SemanticsScope,
|
||||
trait_candidate: &TraitImportCandidate,
|
||||
trait_assoc_item: bool,
|
||||
mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
|
||||
@ -484,11 +487,11 @@ fn trait_applicable_items(
|
||||
if trait_assoc_item {
|
||||
trait_candidate.receiver_ty.iterate_path_candidates(
|
||||
db,
|
||||
current_crate,
|
||||
scope,
|
||||
&trait_candidates,
|
||||
None,
|
||||
None,
|
||||
|_, assoc| {
|
||||
|assoc| {
|
||||
if required_assoc_items.contains(&assoc) {
|
||||
if let AssocItem::Function(f) = assoc {
|
||||
if f.self_param(db).is_some() {
|
||||
@ -511,11 +514,11 @@ fn trait_applicable_items(
|
||||
} else {
|
||||
trait_candidate.receiver_ty.iterate_method_candidates(
|
||||
db,
|
||||
current_crate,
|
||||
scope,
|
||||
&trait_candidates,
|
||||
None,
|
||||
None,
|
||||
|_, function| {
|
||||
|function| {
|
||||
let assoc = function.as_assoc_item(db)?;
|
||||
if required_assoc_items.contains(&assoc) {
|
||||
let located_trait = assoc.containing_trait(db)?;
|
||||
|
@ -222,11 +222,11 @@ impl<'db> ResolutionScope<'db> {
|
||||
let module = self.scope.module()?;
|
||||
adt.ty(self.scope.db).iterate_path_candidates(
|
||||
self.scope.db,
|
||||
module.krate(),
|
||||
&self.scope,
|
||||
&self.scope.visible_traits(),
|
||||
Some(module),
|
||||
None,
|
||||
|_ty, assoc_item| {
|
||||
|assoc_item| {
|
||||
let item_name = assoc_item.name(self.scope.db)?;
|
||||
if item_name.to_smol_str().as_str() == name.text() {
|
||||
Some(hir::PathResolution::AssocItem(assoc_item))
|
||||
|
Loading…
Reference in New Issue
Block a user