Skip to main content

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