Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer.rs')
-rw-r--r--crates/hir-ty/src/infer.rs235
1 files changed, 157 insertions, 78 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 016edb2310..02b8ab8cdd 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -21,9 +21,11 @@ pub(crate) mod diagnostics;
mod expr;
mod fallback;
mod mutability;
+mod op;
mod opaques;
mod pat;
mod path;
+mod place_op;
pub(crate) mod unify;
use std::{cell::OnceCell, convert::identity, iter, ops::Index};
@@ -45,12 +47,14 @@ use hir_expand::{mod_path::ModPath, name::Name};
use indexmap::IndexSet;
use intern::sym;
use la_arena::ArenaMap;
+use macros::{TypeFoldable, TypeVisitable};
use rustc_ast_ir::Mutability;
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_type_ir::{
AliasTyKind, TypeFoldable,
inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _},
};
+use span::Edition;
use stdx::never;
use triomphe::Arc;
@@ -65,10 +69,13 @@ use crate::{
lower::{
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
},
+ method_resolution::{CandidateId, MethodResolutionUnstableFeatures},
mir::MirSpan,
next_solver::{
AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, Ty, TyKind,
- Tys, abi::Safety, infer::traits::ObligationCause,
+ Tys,
+ abi::Safety,
+ infer::{InferCtxt, traits::ObligationCause},
},
traits::FnTrait,
utils::TargetFeatureIsSafeInTarget,
@@ -330,16 +337,21 @@ pub struct TypeMismatch<'db> {
/// At some point, of course, `Box` should move out of the compiler, in which
/// case this is analogous to transforming a struct. E.g., Box<[i32; 4]> ->
/// Box<[i32]> is an `Adjust::Unsize` with the target `Box<[i32]>`.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub struct Adjustment<'db> {
- pub kind: Adjust<'db>,
+ #[type_visitable(ignore)]
+ #[type_foldable(identity)]
+ pub kind: Adjust,
pub target: Ty<'db>,
}
impl<'db> Adjustment<'db> {
pub fn borrow(interner: DbInterner<'db>, m: Mutability, ty: Ty<'db>, lt: Region<'db>) -> Self {
let ty = Ty::new_ref(interner, lt, ty, m);
- Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(lt, m)), target: ty }
+ Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::new(m, AllowTwoPhase::No))),
+ target: ty,
+ }
}
}
@@ -357,20 +369,20 @@ impl<'db> Adjustment<'db> {
/// capable mutable borrows.
/// See #49434 for tracking.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub(crate) enum AllowTwoPhase {
+pub enum AllowTwoPhase {
// FIXME: We should use this when appropriate.
Yes,
No,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub enum Adjust<'db> {
+pub enum Adjust {
/// Go from ! to any type.
NeverToAny,
/// Dereference once, producing a place.
Deref(Option<OverloadedDeref>),
/// Take the address and produce either a `&` or `*` pointer.
- Borrow(AutoBorrow<'db>),
+ Borrow(AutoBorrow),
Pointer(PointerCast),
}
@@ -381,18 +393,47 @@ pub enum Adjust<'db> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct OverloadedDeref(pub Option<Mutability>);
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub enum AutoBorrow<'db> {
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum AutoBorrowMutability {
+ Mut { allow_two_phase_borrow: AllowTwoPhase },
+ Not,
+}
+
+impl AutoBorrowMutability {
+ /// Creates an `AutoBorrowMutability` from a mutability and allowance of two phase borrows.
+ ///
+ /// Note that when `mutbl.is_not()`, `allow_two_phase_borrow` is ignored
+ pub fn new(mutbl: Mutability, allow_two_phase_borrow: AllowTwoPhase) -> Self {
+ match mutbl {
+ Mutability::Not => Self::Not,
+ Mutability::Mut => Self::Mut { allow_two_phase_borrow },
+ }
+ }
+}
+
+impl From<AutoBorrowMutability> for Mutability {
+ fn from(m: AutoBorrowMutability) -> Self {
+ match m {
+ AutoBorrowMutability::Mut { .. } => Mutability::Mut,
+ AutoBorrowMutability::Not => Mutability::Not,
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum AutoBorrow {
/// Converts from T to &T.
- Ref(Region<'db>, Mutability),
+ Ref(AutoBorrowMutability),
/// Converts from T to *T.
RawPtr(Mutability),
}
-impl<'db> AutoBorrow<'db> {
- fn mutability(&self) -> Mutability {
- let (AutoBorrow::Ref(_, m) | AutoBorrow::RawPtr(m)) = self;
- *m
+impl AutoBorrow {
+ fn mutability(self) -> Mutability {
+ match self {
+ AutoBorrow::Ref(mutbl) => mutbl.into(),
+ AutoBorrow::RawPtr(mutbl) => mutbl,
+ }
}
}
@@ -442,7 +483,7 @@ pub struct InferenceResult<'db> {
/// For each struct literal or pattern, records the variant it resolves to.
variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
/// For each associated item record what it resolves to
- assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, GenericArgs<'db>)>,
+ assoc_resolutions: FxHashMap<ExprOrPatId, (CandidateId, GenericArgs<'db>)>,
/// Whenever a tuple field expression access a tuple field, we allocate a tuple id in
/// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of
/// that which allows us to resolve a [`TupleFieldId`]s type.
@@ -457,7 +498,7 @@ pub struct InferenceResult<'db> {
pub(crate) type_of_pat: ArenaMap<PatId, Ty<'db>>,
pub(crate) type_of_binding: ArenaMap<BindingId, Ty<'db>>,
pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, Ty<'db>>,
- type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>,
+ pub(crate) type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>,
/// Whether there are any type-mismatching errors in the result.
// FIXME: This isn't as useful as initially thought due to us falling back placeholders to
// `TyKind::Error`.
@@ -535,16 +576,16 @@ impl<'db> InferenceResult<'db> {
pub fn assoc_resolutions_for_expr(
&self,
id: ExprId,
- ) -> Option<(AssocItemId, GenericArgs<'db>)> {
+ ) -> Option<(CandidateId, GenericArgs<'db>)> {
self.assoc_resolutions.get(&id.into()).copied()
}
- pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, GenericArgs<'db>)> {
+ pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(CandidateId, GenericArgs<'db>)> {
self.assoc_resolutions.get(&id.into()).copied()
}
pub fn assoc_resolutions_for_expr_or_pat(
&self,
id: ExprOrPatId,
- ) -> Option<(AssocItemId, GenericArgs<'db>)> {
+ ) -> Option<(CandidateId, GenericArgs<'db>)> {
match id {
ExprOrPatId::ExprId(id) => self.assoc_resolutions_for_expr(id),
ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id),
@@ -718,7 +759,6 @@ struct InternedStandardTypes<'db> {
re_erased: Region<'db>,
empty_args: GenericArgs<'db>,
- empty_tys: Tys<'db>,
}
impl<'db> InternedStandardTypes<'db> {
@@ -754,7 +794,6 @@ impl<'db> InternedStandardTypes<'db> {
re_erased: Region::new_erased(interner),
empty_args: GenericArgs::new_from_iter(interner, []),
- empty_tys: Tys::new_from_iter(interner, []),
}
}
}
@@ -769,8 +808,10 @@ pub(crate) struct InferenceContext<'body, 'db> {
/// and resolve the path via its methods. This will ensure proper error reporting.
pub(crate) resolver: Resolver<'db>,
target_features: OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>,
+ pub(crate) unstable_features: MethodResolutionUnstableFeatures,
+ pub(crate) edition: Edition,
pub(crate) generic_def: GenericDefId,
- table: unify::InferenceTable<'db>,
+ pub(crate) table: unify::InferenceTable<'db>,
/// The traits in scope, disregarding block modules. This is used for caching purposes.
traits_in_scope: FxHashSet<TraitId>,
pub(crate) result: InferenceResult<'db>,
@@ -873,6 +914,10 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
return_ty: types.error, // set in collect_* calls
types,
target_features: OnceCell::new(),
+ unstable_features: MethodResolutionUnstableFeatures::from_def_map(
+ resolver.top_level_def_map(),
+ ),
+ edition: resolver.krate().data(db).edition,
table,
tuple_field_accesses_rev: Default::default(),
resume_yield_tys: None,
@@ -906,18 +951,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.resolver.krate()
}
- fn target_features<'a>(
- db: &dyn HirDatabase,
- target_features: &'a OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>,
- owner: DefWithBodyId,
- krate: Crate,
- ) -> (&'a TargetFeatures, TargetFeatureIsSafeInTarget) {
- let (target_features, target_feature_is_safe) = target_features.get_or_init(|| {
- let target_features = match owner {
- DefWithBodyId::FunctionId(id) => TargetFeatures::from_attrs(&db.attrs(id.into())),
+ fn target_features(&self) -> (&TargetFeatures, TargetFeatureIsSafeInTarget) {
+ let (target_features, target_feature_is_safe) = self.target_features.get_or_init(|| {
+ let target_features = match self.owner {
+ DefWithBodyId::FunctionId(id) => {
+ TargetFeatures::from_attrs(&self.db.attrs(id.into()))
+ }
_ => TargetFeatures::default(),
};
- let target_feature_is_safe = match &krate.workspace_data(db).target {
+ let target_feature_is_safe = match &self.krate().workspace_data(self.db).target {
Ok(target) => crate::utils::target_feature_is_safe_in_target(target),
Err(_) => TargetFeatureIsSafeInTarget::No,
};
@@ -927,7 +969,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
#[inline]
- pub(crate) fn set_tainted_by_errors(&mut self) {
+ fn set_tainted_by_errors(&mut self) {
self.result.has_errors = true;
}
@@ -1162,6 +1204,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.table.interner()
}
+ #[inline]
+ pub(crate) fn infcx(&self) -> &InferCtxt<'db> {
+ &self.table.infer_ctxt
+ }
+
fn infer_body(&mut self) {
match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr),
@@ -1179,7 +1226,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.result.type_of_expr.insert(expr, ty);
}
- fn write_expr_adj(&mut self, expr: ExprId, adjustments: Box<[Adjustment<'db>]>) {
+ pub(crate) fn write_expr_adj(&mut self, expr: ExprId, adjustments: Box<[Adjustment<'db>]>) {
if adjustments.is_empty() {
return;
}
@@ -1212,7 +1259,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.result.pat_adjustments.entry(pat).or_default().extend(adjustments);
}
- fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: GenericArgs<'db>) {
+ pub(crate) fn write_method_resolution(
+ &mut self,
+ expr: ExprId,
+ func: FunctionId,
+ subst: GenericArgs<'db>,
+ ) {
self.result.method_resolutions.insert(expr, (func, subst));
}
@@ -1223,7 +1275,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
fn write_assoc_resolution(
&mut self,
id: ExprOrPatId,
- item: AssocItemId,
+ item: CandidateId,
subs: GenericArgs<'db>,
) {
self.result.assoc_resolutions.insert(id, (item, subs));
@@ -1237,7 +1289,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.result.type_of_binding.insert(id, ty);
}
- fn push_diagnostic(&self, diagnostic: InferenceDiagnostic<'db>) {
+ pub(crate) fn push_diagnostic(&self, diagnostic: InferenceDiagnostic<'db>) {
self.diagnostics.push(diagnostic);
}
@@ -1284,7 +1336,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.process_user_written_ty(ty)
}
- fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> {
+ pub(crate) fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> {
self.make_ty(
type_ref,
self.body,
@@ -1293,7 +1345,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
)
}
- fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty<'db>) -> Const<'db> {
+ pub(crate) fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty<'db>) -> Const<'db> {
let const_ = self.with_ty_lowering(
self.body,
InferenceTyDiagnosticSource::Body,
@@ -1303,7 +1355,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.insert_type_vars(const_)
}
- fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> {
+ pub(crate) fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> {
let const_ = self.with_ty_lowering(
self.body,
InferenceTyDiagnosticSource::Body,
@@ -1317,7 +1369,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.types.error
}
- fn make_body_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Region<'db> {
+ pub(crate) fn make_body_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Region<'db> {
let lt = self.with_ty_lowering(
self.body,
InferenceTyDiagnosticSource::Body,
@@ -1399,19 +1451,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
/// Whenever you lower a user-written type, you should call this.
- fn process_user_written_ty<T>(&mut self, ty: T) -> T
- where
- T: TypeFoldable<DbInterner<'db>>,
- {
+ fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
self.table.process_user_written_ty(ty)
}
/// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
/// while `process_user_written_ty()` should (but doesn't currently).
- fn process_remote_user_written_ty<T>(&mut self, ty: T) -> T
- where
- T: TypeFoldable<DbInterner<'db>>,
- {
+ fn process_remote_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
self.table.process_remote_user_written_ty(ty)
}
@@ -1427,16 +1473,70 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
}
- fn demand_eqtype(&mut self, expected: Ty<'db>, actual: Ty<'db>) {
+ fn demand_eqtype(
+ &mut self,
+ id: ExprOrPatId,
+ expected: Ty<'db>,
+ actual: Ty<'db>,
+ ) -> Result<(), ()> {
+ let result = self.demand_eqtype_fixme_no_diag(expected, actual);
+ if result.is_err() {
+ self.result.type_mismatches.insert(id, TypeMismatch { expected, actual });
+ }
+ result
+ }
+
+ fn demand_eqtype_fixme_no_diag(
+ &mut self,
+ expected: Ty<'db>,
+ actual: Ty<'db>,
+ ) -> Result<(), ()> {
let result = self
.table
- .infer_ctxt
- .at(&ObligationCause::new(), self.table.trait_env.env)
+ .at(&ObligationCause::new())
.eq(expected, actual)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
+ result.map_err(drop)
+ }
+
+ fn demand_suptype(&mut self, expected: Ty<'db>, actual: Ty<'db>) {
+ let result = self
+ .table
+ .at(&ObligationCause::new())
+ .sup(expected, actual)
+ .map(|infer_ok| self.table.register_infer_ok(infer_ok));
+ if let Err(_err) = result {
+ // FIXME: Emit diagnostic.
+ }
+ }
+
+ fn demand_coerce(
+ &mut self,
+ expr: ExprId,
+ checked_ty: Ty<'db>,
+ expected: Ty<'db>,
+ allow_two_phase: AllowTwoPhase,
+ expr_is_read: ExprIsRead,
+ ) -> Ty<'db> {
+ let result = self.coerce(expr.into(), checked_ty, expected, allow_two_phase, expr_is_read);
if let Err(_err) = result {
// FIXME: Emit diagnostic.
}
+ result.unwrap_or(self.types.error)
+ }
+
+ fn expr_ty(&self, expr: ExprId) -> Ty<'db> {
+ self.result[expr]
+ }
+
+ fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty<'db> {
+ let mut ty = None;
+ if let Some(it) = self.result.expr_adjustments.get(&e)
+ && let Some(it) = it.last()
+ {
+ ty = Some(it.target);
+ }
+ ty.unwrap_or_else(|| self.expr_ty(e))
}
fn resolve_associated_type_with_params(
@@ -1596,9 +1696,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
(ty, _) = path_ctx.lower_partly_resolved_path(resolution, true);
tried_resolving_once = true;
- ty = self.table.insert_type_vars(ty);
- ty = self.table.normalize_associated_types_in(ty);
- ty = self.table.structurally_resolve_type(ty);
+ ty = self.table.process_user_written_ty(ty);
if ty.is_ty_error() {
return (self.err_ty(), None);
}
@@ -1709,18 +1807,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
trait_.trait_items(self.db).associated_type_by_name(&Name::new_symbol_root(sym::Output))
}
- fn resolve_lang_trait(&self, lang: LangItem) -> Option<TraitId> {
- self.resolve_lang_item(lang)?.as_trait()
- }
-
- fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
- self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
- }
-
- fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
- self.resolve_output_on(self.resolve_lang_trait(LangItem::Not)?)
- }
-
fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
let ItemContainerId::TraitId(trait_) = self
.resolve_lang_item(LangItem::IntoFutureIntoFuture)?
@@ -1768,24 +1854,17 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
Some(struct_.into())
}
- fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
- self.resolve_output_on(self.resolve_lang_trait(LangItem::Index)?)
- }
-
fn resolve_va_list(&self) -> Option<AdtId> {
let struct_ = self.resolve_lang_item(LangItem::VaList)?.as_struct()?;
Some(struct_.into())
}
- fn get_traits_in_scope<'a>(
- resolver: &Resolver<'db>,
- traits_in_scope: &'a FxHashSet<TraitId>,
- ) -> Either<FxHashSet<TraitId>, &'a FxHashSet<TraitId>> {
- let mut b_traits = resolver.traits_in_scope_from_block_scopes().peekable();
+ pub(crate) fn get_traits_in_scope(&self) -> Either<FxHashSet<TraitId>, &FxHashSet<TraitId>> {
+ let mut b_traits = self.resolver.traits_in_scope_from_block_scopes().peekable();
if b_traits.peek().is_some() {
- Either::Left(traits_in_scope.iter().copied().chain(b_traits).collect())
+ Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect())
} else {
- Either::Right(traits_in_scope)
+ Either::Right(&self.traits_in_scope)
}
}
}