Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround for #28871 #29241

Merged
merged 4 commits into from
Oct 23, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/librustc/middle/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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);
Expand Down
74 changes: 62 additions & 12 deletions src/librustc/middle/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<A as Trait>::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>),
}

Expand Down Expand Up @@ -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.
//
Expand All @@ -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);
Expand All @@ -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 <<A as Foo>::FooT as Bar>::BarT, we may find
Expand All @@ -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(),
Expand All @@ -575,15 +616,20 @@ 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>(
selcx: &mut SelectionContext<'cx,'tcx>,
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<Item=ty::Predicate<'tcx>>
{
Expand Down Expand Up @@ -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()));
}
}
_ => { }
Expand Down Expand Up @@ -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>(
Expand Down Expand Up @@ -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)
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_driver/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,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),
Expand Down
33 changes: 33 additions & 0 deletions src/test/run-pass/issue-28871.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Item=<S as T>::T>;
}

fn main() { }