Skip to content

Commit 08bf9f6

Browse files
committedAug 12, 2016
typeck: leak auto trait obligations through impl Trait.
1 parent d92e594 commit 08bf9f6

File tree

13 files changed

+613
-163
lines changed

13 files changed

+613
-163
lines changed
 

‎src/librustc/traits/fulfill.rs

+137-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
use dep_graph::DepGraph;
1212
use infer::{InferCtxt, InferOk};
13-
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, TyCtxt};
13+
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, TyCtxt, ToPredicate};
14+
use ty::subst::{Substs, Subst};
1415
use rustc_data_structures::obligation_forest::{ObligationForest, Error};
1516
use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor};
1617
use std::marker::PhantomData;
@@ -22,10 +23,9 @@ use util::nodemap::{FnvHashSet, NodeMap};
2223
use super::CodeAmbiguity;
2324
use super::CodeProjectionError;
2425
use super::CodeSelectionError;
25-
use super::FulfillmentError;
26-
use super::FulfillmentErrorCode;
27-
use super::ObligationCause;
28-
use super::PredicateObligation;
26+
use super::{FulfillmentError, FulfillmentErrorCode, SelectionError};
27+
use super::{ObligationCause, BuiltinDerivedObligation};
28+
use super::{PredicateObligation, TraitObligation, Obligation};
2929
use super::project;
3030
use super::select::SelectionContext;
3131
use super::Unimplemented;
@@ -51,6 +51,7 @@ pub struct GlobalFulfilledPredicates<'tcx> {
5151
/// along. Once all type inference constraints have been generated, the
5252
/// method `select_all_or_error` can be used to report any remaining
5353
/// ambiguous cases as errors.
54+
5455
pub struct FulfillmentContext<'tcx> {
5556
// A list of all obligations that have been registered with this
5657
// fulfillment context.
@@ -84,6 +85,10 @@ pub struct FulfillmentContext<'tcx> {
8485
// obligations (otherwise, it's easy to fail to walk to a
8586
// particular node-id).
8687
region_obligations: NodeMap<Vec<RegionObligation<'tcx>>>,
88+
89+
// A list of obligations that need to be deferred to
90+
// a later time for them to be properly fulfilled.
91+
deferred_obligations: Vec<DeferredObligation<'tcx>>,
8792
}
8893

8994
#[derive(Clone)]
@@ -99,13 +104,98 @@ pub struct PendingPredicateObligation<'tcx> {
99104
pub stalled_on: Vec<Ty<'tcx>>,
100105
}
101106

107+
/// An obligation which cannot be fulfilled in the context
108+
/// it was registered in, such as auto trait obligations on
109+
/// `impl Trait`, which require the concrete type to be
110+
/// available, only guaranteed after finishing type-checking.
111+
#[derive(Clone, Debug)]
112+
pub struct DeferredObligation<'tcx> {
113+
pub predicate: ty::PolyTraitPredicate<'tcx>,
114+
pub cause: ObligationCause<'tcx>
115+
}
116+
117+
impl<'a, 'gcx, 'tcx> DeferredObligation<'tcx> {
118+
/// If possible, create a `DeferredObligation` from
119+
/// a trait predicate which had failed selection,
120+
/// but could succeed later.
121+
pub fn from_select_error(tcx: TyCtxt<'a, 'gcx, 'tcx>,
122+
obligation: &TraitObligation<'tcx>,
123+
selection_err: &SelectionError<'tcx>)
124+
-> Option<DeferredObligation<'tcx>> {
125+
if let Unimplemented = *selection_err {
126+
if DeferredObligation::must_defer(tcx, &obligation.predicate) {
127+
return Some(DeferredObligation {
128+
predicate: obligation.predicate.clone(),
129+
cause: obligation.cause.clone()
130+
});
131+
}
132+
}
133+
134+
None
135+
}
136+
137+
/// Returns true if the given trait predicate can be
138+
/// fulfilled at a later time.
139+
pub fn must_defer(tcx: TyCtxt<'a, 'gcx, 'tcx>,
140+
predicate: &ty::PolyTraitPredicate<'tcx>)
141+
-> bool {
142+
// Auto trait obligations on `impl Trait`.
143+
if tcx.trait_has_default_impl(predicate.def_id()) {
144+
let substs = predicate.skip_binder().trait_ref.substs;
145+
if substs.types.as_slice().len() == 1 && substs.regions.is_empty() {
146+
if let ty::TyAnon(..) = predicate.skip_binder().self_ty().sty {
147+
return true;
148+
}
149+
}
150+
}
151+
152+
false
153+
}
154+
155+
/// If possible, return the nested obligations required
156+
/// to fulfill this obligation.
157+
pub fn try_select(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
158+
-> Option<Vec<PredicateObligation<'tcx>>> {
159+
if let ty::TyAnon(def_id, substs) = self.predicate.skip_binder().self_ty().sty {
160+
// We can resolve the `impl Trait` to its concrete type.
161+
if let Some(ty_scheme) = tcx.opt_lookup_item_type(def_id) {
162+
let concrete_ty = ty_scheme.ty.subst(tcx, substs);
163+
let concrete_substs = Substs::new_trait(vec![], vec![], concrete_ty);
164+
let predicate = ty::TraitRef {
165+
def_id: self.predicate.def_id(),
166+
substs: tcx.mk_substs(concrete_substs)
167+
}.to_predicate();
168+
169+
let original_obligation = Obligation::new(self.cause.clone(),
170+
self.predicate.clone());
171+
let cause = original_obligation.derived_cause(BuiltinDerivedObligation);
172+
return Some(vec![Obligation::new(cause, predicate)]);
173+
}
174+
}
175+
176+
None
177+
}
178+
179+
/// Return the `PredicateObligation` this was created from.
180+
pub fn to_obligation(&self) -> PredicateObligation<'tcx> {
181+
let predicate = ty::Predicate::Trait(self.predicate.clone());
182+
Obligation::new(self.cause.clone(), predicate)
183+
}
184+
185+
/// Return an error as if this obligation had failed.
186+
pub fn to_error(&self) -> FulfillmentError<'tcx> {
187+
FulfillmentError::new(self.to_obligation(), CodeSelectionError(Unimplemented))
188+
}
189+
}
190+
102191
impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
103192
/// Creates a new fulfillment context.
104193
pub fn new() -> FulfillmentContext<'tcx> {
105194
FulfillmentContext {
106195
predicates: ObligationForest::new(),
107196
rfc1592_obligations: Vec::new(),
108197
region_obligations: NodeMap(),
198+
deferred_obligations: vec![],
109199
}
110200
}
111201

@@ -224,10 +314,16 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
224314
{
225315
self.select_where_possible(infcx)?;
226316

317+
// Fail all of the deferred obligations that haven't
318+
// been otherwise removed from the context.
319+
let deferred_errors = self.deferred_obligations.iter()
320+
.map(|d| d.to_error());
321+
227322
let errors: Vec<_> =
228323
self.predicates.to_errors(CodeAmbiguity)
229324
.into_iter()
230325
.map(|e| to_fulfillment_error(e))
326+
.chain(deferred_errors)
231327
.collect();
232328
if errors.is_empty() {
233329
Ok(())
@@ -248,6 +344,10 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
248344
self.predicates.pending_obligations()
249345
}
250346

347+
pub fn take_deferred_obligations(&mut self) -> Vec<DeferredObligation<'tcx>> {
348+
mem::replace(&mut self.deferred_obligations, vec![])
349+
}
350+
251351
/// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it
252352
/// only attempts to select obligations that haven't been seen before.
253353
fn select(&mut self, selcx: &mut SelectionContext<'a, 'gcx, 'tcx>)
@@ -261,9 +361,10 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
261361

262362
// Process pending obligations.
263363
let outcome = self.predicates.process_obligations(&mut FulfillProcessor {
264-
selcx: selcx,
265-
region_obligations: &mut self.region_obligations,
266-
rfc1592_obligations: &mut self.rfc1592_obligations
364+
selcx: selcx,
365+
region_obligations: &mut self.region_obligations,
366+
rfc1592_obligations: &mut self.rfc1592_obligations,
367+
deferred_obligations: &mut self.deferred_obligations
267368
});
268369
debug!("select: outcome={:?}", outcome);
269370

@@ -298,7 +399,8 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
298399
struct FulfillProcessor<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> {
299400
selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>,
300401
region_obligations: &'a mut NodeMap<Vec<RegionObligation<'tcx>>>,
301-
rfc1592_obligations: &'a mut Vec<PredicateObligation<'tcx>>
402+
rfc1592_obligations: &'a mut Vec<PredicateObligation<'tcx>>,
403+
deferred_obligations: &'a mut Vec<DeferredObligation<'tcx>>
302404
}
303405

304406
impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, 'tcx> {
@@ -312,7 +414,8 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
312414
process_predicate(self.selcx,
313415
obligation,
314416
self.region_obligations,
315-
self.rfc1592_obligations)
417+
self.rfc1592_obligations,
418+
self.deferred_obligations)
316419
.map(|os| os.map(|os| os.into_iter().map(|o| PendingPredicateObligation {
317420
obligation: o,
318421
stalled_on: vec![]
@@ -354,7 +457,8 @@ fn process_predicate<'a, 'gcx, 'tcx>(
354457
selcx: &mut SelectionContext<'a, 'gcx, 'tcx>,
355458
pending_obligation: &mut PendingPredicateObligation<'tcx>,
356459
region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>,
357-
rfc1592_obligations: &mut Vec<PredicateObligation<'tcx>>)
460+
rfc1592_obligations: &mut Vec<PredicateObligation<'tcx>>,
461+
deferred_obligations: &mut Vec<DeferredObligation<'tcx>>)
358462
-> Result<Option<Vec<PredicateObligation<'tcx>>>,
359463
FulfillmentErrorCode<'tcx>>
360464
{
@@ -422,7 +526,22 @@ fn process_predicate<'a, 'gcx, 'tcx>(
422526
Err(selection_err) => {
423527
info!("selecting trait `{:?}` at depth {} yielded Err",
424528
data, obligation.recursion_depth);
425-
Err(CodeSelectionError(selection_err))
529+
530+
let defer = DeferredObligation::from_select_error(selcx.tcx(),
531+
&trait_obligation,
532+
&selection_err);
533+
if let Some(deferred_obligation) = defer {
534+
if let Some(nested) = deferred_obligation.try_select(selcx.tcx()) {
535+
Ok(Some(nested))
536+
} else {
537+
// Pretend that the obligation succeeded,
538+
// but record it for later.
539+
deferred_obligations.push(deferred_obligation);
540+
Ok(Some(vec![]))
541+
}
542+
} else {
543+
Err(CodeSelectionError(selection_err))
544+
}
426545
}
427546
}
428547
}
@@ -629,6 +748,12 @@ impl<'a, 'gcx, 'tcx> GlobalFulfilledPredicates<'gcx> {
629748
// already has the required read edges, so we don't need
630749
// to add any more edges here.
631750
if data.is_global() {
751+
// Don't cache predicates which were fulfilled
752+
// by deferring them for later fulfillment.
753+
if DeferredObligation::must_defer(tcx, data) {
754+
return;
755+
}
756+
632757
if let Some(data) = tcx.lift_to_global(data) {
633758
if self.set.insert(data.clone()) {
634759
debug!("add_if_global: global predicate `{:?}` added", data);

‎src/librustc/traits/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub use self::coherence::orphan_check;
3030
pub use self::coherence::overlapping_impls;
3131
pub use self::coherence::OrphanCheckErr;
3232
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
33+
pub use self::fulfill::DeferredObligation;
3334
pub use self::project::MismatchedProjectionTypes;
3435
pub use self::project::{normalize, normalize_projection_type, Normalized};
3536
pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal};

‎src/librustc/traits/select.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -2128,7 +2128,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
21282128
obligation)
21292129
};
21302130

2131-
let cause = self.derived_cause(obligation, BuiltinDerivedObligation);
2131+
let cause = obligation.derived_cause(BuiltinDerivedObligation);
21322132
self.collect_predicates_for_types(cause,
21332133
obligation.recursion_depth+1,
21342134
trait_def,
@@ -2208,7 +2208,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
22082208
{
22092209
debug!("vtable_default_impl: nested={:?}", nested);
22102210

2211-
let cause = self.derived_cause(obligation, BuiltinDerivedObligation);
2211+
let cause = obligation.derived_cause(BuiltinDerivedObligation);
22122212
let mut obligations = self.collect_predicates_for_types(
22132213
cause,
22142214
obligation.recursion_depth+1,
@@ -2219,7 +2219,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
22192219
let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
22202220
let (trait_ref, skol_map) =
22212221
this.infcx().skolemize_late_bound_regions(&poly_trait_ref, snapshot);
2222-
let cause = this.derived_cause(obligation, ImplDerivedObligation);
2222+
let cause = obligation.derived_cause(ImplDerivedObligation);
22232223
this.impl_or_trait_obligations(cause,
22242224
obligation.recursion_depth + 1,
22252225
trait_def_id,
@@ -2254,7 +2254,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
22542254
this.rematch_impl(impl_def_id, obligation,
22552255
snapshot);
22562256
debug!("confirm_impl_candidate substs={:?}", substs);
2257-
let cause = this.derived_cause(obligation, ImplDerivedObligation);
2257+
let cause = obligation.derived_cause(ImplDerivedObligation);
22582258
this.vtable_impl(impl_def_id, substs, cause,
22592259
obligation.recursion_depth + 1,
22602260
skol_map, snapshot)
@@ -2907,12 +2907,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
29072907
}).collect();
29082908
self.infcx().plug_leaks(skol_map, snapshot, &predicates)
29092909
}
2910+
}
29102911

2912+
impl<'tcx> TraitObligation<'tcx> {
29112913
#[allow(unused_comparisons)]
2912-
fn derived_cause(&self,
2913-
obligation: &TraitObligation<'tcx>,
2914-
variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>)
2915-
-> ObligationCause<'tcx>
2914+
pub fn derived_cause(&self,
2915+
variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>)
2916+
-> ObligationCause<'tcx>
29162917
{
29172918
/*!
29182919
* Creates a cause for obligations that are derived from
@@ -2924,6 +2925,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
29242925
* reporting.
29252926
*/
29262927

2928+
let obligation = self;
2929+
29272930
// NOTE(flaper87): As of now, it keeps track of the whole error
29282931
// chain. Ideally, we should have a way to configure this either
29292932
// by using -Z verbose or just a CLI argument.

0 commit comments

Comments
 (0)
Please sign in to comment.