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:
bors[bot] 2022-02-25 16:11:22 +00:00 committed by GitHub
commit ab896e38e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 775 additions and 663 deletions

View File

@ -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),
);
}

View File

@ -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;

View File

@ -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))
}

View File

@ -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);
}

View File

@ -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()
}

View File

@ -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, &parameters),
)
} 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, &parameters),
)
} 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, &parameters);
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 {

View File

@ -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),

View File

@ -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> {

View File

@ -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)
}

View File

@ -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 {

View File

@ -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(

View File

@ -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))
"#,
);
}

View File

@ -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(

View File

@ -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>,
}

View File

@ -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(());
}

View File

@ -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);

View File

@ -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)
})
}

View File

@ -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)]

View File

@ -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::<()>
},
);
}
_ => {}
}

View File

@ -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::<()>
},
);
}
_ => {}
}

View File

@ -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)?;

View File

@ -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))