1 // Copyright 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.
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.
11 use middle
::infer
::InferCtxt
;
12 use middle
::ty
::{self, RegionEscape, Ty}
;
14 use std
::collections
::HashSet
;
17 use util
::common
::ErrorReported
;
18 use util
::nodemap
::NodeMap
;
20 use super::CodeAmbiguity
;
21 use super::CodeProjectionError
;
22 use super::CodeSelectionError
;
23 use super::FulfillmentError
;
24 use super::ObligationCause
;
25 use super::PredicateObligation
;
27 use super::select
::SelectionContext
;
28 use super::Unimplemented
;
29 use super::util
::predicate_for_builtin_bound
;
31 pub struct FulfilledPredicates
<'tcx
> {
32 set
: HashSet
<ty
::Predicate
<'tcx
>>
35 /// The fulfillment context is used to drive trait resolution. It
36 /// consists of a list of obligations that must be (eventually)
37 /// satisfied. The job is to track which are satisfied, which yielded
38 /// errors, and which are still pending. At any point, users can call
39 /// `select_where_possible`, and the fulfilment context will try to do
40 /// selection, retaining only those obligations that remain
41 /// ambiguous. This may be helpful in pushing type inference
42 /// along. Once all type inference constraints have been generated, the
43 /// method `select_all_or_error` can be used to report any remaining
44 /// ambiguous cases as errors.
45 pub struct FulfillmentContext
<'tcx
> {
46 // a simple cache that aims to cache *exact duplicate obligations*
47 // and avoid adding them twice. This serves a different purpose
48 // than the `SelectionCache`: it avoids duplicate errors and
49 // permits recursive obligations, which are often generated from
50 // traits like `Send` et al.
51 duplicate_set
: FulfilledPredicates
<'tcx
>,
53 // A list of all obligations that have been registered with this
54 // fulfillment context.
55 predicates
: Vec
<PredicateObligation
<'tcx
>>,
57 // Remembers the count of trait obligations that we have already
58 // attempted to select. This is used to avoid repeating work
59 // when `select_new_obligations` is called.
60 attempted_mark
: usize,
62 // A set of constraints that regionck must validate. Each
63 // constraint has the form `T:'a`, meaning "some type `T` must
64 // outlive the lifetime 'a". These constraints derive from
65 // instantiated type parameters. So if you had a struct defined
68 // struct Foo<T:'static> { ... }
70 // then in some expression `let x = Foo { ... }` it will
71 // instantiate the type parameter `T` with a fresh type `$0`. At
72 // the same time, it will record a region obligation of
73 // `$0:'static`. This will get checked later by regionck. (We
74 // can't generally check these things right away because we have
75 // to wait until types are resolved.)
77 // These are stored in a map keyed to the id of the innermost
78 // enclosing fn body / static initializer expression. This is
79 // because the location where the obligation was incurred can be
80 // relevant with respect to which sublifetime assumptions are in
81 // place. The reason that we store under the fn-id, and not
82 // something more fine-grained, is so that it is easier for
83 // regionck to be sure that it has found *all* the region
84 // obligations (otherwise, it's easy to fail to walk to a
85 // particular node-id).
86 region_obligations
: NodeMap
<Vec
<RegionObligation
<'tcx
>>>,
88 errors_will_be_reported
: bool
,
92 pub struct RegionObligation
<'tcx
> {
93 pub sub_region
: ty
::Region
,
94 pub sup_type
: Ty
<'tcx
>,
95 pub cause
: ObligationCause
<'tcx
>,
98 impl<'tcx
> FulfillmentContext
<'tcx
> {
99 /// Creates a new fulfillment context.
101 /// `errors_will_be_reported` indicates whether ALL errors that
102 /// are generated by this fulfillment context will be reported to
103 /// the end user. This is used to inform caching, because it
104 /// allows us to conclude that traits that resolve successfully
105 /// will in fact always resolve successfully (in particular, it
106 /// guarantees that if some dependent obligation encounters a
107 /// problem, compilation will be aborted). If you're not sure of
108 /// the right value here, pass `false`, as that is the more
109 /// conservative option.
111 /// FIXME -- a better option would be to hold back on modifying
112 /// the global cache until we know that all dependent obligations
113 /// are also satisfied. In that case, we could actually remove
114 /// this boolean flag, and we'd also avoid the problem of squelching
115 /// duplicate errors that occur across fns.
116 pub fn new(errors_will_be_reported
: bool
) -> FulfillmentContext
<'tcx
> {
118 duplicate_set
: FulfilledPredicates
::new(),
119 predicates
: Vec
::new(),
121 region_obligations
: NodeMap(),
122 errors_will_be_reported
: errors_will_be_reported
,
126 /// "Normalize" a projection type `<SomeType as SomeTrait>::X` by
127 /// creating a fresh type variable `$0` as well as a projection
128 /// predicate `<SomeType as SomeTrait>::X == $0`. When the
129 /// inference engine runs, it will attempt to find an impl of
130 /// `SomeTrait` or a where clause that lets us unify `$0` with
131 /// something concrete. If this fails, we'll unify `$0` with
132 /// `projection_ty` again.
133 pub fn normalize_projection_type
<'a
>(&mut self,
134 infcx
: &InferCtxt
<'a
,'tcx
>,
135 typer
: &ty
::ClosureTyper
<'tcx
>,
136 projection_ty
: ty
::ProjectionTy
<'tcx
>,
137 cause
: ObligationCause
<'tcx
>)
140 debug
!("normalize_associated_type(projection_ty={:?})",
143 assert
!(!projection_ty
.has_escaping_regions());
145 // FIXME(#20304) -- cache
147 let mut selcx
= SelectionContext
::new(infcx
, typer
);
148 let normalized
= project
::normalize_projection_type(&mut selcx
, projection_ty
, cause
, 0);
150 for obligation
in normalized
.obligations
{
151 self.register_predicate_obligation(infcx
, obligation
);
154 debug
!("normalize_associated_type: result={:?}", normalized
.value
);
159 pub fn register_builtin_bound
<'a
>(&mut self,
160 infcx
: &InferCtxt
<'a
,'tcx
>,
162 builtin_bound
: ty
::BuiltinBound
,
163 cause
: ObligationCause
<'tcx
>)
165 match predicate_for_builtin_bound(infcx
.tcx
, cause
, builtin_bound
, 0, ty
) {
167 self.register_predicate_obligation(infcx
, predicate
);
169 Err(ErrorReported
) => { }
173 pub fn register_region_obligation
<'a
>(&mut self,
176 cause
: ObligationCause
<'tcx
>)
178 register_region_obligation(t_a
, r_b
, cause
, &mut self.region_obligations
);
181 pub fn register_predicate_obligation
<'a
>(&mut self,
182 infcx
: &InferCtxt
<'a
,'tcx
>,
183 obligation
: PredicateObligation
<'tcx
>)
185 // this helps to reduce duplicate errors, as well as making
186 // debug output much nicer to read and so on.
187 let obligation
= infcx
.resolve_type_vars_if_possible(&obligation
);
189 assert
!(!obligation
.has_escaping_regions());
191 if self.is_duplicate_or_add(infcx
.tcx
, &obligation
.predicate
) {
192 debug
!("register_predicate({:?}) -- already seen, skip", obligation
);
196 debug
!("register_predicate({:?})", obligation
);
197 self.predicates
.push(obligation
);
200 pub fn region_obligations(&self,
201 body_id
: ast
::NodeId
)
202 -> &[RegionObligation
<'tcx
>]
204 match self.region_obligations
.get(&body_id
) {
205 None
=> Default
::default(),
210 pub fn select_all_or_error
<'a
>(&mut self,
211 infcx
: &InferCtxt
<'a
,'tcx
>,
212 typer
: &ty
::ClosureTyper
<'tcx
>)
213 -> Result
<(),Vec
<FulfillmentError
<'tcx
>>>
215 try
!(self.select_where_possible(infcx
, typer
));
217 // Anything left is ambiguous.
218 let errors
: Vec
<FulfillmentError
> =
221 .map(|o
| FulfillmentError
::new((*o
).clone(), CodeAmbiguity
))
224 if errors
.is_empty() {
231 /// Attempts to select obligations that were registered since the call to a selection routine.
232 /// This is used by the type checker to eagerly attempt to resolve obligations in hopes of
233 /// gaining type information. It'd be equally valid to use `select_where_possible` but it
234 /// results in `O(n^2)` performance (#18208).
235 pub fn select_new_obligations
<'a
>(&mut self,
236 infcx
: &InferCtxt
<'a
,'tcx
>,
237 typer
: &ty
::ClosureTyper
<'tcx
>)
238 -> Result
<(),Vec
<FulfillmentError
<'tcx
>>>
240 let mut selcx
= SelectionContext
::new(infcx
, typer
);
241 self.select(&mut selcx
, true)
244 pub fn select_where_possible
<'a
>(&mut self,
245 infcx
: &InferCtxt
<'a
,'tcx
>,
246 typer
: &ty
::ClosureTyper
<'tcx
>)
247 -> Result
<(),Vec
<FulfillmentError
<'tcx
>>>
249 let mut selcx
= SelectionContext
::new(infcx
, typer
);
250 self.select(&mut selcx
, false)
253 pub fn pending_obligations(&self) -> &[PredicateObligation
<'tcx
>] {
257 fn is_duplicate_or_add(&mut self, tcx
: &ty
::ctxt
<'tcx
>,
258 predicate
: &ty
::Predicate
<'tcx
>)
260 // This is a kind of dirty hack to allow us to avoid "rederiving"
261 // things that we have already proven in other methods.
263 // The idea is that any predicate that doesn't involve type
264 // parameters and which only involves the 'static region (and
265 // no other regions) is universally solvable, since impls are global.
267 // This is particularly important since even if we have a
268 // cache hit in the selection context, we still wind up
269 // evaluating the 'nested obligations'. This cache lets us
272 if self.errors_will_be_reported
&& predicate
.is_global() {
273 tcx
.fulfilled_predicates
.borrow_mut().is_duplicate_or_add(predicate
)
275 self.duplicate_set
.is_duplicate_or_add(predicate
)
279 /// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it
280 /// only attempts to select obligations that haven't been seen before.
281 fn select
<'a
>(&mut self,
282 selcx
: &mut SelectionContext
<'a
, 'tcx
>,
283 only_new_obligations
: bool
)
284 -> Result
<(),Vec
<FulfillmentError
<'tcx
>>>
286 debug
!("select({} obligations, only_new_obligations={}) start",
287 self.predicates
.len(),
288 only_new_obligations
);
290 let mut errors
= Vec
::new();
293 let count
= self.predicates
.len();
295 debug
!("select_where_possible({} obligations) iteration",
298 let mut new_obligations
= Vec
::new();
300 // If we are only attempting obligations we haven't seen yet,
301 // then set `skip` to the number of obligations we've already
303 let mut skip
= if only_new_obligations
{
309 // First pass: walk each obligation, retaining
310 // only those that we cannot yet process.
312 let region_obligations
= &mut self.region_obligations
;
313 self.predicates
.retain(|predicate
| {
314 // Hack: Retain does not pass in the index, but we want
315 // to avoid processing the first `start_count` entries.
318 process_predicate(selcx
, predicate
,
319 &mut new_obligations
, &mut errors
, region_obligations
)
328 self.attempted_mark
= self.predicates
.len();
330 if self.predicates
.len() == count
{
335 // Now go through all the successful ones,
336 // registering any nested obligations for the future.
337 for new_obligation
in new_obligations
{
338 self.register_predicate_obligation(selcx
.infcx(), new_obligation
);
342 debug
!("select({} obligations, {} errors) done",
343 self.predicates
.len(),
346 if errors
.is_empty() {
354 fn process_predicate
<'a
,'tcx
>(selcx
: &mut SelectionContext
<'a
,'tcx
>,
355 obligation
: &PredicateObligation
<'tcx
>,
356 new_obligations
: &mut Vec
<PredicateObligation
<'tcx
>>,
357 errors
: &mut Vec
<FulfillmentError
<'tcx
>>,
358 region_obligations
: &mut NodeMap
<Vec
<RegionObligation
<'tcx
>>>)
362 * Processes a predicate obligation and modifies the appropriate
363 * output array with the successful/error result. Returns `false`
364 * if the predicate could not be processed due to insufficient
368 match obligation
.predicate
{
369 ty
::Predicate
::Trait(ref data
) => {
370 let trait_obligation
= obligation
.with(data
.clone());
371 match selcx
.select(&trait_obligation
) {
376 new_obligations
.append(&mut s
.nested_obligations());
379 Err(selection_err
) => {
380 debug
!("predicate: {:?} error: {:?}",
384 FulfillmentError
::new(
386 CodeSelectionError(selection_err
)));
392 ty
::Predicate
::Equate(ref binder
) => {
393 match selcx
.infcx().equality_predicate(obligation
.cause
.span
, binder
) {
397 FulfillmentError
::new(
399 CodeSelectionError(Unimplemented
)));
405 ty
::Predicate
::RegionOutlives(ref binder
) => {
406 match selcx
.infcx().region_outlives_predicate(obligation
.cause
.span
, binder
) {
410 FulfillmentError
::new(
412 CodeSelectionError(Unimplemented
)));
419 ty
::Predicate
::TypeOutlives(ref binder
) => {
420 // For now, we just check that there are no higher-ranked
421 // regions. If there are, we will call this obligation an
422 // error. Eventually we should be able to support some
423 // cases here, I imagine (e.g., `for<'a> int : 'a`).
424 if ty
::count_late_bound_regions(selcx
.tcx(), binder
) != 0 {
426 FulfillmentError
::new(
428 CodeSelectionError(Unimplemented
)));
430 let ty
::OutlivesPredicate(t_a
, r_b
) = binder
.0;
431 register_region_obligation(t_a
, r_b
,
432 obligation
.cause
.clone(),
438 ty
::Predicate
::Projection(ref data
) => {
439 let project_obligation
= obligation
.with(data
.clone());
440 let result
= project
::poly_project_and_unify_type(selcx
, &project_obligation
);
441 debug
!("process_predicate: poly_project_and_unify_type({:?}) returned {:?}",
445 Ok(Some(obligations
)) => {
446 new_obligations
.extend(obligations
);
454 FulfillmentError
::new(
456 CodeProjectionError(err
)));
464 impl<'tcx
> fmt
::Debug
for RegionObligation
<'tcx
> {
465 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
466 write
!(f
, "RegionObligation(sub_region={:?}, sup_type={:?})",
472 fn register_region_obligation
<'tcx
>(t_a
: Ty
<'tcx
>,
474 cause
: ObligationCause
<'tcx
>,
475 region_obligations
: &mut NodeMap
<Vec
<RegionObligation
<'tcx
>>>)
477 let region_obligation
= RegionObligation
{ sup_type
: t_a
,
481 debug
!("register_region_obligation({:?})",
484 region_obligations
.entry(region_obligation
.cause
.body_id
).or_insert(vec
![])
485 .push(region_obligation
);
489 impl<'tcx
> FulfilledPredicates
<'tcx
> {
490 pub fn new() -> FulfilledPredicates
<'tcx
> {
491 FulfilledPredicates
{
496 pub fn is_duplicate(&self, p
: &ty
::Predicate
<'tcx
>) -> bool
{
500 fn is_duplicate_or_add(&mut self, p
: &ty
::Predicate
<'tcx
>) -> bool
{
501 !self.set
.insert(p
.clone())