diff --git a/mk/main.mk b/mk/main.mk
index 22c9b4e8c86d..f13355517d86 100644
--- a/mk/main.mk
+++ b/mk/main.mk
@@ -18,7 +18,7 @@ CFG_RELEASE_NUM=1.4.0
# An optional number to put after the label, e.g. '.2' -> '-beta.2'
# NB Make sure it starts with a dot to conform to semver pre-release
# versions (section 9)
-CFG_PRERELEASE_VERSION=.3
+CFG_PRERELEASE_VERSION=.4
# Append a version-dependent hash to each library, so we can install different
# versions in the same place
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index f08cbb414385..af6211665f16 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -24,6 +24,7 @@ use middle::ty::{self, Ty};
use std::cell::RefCell;
use std::collections::hash_map::Entry;
+use std::fmt;
use std::mem;
use syntax::codemap::{self, Span};
use syntax::ast::{self, NodeId};
@@ -34,9 +35,25 @@ use rustc_front::hir::{Block, Item, FnDecl, Arm, Pat, Stmt, Expr, Local};
use rustc_front::util::stmt_id;
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable,
- RustcDecodable, Debug, Copy)]
+ RustcDecodable, Copy)]
pub struct CodeExtent(u32);
+impl fmt::Debug for CodeExtent {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ try!(write!(f, "CodeExtent({:?}", self.0));
+
+ try!(ty::tls::with_opt(|opt_tcx| {
+ if let Some(tcx) = opt_tcx {
+ let data = tcx.region_maps.code_extents.borrow()[self.0 as usize];
+ try!(write!(f, "/{:?}", data));
+ }
+ Ok(())
+ }));
+
+ write!(f, ")")
+ }
+}
+
/// The root of everything. I should be using NonZero or profiling
/// instead of this (probably).
pub const ROOT_CODE_EXTENT : CodeExtent = CodeExtent(0);
diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs
index a2c09f88ab33..bf90b3a02a87 100644
--- a/src/librustc/middle/traits/project.rs
+++ b/src/librustc/middle/traits/project.rs
@@ -54,9 +54,19 @@ pub struct MismatchedProjectionTypes<'tcx> {
#[derive(PartialEq, Eq, Debug)]
enum ProjectionTyCandidate<'tcx> {
+ // from a where-clause in the env or object type
ParamEnv(ty::PolyProjectionPredicate<'tcx>),
+
+ // from the definition of `Trait` when you have something like <::B as Trait2>::C
+ TraitDef(ty::PolyProjectionPredicate<'tcx>),
+
+ // defined in an impl
Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>),
+
+ // closure return type
Closure(VtableClosureData<'tcx, PredicateObligation<'tcx>>),
+
+ // fn pointer return type
FnPointer(Ty<'tcx>),
}
@@ -491,7 +501,11 @@ fn project_type<'cx,'tcx>(
candidates.vec.len(),
candidates.ambiguous);
- // We probably need some winnowing logic similar to select here.
+ // Inherent ambiguity that prevents us from even enumerating the
+ // candidates.
+ if candidates.ambiguous {
+ return Err(ProjectionTyError::TooManyCandidates);
+ }
// Drop duplicates.
//
@@ -512,10 +526,30 @@ fn project_type<'cx,'tcx>(
}
}
- if candidates.ambiguous || candidates.vec.len() > 1 {
- return Err(ProjectionTyError::TooManyCandidates);
+ // Prefer where-clauses. As in select, if there are multiple
+ // candidates, we prefer where-clause candidates over impls. This
+ // may seem a bit surprising, since impls are the source of
+ // "truth" in some sense, but in fact some of the impls that SEEM
+ // applicable are not, because of nested obligations. Where
+ // clauses are the safer choice. See the comment on
+ // `select::SelectionCandidate` and #21974 for more details.
+ if candidates.vec.len() > 1 {
+ debug!("retaining param-env candidates only from {:?}", candidates.vec);
+ candidates.vec.retain(|c| match *c {
+ ProjectionTyCandidate::ParamEnv(..) => true,
+ ProjectionTyCandidate::Impl(..) |
+ ProjectionTyCandidate::Closure(..) |
+ ProjectionTyCandidate::TraitDef(..) |
+ ProjectionTyCandidate::FnPointer(..) => false,
+ });
+ debug!("resulting candidate set: {:?}", candidates.vec);
+ if candidates.vec.len() != 1 {
+ return Err(ProjectionTyError::TooManyCandidates);
+ }
}
+ assert!(candidates.vec.len() <= 1);
+
match candidates.vec.pop() {
Some(candidate) => {
let (ty, obligations) = confirm_candidate(selcx, obligation, candidate);
@@ -538,9 +572,14 @@ fn assemble_candidates_from_param_env<'cx,'tcx>(
obligation_trait_ref: &ty::TraitRef<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
{
+ debug!("assemble_candidates_from_param_env(..)");
let env_predicates = selcx.param_env().caller_bounds.iter().cloned();
- assemble_candidates_from_predicates(selcx, obligation, obligation_trait_ref,
- candidate_set, env_predicates);
+ assemble_candidates_from_predicates(selcx,
+ obligation,
+ obligation_trait_ref,
+ candidate_set,
+ ProjectionTyCandidate::ParamEnv,
+ env_predicates);
}
/// In the case of a nested projection like <::FooT as Bar>::BarT, we may find
@@ -559,6 +598,8 @@ fn assemble_candidates_from_trait_def<'cx,'tcx>(
obligation_trait_ref: &ty::TraitRef<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>)
{
+ debug!("assemble_candidates_from_trait_def(..)");
+
// Check whether the self-type is itself a projection.
let trait_ref = match obligation_trait_ref.self_ty().sty {
ty::TyProjection(ref data) => data.trait_ref.clone(),
@@ -575,8 +616,12 @@ fn assemble_candidates_from_trait_def<'cx,'tcx>(
let trait_predicates = selcx.tcx().lookup_predicates(trait_ref.def_id);
let bounds = trait_predicates.instantiate(selcx.tcx(), trait_ref.substs);
let bounds = elaborate_predicates(selcx.tcx(), bounds.predicates.into_vec());
- assemble_candidates_from_predicates(selcx, obligation, obligation_trait_ref,
- candidate_set, bounds)
+ assemble_candidates_from_predicates(selcx,
+ obligation,
+ obligation_trait_ref,
+ candidate_set,
+ ProjectionTyCandidate::TraitDef,
+ bounds)
}
fn assemble_candidates_from_predicates<'cx,'tcx,I>(
@@ -584,6 +629,7 @@ fn assemble_candidates_from_predicates<'cx,'tcx,I>(
obligation: &ProjectionTyObligation<'tcx>,
obligation_trait_ref: &ty::TraitRef<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
+ ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>,
env_predicates: I)
where I: Iterator- >
{
@@ -614,8 +660,7 @@ fn assemble_candidates_from_predicates<'cx,'tcx,I>(
data, is_match, same_name);
if is_match {
- candidate_set.vec.push(
- ProjectionTyCandidate::ParamEnv(data.clone()));
+ candidate_set.vec.push(ctor(data.clone()));
}
}
_ => { }
@@ -647,8 +692,12 @@ fn assemble_candidates_from_object_type<'cx,'tcx>(
.map(|p| p.to_predicate())
.collect();
let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates);
- assemble_candidates_from_predicates(selcx, obligation, obligation_trait_ref,
- candidate_set, env_predicates)
+ assemble_candidates_from_predicates(selcx,
+ obligation,
+ obligation_trait_ref,
+ candidate_set,
+ ProjectionTyCandidate::ParamEnv,
+ env_predicates)
}
fn assemble_candidates_from_impls<'cx,'tcx>(
@@ -746,7 +795,8 @@ fn confirm_candidate<'cx,'tcx>(
obligation);
match candidate {
- ProjectionTyCandidate::ParamEnv(poly_projection) => {
+ ProjectionTyCandidate::ParamEnv(poly_projection) |
+ ProjectionTyCandidate::TraitDef(poly_projection) => {
confirm_param_env_candidate(selcx, obligation, poly_projection)
}
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index 53beb6d312d8..989a586cee93 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -90,7 +90,7 @@ pub fn parse_pretty(sess: &Session,
("expanded,identified", _) => PpmSource(PpmExpandedIdentified),
("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene),
("hir", true) => PpmHir(PpmNormal),
- ("hir,identified", true) => PpmHir(PpmExpandedIdentified),
+ ("hir,identified", true) => PpmHir(PpmIdentified),
("hir,typed", true) => PpmHir(PpmTyped),
("flowgraph", true) => PpmFlowGraph(PpFlowGraphMode::Default),
("flowgraph,unlabelled", true) => PpmFlowGraph(PpFlowGraphMode::UnlabelledEdges),
diff --git a/src/test/run-pass/issue-28871.rs b/src/test/run-pass/issue-28871.rs
new file mode 100644
index 000000000000..92ba98f50625
--- /dev/null
+++ b/src/test/run-pass/issue-28871.rs
@@ -0,0 +1,33 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #28871. The problem is that rustc encountered
+// two ways to project, one from a where clause and one from the where
+// clauses on the trait definition. (In fact, in this case, the where
+// clauses originated from the trait definition as well.) The true
+// cause of the error is that the trait definition where clauses are
+// not being normalized, and hence the two sources are considered in
+// conflict, and not a duplicate. Hacky solution is to prefer where
+// clauses over the data found in the trait definition.
+
+trait T {
+ type T;
+}
+
+struct S;
+impl T for S {
+ type T = S;
+}
+
+trait T2 {
+ type T: Iterator
- ::T>;
+}
+
+fn main() { }