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.rs194
1 files changed, 119 insertions, 75 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 3d91a2558f..017119781a 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -13,6 +13,7 @@
//! to certain types. To record this, we use the union-find implementation from
//! the `ena` crate, which is extracted from rustc.
+mod autoderef;
pub(crate) mod cast;
pub(crate) mod closure;
mod coerce;
@@ -25,6 +26,7 @@ pub(crate) mod unify;
use std::{cell::OnceCell, convert::identity, iter, ops::Index};
+use base_db::Crate;
use chalk_ir::{
DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance,
cast::Cast,
@@ -54,27 +56,30 @@ use rustc_hash::{FxHashMap, FxHashSet};
use stdx::{always, never};
use triomphe::Arc;
-use crate::next_solver::DbInterner;
-use crate::next_solver::mapping::NextSolverToChalk;
+use crate::db::InternedClosureId;
use crate::{
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx,
IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
- PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
+ PathLoweringDiagnostic, ProjectionTy, Substitution, TargetFeatures, TraitEnvironment, Ty,
+ TyBuilder, TyExt,
db::HirDatabase,
fold_tys,
generics::Generics,
infer::{
- coerce::CoerceMany,
+ coerce::{CoerceMany, DynamicCoerceMany},
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
expr::ExprIsRead,
unify::InferenceTable,
},
lower::{ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic},
mir::MirSpan,
- next_solver::{self, mapping::ChalkToNextSolver},
+ next_solver::{
+ self, DbInterner,
+ mapping::{ChalkToNextSolver, NextSolverToChalk},
+ },
static_lifetime, to_assoc_type_id,
traits::FnTrait,
- utils::UnevaluatedConstEvaluatorFolder,
+ utils::{TargetFeatureIsSafeInTarget, UnevaluatedConstEvaluatorFolder},
};
// This lint has a false positive here. See the link below for details.
@@ -86,7 +91,7 @@ pub use coerce::could_coerce;
pub use unify::{could_unify, could_unify_deeply};
use cast::{CastCheck, CastError};
-pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
+pub(crate) use closure::analysis::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
/// The entry point of type inference.
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
@@ -159,7 +164,7 @@ pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>,
let mut table = unify::InferenceTable::new(db, trait_env);
let ty_with_vars = table.normalize_associated_types_in(ty);
- table.resolve_obligations_as_possible();
+ table.select_obligations_where_possible();
table.propagate_diverging_flag();
table.resolve_completely(ty_with_vars)
}
@@ -183,18 +188,14 @@ impl BindingMode {
}
}
+// FIXME: Remove this `InferOk`, switch all code to the second one, that uses `Obligation` instead of `Goal`.
#[derive(Debug)]
pub(crate) struct InferOk<'db, T> {
+ #[allow(dead_code)]
value: T,
goals: Vec<next_solver::Goal<'db, next_solver::Predicate<'db>>>,
}
-impl<'db, T> InferOk<'db, T> {
- fn map<U>(self, f: impl FnOnce(T) -> U) -> InferOk<'db, U> {
- InferOk { value: f(self.value), goals: self.goals }
- }
-}
-
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum InferenceTyDiagnosticSource {
/// Diagnostics that come from types in the body.
@@ -378,6 +379,26 @@ impl Adjustment {
}
}
+/// At least for initial deployment, we want to limit two-phase borrows to
+/// only a few specific cases. Right now, those are mostly "things that desugar"
+/// into method calls:
+/// - using `x.some_method()` syntax, where some_method takes `&mut self`,
+/// - using `Foo::some_method(&mut x, ...)` syntax,
+/// - binary assignment operators (`+=`, `-=`, `*=`, etc.).
+///
+/// Anything else should be rejected until generalized two-phase borrow support
+/// is implemented. Right now, dataflow can't handle the general case where there
+/// is more than one use of a mutable borrow, and we don't want to accept too much
+/// new code via two-phase borrows, so we try to limit where we create two-phase
+/// capable mutable borrows.
+/// See #49434 for tracking.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub(crate) enum AllowTwoPhase {
+ // FIXME: We should use this when appropriate.
+ Yes,
+ No,
+}
+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Adjust {
/// Go from ! to any type.
@@ -393,8 +414,6 @@ pub enum Adjust {
/// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
/// The target type is `U` in both cases, with the region and mutability
/// being those shared by both the receiver and the returned reference.
-///
-/// Mutability is `None` when we are not sure.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct OverloadedDeref(pub Option<Mutability>);
@@ -656,6 +675,7 @@ pub(crate) struct InferenceContext<'db> {
/// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext
/// and resolve the path via its methods. This will ensure proper error reporting.
pub(crate) resolver: Resolver<'db>,
+ target_features: OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>,
generic_def: GenericDefId,
generics: OnceCell<Generics>,
table: unify::InferenceTable<'db>,
@@ -673,11 +693,11 @@ pub(crate) struct InferenceContext<'db> {
/// If `Some`, this stores coercion information for returned
/// expressions. If `None`, this is in a context where return is
/// inappropriate, such as a const expression.
- return_coercion: Option<CoerceMany>,
+ return_coercion: Option<DynamicCoerceMany<'db>>,
/// The resume type and the yield type, respectively, of the coroutine being inferred.
resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
- breakables: Vec<BreakableContext>,
+ breakables: Vec<BreakableContext<'db>>,
/// Whether we are inside the pattern of a destructuring assignment.
inside_assignment: bool,
@@ -692,21 +712,21 @@ pub(crate) struct InferenceContext<'db> {
/// We do that because sometimes we truncate projections (when a closure captures
/// both `a.b` and `a.b.c`), and we want to provide accurate spans in this case.
current_capture_span_stack: Vec<MirSpan>,
- current_closure: Option<ClosureId>,
+ current_closure: Option<InternedClosureId>,
/// Stores the list of closure ids that need to be analyzed before this closure. See the
/// comment on `InferenceContext::sort_closures`
- closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>,
- deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
+ closure_dependencies: FxHashMap<InternedClosureId, Vec<InternedClosureId>>,
+ deferred_closures: FxHashMap<InternedClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
diagnostics: Diagnostics,
}
#[derive(Clone, Debug)]
-struct BreakableContext {
+struct BreakableContext<'db> {
/// Whether this context contains at least one break expression.
may_break: bool,
/// The coercion target of the context.
- coerce: Option<CoerceMany>,
+ coerce: Option<DynamicCoerceMany<'db>>,
/// The optional label of the context.
label: Option<LabelId>,
kind: BreakableKind,
@@ -721,10 +741,10 @@ enum BreakableKind {
Border,
}
-fn find_breakable(
- ctxs: &mut [BreakableContext],
+fn find_breakable<'a, 'db>(
+ ctxs: &'a mut [BreakableContext<'db>],
label: Option<LabelId>,
-) -> Option<&mut BreakableContext> {
+) -> Option<&'a mut BreakableContext<'db>> {
let mut ctxs = ctxs
.iter_mut()
.rev()
@@ -735,10 +755,10 @@ fn find_breakable(
}
}
-fn find_continuable(
- ctxs: &mut [BreakableContext],
+fn find_continuable<'a, 'db>(
+ ctxs: &'a mut [BreakableContext<'db>],
label: Option<LabelId>,
-) -> Option<&mut BreakableContext> {
+) -> Option<&'a mut BreakableContext<'db>> {
match label {
Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
None => find_breakable(ctxs, label),
@@ -759,6 +779,7 @@ impl<'db> InferenceContext<'db> {
) -> Self {
let trait_env = db.trait_environment_for_body(owner);
InferenceContext {
+ target_features: OnceCell::new(),
generics: OnceCell::new(),
result: InferenceResult::default(),
table: unify::InferenceTable::new(db, trait_env),
@@ -794,18 +815,56 @@ impl<'db> InferenceContext<'db> {
self.generics.get_or_init(|| crate::generics::generics(self.db, self.generic_def))
}
+ #[inline]
+ fn krate(&self) -> Crate {
+ 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())),
+ _ => TargetFeatures::default(),
+ };
+ let target_feature_is_safe = match &krate.workspace_data(db).target {
+ Ok(target) => crate::utils::target_feature_is_safe_in_target(target),
+ Err(_) => TargetFeatureIsSafeInTarget::No,
+ };
+ (target_features, target_feature_is_safe)
+ });
+ (target_features, *target_feature_is_safe)
+ }
+
+ #[inline]
+ pub(crate) fn set_tainted_by_errors(&mut self) {
+ self.result.has_errors = true;
+ }
+
// FIXME: This function should be private in module. It is currently only used in the consteval, since we need
// `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
// used this function for another workaround, mention it here. If you really need this function and believe that
// there is no problem in it being `pub(crate)`, remove this comment.
- pub(crate) fn resolve_all(self) -> InferenceResult {
+ pub(crate) fn resolve_all(mut self) -> InferenceResult {
+ self.table.select_obligations_where_possible();
+ self.table.fallback_if_possible();
+
+ // Comment from rustc:
+ // Even though coercion casts provide type hints, we check casts after fallback for
+ // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
+ let cast_checks = std::mem::take(&mut self.deferred_cast_checks);
+ for mut cast in cast_checks.into_iter() {
+ if let Err(diag) = cast.check(&mut self) {
+ self.diagnostics.push(diag);
+ }
+ }
+
let InferenceContext {
- mut table,
- mut result,
- mut deferred_cast_checks,
- tuple_field_accesses_rev,
- diagnostics,
- ..
+ mut table, mut result, tuple_field_accesses_rev, diagnostics, ..
} = self;
let mut diagnostics = diagnostics.finish();
// Destructure every single field so whenever new fields are added to `InferenceResult` we
@@ -831,31 +890,12 @@ impl<'db> InferenceContext<'db> {
closure_info: _,
mutated_bindings_in_closure: _,
tuple_field_access_types: _,
- coercion_casts,
+ coercion_casts: _,
diagnostics: _,
} = &mut result;
- table.resolve_obligations_as_possible();
- table.fallback_if_possible();
-
- // Comment from rustc:
- // Even though coercion casts provide type hints, we check casts after fallback for
- // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
- let mut apply_adjustments = |expr, adj: Vec<_>| {
- expr_adjustments.insert(expr, adj.into_boxed_slice());
- };
- let mut set_coercion_cast = |expr| {
- coercion_casts.insert(expr);
- };
- for cast in deferred_cast_checks.iter_mut() {
- if let Err(diag) =
- cast.check(&mut table, &mut apply_adjustments, &mut set_coercion_cast)
- {
- diagnostics.push(diag);
- }
- }
// FIXME resolve obligations as well (use Guidance if necessary)
- table.resolve_obligations_as_possible();
+ table.select_obligations_where_possible();
// make sure diverging type variables are marked as such
table.propagate_diverging_flag();
@@ -1081,7 +1121,8 @@ impl<'db> InferenceContext<'db> {
};
self.return_ty = self.process_user_written_ty(return_ty);
- self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
+ self.return_coercion =
+ Some(CoerceMany::new(self.return_ty.to_nextsolver(self.table.interner)));
// Functions might be defining usage sites of TAITs.
// To define an TAITs, that TAIT must appear in the function's signatures.
@@ -1117,8 +1158,12 @@ impl<'db> InferenceContext<'db> {
fold_tys(
t,
|ty, _| {
+ let ty = self.table.structurally_resolve_type(&ty);
let opaque_ty_id = match ty.kind(Interner) {
- TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
+ TyKind::OpaqueType(opaque_ty_id, _)
+ | TyKind::Alias(AliasTy::Opaque(crate::OpaqueTy { opaque_ty_id, .. })) => {
+ *opaque_ty_id
+ }
_ => return ty,
};
let (impl_traits, idx) =
@@ -1214,9 +1259,11 @@ impl<'db> InferenceContext<'db> {
ty: &chalk_ir::Ty<Interner>,
outer_binder: DebruijnIndex,
) -> std::ops::ControlFlow<Self::BreakTy> {
- let ty = self.table.resolve_ty_shallow(ty);
+ let ty = self.table.structurally_resolve_type(ty);
- if let TyKind::OpaqueType(id, _) = ty.kind(Interner)
+ if let TyKind::OpaqueType(id, _)
+ | TyKind::Alias(AliasTy::Opaque(crate::OpaqueTy { opaque_ty_id: id, .. })) =
+ ty.kind(Interner)
&& let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
self.db.lookup_intern_impl_trait_id((*id).into())
{
@@ -1361,6 +1408,13 @@ impl<'db> InferenceContext<'db> {
}
}
+ fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[Ty]>) {
+ if adjustments.is_empty() {
+ return;
+ }
+ self.result.pat_adjustments.entry(pat).or_default().extend(adjustments);
+ }
+
fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) {
self.result.method_resolutions.insert(expr, (func, subst));
}
@@ -1587,24 +1641,14 @@ impl<'db> InferenceContext<'db> {
self.table.process_remote_user_written_ty(ty)
}
- /// Recurses through the given type, normalizing associated types mentioned
- /// in it by replacing them by type variables and registering obligations to
- /// resolve later. This should be done once for every type we get from some
- /// 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.
- fn normalize_associated_types_in<T, U>(&mut self, ty: T) -> T
- where
- T: HasInterner<Interner = Interner> + TypeFoldable<Interner> + ChalkToNextSolver<'db, U>,
- U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable<DbInterner<'db>>,
- {
- self.table.normalize_associated_types_in(ty)
- }
-
fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
self.table.resolve_ty_shallow(ty)
}
+ fn shallow_resolve(&self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> {
+ self.table.shallow_resolve(ty)
+ }
+
fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option<TypeAliasId>) -> Ty {
self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
}