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