Skip to content

Commit f58ad99

Browse files
committed
ergonomic improvements to the methods in infcx
1 parent 9f8392e commit f58ad99

File tree

18 files changed

+434
-297
lines changed

18 files changed

+434
-297
lines changed

src/librustc/infer/at.rs

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! A nice interface for working with the infcx. The basic idea is to
12+
//! do `infcx.at(cause, param_env)`, which sets the "cause" of the
13+
//! operation as well as the surrounding parameter environment. Then
14+
//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a
15+
//! subtype or equality relationship respectively. The first argument
16+
//! is always the "expected" output from the POV of diagnostics.
17+
//!
18+
//! Examples:
19+
//!
20+
//! infcx.at(cause, param_env).sub(a, b)
21+
//! // requires that `a <: b`, with `a` considered the "expected" type
22+
//!
23+
//! infcx.at(cause, param_env).sup(a, b)
24+
//! // requires that `b <: a`, with `a` considered the "expected" type
25+
//!
26+
//! infcx.at(cause, param_env).eq(a, b)
27+
//! // requires that `a == b`, with `a` considered the "expected" type
28+
//!
29+
//! For finer-grained control, you can also do use `trace`:
30+
//!
31+
//! infcx.at(...).trace(a, b).sub(&c, &d)
32+
//!
33+
//! This will set `a` and `b` as the "root" values for
34+
//! error-reporting, but actually operate on `c` and `d`. This is
35+
//! sometimes useful when the types of `c` and `d` are not traceable
36+
//! things. (That system should probably be refactored.)
37+
38+
use super::*;
39+
40+
use ty::relate::{Relate, TypeRelation};
41+
42+
pub struct At<'a, 'gcx: 'tcx, 'tcx: 'a> {
43+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
44+
cause: &'a ObligationCause<'tcx>,
45+
param_env: ty::ParamEnv<'tcx>,
46+
}
47+
48+
pub struct Trace<'a, 'gcx: 'tcx, 'tcx: 'a> {
49+
at: At<'a, 'gcx, 'tcx>,
50+
a_is_expected: bool,
51+
trace: TypeTrace<'tcx>,
52+
}
53+
54+
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
55+
pub fn at(&'a self,
56+
cause: &'a ObligationCause<'tcx>,
57+
param_env: ty::ParamEnv<'tcx>)
58+
-> At<'a, 'gcx, 'tcx>
59+
{
60+
At { infcx: self, cause, param_env }
61+
}
62+
}
63+
64+
pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
65+
fn to_trace(cause: &ObligationCause<'tcx>,
66+
a_is_expected: bool,
67+
a: Self,
68+
b: Self)
69+
-> TypeTrace<'tcx>;
70+
}
71+
72+
impl<'a, 'gcx, 'tcx> At<'a, 'gcx, 'tcx> {
73+
/// Hacky routine for equating two impl headers in coherence.
74+
pub fn eq_impl_headers(self,
75+
expected: &ty::ImplHeader<'tcx>,
76+
actual: &ty::ImplHeader<'tcx>)
77+
-> InferResult<'tcx, ()>
78+
{
79+
debug!("eq_impl_header({:?} = {:?})", expected, actual);
80+
match (expected.trait_ref, actual.trait_ref) {
81+
(Some(a_ref), Some(b_ref)) =>
82+
self.eq(a_ref, b_ref),
83+
(None, None) =>
84+
self.eq(expected.self_ty, actual.self_ty),
85+
_ =>
86+
bug!("mk_eq_impl_headers given mismatched impl kinds"),
87+
}
88+
}
89+
90+
/// Make `a <: b` where `a` may or may not be expected
91+
pub fn sub_exp<T>(self,
92+
a_is_expected: bool,
93+
a: T,
94+
b: T)
95+
-> InferResult<'tcx, ()>
96+
where T: ToTrace<'tcx>
97+
{
98+
self.trace_exp(a_is_expected, a, b).sub(&a, &b)
99+
}
100+
101+
/// Make `actual <: expected`. For example, if type-checking a
102+
/// call like `foo(x)`, where `foo: fn(i32)`, you might have
103+
/// `sup(i32, x)`, since the "expected" type is the type that
104+
/// appears in the signature.
105+
pub fn sup<T>(self,
106+
expected: T,
107+
actual: T)
108+
-> InferResult<'tcx, ()>
109+
where T: ToTrace<'tcx>
110+
{
111+
self.sub_exp(false, actual, expected)
112+
}
113+
114+
/// Make `expected <: actual`
115+
pub fn sub<T>(self,
116+
expected: T,
117+
actual: T)
118+
-> InferResult<'tcx, ()>
119+
where T: ToTrace<'tcx>
120+
{
121+
self.sub_exp(true, expected, actual)
122+
}
123+
124+
/// Make `expected <: actual`
125+
pub fn eq_exp<T>(self,
126+
a_is_expected: bool,
127+
a: T,
128+
b: T)
129+
-> InferResult<'tcx, ()>
130+
where T: ToTrace<'tcx>
131+
{
132+
self.trace_exp(a_is_expected, a, b).eq(&a, &b)
133+
}
134+
135+
/// Make `expected <: actual`
136+
pub fn eq<T>(self,
137+
expected: T,
138+
actual: T)
139+
-> InferResult<'tcx, ()>
140+
where T: ToTrace<'tcx>
141+
{
142+
self.trace(expected, actual).eq(&expected, &actual)
143+
}
144+
145+
/// Compute the least-upper-bound, or mutual supertype, of two
146+
/// values. The order of the arguments doesn't matter, but since
147+
/// this can result in an error (e.g., if asked to compute LUB of
148+
/// u32 and i32), it is meaningful to call one of them the
149+
/// "expected type".
150+
pub fn lub<T>(self,
151+
expected: T,
152+
actual: T)
153+
-> InferResult<'tcx, T>
154+
where T: ToTrace<'tcx>
155+
{
156+
self.trace(expected, actual).lub(&expected, &actual)
157+
}
158+
159+
/// Compute the greatest-lower-bound, or mutual subtype, of two
160+
/// values. As with `lub` order doesn't matter, except for error
161+
/// cases.
162+
pub fn glb<T>(self,
163+
expected: T,
164+
actual: T)
165+
-> InferResult<'tcx, T>
166+
where T: ToTrace<'tcx>
167+
{
168+
self.trace(expected, actual).glb(&expected, &actual)
169+
}
170+
171+
/// Sets the "trace" values that will be used for
172+
/// error-repporting, but doesn't actually perform any operation
173+
/// yet (this is useful when you want to set the trace using
174+
/// distinct values from those you wish to operate upon).
175+
pub fn trace<T>(self,
176+
expected: T,
177+
actual: T)
178+
-> Trace<'a, 'gcx, 'tcx>
179+
where T: ToTrace<'tcx>
180+
{
181+
self.trace_exp(true, expected, actual)
182+
}
183+
184+
/// Like `trace`, but the expected value is determined by the
185+
/// boolean argument (if true, then the first argument `a` is the
186+
/// "expected" value).
187+
pub fn trace_exp<T>(self,
188+
a_is_expected: bool,
189+
a: T,
190+
b: T)
191+
-> Trace<'a, 'gcx, 'tcx>
192+
where T: ToTrace<'tcx>
193+
{
194+
let trace = ToTrace::to_trace(self.cause, a_is_expected, a, b);
195+
Trace { at: self, trace: trace, a_is_expected }
196+
}
197+
}
198+
199+
impl<'a, 'gcx, 'tcx> Trace<'a, 'gcx, 'tcx> {
200+
/// Make `a <: b` where `a` may or may not be expected (if
201+
/// `a_is_expected` is true, then `a` is expected).
202+
/// Make `expected <: actual`
203+
pub fn sub<T>(self,
204+
a: &T,
205+
b: &T)
206+
-> InferResult<'tcx, ()>
207+
where T: Relate<'tcx>
208+
{
209+
debug!("sub({:?} <: {:?})", a, b);
210+
let Trace { at, trace, a_is_expected } = self;
211+
at.infcx.commit_if_ok(|_| {
212+
let mut fields = at.infcx.combine_fields(trace, at.param_env);
213+
fields.sub(a_is_expected)
214+
.relate(a, b)
215+
.map(move |_| InferOk { value: (), obligations: fields.obligations })
216+
})
217+
}
218+
219+
/// Make `a == b`; the expectation is set by the call to
220+
/// `trace()`.
221+
pub fn eq<T>(self,
222+
a: &T,
223+
b: &T)
224+
-> InferResult<'tcx, ()>
225+
where T: Relate<'tcx>
226+
{
227+
debug!("eq({:?} == {:?})", a, b);
228+
let Trace { at, trace, a_is_expected } = self;
229+
at.infcx.commit_if_ok(|_| {
230+
let mut fields = at.infcx.combine_fields(trace, at.param_env);
231+
fields.equate(a_is_expected)
232+
.relate(a, b)
233+
.map(move |_| InferOk { value: (), obligations: fields.obligations })
234+
})
235+
}
236+
237+
pub fn lub<T>(self,
238+
a: &T,
239+
b: &T)
240+
-> InferResult<'tcx, T>
241+
where T: Relate<'tcx>
242+
{
243+
debug!("lub({:?} \\/ {:?})", a, b);
244+
let Trace { at, trace, a_is_expected } = self;
245+
at.infcx.commit_if_ok(|_| {
246+
let mut fields = at.infcx.combine_fields(trace, at.param_env);
247+
fields.lub(a_is_expected)
248+
.relate(a, b)
249+
.map(move |t| InferOk { value: t, obligations: fields.obligations })
250+
})
251+
}
252+
253+
pub fn glb<T>(self,
254+
a: &T,
255+
b: &T)
256+
-> InferResult<'tcx, T>
257+
where T: Relate<'tcx>
258+
{
259+
debug!("glb({:?} /\\ {:?})", a, b);
260+
let Trace { at, trace, a_is_expected } = self;
261+
at.infcx.commit_if_ok(|_| {
262+
let mut fields = at.infcx.combine_fields(trace, at.param_env);
263+
fields.glb(a_is_expected)
264+
.relate(a, b)
265+
.map(move |t| InferOk { value: t, obligations: fields.obligations })
266+
})
267+
}
268+
}
269+
270+
impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
271+
fn to_trace(cause: &ObligationCause<'tcx>,
272+
a_is_expected: bool,
273+
a: Self,
274+
b: Self)
275+
-> TypeTrace<'tcx>
276+
{
277+
TypeTrace {
278+
cause: cause.clone(),
279+
values: Types(ExpectedFound::new(a_is_expected, a, b))
280+
}
281+
}
282+
}
283+
284+
impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {
285+
fn to_trace(cause: &ObligationCause<'tcx>,
286+
a_is_expected: bool,
287+
a: Self,
288+
b: Self)
289+
-> TypeTrace<'tcx>
290+
{
291+
TypeTrace {
292+
cause: cause.clone(),
293+
values: TraitRefs(ExpectedFound::new(a_is_expected, a, b))
294+
}
295+
}
296+
}
297+
298+
impl<'tcx> ToTrace<'tcx> for ty::PolyTraitRef<'tcx> {
299+
fn to_trace(cause: &ObligationCause<'tcx>,
300+
a_is_expected: bool,
301+
a: Self,
302+
b: Self)
303+
-> TypeTrace<'tcx>
304+
{
305+
TypeTrace {
306+
cause: cause.clone(),
307+
values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b))
308+
}
309+
}
310+
}

0 commit comments

Comments
 (0)