hir_ty/next_solver/infer/
at.rs

1//! A nice interface for working with the infcx. The basic idea is to
2//! do `infcx.at(cause, param_env)`, which sets the "cause" of the
3//! operation as well as the surrounding parameter environment. Then
4//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a
5//! subtype or equality relationship respectively. The first argument
6//! is always the "expected" output from the POV of diagnostics.
7//!
8//! Examples:
9//! ```ignore (fragment)
10//!     infcx.at(cause, param_env).sub(a, b)
11//!     // requires that `a <: b`, with `a` considered the "expected" type
12//!
13//!     infcx.at(cause, param_env).sup(a, b)
14//!     // requires that `b <: a`, with `a` considered the "expected" type
15//!
16//!     infcx.at(cause, param_env).eq(a, b)
17//!     // requires that `a == b`, with `a` considered the "expected" type
18//! ```
19//! For finer-grained control, you can also do use `trace`:
20//! ```ignore (fragment)
21//!     infcx.at(...).trace(a, b).sub(&c, &d)
22//! ```
23//! This will set `a` and `b` as the "root" values for
24//! error-reporting, but actually operate on `c` and `d`. This is
25//! sometimes useful when the types of `c` and `d` are not traceable
26//! things. (That system should probably be refactored.)
27
28use rustc_type_ir::{
29    FnSig, GenericArgKind, TypeFoldable, TypingMode, Variance,
30    error::ExpectedFound,
31    inherent::{IntoKind, Span as _},
32    relate::{Relate, TypeRelation, solver_relating::RelateExt},
33};
34
35use crate::next_solver::{
36    AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv,
37    PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term,
38    TraitRef, Ty,
39    fulfill::NextSolverError,
40    infer::relate::lattice::{LatticeOp, LatticeOpKind},
41};
42
43use super::{
44    InferCtxt, InferOk, InferResult, TypeTrace, ValuePairs,
45    traits::{Obligation, ObligationCause},
46};
47
48#[derive(Clone, Copy)]
49pub struct At<'a, 'db> {
50    pub infcx: &'a InferCtxt<'db>,
51    pub cause: &'a ObligationCause,
52    pub param_env: ParamEnv<'db>,
53}
54
55impl<'db> InferCtxt<'db> {
56    #[inline]
57    pub fn at<'a>(&'a self, cause: &'a ObligationCause, param_env: ParamEnv<'db>) -> At<'a, 'db> {
58        At { infcx: self, cause, param_env }
59    }
60
61    /// Forks the inference context, creating a new inference context with the same inference
62    /// variables in the same state. This can be used to "branch off" many tests from the same
63    /// common state.
64    pub fn fork(&self) -> Self {
65        Self {
66            interner: self.interner,
67            typing_mode: self.typing_mode,
68            inner: self.inner.clone(),
69            tainted_by_errors: self.tainted_by_errors.clone(),
70            universe: self.universe.clone(),
71        }
72    }
73
74    /// Forks the inference context, creating a new inference context with the same inference
75    /// variables in the same state, except possibly changing the intercrate mode. This can be
76    /// used to "branch off" many tests from the same common state. Used in negative coherence.
77    pub fn fork_with_typing_mode(&self, typing_mode: TypingMode<DbInterner<'db>>) -> Self {
78        // Unlike `fork`, this invalidates all cache entries as they may depend on the
79        // typing mode.
80
81        Self {
82            interner: self.interner,
83            typing_mode,
84            inner: self.inner.clone(),
85            tainted_by_errors: self.tainted_by_errors.clone(),
86            universe: self.universe.clone(),
87        }
88    }
89}
90
91pub trait ToTrace<'db>: Relate<DbInterner<'db>> {
92    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db>;
93}
94
95impl<'a, 'db> At<'a, 'db> {
96    /// Makes `actual <: expected`. For example, if type-checking a
97    /// call like `foo(x)`, where `foo: fn(i32)`, you might have
98    /// `sup(i32, x)`, since the "expected" type is the type that
99    /// appears in the signature.
100    pub fn sup<T>(self, expected: T, actual: T) -> InferResult<'db, ()>
101    where
102        T: ToTrace<'db>,
103    {
104        RelateExt::relate(
105            self.infcx,
106            self.param_env,
107            expected,
108            Variance::Contravariant,
109            actual,
110            Span::dummy(),
111        )
112        .map(|goals| self.goals_to_obligations(goals))
113    }
114
115    /// Makes `expected <: actual`.
116    pub fn sub<T>(self, expected: T, actual: T) -> InferResult<'db, ()>
117    where
118        T: ToTrace<'db>,
119    {
120        RelateExt::relate(
121            self.infcx,
122            self.param_env,
123            expected,
124            Variance::Covariant,
125            actual,
126            Span::dummy(),
127        )
128        .map(|goals| self.goals_to_obligations(goals))
129    }
130
131    /// Makes `expected == actual`.
132    pub fn eq<T>(self, expected: T, actual: T) -> InferResult<'db, ()>
133    where
134        T: Relate<DbInterner<'db>>,
135    {
136        RelateExt::relate(
137            self.infcx,
138            self.param_env,
139            expected,
140            Variance::Invariant,
141            actual,
142            Span::dummy(),
143        )
144        .map(|goals| self.goals_to_obligations(goals))
145    }
146
147    pub fn relate<T>(self, expected: T, variance: Variance, actual: T) -> InferResult<'db, ()>
148    where
149        T: ToTrace<'db>,
150    {
151        match variance {
152            Variance::Covariant => self.sub(expected, actual),
153            Variance::Invariant => self.eq(expected, actual),
154            Variance::Contravariant => self.sup(expected, actual),
155
156            // We could make this make sense but it's not readily
157            // exposed and I don't feel like dealing with it. Note
158            // that bivariance in general does a bit more than just
159            // *nothing*, it checks that the types are the same
160            // "modulo variance" basically.
161            Variance::Bivariant => panic!("Bivariant given to `relate()`"),
162        }
163    }
164
165    /// Deeply normalizes `value`, replacing all aliases which can by normalized in
166    /// the current environment. This errors in case normalization fails or is ambiguous.
167    pub fn deeply_normalize<T>(self, value: T) -> Result<T, Vec<NextSolverError<'db>>>
168    where
169        T: TypeFoldable<DbInterner<'db>>,
170    {
171        crate::next_solver::normalize::deeply_normalize(self, value)
172    }
173
174    /// Computes the least-upper-bound, or mutual supertype, of two
175    /// values. The order of the arguments doesn't matter, but since
176    /// this can result in an error (e.g., if asked to compute LUB of
177    /// u32 and i32), it is meaningful to call one of them the
178    /// "expected type".
179    pub fn lub<T>(self, expected: T, actual: T) -> InferResult<'db, T>
180    where
181        T: ToTrace<'db>,
182    {
183        let mut op = LatticeOp::new(
184            self.infcx,
185            ToTrace::to_trace(self.cause, expected, actual),
186            self.param_env,
187            LatticeOpKind::Lub,
188        );
189        let value = op.relate(expected, actual)?;
190        Ok(InferOk { value, obligations: op.into_obligations() })
191    }
192
193    fn goals_to_obligations(&self, goals: Vec<Goal<'db, Predicate<'db>>>) -> InferOk<'db, ()> {
194        InferOk {
195            value: (),
196            obligations: goals
197                .into_iter()
198                .map(|goal| {
199                    Obligation::new(
200                        self.infcx.interner,
201                        self.cause.clone(),
202                        goal.param_env,
203                        goal.predicate,
204                    )
205                })
206                .collect(),
207        }
208    }
209}
210
211impl<'db> ToTrace<'db> for Ty<'db> {
212    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
213        TypeTrace {
214            cause: cause.clone(),
215            values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
216        }
217    }
218}
219
220impl<'db> ToTrace<'db> for Region<'db> {
221    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
222        TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) }
223    }
224}
225
226impl<'db> ToTrace<'db> for Const<'db> {
227    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
228        TypeTrace {
229            cause: cause.clone(),
230            values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
231        }
232    }
233}
234
235impl<'db> ToTrace<'db> for GenericArg<'db> {
236    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
237        TypeTrace {
238            cause: cause.clone(),
239            values: match (a.kind(), b.kind()) {
240                (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => {
241                    ValuePairs::Regions(ExpectedFound::new(a, b))
242                }
243                (GenericArgKind::Type(a), GenericArgKind::Type(b)) => {
244                    ValuePairs::Terms(ExpectedFound::new(a.into(), b.into()))
245                }
246                (GenericArgKind::Const(a), GenericArgKind::Const(b)) => {
247                    ValuePairs::Terms(ExpectedFound::new(a.into(), b.into()))
248                }
249                _ => panic!("relating different kinds: {a:?} {b:?}"),
250            },
251        }
252    }
253}
254
255impl<'db> ToTrace<'db> for Term<'db> {
256    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
257        TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) }
258    }
259}
260
261impl<'db> ToTrace<'db> for TraitRef<'db> {
262    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
263        TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
264    }
265}
266
267impl<'db> ToTrace<'db> for AliasTy<'db> {
268    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
269        TypeTrace {
270            cause: cause.clone(),
271            values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())),
272        }
273    }
274}
275
276impl<'db> ToTrace<'db> for AliasTerm<'db> {
277    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
278        TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) }
279    }
280}
281
282impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> {
283    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
284        TypeTrace {
285            cause: cause.clone(),
286            values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))),
287        }
288    }
289}
290
291impl<'db> ToTrace<'db> for PolyFnSig<'db> {
292    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
293        TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) }
294    }
295}
296
297impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> {
298    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
299        TypeTrace {
300            cause: cause.clone(),
301            values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)),
302        }
303    }
304}
305
306impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> {
307    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
308        TypeTrace {
309            cause: cause.clone(),
310            values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)),
311        }
312    }
313}