Skip to content

Commit f37b4fe

Browse files
author
Ariel Ben-Yehuda
committed
put projections in RFC447 order
1 parent a43533a commit f37b4fe

File tree

3 files changed

+137
-59
lines changed

3 files changed

+137
-59
lines changed

src/librustc/middle/traits/select.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -663,9 +663,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
663663
{
664664
// Avoid caching results that depend on more than just the trait-ref:
665665
// The stack can create EvaluatedToUnknown, and closure signatures
666-
// being yet uninferred can create "spurious" EvaluatedToAmbig.
666+
// being yet uninferred can create "spurious" EvaluatedToAmbig
667+
// and EvaluatedToOk.
667668
if result == EvaluatedToUnknown ||
668-
(result == EvaluatedToAmbig && trait_ref.has_closure_types())
669+
((result == EvaluatedToAmbig || result == EvaluatedToOk)
670+
&& trait_ref.has_closure_types())
669671
{
670672
return;
671673
}
@@ -2297,6 +2299,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
22972299
impl_def_id,
22982300
impl_obligations);
22992301

2302+
// Because of RFC447, the impl-trait-ref and obligations
2303+
// are sufficient to determine the impl substs, without
2304+
// relying on projections in the impl-trait-ref.
2305+
//
2306+
// e.g. `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V`
23002307
impl_obligations.append(&mut substs.obligations);
23012308

23022309
VtableImplData { impl_def_id: impl_def_id,
@@ -2933,12 +2940,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
29332940
let predicates = self.tcx().lookup_predicates(def_id);
29342941
let predicates = predicates.instantiate(self.tcx(), substs);
29352942
let predicates = normalize_with_depth(self, cause.clone(), recursion_depth, &predicates);
2936-
let mut predicates = self.infcx().plug_leaks(skol_map, snapshot, &predicates);
2937-
let mut obligations =
2938-
util::predicates_for_generics(cause,
2939-
recursion_depth,
2940-
&predicates.value);
2941-
obligations.append(&mut predicates.obligations);
2943+
let predicates = self.infcx().plug_leaks(skol_map, snapshot, &predicates);
2944+
2945+
let mut obligations = predicates.obligations;
2946+
obligations.append(
2947+
&mut util::predicates_for_generics(cause,
2948+
recursion_depth,
2949+
&predicates.value));
29422950
obligations
29432951
}
29442952

src/librustc_typeck/collect.rs

+38-20
Original file line numberDiff line numberDiff line change
@@ -775,31 +775,33 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
775775
ref impl_items) => {
776776
// Create generics from the generics specified in the impl head.
777777
debug!("convert: ast_generics={:?}", generics);
778+
let def_id = ccx.tcx.map.local_def_id(it.id);
778779
let ty_generics = ty_generics_for_type_or_impl(ccx, generics);
779-
let ty_predicates = ty_generic_predicates_for_type_or_impl(ccx, generics);
780+
let mut ty_predicates = ty_generic_predicates_for_type_or_impl(ccx, generics);
780781

781782
debug!("convert: impl_bounds={:?}", ty_predicates);
782783

783784
let selfty = ccx.icx(&ty_predicates).to_ty(&ExplicitRscope, &**selfty);
784785
write_ty_to_tcx(tcx, it.id, selfty);
785786

786-
tcx.register_item_type(ccx.tcx.map.local_def_id(it.id),
787+
tcx.register_item_type(def_id,
787788
TypeScheme { generics: ty_generics.clone(),
788789
ty: selfty });
789-
tcx.predicates.borrow_mut().insert(ccx.tcx.map.local_def_id(it.id),
790-
ty_predicates.clone());
791790
if let &Some(ref ast_trait_ref) = opt_trait_ref {
792791
tcx.impl_trait_refs.borrow_mut().insert(
793-
ccx.tcx.map.local_def_id(it.id),
792+
def_id,
794793
Some(astconv::instantiate_mono_trait_ref(&ccx.icx(&ty_predicates),
795794
&ExplicitRscope,
796795
ast_trait_ref,
797796
Some(selfty)))
798797
);
799798
} else {
800-
tcx.impl_trait_refs.borrow_mut().insert(ccx.tcx.map.local_def_id(it.id), None);
799+
tcx.impl_trait_refs.borrow_mut().insert(def_id, None);
801800
}
802801

802+
enforce_impl_params_are_constrained(tcx, generics, &mut ty_predicates, def_id);
803+
tcx.predicates.borrow_mut().insert(def_id, ty_predicates.clone());
804+
803805

804806
// If there is a trait reference, treat the methods as always public.
805807
// This is to work around some incorrect behavior in privacy checking:
@@ -844,7 +846,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
844846
generics: ty_generics.clone(),
845847
ty: ty,
846848
});
847-
convert_associated_const(ccx, ImplContainer(ccx.tcx.map.local_def_id(it.id)),
849+
convert_associated_const(ccx, ImplContainer(def_id),
848850
impl_item.name, impl_item.id,
849851
impl_item.vis.inherit_from(parent_visibility),
850852
ty, true /* has_value */);
@@ -861,7 +863,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
861863

862864
let typ = ccx.icx(&ty_predicates).to_ty(&ExplicitRscope, ty);
863865

864-
convert_associated_type(ccx, ImplContainer(ccx.tcx.map.local_def_id(it.id)),
866+
convert_associated_type(ccx, ImplContainer(def_id),
865867
impl_item.name, impl_item.id, impl_item.vis,
866868
Some(typ));
867869
}
@@ -880,7 +882,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
880882
}
881883
});
882884
convert_methods(ccx,
883-
ImplContainer(ccx.tcx.map.local_def_id(it.id)),
885+
ImplContainer(def_id),
884886
methods,
885887
selfty,
886888
&ty_generics,
@@ -898,10 +900,7 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
898900
}
899901
}
900902

901-
enforce_impl_params_are_constrained(tcx,
902-
generics,
903-
ccx.tcx.map.local_def_id(it.id),
904-
impl_items);
903+
enforce_impl_lifetimes_are_constrained(tcx, generics, def_id, impl_items);
905904
},
906905
hir::ItemTrait(_, _, _, ref trait_items) => {
907906
let trait_def = trait_def_of_item(ccx, it);
@@ -2377,13 +2376,15 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
23772376
/// Checks that all the type parameters on an impl
23782377
fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
23792378
ast_generics: &hir::Generics,
2380-
impl_def_id: DefId,
2381-
impl_items: &[P<hir::ImplItem>])
2379+
impl_predicates: &mut ty::GenericPredicates<'tcx>,
2380+
impl_def_id: DefId)
23822381
{
23832382
let impl_scheme = tcx.lookup_item_type(impl_def_id);
2384-
let impl_predicates = tcx.lookup_predicates(impl_def_id);
23852383
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
23862384

2385+
assert!(impl_predicates.predicates.is_empty_in(FnSpace));
2386+
assert!(impl_predicates.predicates.is_empty_in(SelfSpace));
2387+
23872388
// The trait reference is an input, so find all type parameters
23882389
// reachable from there, to start (if this is an inherent impl,
23892390
// then just examine the self type).
@@ -2393,10 +2394,10 @@ fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
23932394
input_parameters.extend(ctp::parameters_for_trait_ref(trait_ref));
23942395
}
23952396

2396-
ctp::identify_constrained_type_params(tcx,
2397-
impl_predicates.predicates.as_slice(),
2398-
impl_trait_ref,
2399-
&mut input_parameters);
2397+
ctp::setup_constraining_predicates(tcx,
2398+
impl_predicates.predicates.get_mut_slice(TypeSpace),
2399+
impl_trait_ref,
2400+
&mut input_parameters);
24002401

24012402
for (index, ty_param) in ast_generics.ty_params.iter().enumerate() {
24022403
let param_ty = ty::ParamTy { space: TypeSpace,
@@ -2406,8 +2407,25 @@ fn enforce_impl_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
24062407
report_unused_parameter(tcx, ty_param.span, "type", &param_ty.to_string());
24072408
}
24082409
}
2410+
}
24092411

2412+
fn enforce_impl_lifetimes_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
2413+
ast_generics: &hir::Generics,
2414+
impl_def_id: DefId,
2415+
impl_items: &[P<hir::ImplItem>])
2416+
{
24102417
// Every lifetime used in an associated type must be constrained.
2418+
let impl_scheme = tcx.lookup_item_type(impl_def_id);
2419+
let impl_predicates = tcx.lookup_predicates(impl_def_id);
2420+
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
2421+
2422+
let mut input_parameters: HashSet<_> =
2423+
ctp::parameters_for_type(impl_scheme.ty).into_iter().collect();
2424+
if let Some(ref trait_ref) = impl_trait_ref {
2425+
input_parameters.extend(ctp::parameters_for_trait_ref(trait_ref));
2426+
}
2427+
ctp::identify_constrained_type_params(tcx,
2428+
&impl_predicates.predicates.as_slice(), impl_trait_ref, &mut input_parameters);
24112429

24122430
let lifetimes_in_associated_types: HashSet<_> =
24132431
impl_items.iter()

src/librustc_typeck/constrained_type_params.rs

+83-31
Original file line numberDiff line numberDiff line change
@@ -84,40 +84,92 @@ pub fn identify_constrained_type_params<'tcx>(_tcx: &ty::ctxt<'tcx>,
8484
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
8585
input_parameters: &mut HashSet<Parameter>)
8686
{
87-
loop {
88-
let num_inputs = input_parameters.len();
89-
90-
let poly_projection_predicates = // : iterator over PolyProjectionPredicate
91-
predicates.iter()
92-
.filter_map(|predicate| {
93-
match *predicate {
94-
ty::Predicate::Projection(ref data) => Some(data.clone()),
95-
_ => None,
96-
}
97-
});
98-
99-
for poly_projection in poly_projection_predicates {
100-
// Note that we can skip binder here because the impl
101-
// trait ref never contains any late-bound regions.
102-
let projection = poly_projection.skip_binder();
103-
104-
// Special case: watch out for some kind of sneaky attempt
105-
// to project out an associated type defined by this very
106-
// trait.
107-
let unbound_trait_ref = &projection.projection_ty.trait_ref;
108-
if Some(unbound_trait_ref.clone()) == impl_trait_ref {
109-
continue;
110-
}
87+
let mut predicates = predicates.to_owned();
88+
setup_constraining_predicates(_tcx, &mut predicates, impl_trait_ref, input_parameters);
89+
}
90+
11191

112-
let inputs = parameters_for_trait_ref(&projection.projection_ty.trait_ref);
113-
let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(&p));
114-
if relies_only_on_inputs {
92+
/// Order the predicates in `predicates` such that each parameter is
93+
/// constrained before it is used, if that is possible, and add the
94+
/// paramaters so constrained to `input_parameters`. For example,
95+
/// imagine the following impl:
96+
///
97+
/// impl<T: Debug, U: Iterator<Item=T>> Trait for U
98+
///
99+
/// The impl's predicates are collected from left to right. Ignoring
100+
/// the implicit `Sized` bounds, these are
101+
/// * T: Debug
102+
/// * U: Iterator
103+
/// * <U as Iterator>::Item = T -- a desugared ProjectionPredicate
104+
///
105+
/// When we, for example, try to go over the trait-reference
106+
/// `IntoIter<u32> as Trait`, we substitute the impl parameters with fresh
107+
/// variables and match them with the impl trait-ref, so we know that
108+
/// `$U = IntoIter<u32>`.
109+
///
110+
/// However, in order to process the `$T: Debug` predicate, we must first
111+
/// know the value of `$T` - which is only given by processing the
112+
/// projection. As we occasionally want to process predicates in a single
113+
/// pass, we want the projection to come first. In fact, as projections
114+
/// can (acyclically) depend on one another - see RFC447 for details - we
115+
/// need to topologically sort them.
116+
pub fn setup_constraining_predicates<'tcx>(_tcx: &ty::ctxt<'tcx>,
117+
predicates: &mut [ty::Predicate<'tcx>],
118+
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
119+
input_parameters: &mut HashSet<Parameter>)
120+
{
121+
// The canonical way of doing the needed topological sort
122+
// would be a DFS, but getting the graph and its ownership
123+
// right is annoying, so I am using an in-place fixed-point iteration,
124+
// which is `O(nt)` where `t` is the depth of type-parameter constraints,
125+
// remembering that `t` should be less than 7 in practice.
126+
//
127+
// Basically, I iterate over all projections and swap every
128+
// "ready" projection to the start of the list, such that
129+
// all of the projections before `i` are topologically sorted
130+
// and constrain all the parameters in `input_parameters`.
131+
//
132+
// In the example, `input_parameters` starts by containing `U` - which
133+
// is constrained by the trait-ref - and so on the first pass we
134+
// observe that `<U as Iterator>::Item = T` is a "ready" projection that
135+
// constrains `T` and swap it to front. As it is the sole projection,
136+
// no more swaps can take place afterwards, with the result being
137+
// * <U as Iterator>::Item = T
138+
// * T: Debug
139+
// * U: Iterator
140+
let mut i = 0;
141+
let mut changed = true;
142+
while changed {
143+
changed = false;
144+
145+
for j in i..predicates.len() {
146+
147+
if let ty::Predicate::Projection(ref poly_projection) = predicates[j] {
148+
// Note that we can skip binder here because the impl
149+
// trait ref never contains any late-bound regions.
150+
let projection = poly_projection.skip_binder();
151+
152+
// Special case: watch out for some kind of sneaky attempt
153+
// to project out an associated type defined by this very
154+
// trait.
155+
let unbound_trait_ref = &projection.projection_ty.trait_ref;
156+
if Some(unbound_trait_ref.clone()) == impl_trait_ref {
157+
continue;
158+
}
159+
160+
let inputs = parameters_for_trait_ref(&projection.projection_ty.trait_ref);
161+
let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(&p));
162+
if !relies_only_on_inputs {
163+
continue;
164+
}
115165
input_parameters.extend(parameters_for_type(projection.ty));
166+
} else {
167+
continue;
116168
}
117-
}
118-
119-
if input_parameters.len() == num_inputs {
120-
break;
169+
// fancy control flow to bypass borrow checker
170+
predicates.swap(i, j);
171+
i += 1;
172+
changed = true;
121173
}
122174
}
123175
}

0 commit comments

Comments
 (0)