Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir/src/lib.rs92
-rw-r--r--crates/hir/src/semantics.rs4
-rw-r--r--crates/hir_ty/src/autoderef.rs220
-rw-r--r--crates/hir_ty/src/infer.rs16
-rw-r--r--crates/hir_ty/src/infer/coerce.rs53
-rw-r--r--crates/hir_ty/src/infer/expr.rs269
-rw-r--r--crates/hir_ty/src/infer/path.rs2
-rw-r--r--crates/hir_ty/src/infer/unify.rs98
-rw-r--r--crates/hir_ty/src/method_resolution.rs283
-rw-r--r--crates/hir_ty/src/tests.rs1
-rw-r--r--crates/hir_ty/src/tests/coercion.rs39
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs118
-rw-r--r--crates/hir_ty/src/tests/traits.rs46
-rw-r--r--crates/hir_ty/src/traits.rs3
-rw-r--r--crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs4
-rw-r--r--crates/ide_assists/src/handlers/destructure_tuple_binding.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs3
-rw-r--r--crates/ide_completion/src/completions/dot.rs46
-rw-r--r--crates/ide_completion/src/completions/pattern.rs58
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs56
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs21
-rw-r--r--crates/ide_ssr/src/resolving.rs4
22 files changed, 775 insertions, 663 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index bd1e8278d5..54f35cf0a6 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -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),
);
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 20c360e302..2e0dbf82b7 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -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;
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs
index 6266554ecf..dffb36b5de 100644
--- a/crates/hir_ty/src/autoderef.rs
+++ b/crates/hir_ty/src/autoderef.rs
@@ -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),
- };
-
- 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
- }
- }
-}
+ table.register_obligation(implements_goal);
-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))
}
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 0a50df493f..c84604d69b 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -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);
}
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index bddb79c501..c24772f29b 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -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()
+}
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index b08a9618d8..c305af2e91 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -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 {
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index b63ef2ffdc..0d6c8f12d2 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -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),
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index bb7cdb677e..21b48b9d80 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -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> {
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index c91b6f2e82..44ece57a8e 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -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();
+ 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,
+ });
+ }
// 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();
+ 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: kinds })
+ deref_chain.push(Canonical { value: unsized_ty, binders });
+ adjustments.push(ReceiverAdjustments { unsize_array: true, ..adj });
}
- deref_chain
+ (deref_chain, adjustments)
}
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 29250dca00..7385da5662 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -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 {
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index dd3b86f050..c0dddb608e 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -243,6 +243,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(
r"
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 9c0c00da3b..c118ae24cf 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -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))
+"#,
+ );
+}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index c2669646e2..04d8b91e35 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -541,6 +541,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(
r#"
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs
index bd280ba774..b139edbee9 100644
--- a/crates/hir_ty/src/traits.rs
+++ b/crates/hir_ty/src/traits.rs
@@ -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>,
}
diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
index 0d2daa8dc3..4a4ad984db 100644
--- a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -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(());
}
diff --git a/crates/ide_assists/src/handlers/destructure_tuple_binding.rs b/crates/ide_assists/src/handlers/destructure_tuple_binding.rs
index 5f361f01dc..ac17e3f057 100644
--- a/crates/ide_assists/src/handlers/destructure_tuple_binding.rs
+++ b/crates/ide_assists/src/handlers/destructure_tuple_binding.rs
@@ -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);
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
index cb3fbed219..db0cfbe2ec 100644
--- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -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)
})
}
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 3bb3f883cd..981e25b55c 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -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();
-
- // 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::<()>
- },
- );
- }
+ 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,
+ &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)]
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index f1b4fa7205..c8a9cf21da 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -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));
- }
+ 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);
- }
+ 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::<()>
- },
- );
- }
+ }
+ None::<()>
+ },
+ );
}
_ => {}
}
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index cf78f7c1ad..d63aacbadb 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -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,
- };
-
- if let Some(hir::Adt::Enum(e)) = ty.as_adt() {
- add_enum_variants(acc, ctx, e);
- }
+ 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 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::<()>
+ },
+ );
}
_ => {}
}
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 319a217352..d91627e0a9 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -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)?;
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs
index 844b19779a..e15cb874ef 100644
--- a/crates/ide_ssr/src/resolving.rs
+++ b/crates/ide_ssr/src/resolving.rs
@@ -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))