]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | //! A nice interface for working with the infcx. The basic idea is to |
7cac9316 | 2 | //! do `infcx.at(cause, param_env)`, which sets the "cause" of the |
9fa01778 | 3 | //! operation as well as the surrounding parameter environment. Then |
7cac9316 XL |
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 | //! | |
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 | //! | |
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 | ||
28 | use super::*; | |
29 | ||
ba9703b0 XL |
30 | use rustc_middle::ty::relate::{Relate, TypeRelation}; |
31 | use rustc_middle::ty::Const; | |
7cac9316 | 32 | |
dc9dc135 XL |
33 | pub struct At<'a, 'tcx> { |
34 | pub infcx: &'a InferCtxt<'a, 'tcx>, | |
0531ce1d XL |
35 | pub cause: &'a ObligationCause<'tcx>, |
36 | pub param_env: ty::ParamEnv<'tcx>, | |
7cac9316 XL |
37 | } |
38 | ||
dc9dc135 XL |
39 | pub struct Trace<'a, 'tcx> { |
40 | at: At<'a, 'tcx>, | |
7cac9316 XL |
41 | a_is_expected: bool, |
42 | trace: TypeTrace<'tcx>, | |
43 | } | |
44 | ||
dc9dc135 | 45 | impl<'a, 'tcx> InferCtxt<'a, 'tcx> { |
a1dfa0c6 | 46 | #[inline] |
dc9dc135 XL |
47 | pub fn at( |
48 | &'a self, | |
49 | cause: &'a ObligationCause<'tcx>, | |
50 | param_env: ty::ParamEnv<'tcx>, | |
51 | ) -> At<'a, 'tcx> { | |
7cac9316 XL |
52 | At { infcx: self, cause, param_env } |
53 | } | |
54 | } | |
55 | ||
56 | pub trait ToTrace<'tcx>: Relate<'tcx> + Copy { | |
dfeec247 | 57 | fn to_trace( |
6a06907d | 58 | tcx: TyCtxt<'tcx>, |
dfeec247 XL |
59 | cause: &ObligationCause<'tcx>, |
60 | a_is_expected: bool, | |
61 | a: Self, | |
62 | b: Self, | |
63 | ) -> TypeTrace<'tcx>; | |
7cac9316 XL |
64 | } |
65 | ||
dc9dc135 | 66 | impl<'a, 'tcx> At<'a, 'tcx> { |
7cac9316 | 67 | /// Hacky routine for equating two impl headers in coherence. |
dfeec247 XL |
68 | pub fn eq_impl_headers( |
69 | self, | |
70 | expected: &ty::ImplHeader<'tcx>, | |
71 | actual: &ty::ImplHeader<'tcx>, | |
72 | ) -> InferResult<'tcx, ()> { | |
7cac9316 XL |
73 | debug!("eq_impl_header({:?} = {:?})", expected, actual); |
74 | match (expected.trait_ref, actual.trait_ref) { | |
dfeec247 XL |
75 | (Some(a_ref), Some(b_ref)) => self.eq(a_ref, b_ref), |
76 | (None, None) => self.eq(expected.self_ty, actual.self_ty), | |
77 | _ => bug!("mk_eq_impl_headers given mismatched impl kinds"), | |
7cac9316 XL |
78 | } |
79 | } | |
80 | ||
9fa01778 | 81 | /// Makes `a <: b`, where `a` may or may not be expected. |
dfeec247 XL |
82 | pub fn sub_exp<T>(self, a_is_expected: bool, a: T, b: T) -> InferResult<'tcx, ()> |
83 | where | |
84 | T: ToTrace<'tcx>, | |
7cac9316 | 85 | { |
f035d41b | 86 | self.trace_exp(a_is_expected, a, b).sub(a, b) |
7cac9316 XL |
87 | } |
88 | ||
9fa01778 | 89 | /// Makes `actual <: expected`. For example, if type-checking a |
7cac9316 XL |
90 | /// call like `foo(x)`, where `foo: fn(i32)`, you might have |
91 | /// `sup(i32, x)`, since the "expected" type is the type that | |
92 | /// appears in the signature. | |
dfeec247 XL |
93 | pub fn sup<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()> |
94 | where | |
95 | T: ToTrace<'tcx>, | |
7cac9316 XL |
96 | { |
97 | self.sub_exp(false, actual, expected) | |
98 | } | |
99 | ||
9fa01778 | 100 | /// Makes `expected <: actual`. |
dfeec247 XL |
101 | pub fn sub<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()> |
102 | where | |
103 | T: ToTrace<'tcx>, | |
7cac9316 XL |
104 | { |
105 | self.sub_exp(true, expected, actual) | |
106 | } | |
107 | ||
9fa01778 | 108 | /// Makes `expected <: actual`. |
dfeec247 XL |
109 | pub fn eq_exp<T>(self, a_is_expected: bool, a: T, b: T) -> InferResult<'tcx, ()> |
110 | where | |
111 | T: ToTrace<'tcx>, | |
7cac9316 | 112 | { |
f035d41b | 113 | self.trace_exp(a_is_expected, a, b).eq(a, b) |
7cac9316 XL |
114 | } |
115 | ||
9fa01778 | 116 | /// Makes `expected <: actual`. |
dfeec247 XL |
117 | pub fn eq<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()> |
118 | where | |
119 | T: ToTrace<'tcx>, | |
7cac9316 | 120 | { |
f035d41b | 121 | self.trace(expected, actual).eq(expected, actual) |
7cac9316 XL |
122 | } |
123 | ||
dfeec247 XL |
124 | pub fn relate<T>(self, expected: T, variance: ty::Variance, actual: T) -> InferResult<'tcx, ()> |
125 | where | |
126 | T: ToTrace<'tcx>, | |
0bf4aa26 XL |
127 | { |
128 | match variance { | |
129 | ty::Variance::Covariant => self.sub(expected, actual), | |
130 | ty::Variance::Invariant => self.eq(expected, actual), | |
131 | ty::Variance::Contravariant => self.sup(expected, actual), | |
132 | ||
133 | // We could make this make sense but it's not readily | |
134 | // exposed and I don't feel like dealing with it. Note | |
135 | // that bivariance in general does a bit more than just | |
136 | // *nothing*, it checks that the types are the same | |
137 | // "modulo variance" basically. | |
138 | ty::Variance::Bivariant => panic!("Bivariant given to `relate()`"), | |
139 | } | |
140 | } | |
141 | ||
9fa01778 | 142 | /// Computes the least-upper-bound, or mutual supertype, of two |
7cac9316 XL |
143 | /// values. The order of the arguments doesn't matter, but since |
144 | /// this can result in an error (e.g., if asked to compute LUB of | |
145 | /// u32 and i32), it is meaningful to call one of them the | |
146 | /// "expected type". | |
dfeec247 XL |
147 | pub fn lub<T>(self, expected: T, actual: T) -> InferResult<'tcx, T> |
148 | where | |
149 | T: ToTrace<'tcx>, | |
7cac9316 | 150 | { |
f035d41b | 151 | self.trace(expected, actual).lub(expected, actual) |
7cac9316 XL |
152 | } |
153 | ||
9fa01778 | 154 | /// Computes the greatest-lower-bound, or mutual subtype, of two |
7cac9316 XL |
155 | /// values. As with `lub` order doesn't matter, except for error |
156 | /// cases. | |
dfeec247 XL |
157 | pub fn glb<T>(self, expected: T, actual: T) -> InferResult<'tcx, T> |
158 | where | |
159 | T: ToTrace<'tcx>, | |
7cac9316 | 160 | { |
f035d41b | 161 | self.trace(expected, actual).glb(expected, actual) |
7cac9316 XL |
162 | } |
163 | ||
164 | /// Sets the "trace" values that will be used for | |
3b2f2976 | 165 | /// error-reporting, but doesn't actually perform any operation |
7cac9316 XL |
166 | /// yet (this is useful when you want to set the trace using |
167 | /// distinct values from those you wish to operate upon). | |
dc9dc135 XL |
168 | pub fn trace<T>(self, expected: T, actual: T) -> Trace<'a, 'tcx> |
169 | where | |
170 | T: ToTrace<'tcx>, | |
7cac9316 XL |
171 | { |
172 | self.trace_exp(true, expected, actual) | |
173 | } | |
174 | ||
175 | /// Like `trace`, but the expected value is determined by the | |
176 | /// boolean argument (if true, then the first argument `a` is the | |
177 | /// "expected" value). | |
dc9dc135 XL |
178 | pub fn trace_exp<T>(self, a_is_expected: bool, a: T, b: T) -> Trace<'a, 'tcx> |
179 | where | |
180 | T: ToTrace<'tcx>, | |
7cac9316 | 181 | { |
6a06907d | 182 | let trace = ToTrace::to_trace(self.infcx.tcx, self.cause, a_is_expected, a, b); |
74b04a01 | 183 | Trace { at: self, trace, a_is_expected } |
7cac9316 XL |
184 | } |
185 | } | |
186 | ||
dc9dc135 | 187 | impl<'a, 'tcx> Trace<'a, 'tcx> { |
9fa01778 | 188 | /// Makes `a <: b` where `a` may or may not be expected (if |
7cac9316 | 189 | /// `a_is_expected` is true, then `a` is expected). |
f035d41b | 190 | pub fn sub<T>(self, a: T, b: T) -> InferResult<'tcx, ()> |
dfeec247 XL |
191 | where |
192 | T: Relate<'tcx>, | |
7cac9316 XL |
193 | { |
194 | debug!("sub({:?} <: {:?})", a, b); | |
195 | let Trace { at, trace, a_is_expected } = self; | |
196 | at.infcx.commit_if_ok(|_| { | |
197 | let mut fields = at.infcx.combine_fields(trace, at.param_env); | |
dfeec247 XL |
198 | fields |
199 | .sub(a_is_expected) | |
200 | .relate(a, b) | |
201 | .map(move |_| InferOk { value: (), obligations: fields.obligations }) | |
7cac9316 XL |
202 | }) |
203 | } | |
204 | ||
9fa01778 | 205 | /// Makes `a == b`; the expectation is set by the call to |
7cac9316 | 206 | /// `trace()`. |
f035d41b | 207 | pub fn eq<T>(self, a: T, b: T) -> InferResult<'tcx, ()> |
dfeec247 XL |
208 | where |
209 | T: Relate<'tcx>, | |
7cac9316 XL |
210 | { |
211 | debug!("eq({:?} == {:?})", a, b); | |
212 | let Trace { at, trace, a_is_expected } = self; | |
213 | at.infcx.commit_if_ok(|_| { | |
214 | let mut fields = at.infcx.combine_fields(trace, at.param_env); | |
dfeec247 XL |
215 | fields |
216 | .equate(a_is_expected) | |
217 | .relate(a, b) | |
218 | .map(move |_| InferOk { value: (), obligations: fields.obligations }) | |
7cac9316 XL |
219 | }) |
220 | } | |
221 | ||
f035d41b | 222 | pub fn lub<T>(self, a: T, b: T) -> InferResult<'tcx, T> |
dfeec247 XL |
223 | where |
224 | T: Relate<'tcx>, | |
7cac9316 XL |
225 | { |
226 | debug!("lub({:?} \\/ {:?})", a, b); | |
227 | let Trace { at, trace, a_is_expected } = self; | |
228 | at.infcx.commit_if_ok(|_| { | |
229 | let mut fields = at.infcx.combine_fields(trace, at.param_env); | |
dfeec247 XL |
230 | fields |
231 | .lub(a_is_expected) | |
232 | .relate(a, b) | |
233 | .map(move |t| InferOk { value: t, obligations: fields.obligations }) | |
7cac9316 XL |
234 | }) |
235 | } | |
236 | ||
f035d41b | 237 | pub fn glb<T>(self, a: T, b: T) -> InferResult<'tcx, T> |
dfeec247 XL |
238 | where |
239 | T: Relate<'tcx>, | |
7cac9316 XL |
240 | { |
241 | debug!("glb({:?} /\\ {:?})", a, b); | |
242 | let Trace { at, trace, a_is_expected } = self; | |
243 | at.infcx.commit_if_ok(|_| { | |
244 | let mut fields = at.infcx.combine_fields(trace, at.param_env); | |
dfeec247 XL |
245 | fields |
246 | .glb(a_is_expected) | |
247 | .relate(a, b) | |
248 | .map(move |t| InferOk { value: t, obligations: fields.obligations }) | |
7cac9316 XL |
249 | }) |
250 | } | |
251 | } | |
252 | ||
253 | impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { | |
dfeec247 | 254 | fn to_trace( |
6a06907d | 255 | _: TyCtxt<'tcx>, |
dfeec247 XL |
256 | cause: &ObligationCause<'tcx>, |
257 | a_is_expected: bool, | |
258 | a: Self, | |
259 | b: Self, | |
260 | ) -> TypeTrace<'tcx> { | |
261 | TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) } | |
7cac9316 XL |
262 | } |
263 | } | |
264 | ||
0531ce1d | 265 | impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> { |
dfeec247 | 266 | fn to_trace( |
6a06907d | 267 | _: TyCtxt<'tcx>, |
dfeec247 XL |
268 | cause: &ObligationCause<'tcx>, |
269 | a_is_expected: bool, | |
270 | a: Self, | |
271 | b: Self, | |
272 | ) -> TypeTrace<'tcx> { | |
273 | TypeTrace { cause: cause.clone(), values: Regions(ExpectedFound::new(a_is_expected, a, b)) } | |
0531ce1d XL |
274 | } |
275 | } | |
276 | ||
48663c56 | 277 | impl<'tcx> ToTrace<'tcx> for &'tcx Const<'tcx> { |
dfeec247 | 278 | fn to_trace( |
6a06907d | 279 | _: TyCtxt<'tcx>, |
dfeec247 XL |
280 | cause: &ObligationCause<'tcx>, |
281 | a_is_expected: bool, | |
282 | a: Self, | |
283 | b: Self, | |
284 | ) -> TypeTrace<'tcx> { | |
285 | TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) } | |
48663c56 XL |
286 | } |
287 | } | |
288 | ||
7cac9316 | 289 | impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> { |
dfeec247 | 290 | fn to_trace( |
6a06907d | 291 | _: TyCtxt<'tcx>, |
dfeec247 XL |
292 | cause: &ObligationCause<'tcx>, |
293 | a_is_expected: bool, | |
294 | a: Self, | |
295 | b: Self, | |
296 | ) -> TypeTrace<'tcx> { | |
7cac9316 XL |
297 | TypeTrace { |
298 | cause: cause.clone(), | |
dfeec247 | 299 | values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)), |
7cac9316 XL |
300 | } |
301 | } | |
302 | } | |
303 | ||
304 | impl<'tcx> ToTrace<'tcx> for ty::PolyTraitRef<'tcx> { | |
dfeec247 | 305 | fn to_trace( |
6a06907d | 306 | _: TyCtxt<'tcx>, |
dfeec247 XL |
307 | cause: &ObligationCause<'tcx>, |
308 | a_is_expected: bool, | |
309 | a: Self, | |
310 | b: Self, | |
311 | ) -> TypeTrace<'tcx> { | |
7cac9316 XL |
312 | TypeTrace { |
313 | cause: cause.clone(), | |
dfeec247 | 314 | values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b)), |
7cac9316 XL |
315 | } |
316 | } | |
317 | } | |
6a06907d XL |
318 | |
319 | impl<'tcx> ToTrace<'tcx> for ty::ProjectionTy<'tcx> { | |
320 | fn to_trace( | |
321 | tcx: TyCtxt<'tcx>, | |
322 | cause: &ObligationCause<'tcx>, | |
323 | a_is_expected: bool, | |
324 | a: Self, | |
325 | b: Self, | |
326 | ) -> TypeTrace<'tcx> { | |
327 | let a_ty = tcx.mk_projection(a.item_def_id, a.substs); | |
328 | let b_ty = tcx.mk_projection(b.item_def_id, b.substs); | |
329 | TypeTrace { | |
330 | cause: cause.clone(), | |
331 | values: Types(ExpectedFound::new(a_is_expected, a_ty, b_ty)), | |
332 | } | |
333 | } | |
334 | } |