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, 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};
40
41use super::{
42    InferCtxt, InferOk, InferResult, TypeTrace, ValuePairs,
43    traits::{Obligation, ObligationCause},
44};
45
46/// Whether we should define opaque types or just treat them opaquely.
47///
48/// Currently only used to prevent predicate matching from matching anything
49/// against opaque types.
50#[derive(Debug, PartialEq, Eq, Clone, Copy)]
51pub enum DefineOpaqueTypes {
52    Yes,
53    No,
54}
55
56#[derive(Clone, Copy)]
57pub struct At<'a, 'db> {
58    pub infcx: &'a InferCtxt<'db>,
59    pub cause: &'a ObligationCause,
60    pub param_env: ParamEnv<'db>,
61}
62
63impl<'db> InferCtxt<'db> {
64    #[inline]
65    pub fn at<'a>(&'a self, cause: &'a ObligationCause, param_env: ParamEnv<'db>) -> At<'a, 'db> {
66        At { infcx: self, cause, param_env }
67    }
68
69    /// Forks the inference context, creating a new inference context with the same inference
70    /// variables in the same state. This can be used to "branch off" many tests from the same
71    /// common state.
72    pub fn fork(&self) -> Self {
73        Self {
74            interner: self.interner,
75            typing_mode: self.typing_mode,
76            inner: self.inner.clone(),
77            tainted_by_errors: self.tainted_by_errors.clone(),
78            universe: self.universe.clone(),
79        }
80    }
81
82    /// Forks the inference context, creating a new inference context with the same inference
83    /// variables in the same state, except possibly changing the intercrate mode. This can be
84    /// used to "branch off" many tests from the same common state. Used in negative coherence.
85    pub fn fork_with_typing_mode(&self, typing_mode: TypingMode<DbInterner<'db>>) -> Self {
86        // Unlike `fork`, this invalidates all cache entries as they may depend on the
87        // typing mode.
88
89        Self {
90            interner: self.interner,
91            typing_mode,
92            inner: self.inner.clone(),
93            tainted_by_errors: self.tainted_by_errors.clone(),
94            universe: self.universe.clone(),
95        }
96    }
97}
98
99pub trait ToTrace<'db>: Relate<DbInterner<'db>> {
100    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db>;
101}
102
103impl<'a, 'db> At<'a, 'db> {
104    /// Makes `actual <: expected`. For example, if type-checking a
105    /// call like `foo(x)`, where `foo: fn(i32)`, you might have
106    /// `sup(i32, x)`, since the "expected" type is the type that
107    /// appears in the signature.
108    pub fn sup<T>(
109        self,
110        define_opaque_types: DefineOpaqueTypes,
111        expected: T,
112        actual: T,
113    ) -> InferResult<'db, ()>
114    where
115        T: ToTrace<'db>,
116    {
117        RelateExt::relate(
118            self.infcx,
119            self.param_env,
120            expected,
121            Variance::Contravariant,
122            actual,
123            Span::dummy(),
124        )
125        .map(|goals| self.goals_to_obligations(goals))
126    }
127
128    /// Makes `expected <: actual`.
129    pub fn sub<T>(
130        self,
131        define_opaque_types: DefineOpaqueTypes,
132        expected: T,
133        actual: T,
134    ) -> InferResult<'db, ()>
135    where
136        T: ToTrace<'db>,
137    {
138        RelateExt::relate(
139            self.infcx,
140            self.param_env,
141            expected,
142            Variance::Covariant,
143            actual,
144            Span::dummy(),
145        )
146        .map(|goals| self.goals_to_obligations(goals))
147    }
148
149    /// Makes `expected == actual`.
150    pub fn eq<T>(
151        self,
152        define_opaque_types: DefineOpaqueTypes,
153        expected: T,
154        actual: T,
155    ) -> InferResult<'db, ()>
156    where
157        T: ToTrace<'db>,
158    {
159        self.eq_trace(
160            define_opaque_types,
161            ToTrace::to_trace(self.cause, expected, actual),
162            expected,
163            actual,
164        )
165    }
166
167    /// Makes `expected == actual`.
168    pub fn eq_trace<T>(
169        self,
170        define_opaque_types: DefineOpaqueTypes,
171        trace: TypeTrace<'db>,
172        expected: T,
173        actual: T,
174    ) -> InferResult<'db, ()>
175    where
176        T: Relate<DbInterner<'db>>,
177    {
178        RelateExt::relate(
179            self.infcx,
180            self.param_env,
181            expected,
182            Variance::Invariant,
183            actual,
184            Span::dummy(),
185        )
186        .map(|goals| self.goals_to_obligations(goals))
187    }
188
189    pub fn relate<T>(
190        self,
191        define_opaque_types: DefineOpaqueTypes,
192        expected: T,
193        variance: Variance,
194        actual: T,
195    ) -> InferResult<'db, ()>
196    where
197        T: ToTrace<'db>,
198    {
199        match variance {
200            Variance::Covariant => self.sub(define_opaque_types, expected, actual),
201            Variance::Invariant => self.eq(define_opaque_types, expected, actual),
202            Variance::Contravariant => self.sup(define_opaque_types, expected, actual),
203
204            // We could make this make sense but it's not readily
205            // exposed and I don't feel like dealing with it. Note
206            // that bivariance in general does a bit more than just
207            // *nothing*, it checks that the types are the same
208            // "modulo variance" basically.
209            Variance::Bivariant => panic!("Bivariant given to `relate()`"),
210        }
211    }
212
213    fn goals_to_obligations(&self, goals: Vec<Goal<'db, Predicate<'db>>>) -> InferOk<'db, ()> {
214        InferOk {
215            value: (),
216            obligations: goals
217                .into_iter()
218                .map(|goal| {
219                    Obligation::new(
220                        self.infcx.interner,
221                        self.cause.clone(),
222                        goal.param_env,
223                        goal.predicate,
224                    )
225                })
226                .collect(),
227        }
228    }
229}
230
231impl<'db> ToTrace<'db> for Ty<'db> {
232    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
233        TypeTrace {
234            cause: cause.clone(),
235            values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
236        }
237    }
238}
239
240impl<'db> ToTrace<'db> for Region<'db> {
241    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
242        TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) }
243    }
244}
245
246impl<'db> ToTrace<'db> for Const<'db> {
247    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
248        TypeTrace {
249            cause: cause.clone(),
250            values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
251        }
252    }
253}
254
255impl<'db> ToTrace<'db> for GenericArg<'db> {
256    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
257        TypeTrace {
258            cause: cause.clone(),
259            values: match (a.kind(), b.kind()) {
260                (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => {
261                    ValuePairs::Regions(ExpectedFound::new(a, b))
262                }
263                (GenericArgKind::Type(a), GenericArgKind::Type(b)) => {
264                    ValuePairs::Terms(ExpectedFound::new(a.into(), b.into()))
265                }
266                (GenericArgKind::Const(a), GenericArgKind::Const(b)) => {
267                    ValuePairs::Terms(ExpectedFound::new(a.into(), b.into()))
268                }
269                _ => panic!("relating different kinds: {a:?} {b:?}"),
270            },
271        }
272    }
273}
274
275impl<'db> ToTrace<'db> for Term<'db> {
276    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
277        TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) }
278    }
279}
280
281impl<'db> ToTrace<'db> for TraitRef<'db> {
282    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
283        TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
284    }
285}
286
287impl<'db> ToTrace<'db> for AliasTy<'db> {
288    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
289        TypeTrace {
290            cause: cause.clone(),
291            values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())),
292        }
293    }
294}
295
296impl<'db> ToTrace<'db> for AliasTerm<'db> {
297    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
298        TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) }
299    }
300}
301
302impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> {
303    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
304        TypeTrace {
305            cause: cause.clone(),
306            values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))),
307        }
308    }
309}
310
311impl<'db> ToTrace<'db> for PolyFnSig<'db> {
312    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
313        TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) }
314    }
315}
316
317impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> {
318    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
319        TypeTrace {
320            cause: cause.clone(),
321            values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)),
322        }
323    }
324}
325
326impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> {
327    fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
328        TypeTrace {
329            cause: cause.clone(),
330            values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)),
331        }
332    }
333}