Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/next_solver/infer/at.rs')
-rw-r--r--crates/hir-ty/src/next_solver/infer/at.rs363
1 files changed, 363 insertions, 0 deletions
diff --git a/crates/hir-ty/src/next_solver/infer/at.rs b/crates/hir-ty/src/next_solver/infer/at.rs
new file mode 100644
index 0000000000..8dfffe0d36
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/at.rs
@@ -0,0 +1,363 @@
+//! A nice interface for working with the infcx. The basic idea is to
+//! do `infcx.at(cause, param_env)`, which sets the "cause" of the
+//! operation as well as the surrounding parameter environment. Then
+//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a
+//! subtype or equality relationship respectively. The first argument
+//! is always the "expected" output from the POV of diagnostics.
+//!
+//! Examples:
+//! ```ignore (fragment)
+//! infcx.at(cause, param_env).sub(a, b)
+//! // requires that `a <: b`, with `a` considered the "expected" type
+//!
+//! infcx.at(cause, param_env).sup(a, b)
+//! // requires that `b <: a`, with `a` considered the "expected" type
+//!
+//! infcx.at(cause, param_env).eq(a, b)
+//! // requires that `a == b`, with `a` considered the "expected" type
+//! ```
+//! For finer-grained control, you can also do use `trace`:
+//! ```ignore (fragment)
+//! infcx.at(...).trace(a, b).sub(&c, &d)
+//! ```
+//! This will set `a` and `b` as the "root" values for
+//! error-reporting, but actually operate on `c` and `d`. This is
+//! sometimes useful when the types of `c` and `d` are not traceable
+//! things. (That system should probably be refactored.)
+
+use rustc_type_ir::{
+ FnSig, GenericArgKind, TypeFoldable, TypingMode, Variance,
+ error::ExpectedFound,
+ inherent::{IntoKind, Span as _},
+ relate::{Relate, TypeRelation, solver_relating::RelateExt},
+};
+
+use crate::next_solver::{
+ AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv,
+ PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term,
+ TraitRef, Ty,
+ fulfill::{FulfillmentCtxt, NextSolverError},
+ infer::relate::lattice::{LatticeOp, LatticeOpKind},
+};
+
+use super::{
+ InferCtxt, InferOk, InferResult, TypeTrace, ValuePairs,
+ traits::{Obligation, ObligationCause},
+};
+
+/// Whether we should define opaque types or just treat them opaquely.
+///
+/// Currently only used to prevent predicate matching from matching anything
+/// against opaque types.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum DefineOpaqueTypes {
+ Yes,
+ No,
+}
+
+#[derive(Clone, Copy)]
+pub struct At<'a, 'db> {
+ pub infcx: &'a InferCtxt<'db>,
+ pub cause: &'a ObligationCause,
+ pub param_env: ParamEnv<'db>,
+}
+
+impl<'db> InferCtxt<'db> {
+ #[inline]
+ pub fn at<'a>(&'a self, cause: &'a ObligationCause, param_env: ParamEnv<'db>) -> At<'a, 'db> {
+ At { infcx: self, cause, param_env }
+ }
+
+ /// Forks the inference context, creating a new inference context with the same inference
+ /// variables in the same state. This can be used to "branch off" many tests from the same
+ /// common state.
+ pub fn fork(&self) -> Self {
+ Self {
+ interner: self.interner,
+ typing_mode: self.typing_mode,
+ inner: self.inner.clone(),
+ tainted_by_errors: self.tainted_by_errors.clone(),
+ universe: self.universe.clone(),
+ }
+ }
+
+ /// Forks the inference context, creating a new inference context with the same inference
+ /// variables in the same state, except possibly changing the intercrate mode. This can be
+ /// used to "branch off" many tests from the same common state. Used in negative coherence.
+ pub fn fork_with_typing_mode(&self, typing_mode: TypingMode<DbInterner<'db>>) -> Self {
+ // Unlike `fork`, this invalidates all cache entries as they may depend on the
+ // typing mode.
+
+ Self {
+ interner: self.interner,
+ typing_mode,
+ inner: self.inner.clone(),
+ tainted_by_errors: self.tainted_by_errors.clone(),
+ universe: self.universe.clone(),
+ }
+ }
+}
+
+pub trait ToTrace<'db>: Relate<DbInterner<'db>> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db>;
+}
+
+impl<'a, 'db> At<'a, 'db> {
+ /// Makes `actual <: expected`. For example, if type-checking a
+ /// call like `foo(x)`, where `foo: fn(i32)`, you might have
+ /// `sup(i32, x)`, since the "expected" type is the type that
+ /// appears in the signature.
+ pub fn sup<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ expected: T,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: ToTrace<'db>,
+ {
+ RelateExt::relate(
+ self.infcx,
+ self.param_env,
+ expected,
+ Variance::Contravariant,
+ actual,
+ Span::dummy(),
+ )
+ .map(|goals| self.goals_to_obligations(goals))
+ }
+
+ /// Makes `expected <: actual`.
+ pub fn sub<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ expected: T,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: ToTrace<'db>,
+ {
+ RelateExt::relate(
+ self.infcx,
+ self.param_env,
+ expected,
+ Variance::Covariant,
+ actual,
+ Span::dummy(),
+ )
+ .map(|goals| self.goals_to_obligations(goals))
+ }
+
+ /// Makes `expected == actual`.
+ pub fn eq<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ expected: T,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: ToTrace<'db>,
+ {
+ self.eq_trace(
+ define_opaque_types,
+ ToTrace::to_trace(self.cause, expected, actual),
+ expected,
+ actual,
+ )
+ }
+
+ /// Makes `expected == actual`.
+ pub fn eq_trace<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ trace: TypeTrace<'db>,
+ expected: T,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: Relate<DbInterner<'db>>,
+ {
+ RelateExt::relate(
+ self.infcx,
+ self.param_env,
+ expected,
+ Variance::Invariant,
+ actual,
+ Span::dummy(),
+ )
+ .map(|goals| self.goals_to_obligations(goals))
+ }
+
+ pub fn relate<T>(
+ self,
+ define_opaque_types: DefineOpaqueTypes,
+ expected: T,
+ variance: Variance,
+ actual: T,
+ ) -> InferResult<'db, ()>
+ where
+ T: ToTrace<'db>,
+ {
+ match variance {
+ Variance::Covariant => self.sub(define_opaque_types, expected, actual),
+ Variance::Invariant => self.eq(define_opaque_types, expected, actual),
+ Variance::Contravariant => self.sup(define_opaque_types, expected, actual),
+
+ // We could make this make sense but it's not readily
+ // exposed and I don't feel like dealing with it. Note
+ // that bivariance in general does a bit more than just
+ // *nothing*, it checks that the types are the same
+ // "modulo variance" basically.
+ Variance::Bivariant => panic!("Bivariant given to `relate()`"),
+ }
+ }
+
+ /// Deeply normalizes `value`, replacing all aliases which can by normalized in
+ /// the current environment. This errors in case normalization fails or is ambiguous.
+ pub fn deeply_normalize<T>(self, value: T) -> Result<T, Vec<NextSolverError<'db>>>
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ crate::next_solver::normalize::deeply_normalize(self, value)
+ }
+
+ /// Computes the least-upper-bound, or mutual supertype, of two
+ /// values. The order of the arguments doesn't matter, but since
+ /// this can result in an error (e.g., if asked to compute LUB of
+ /// u32 and i32), it is meaningful to call one of them the
+ /// "expected type".
+ pub fn lub<T>(self, expected: T, actual: T) -> InferResult<'db, T>
+ where
+ T: ToTrace<'db>,
+ {
+ let mut op = LatticeOp::new(
+ self.infcx,
+ ToTrace::to_trace(self.cause, expected, actual),
+ self.param_env,
+ LatticeOpKind::Lub,
+ );
+ let value = op.relate(expected, actual)?;
+ Ok(InferOk { value, obligations: op.into_obligations() })
+ }
+
+ fn goals_to_obligations(&self, goals: Vec<Goal<'db, Predicate<'db>>>) -> InferOk<'db, ()> {
+ InferOk {
+ value: (),
+ obligations: goals
+ .into_iter()
+ .map(|goal| {
+ Obligation::new(
+ self.infcx.interner,
+ self.cause.clone(),
+ goal.param_env,
+ goal.predicate,
+ )
+ })
+ .collect(),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for Ty<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for Region<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for Const<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for GenericArg<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: match (a.kind(), b.kind()) {
+ (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => {
+ ValuePairs::Regions(ExpectedFound::new(a, b))
+ }
+ (GenericArgKind::Type(a), GenericArgKind::Type(b)) => {
+ ValuePairs::Terms(ExpectedFound::new(a.into(), b.into()))
+ }
+ (GenericArgKind::Const(a), GenericArgKind::Const(b)) => {
+ ValuePairs::Terms(ExpectedFound::new(a.into(), b.into()))
+ }
+ _ => panic!("relating different kinds: {a:?} {b:?}"),
+ },
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for Term<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for TraitRef<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for AliasTy<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for AliasTerm<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for PolyFnSig<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) }
+ }
+}
+
+impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)),
+ }
+ }
+}
+
+impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> {
+ fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
+ TypeTrace {
+ cause: cause.clone(),
+ values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)),
+ }
+ }
+}