Skip to content

Commit 47424cd

Browse files
committed
Normalize bounds that we extract from where clauses. Fixes rust-lang#20765.
1 parent b21a6da commit 47424cd

File tree

6 files changed

+129
-30
lines changed

6 files changed

+129
-30
lines changed

src/librustc/middle/traits/mod.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,10 @@ pub enum Vtable<'tcx, N> {
218218
VtableImpl(VtableImplData<'tcx, N>),
219219

220220
/// Successful resolution to an obligation provided by the caller
221-
/// for some type parameter.
222-
VtableParam,
221+
/// for some type parameter. The `Vec<N>` represents the
222+
/// obligations incurred from normalizing the where-clause (if
223+
/// any).
224+
VtableParam(Vec<N>),
223225

224226
/// Virtual calls through an object
225227
VtableObject(VtableObjectData<'tcx>),
@@ -443,7 +445,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
443445
VtableImpl(ref i) => i.iter_nested(),
444446
VtableFnPointer(..) => (&[]).iter(),
445447
VtableUnboxedClosure(..) => (&[]).iter(),
446-
VtableParam => (&[]).iter(),
448+
VtableParam(ref n) => n.iter(),
447449
VtableObject(_) => (&[]).iter(),
448450
VtableBuiltin(ref i) => i.iter_nested(),
449451
}
@@ -454,7 +456,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
454456
VtableImpl(ref i) => VtableImpl(i.map_nested(op)),
455457
VtableFnPointer(ref sig) => VtableFnPointer((*sig).clone()),
456458
VtableUnboxedClosure(d, ref s) => VtableUnboxedClosure(d, s.clone()),
457-
VtableParam => VtableParam,
459+
VtableParam(ref n) => VtableParam(n.iter().map(op).collect()),
458460
VtableObject(ref p) => VtableObject(p.clone()),
459461
VtableBuiltin(ref b) => VtableBuiltin(b.map_nested(op)),
460462
}
@@ -467,7 +469,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
467469
VtableImpl(i) => VtableImpl(i.map_move_nested(op)),
468470
VtableFnPointer(sig) => VtableFnPointer(sig),
469471
VtableUnboxedClosure(d, s) => VtableUnboxedClosure(d, s),
470-
VtableParam => VtableParam,
472+
VtableParam(n) => VtableParam(n.into_iter().map(op).collect()),
471473
VtableObject(p) => VtableObject(p),
472474
VtableBuiltin(no) => VtableBuiltin(no.map_move_nested(op)),
473475
}

src/librustc/middle/traits/select.rs

+83-21
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
747747
}
748748

749749
self.assemble_candidates_from_projected_tys(obligation, &mut candidates);
750-
try!(self.assemble_candidates_from_caller_bounds(obligation, &mut candidates));
750+
try!(self.assemble_candidates_from_caller_bounds(stack, &mut candidates));
751751
debug!("candidate list size: {}", candidates.vec.len());
752752
Ok(candidates)
753753
}
@@ -884,13 +884,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
884884
/// supplied to find out whether it is listed among them.
885885
///
886886
/// Never affects inference environment.
887-
fn assemble_candidates_from_caller_bounds(&mut self,
888-
obligation: &TraitObligation<'tcx>,
889-
candidates: &mut SelectionCandidateSet<'tcx>)
890-
-> Result<(),SelectionError<'tcx>>
887+
fn assemble_candidates_from_caller_bounds<'o>(&mut self,
888+
stack: &TraitObligationStack<'o, 'tcx>,
889+
candidates: &mut SelectionCandidateSet<'tcx>)
890+
-> Result<(),SelectionError<'tcx>>
891891
{
892892
debug!("assemble_candidates_from_caller_bounds({})",
893-
obligation.repr(self.tcx()));
893+
stack.obligation.repr(self.tcx()));
894894

895895
let caller_trait_refs: Vec<_> =
896896
self.param_env().caller_bounds.predicates.iter()
@@ -903,8 +903,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
903903

904904
let matching_bounds =
905905
all_bounds.filter(
906-
|bound| self.infcx.probe(
907-
|_| self.match_poly_trait_ref(obligation, bound.clone())).is_ok());
906+
|bound| self.evaluate_where_clause(stack, bound.clone()).may_apply());
908907

909908
let param_candidates =
910909
matching_bounds.map(|bound| ParamCandidate(bound));
@@ -914,6 +913,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
914913
Ok(())
915914
}
916915

916+
fn evaluate_where_clause<'o>(&mut self,
917+
stack: &TraitObligationStack<'o, 'tcx>,
918+
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
919+
-> EvaluationResult<'tcx>
920+
{
921+
self.infcx().probe(move |_| {
922+
match self.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) {
923+
Ok(obligations) => {
924+
self.evaluate_predicates_recursively(Some(stack), obligations.iter())
925+
}
926+
Err(()) => {
927+
EvaluatedToErr(Unimplemented)
928+
}
929+
}
930+
})
931+
}
932+
917933
/// Check for the artificial impl that the compiler will create for an obligation like `X :
918934
/// FnMut<..>` where `X` is an unboxed closure type.
919935
///
@@ -1140,6 +1156,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11401156
candidate_j: &SelectionCandidate<'tcx>)
11411157
-> bool
11421158
{
1159+
if candidate_i == candidate_j {
1160+
return true;
1161+
}
1162+
11431163
match (candidate_i, candidate_j) {
11441164
(&ImplCandidate(impl_def_id), &ParamCandidate(ref bound)) => {
11451165
debug!("Considering whether to drop param {} in favor of impl {}",
@@ -1179,8 +1199,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11791199
// the where clauses are in scope.
11801200
true
11811201
}
1202+
(&ParamCandidate(ref bound1), &ParamCandidate(ref bound2)) => {
1203+
self.infcx.probe(|_| {
1204+
let bound1 =
1205+
project::normalize_with_depth(self,
1206+
stack.obligation.cause.clone(),
1207+
stack.obligation.recursion_depth+1,
1208+
bound1);
1209+
let bound2 =
1210+
project::normalize_with_depth(self,
1211+
stack.obligation.cause.clone(),
1212+
stack.obligation.recursion_depth+1,
1213+
bound2);
1214+
let origin =
1215+
infer::RelateOutputImplTypes(stack.obligation.cause.span);
1216+
self.infcx
1217+
.sub_poly_trait_refs(false, origin, bound1.value, bound2.value)
1218+
.is_ok()
1219+
})
1220+
}
11821221
_ => {
1183-
*candidate_i == *candidate_j
1222+
false
11841223
}
11851224
}
11861225
}
@@ -1548,8 +1587,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15481587
}
15491588

15501589
ParamCandidate(param) => {
1551-
self.confirm_param_candidate(obligation, param);
1552-
Ok(VtableParam)
1590+
let obligations = self.confirm_param_candidate(obligation, param);
1591+
Ok(VtableParam(obligations))
15531592
}
15541593

15551594
ImplCandidate(impl_def_id) => {
@@ -1576,7 +1615,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15761615

15771616
ProjectionCandidate => {
15781617
self.confirm_projection_candidate(obligation);
1579-
Ok(VtableParam)
1618+
Ok(VtableParam(Vec::new()))
15801619
}
15811620
}
15821621
}
@@ -1597,6 +1636,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15971636
fn confirm_param_candidate(&mut self,
15981637
obligation: &TraitObligation<'tcx>,
15991638
param: ty::PolyTraitRef<'tcx>)
1639+
-> Vec<PredicateObligation<'tcx>>
16001640
{
16011641
debug!("confirm_param_candidate({},{})",
16021642
obligation.repr(self.tcx()),
@@ -1606,11 +1646,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16061646
// where-clause trait-ref could be unified with the obligation
16071647
// trait-ref. Repeat that unification now without any
16081648
// transactional boundary; it should not fail.
1609-
match self.confirm_poly_trait_refs(obligation.cause.clone(),
1610-
obligation.predicate.to_poly_trait_ref(),
1611-
param.clone()) {
1612-
Ok(()) => { }
1613-
Err(_) => {
1649+
match self.match_where_clause_trait_ref(obligation, param.clone()) {
1650+
Ok(obligations) => obligations,
1651+
Err(()) => {
16141652
self.tcx().sess.bug(
16151653
format!("Where clause `{}` was applicable to `{}` but now is not",
16161654
param.repr(self.tcx()),
@@ -2037,19 +2075,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
20372075
})
20382076
}
20392077

2078+
/// Normalize `where_clause_trait_ref` and try to match it against
2079+
/// `obligation`. If successful, return any predicates that
2080+
/// result from the normalization. Normalization is necessary
2081+
/// because where-clauses are stored in the parameter environment
2082+
/// unnormalized.
2083+
fn match_where_clause_trait_ref(&mut self,
2084+
obligation: &TraitObligation<'tcx>,
2085+
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
2086+
-> Result<Vec<PredicateObligation<'tcx>>,()>
2087+
{
2088+
let where_clause_trait_ref =
2089+
project::normalize_with_depth(self,
2090+
obligation.cause.clone(),
2091+
obligation.recursion_depth+1,
2092+
&where_clause_trait_ref);
2093+
2094+
let () =
2095+
try!(self.match_poly_trait_ref(obligation, where_clause_trait_ref.value.clone()));
2096+
2097+
Ok(where_clause_trait_ref.obligations)
2098+
}
2099+
2100+
/// Returns `Ok` if `poly_trait_ref` being true implies that the
2101+
/// obligation is satisfied.
20402102
fn match_poly_trait_ref(&mut self,
20412103
obligation: &TraitObligation<'tcx>,
2042-
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
2104+
poly_trait_ref: ty::PolyTraitRef<'tcx>)
20432105
-> Result<(),()>
20442106
{
2045-
debug!("match_poly_trait_ref: obligation={} where_clause_trait_ref={}",
2107+
debug!("match_poly_trait_ref: obligation={} poly_trait_ref={}",
20462108
obligation.repr(self.tcx()),
2047-
where_clause_trait_ref.repr(self.tcx()));
2109+
poly_trait_ref.repr(self.tcx()));
20482110

20492111
let origin = infer::RelateOutputImplTypes(obligation.cause.span);
20502112
match self.infcx.sub_poly_trait_refs(false,
20512113
origin,
2052-
where_clause_trait_ref,
2114+
poly_trait_ref,
20532115
obligation.predicate.to_poly_trait_ref()) {
20542116
Ok(()) => Ok(()),
20552117
Err(_) => Err(()),

src/librustc/middle/traits/util.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,9 @@ impl<'tcx, N:Repr<'tcx>> Repr<'tcx> for super::Vtable<'tcx, N> {
380380
format!("VtableObject({})",
381381
d.repr(tcx)),
382382

383-
super::VtableParam =>
384-
format!("VtableParam"),
383+
super::VtableParam(ref n) =>
384+
format!("VtableParam({})",
385+
n.repr(tcx)),
385386

386387
super::VtableBuiltin(ref d) =>
387388
d.repr(tcx)

src/librustc/middle/ty_fold.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
514514
traits::VtableFnPointer(ref d) => {
515515
traits::VtableFnPointer(d.fold_with(folder))
516516
}
517-
traits::VtableParam => traits::VtableParam,
517+
traits::VtableParam(ref n) => traits::VtableParam(n.fold_with(folder)),
518518
traits::VtableBuiltin(ref d) => traits::VtableBuiltin(d.fold_with(folder)),
519519
traits::VtableObject(ref d) => traits::VtableObject(d.fold_with(folder)),
520520
}

src/librustc_trans/trans/meth.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ pub fn get_vtable<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
736736
format!("cannot get vtable for an object type: {}",
737737
data.repr(bcx.tcx())).as_slice());
738738
}
739-
traits::VtableParam => {
739+
traits::VtableParam(..) => {
740740
bcx.sess().bug(
741741
&format!("resolved vtable for {} to bad vtable {} in trans",
742742
trait_ref.repr(bcx.tcx()),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2015 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+
// Test a where clause that uses a non-normalized projection type.
12+
13+
trait Int
14+
{
15+
type T;
16+
}
17+
18+
trait NonZero
19+
{
20+
fn non_zero(self) -> bool;
21+
}
22+
23+
fn foo<I:Int<T=J>,J>(t: I) -> bool
24+
where <I as Int>::T : NonZero
25+
// ^~~~~~~~~~~~~ canonical form is just J
26+
{
27+
bar::<J>()
28+
}
29+
30+
fn bar<NZ:NonZero>() -> bool { true }
31+
32+
fn main ()
33+
{
34+
}

0 commit comments

Comments
 (0)