1
Fork 0

Auto merge of #30533 - nikomatsakis:fulfillment-tree, r=aturon

This PR introduces an `ObligationForest` data structure that the fulfillment context can use to track what's going on, instead of the current flat vector. This enables a number of improvements:

1. transactional support, at least for pushing new obligations
2. remove the "errors will be reported" hack -- instead, we only add types to the global cache once their entire subtree has been proven safe. Before, we never knew when this point was reached because we didn't track the subtree.
   - this in turn allows us to limit coinductive reasoning to structural traits, which sidesteps #29859
3. keeping the backtrace should allow for an improved error message, where we give the user full context
    - we can also remove chained obligation causes

This PR is not 100% complete. In particular:

- [x] Currently, types that embed themselves like `struct Foo { f: Foo }` give an overflow when evaluating whether `Foo: Sized`. This is not a very user-friendly error message, and this is a common beginner error. I plan to special-case this scenario, I think.
- [x] I should do some perf. measurements. (Update: 2% regression.)
- [x] More tests targeting #29859
- [ ] The transactional support is not fully integrated, though that should be easy enough.
- [ ] The error messages are not taking advantage of the backtrace.

I'd certainly like to do 1 through 3 before landing, but 4 and 5 could come as separate PRs.

r? @aturon // good way to learn more about this part of the trait system
f? @arielb1 // already knows this part of the trait system :)
This commit is contained in:
bors 2016-01-16 16:03:22 +00:00
commit c14b615534
66 changed files with 1467 additions and 356 deletions

View file

@ -679,6 +679,43 @@ There's no easy fix for this, generally code will need to be refactored so that
you no longer need to derive from `Super<Self>`. you no longer need to derive from `Super<Self>`.
"####, "####,
E0072: r##"
When defining a recursive struct or enum, any use of the type being defined
from inside the definition must occur behind a pointer (like `Box` or `&`).
This is because structs and enums must have a well-defined size, and without
the pointer the size of the type would need to be unbounded.
Consider the following erroneous definition of a type for a list of bytes:
```
// error, invalid recursive struct type
struct ListNode {
head: u8,
tail: Option<ListNode>,
}
```
This type cannot have a well-defined size, because it needs to be arbitrarily
large (since we would be able to nest `ListNode`s to any depth). Specifically,
```plain
size of `ListNode` = 1 byte for `head`
+ 1 byte for the discriminant of the `Option`
+ size of `ListNode`
```
One way to fix this is by wrapping `ListNode` in a `Box`, like so:
```
struct ListNode {
head: u8,
tail: Option<Box<ListNode>>,
}
```
This works because `Box` is a pointer, so its size is well-known.
"##,
E0109: r##" E0109: r##"
You tried to give a type parameter to a type which doesn't need it. Erroneous You tried to give a type parameter to a type which doesn't need it. Erroneous
code example: code example:

View file

@ -125,7 +125,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
None => self.tcx.empty_parameter_environment() None => self.tcx.empty_parameter_environment()
}; };
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env), false); let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
f(&mut euv::ExprUseVisitor::new(self, &infcx)) f(&mut euv::ExprUseVisitor::new(self, &infcx))
} }
@ -280,7 +280,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
fn check_static_type(&self, e: &hir::Expr) { fn check_static_type(&self, e: &hir::Expr) {
let ty = self.tcx.node_id_to_type(e.id); let ty = self.tcx.node_id_to_type(e.id);
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None, false); let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic); let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut(); let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut();
fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause); fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);

View file

@ -1095,8 +1095,7 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
//FIXME: (@jroesch) this code should be floated up as well //FIXME: (@jroesch) this code should be floated up as well
let infcx = infer::new_infer_ctxt(cx.tcx, let infcx = infer::new_infer_ctxt(cx.tcx,
&cx.tcx.tables, &cx.tcx.tables,
Some(cx.param_env.clone()), Some(cx.param_env.clone()));
false);
if infcx.type_moves_by_default(pat_ty, pat.span) { if infcx.type_moves_by_default(pat_ty, pat.span) {
check_move(p, sub.as_ref().map(|p| &**p)); check_move(p, sub.as_ref().map(|p| &**p));
} }
@ -1128,8 +1127,7 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>,
let infcx = infer::new_infer_ctxt(cx.tcx, let infcx = infer::new_infer_ctxt(cx.tcx,
&cx.tcx.tables, &cx.tcx.tables,
Some(checker.cx.param_env.clone()), Some(checker.cx.param_env.clone()));
false);
let mut visitor = ExprUseVisitor::new(&mut checker, &infcx); let mut visitor = ExprUseVisitor::new(&mut checker, &infcx);
visitor.walk_expr(guard); visitor.walk_expr(guard);

View file

@ -44,8 +44,7 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for RvalueContext<'a, 'tcx> {
let param_env = ParameterEnvironment::for_item(self.tcx, fn_id); let param_env = ParameterEnvironment::for_item(self.tcx, fn_id);
let infcx = infer::new_infer_ctxt(self.tcx, let infcx = infer::new_infer_ctxt(self.tcx,
&self.tcx.tables, &self.tcx.tables,
Some(param_env.clone()), Some(param_env.clone()));
false);
let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: &param_env }; let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: &param_env };
let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx); let mut euv = euv::ExprUseVisitor::new(&mut delegate, &infcx);
euv.walk_fn(fd, b); euv.walk_fn(fd, b);

View file

@ -1262,7 +1262,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
substs: trait_substs }); substs: trait_substs });
tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id()); tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id());
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
let mut selcx = traits::SelectionContext::new(&infcx); let mut selcx = traits::SelectionContext::new(&infcx);
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),

View file

@ -354,16 +354,9 @@ pub fn fixup_err_to_string(f: FixupError) -> String {
} }
} }
/// errors_will_be_reported is required to proxy to the fulfillment context
/// FIXME -- a better option would be to hold back on modifying
/// the global cache until we know that all dependent obligations
/// are also satisfied. In that case, we could actually remove
/// this boolean flag, and we'd also avoid the problem of squelching
/// duplicate errors that occur across fns.
pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>, pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
tables: &'a RefCell<ty::Tables<'tcx>>, tables: &'a RefCell<ty::Tables<'tcx>>,
param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>, param_env: Option<ty::ParameterEnvironment<'a, 'tcx>>)
errors_will_be_reported: bool)
-> InferCtxt<'a, 'tcx> { -> InferCtxt<'a, 'tcx> {
InferCtxt { InferCtxt {
tcx: tcx, tcx: tcx,
@ -373,7 +366,7 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
float_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()),
region_vars: RegionVarBindings::new(tcx), region_vars: RegionVarBindings::new(tcx),
parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()), parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()),
fulfillment_cx: RefCell::new(traits::FulfillmentContext::new(errors_will_be_reported)), fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()),
reported_trait_errors: RefCell::new(FnvHashSet()), reported_trait_errors: RefCell::new(FnvHashSet()),
normalize: false, normalize: false,
err_count_on_creation: tcx.sess.err_count() err_count_on_creation: tcx.sess.err_count()
@ -383,7 +376,7 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
pub fn normalizing_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>, pub fn normalizing_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
tables: &'a RefCell<ty::Tables<'tcx>>) tables: &'a RefCell<ty::Tables<'tcx>>)
-> InferCtxt<'a, 'tcx> { -> InferCtxt<'a, 'tcx> {
let mut infcx = new_infer_ctxt(tcx, tables, None, false); let mut infcx = new_infer_ctxt(tcx, tables, None);
infcx.normalize = true; infcx.normalize = true;
infcx infcx
} }
@ -522,7 +515,7 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T
return value; return value;
} }
let infcx = new_infer_ctxt(tcx, &tcx.tables, None, true); let infcx = new_infer_ctxt(tcx, &tcx.tables, None);
let mut selcx = traits::SelectionContext::new(&infcx); let mut selcx = traits::SelectionContext::new(&infcx);
let cause = traits::ObligationCause::dummy(); let cause = traits::ObligationCause::dummy();
let traits::Normalized { value: result, obligations } = let traits::Normalized { value: result, obligations } =

View file

@ -182,7 +182,8 @@ fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
/// if the program type checks or not -- and they are unusual /// if the program type checks or not -- and they are unusual
/// occurrences in any case. /// occurrences in any case.
pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
obligation: &Obligation<'tcx, T>) obligation: &Obligation<'tcx, T>,
suggest_increasing_limit: bool)
-> ! -> !
where T: fmt::Display + TypeFoldable<'tcx> where T: fmt::Display + TypeFoldable<'tcx>
{ {
@ -192,7 +193,9 @@ pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
"overflow evaluating the requirement `{}`", "overflow evaluating the requirement `{}`",
predicate); predicate);
suggest_new_overflow_limit(infcx.tcx, &mut err, obligation.cause.span); if suggest_increasing_limit {
suggest_new_overflow_limit(infcx.tcx, &mut err, obligation.cause.span);
}
note_obligation_cause(infcx, &mut err, obligation); note_obligation_cause(infcx, &mut err, obligation);
@ -201,6 +204,142 @@ pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
unreachable!(); unreachable!();
} }
/// Reports that a cycle was detected which led to overflow and halts
/// compilation. This is equivalent to `report_overflow_error` except
/// that we can give a more helpful error message (and, in particular,
/// we do not suggest increasing the overflow limit, which is not
/// going to help).
pub fn report_overflow_error_cycle<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
cycle: &Vec<PredicateObligation<'tcx>>)
-> !
{
assert!(cycle.len() > 1);
debug!("report_overflow_error_cycle(cycle length = {})", cycle.len());
let cycle = infcx.resolve_type_vars_if_possible(cycle);
debug!("report_overflow_error_cycle: cycle={:?}", cycle);
assert_eq!(&cycle[0].predicate, &cycle.last().unwrap().predicate);
try_report_overflow_error_type_of_infinite_size(infcx, &cycle);
report_overflow_error(infcx, &cycle[0], false);
}
/// If a cycle results from evaluated whether something is Sized, that
/// is a particular special case that always results from a struct or
/// enum definition that lacks indirection (e.g., `struct Foo { x: Foo
/// }`). We wish to report a targeted error for this case.
pub fn try_report_overflow_error_type_of_infinite_size<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
cycle: &[PredicateObligation<'tcx>])
{
let sized_trait = match infcx.tcx.lang_items.sized_trait() {
Some(v) => v,
None => return,
};
let top_is_sized = {
match cycle[0].predicate {
ty::Predicate::Trait(ref data) => data.def_id() == sized_trait,
_ => false,
}
};
if !top_is_sized {
return;
}
// The only way to have a type of infinite size is to have,
// somewhere, a struct/enum type involved. Identify all such types
// and report the cycle to the user.
let struct_enum_tys: Vec<_> =
cycle.iter()
.flat_map(|obligation| match obligation.predicate {
ty::Predicate::Trait(ref data) => {
assert_eq!(data.def_id(), sized_trait);
let self_ty = data.skip_binder().trait_ref.self_ty(); // (*)
// (*) ok to skip binder because this is just
// error reporting and regions don't really
// matter
match self_ty.sty {
ty::TyEnum(..) | ty::TyStruct(..) => Some(self_ty),
_ => None,
}
}
_ => {
infcx.tcx.sess.span_bug(obligation.cause.span,
&format!("Sized cycle involving non-trait-ref: {:?}",
obligation.predicate));
}
})
.collect();
assert!(!struct_enum_tys.is_empty());
// This is a bit tricky. We want to pick a "main type" in the
// listing that is local to the current crate, so we can give a
// good span to the user. But it might not be the first one in our
// cycle list. So find the first one that is local and then
// rotate.
let (main_index, main_def_id) =
struct_enum_tys.iter()
.enumerate()
.filter_map(|(index, ty)| match ty.sty {
ty::TyEnum(adt_def, _) | ty::TyStruct(adt_def, _)
if adt_def.did.is_local() =>
Some((index, adt_def.did)),
_ =>
None,
})
.next()
.unwrap(); // should always be SOME local type involved!
// Rotate so that the "main" type is at index 0.
let struct_enum_tys: Vec<_> =
struct_enum_tys.iter()
.cloned()
.skip(main_index)
.chain(struct_enum_tys.iter().cloned().take(main_index))
.collect();
let tcx = infcx.tcx;
let mut err = recursive_type_with_infinite_size_error(tcx, main_def_id);
let len = struct_enum_tys.len();
if len > 2 {
let span = tcx.map.span_if_local(main_def_id).unwrap();
err.fileline_note(span,
&format!("type `{}` is embedded within `{}`...",
struct_enum_tys[0],
struct_enum_tys[1]));
for &next_ty in &struct_enum_tys[1..len-1] {
err.fileline_note(span,
&format!("...which in turn is embedded within `{}`...", next_ty));
}
err.fileline_note(span,
&format!("...which in turn is embedded within `{}`, \
completing the cycle.",
struct_enum_tys[len-1]));
}
err.emit();
infcx.tcx.sess.abort_if_errors();
unreachable!();
}
pub fn recursive_type_with_infinite_size_error<'tcx>(tcx: &ty::ctxt<'tcx>,
type_def_id: DefId)
-> DiagnosticBuilder<'tcx>
{
assert!(type_def_id.is_local());
let span = tcx.map.span_if_local(type_def_id).unwrap();
let mut err = struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size",
tcx.item_path_str(type_def_id));
err.fileline_help(span, &format!("insert indirection (e.g., a `Box`, `Rc`, or `&`) \
at some point to make `{}` representable",
tcx.item_path_str(type_def_id)));
err
}
pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>) error: &SelectionError<'tcx>)

View file

@ -10,19 +10,22 @@
use middle::infer::InferCtxt; use middle::infer::InferCtxt;
use middle::ty::{self, Ty, TypeFoldable}; use middle::ty::{self, Ty, TypeFoldable};
use rustc_data_structures::obligation_forest::{Backtrace, ObligationForest, Error};
use std::iter;
use syntax::ast; use syntax::ast;
use util::common::ErrorReported; use util::common::ErrorReported;
use util::nodemap::{FnvHashSet, NodeMap}; use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
use super::CodeAmbiguity; use super::CodeAmbiguity;
use super::CodeProjectionError; use super::CodeProjectionError;
use super::CodeSelectionError; use super::CodeSelectionError;
use super::is_object_safe; use super::is_object_safe;
use super::FulfillmentError; use super::FulfillmentError;
use super::FulfillmentErrorCode;
use super::ObligationCause; use super::ObligationCause;
use super::PredicateObligation; use super::PredicateObligation;
use super::project; use super::project;
use super::report_overflow_error_cycle;
use super::select::SelectionContext; use super::select::SelectionContext;
use super::Unimplemented; use super::Unimplemented;
use super::util::predicate_for_builtin_bound; use super::util::predicate_for_builtin_bound;
@ -57,12 +60,7 @@ pub struct FulfillmentContext<'tcx> {
// A list of all obligations that have been registered with this // A list of all obligations that have been registered with this
// fulfillment context. // fulfillment context.
predicates: Vec<PredicateObligation<'tcx>>, predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
// Remembers the count of trait obligations that we have already
// attempted to select. This is used to avoid repeating work
// when `select_new_obligations` is called.
attempted_mark: usize,
// A set of constraints that regionck must validate. Each // A set of constraints that regionck must validate. Each
// constraint has the form `T:'a`, meaning "some type `T` must // constraint has the form `T:'a`, meaning "some type `T` must
@ -89,8 +87,6 @@ pub struct FulfillmentContext<'tcx> {
// obligations (otherwise, it's easy to fail to walk to a // obligations (otherwise, it's easy to fail to walk to a
// particular node-id). // particular node-id).
region_obligations: NodeMap<Vec<RegionObligation<'tcx>>>, region_obligations: NodeMap<Vec<RegionObligation<'tcx>>>,
pub errors_will_be_reported: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -100,31 +96,19 @@ pub struct RegionObligation<'tcx> {
pub cause: ObligationCause<'tcx>, pub cause: ObligationCause<'tcx>,
} }
#[derive(Clone, Debug)]
pub struct PendingPredicateObligation<'tcx> {
pub obligation: PredicateObligation<'tcx>,
pub stalled_on: Vec<Ty<'tcx>>,
}
impl<'tcx> FulfillmentContext<'tcx> { impl<'tcx> FulfillmentContext<'tcx> {
/// Creates a new fulfillment context. /// Creates a new fulfillment context.
/// pub fn new() -> FulfillmentContext<'tcx> {
/// `errors_will_be_reported` indicates whether ALL errors that
/// are generated by this fulfillment context will be reported to
/// the end user. This is used to inform caching, because it
/// allows us to conclude that traits that resolve successfully
/// will in fact always resolve successfully (in particular, it
/// guarantees that if some dependent obligation encounters a
/// problem, compilation will be aborted). If you're not sure of
/// the right value here, pass `false`, as that is the more
/// conservative option.
///
/// FIXME -- a better option would be to hold back on modifying
/// the global cache until we know that all dependent obligations
/// are also satisfied. In that case, we could actually remove
/// this boolean flag, and we'd also avoid the problem of squelching
/// duplicate errors that occur across fns.
pub fn new(errors_will_be_reported: bool) -> FulfillmentContext<'tcx> {
FulfillmentContext { FulfillmentContext {
duplicate_set: FulfilledPredicates::new(), duplicate_set: FulfilledPredicates::new(),
predicates: Vec::new(), predicates: ObligationForest::new(),
attempted_mark: 0,
region_obligations: NodeMap(), region_obligations: NodeMap(),
errors_will_be_reported: errors_will_be_reported,
} }
} }
@ -198,7 +182,11 @@ impl<'tcx> FulfillmentContext<'tcx> {
} }
debug!("register_predicate({:?})", obligation); debug!("register_predicate({:?})", obligation);
self.predicates.push(obligation); let obligation = PendingPredicateObligation {
obligation: obligation,
stalled_on: vec![]
};
self.predicates.push_root(obligation);
} }
pub fn region_obligations(&self, pub fn region_obligations(&self,
@ -216,14 +204,11 @@ impl<'tcx> FulfillmentContext<'tcx> {
-> Result<(),Vec<FulfillmentError<'tcx>>> -> Result<(),Vec<FulfillmentError<'tcx>>>
{ {
try!(self.select_where_possible(infcx)); try!(self.select_where_possible(infcx));
let errors: Vec<_> =
// Anything left is ambiguous. self.predicates.to_errors(CodeAmbiguity)
let errors: Vec<FulfillmentError> = .into_iter()
self.predicates .map(|e| to_fulfillment_error(e))
.iter() .collect();
.map(|o| FulfillmentError::new((*o).clone(), CodeAmbiguity))
.collect();
if errors.is_empty() { if errors.is_empty() {
Ok(()) Ok(())
} else { } else {
@ -231,119 +216,91 @@ impl<'tcx> FulfillmentContext<'tcx> {
} }
} }
/// Attempts to select obligations that were registered since the call to a selection routine.
/// This is used by the type checker to eagerly attempt to resolve obligations in hopes of
/// gaining type information. It'd be equally valid to use `select_where_possible` but it
/// results in `O(n^2)` performance (#18208).
pub fn select_new_obligations<'a>(&mut self,
infcx: &InferCtxt<'a,'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>>
{
let mut selcx = SelectionContext::new(infcx);
self.select(&mut selcx, true)
}
pub fn select_where_possible<'a>(&mut self, pub fn select_where_possible<'a>(&mut self,
infcx: &InferCtxt<'a,'tcx>) infcx: &InferCtxt<'a,'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>> -> Result<(),Vec<FulfillmentError<'tcx>>>
{ {
let mut selcx = SelectionContext::new(infcx); let mut selcx = SelectionContext::new(infcx);
self.select(&mut selcx, false) self.select(&mut selcx)
} }
pub fn pending_obligations(&self) -> &[PredicateObligation<'tcx>] { pub fn pending_obligations(&self) -> Vec<PendingPredicateObligation<'tcx>> {
&self.predicates self.predicates.pending_obligations()
} }
fn is_duplicate_or_add(&mut self, fn is_duplicate_or_add(&mut self,
tcx: &ty::ctxt<'tcx>, tcx: &ty::ctxt<'tcx>,
predicate: &ty::Predicate<'tcx>) predicate: &ty::Predicate<'tcx>)
-> bool { -> bool {
// This is a kind of dirty hack to allow us to avoid "rederiving" // For "global" predicates -- that is, predicates that don't
// things that we have already proven in other methods. // involve type parameters, inference variables, or regions
// // other than 'static -- we can check the cache in the tcx,
// The idea is that any predicate that doesn't involve type // which allows us to leverage work from other threads. Note
// parameters and which only involves the 'static region (and // that we don't add anything to this cache yet (unlike the
// no other regions) is universally solvable, since impls are global. // local cache). This is because the tcx cache maintains the
// // invariant that it only contains things that have been
// This is particularly important since even if we have a // proven, and we have not yet proven that `predicate` holds.
// cache hit in the selection context, we still wind up if predicate.is_global() && tcx.fulfilled_predicates.borrow().is_duplicate(predicate) {
// evaluating the 'nested obligations'. This cache lets us return true;
// skip those.
if self.errors_will_be_reported && predicate.is_global() {
tcx.fulfilled_predicates.borrow_mut().is_duplicate_or_add(predicate)
} else {
self.duplicate_set.is_duplicate_or_add(predicate)
} }
// If `predicate` is not global, or not present in the tcx
// cache, we can still check for it in our local cache and add
// it if not present. Note that if we find this predicate in
// the local cache we can stop immediately, without reporting
// any errors, even though we don't know yet if it is
// true. This is because, while we don't yet know if the
// predicate holds, we know that this same fulfillment context
// already is in the process of finding out.
self.duplicate_set.is_duplicate_or_add(predicate)
} }
/// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it /// Attempts to select obligations using `selcx`. If `only_new_obligations` is true, then it
/// only attempts to select obligations that haven't been seen before. /// only attempts to select obligations that haven't been seen before.
fn select<'a>(&mut self, fn select<'a>(&mut self,
selcx: &mut SelectionContext<'a, 'tcx>, selcx: &mut SelectionContext<'a, 'tcx>)
only_new_obligations: bool)
-> Result<(),Vec<FulfillmentError<'tcx>>> -> Result<(),Vec<FulfillmentError<'tcx>>>
{ {
debug!("select({} obligations, only_new_obligations={}) start", debug!("select(obligation-forest-size={})", self.predicates.len());
self.predicates.len(),
only_new_obligations);
let mut errors = Vec::new(); let mut errors = Vec::new();
loop { loop {
let count = self.predicates.len(); debug!("select_where_possible: starting another iteration");
debug!("select_where_possible({} obligations) iteration", // Process pending obligations.
count); let outcome = {
let region_obligations = &mut self.region_obligations;
let mut new_obligations = Vec::new(); self.predicates.process_obligations(
|obligation, backtrace| process_predicate(selcx,
// If we are only attempting obligations we haven't seen yet, obligation,
// then set `skip` to the number of obligations we've already backtrace,
// seen. region_obligations))
let mut skip = if only_new_obligations {
self.attempted_mark
} else {
0
}; };
// First pass: walk each obligation, retaining debug!("select_where_possible: outcome={:?}", outcome);
// only those that we cannot yet process.
{ // these are obligations that were proven to be true.
let region_obligations = &mut self.region_obligations; for pending_obligation in outcome.completed {
self.predicates.retain(|predicate| { let predicate = &pending_obligation.obligation.predicate;
// Hack: Retain does not pass in the index, but we want if predicate.is_global() {
// to avoid processing the first `start_count` entries. selcx.tcx().fulfilled_predicates.borrow_mut()
let processed = .is_duplicate_or_add(predicate);
if skip == 0 { }
process_predicate(selcx, predicate,
&mut new_obligations, &mut errors, region_obligations)
} else {
skip -= 1;
false
};
!processed
});
} }
self.attempted_mark = self.predicates.len(); errors.extend(
outcome.errors.into_iter()
.map(|e| to_fulfillment_error(e)));
if self.predicates.len() == count { // If nothing new was added, no need to keep looping.
// Nothing changed. if outcome.stalled {
break; break;
} }
// Now go through all the successful ones,
// registering any nested obligations for the future.
for new_obligation in new_obligations {
self.register_predicate_obligation(selcx.infcx(), new_obligation);
}
} }
debug!("select({} obligations, {} errors) done", debug!("select({} predicates remaining, {} errors) done",
self.predicates.len(), self.predicates.len(), errors.len());
errors.len());
if errors.is_empty() { if errors.is_empty() {
Ok(()) Ok(())
@ -353,69 +310,168 @@ impl<'tcx> FulfillmentContext<'tcx> {
} }
} }
/// Like `process_predicate1`, but wrap result into a pending predicate.
fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>, fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
obligation: &PredicateObligation<'tcx>, pending_obligation: &mut PendingPredicateObligation<'tcx>,
new_obligations: &mut Vec<PredicateObligation<'tcx>>, backtrace: Backtrace<PendingPredicateObligation<'tcx>>,
errors: &mut Vec<FulfillmentError<'tcx>>,
region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>) region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>)
-> bool -> Result<Option<Vec<PendingPredicateObligation<'tcx>>>,
FulfillmentErrorCode<'tcx>>
{ {
/*! match process_predicate1(selcx, pending_obligation, backtrace, region_obligations) {
* Processes a predicate obligation and modifies the appropriate Ok(Some(v)) => {
* output array with the successful/error result. Returns `false` // FIXME the right thing to do here, I think, is to permit
* if the predicate could not be processed due to insufficient // DAGs. That is, we should detect whenever this predicate
* type inference. // has appeared somewhere in the current tree./ If it's a
*/ // parent, that's a cycle, and we should either error out
// or consider it ok. But if it's NOT a parent, we can
// ignore it, since it will be proven (or not) separately.
// However, this is a touch tricky, so I'm doing something
// a bit hackier for now so that the `huge-struct.rs` passes.
let retain_vec: Vec<_> = {
let mut dedup = FnvHashSet();
v.iter()
.map(|o| {
// Screen out obligations that we know globally
// are true. This should really be the DAG check
// mentioned above.
if
o.predicate.is_global() &&
selcx.tcx().fulfilled_predicates.borrow().is_duplicate(&o.predicate)
{
return false;
}
// If we see two siblings that are exactly the
// same, no need to add them twice.
if !dedup.insert(&o.predicate) {
return false;
}
true
})
.collect()
};
let pending_predicate_obligations =
v.into_iter()
.zip(retain_vec)
.flat_map(|(o, retain)| {
if retain {
Some(PendingPredicateObligation {
obligation: o,
stalled_on: vec![]
})
} else {
None
}
})
.collect();
Ok(Some(pending_predicate_obligations))
}
Ok(None) => Ok(None),
Err(e) => Err(e)
}
}
/// Processes a predicate obligation and returns either:
/// - `Ok(Some(v))` if the predicate is true, presuming that `v` are also true
/// - `Ok(None)` if we don't have enough info to be sure
/// - `Err` if the predicate does not hold
fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
pending_obligation: &mut PendingPredicateObligation<'tcx>,
backtrace: Backtrace<PendingPredicateObligation<'tcx>>,
region_obligations: &mut NodeMap<Vec<RegionObligation<'tcx>>>)
-> Result<Option<Vec<PredicateObligation<'tcx>>>,
FulfillmentErrorCode<'tcx>>
{
// if we were stalled on some unresolved variables, first check
// whether any of them have been resolved; if not, don't bother
// doing more work yet
if !pending_obligation.stalled_on.is_empty() {
if pending_obligation.stalled_on.iter().all(|&ty| {
let resolved_ty = selcx.infcx().resolve_type_vars_if_possible(&ty);
resolved_ty == ty // nothing changed here
}) {
debug!("process_predicate: pending obligation {:?} still stalled on {:?}",
selcx.infcx().resolve_type_vars_if_possible(&pending_obligation.obligation),
pending_obligation.stalled_on);
return Ok(None);
}
pending_obligation.stalled_on = vec![];
}
let obligation = &pending_obligation.obligation;
// If we exceed the recursion limit, take a moment to look for a
// cycle so we can give a better error report from here, where we
// have more context.
let recursion_limit = selcx.tcx().sess.recursion_limit.get();
if obligation.recursion_depth >= recursion_limit {
if let Some(cycle) = scan_for_cycle(obligation, &backtrace) {
report_overflow_error_cycle(selcx.infcx(), &cycle);
}
}
match obligation.predicate { match obligation.predicate {
ty::Predicate::Trait(ref data) => { ty::Predicate::Trait(ref data) => {
if coinductive_match(selcx, obligation, data, &backtrace) {
return Ok(Some(vec![]));
}
let trait_obligation = obligation.with(data.clone()); let trait_obligation = obligation.with(data.clone());
match selcx.select(&trait_obligation) { match selcx.select(&trait_obligation) {
Ok(None) => { Ok(Some(vtable)) => {
false Ok(Some(vtable.nested_obligations()))
} }
Ok(Some(s)) => { Ok(None) => {
new_obligations.append(&mut s.nested_obligations()); // This is a bit subtle: for the most part, the
true // only reason we can fail to make progress on
// trait selection is because we don't have enough
// information about the types in the trait. One
// exception is that we sometimes haven't decided
// what kind of closure a closure is. *But*, in
// that case, it turns out, the type of the
// closure will also change, because the closure
// also includes references to its upvars as part
// of its type, and those types are resolved at
// the same time.
pending_obligation.stalled_on =
data.skip_binder() // ok b/c this check doesn't care about regions
.input_types()
.iter()
.map(|t| selcx.infcx().resolve_type_vars_if_possible(t))
.filter(|t| t.has_infer_types())
.flat_map(|t| t.walk())
.filter(|t| match t.sty { ty::TyInfer(_) => true, _ => false })
.collect();
debug!("process_predicate: pending obligation {:?} now stalled on {:?}",
selcx.infcx().resolve_type_vars_if_possible(obligation),
pending_obligation.stalled_on);
Ok(None)
} }
Err(selection_err) => { Err(selection_err) => {
debug!("predicate: {:?} error: {:?}", Err(CodeSelectionError(selection_err))
obligation,
selection_err);
errors.push(
FulfillmentError::new(
obligation.clone(),
CodeSelectionError(selection_err)));
true
} }
} }
} }
ty::Predicate::Equate(ref binder) => { ty::Predicate::Equate(ref binder) => {
match selcx.infcx().equality_predicate(obligation.cause.span, binder) { match selcx.infcx().equality_predicate(obligation.cause.span, binder) {
Ok(()) => { } Ok(()) => Ok(Some(Vec::new())),
Err(_) => { Err(_) => Err(CodeSelectionError(Unimplemented)),
errors.push(
FulfillmentError::new(
obligation.clone(),
CodeSelectionError(Unimplemented)));
}
} }
true
} }
ty::Predicate::RegionOutlives(ref binder) => { ty::Predicate::RegionOutlives(ref binder) => {
match selcx.infcx().region_outlives_predicate(obligation.cause.span, binder) { match selcx.infcx().region_outlives_predicate(obligation.cause.span, binder) {
Ok(()) => { } Ok(()) => Ok(Some(Vec::new())),
Err(_) => { Err(_) => Err(CodeSelectionError(Unimplemented)),
errors.push(
FulfillmentError::new(
obligation.clone(),
CodeSelectionError(Unimplemented)));
}
} }
true
} }
ty::Predicate::TypeOutlives(ref binder) => { ty::Predicate::TypeOutlives(ref binder) => {
@ -431,10 +487,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
// If so, this obligation is an error (for now). Eventually we should be // If so, this obligation is an error (for now). Eventually we should be
// able to support additional cases here, like `for<'a> &'a str: 'a`. // able to support additional cases here, like `for<'a> &'a str: 'a`.
None => { None => {
errors.push( Err(CodeSelectionError(Unimplemented))
FulfillmentError::new(
obligation.clone(),
CodeSelectionError(Unimplemented)))
} }
// Otherwise, we have something of the form // Otherwise, we have something of the form
// `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`. // `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`.
@ -442,6 +495,7 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
register_region_obligation(t_a, ty::ReStatic, register_region_obligation(t_a, ty::ReStatic,
obligation.cause.clone(), obligation.cause.clone(),
region_obligations); region_obligations);
Ok(Some(vec![]))
} }
} }
} }
@ -450,57 +504,93 @@ fn process_predicate<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
register_region_obligation(t_a, r_b, register_region_obligation(t_a, r_b,
obligation.cause.clone(), obligation.cause.clone(),
region_obligations); region_obligations);
Ok(Some(vec![]))
} }
} }
true
} }
ty::Predicate::Projection(ref data) => { ty::Predicate::Projection(ref data) => {
let project_obligation = obligation.with(data.clone()); let project_obligation = obligation.with(data.clone());
let result = project::poly_project_and_unify_type(selcx, &project_obligation); match project::poly_project_and_unify_type(selcx, &project_obligation) {
debug!("process_predicate: poly_project_and_unify_type({:?}) returned {:?}", Ok(v) => Ok(v),
project_obligation, Err(e) => Err(CodeProjectionError(e))
result);
match result {
Ok(Some(obligations)) => {
new_obligations.extend(obligations);
true
}
Ok(None) => {
false
}
Err(err) => {
errors.push(
FulfillmentError::new(
obligation.clone(),
CodeProjectionError(err)));
true
}
} }
} }
ty::Predicate::ObjectSafe(trait_def_id) => { ty::Predicate::ObjectSafe(trait_def_id) => {
if !is_object_safe(selcx.tcx(), trait_def_id) { if !is_object_safe(selcx.tcx(), trait_def_id) {
errors.push(FulfillmentError::new( Err(CodeSelectionError(Unimplemented))
obligation.clone(), } else {
CodeSelectionError(Unimplemented))); Ok(Some(Vec::new()))
} }
true
} }
ty::Predicate::WellFormed(ty) => { ty::Predicate::WellFormed(ty) => {
match ty::wf::obligations(selcx.infcx(), obligation.cause.body_id, Ok(ty::wf::obligations(selcx.infcx(), obligation.cause.body_id,
ty, obligation.cause.span) { ty, obligation.cause.span))
Some(obligations) => { }
new_obligations.extend(obligations); }
true }
}
None => { /// For defaulted traits, we use a co-inductive strategy to solve, so
false /// that recursion is ok. This routine returns true if the top of the
/// stack (`top_obligation` and `top_data`):
/// - is a defaulted trait, and
/// - it also appears in the backtrace at some position `X`; and,
/// - all the predicates at positions `X..` between `X` an the top are
/// also defaulted traits.
fn coinductive_match<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
top_obligation: &PredicateObligation<'tcx>,
top_data: &ty::PolyTraitPredicate<'tcx>,
backtrace: &Backtrace<PendingPredicateObligation<'tcx>>)
-> bool
{
if selcx.tcx().trait_has_default_impl(top_data.def_id()) {
debug!("coinductive_match: top_data={:?}", top_data);
for bt_obligation in backtrace.clone() {
debug!("coinductive_match: bt_obligation={:?}", bt_obligation);
// *Everything* in the backtrace must be a defaulted trait.
match bt_obligation.obligation.predicate {
ty::Predicate::Trait(ref data) => {
if !selcx.tcx().trait_has_default_impl(data.def_id()) {
debug!("coinductive_match: trait does not have default impl");
break;
}
} }
_ => { break; }
}
// And we must find a recursive match.
if bt_obligation.obligation.predicate == top_obligation.predicate {
debug!("coinductive_match: found a match in the backtrace");
return true;
} }
} }
} }
false
}
fn scan_for_cycle<'a,'tcx>(top_obligation: &PredicateObligation<'tcx>,
backtrace: &Backtrace<PendingPredicateObligation<'tcx>>)
-> Option<Vec<PredicateObligation<'tcx>>>
{
let mut map = FnvHashMap();
let all_obligations =
|| iter::once(top_obligation)
.chain(backtrace.clone()
.map(|p| &p.obligation));
for (index, bt_obligation) in all_obligations().enumerate() {
if let Some(&start) = map.get(&bt_obligation.predicate) {
// Found a cycle starting at position `start` and running
// until the current position (`index`).
return Some(all_obligations().skip(start).take(index - start + 1).cloned().collect());
} else {
map.insert(bt_obligation.predicate.clone(), index);
}
}
None
} }
fn register_region_obligation<'tcx>(t_a: Ty<'tcx>, fn register_region_obligation<'tcx>(t_a: Ty<'tcx>,
@ -536,3 +626,11 @@ impl<'tcx> FulfilledPredicates<'tcx> {
!self.set.insert(key.clone()) !self.set.insert(key.clone())
} }
} }
fn to_fulfillment_error<'tcx>(
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>)
-> FulfillmentError<'tcx>
{
let obligation = error.backtrace.into_iter().next().unwrap().obligation;
FulfillmentError::new(obligation, error.error)
}

View file

@ -28,8 +28,10 @@ use syntax::ast;
use syntax::codemap::{Span, DUMMY_SP}; use syntax::codemap::{Span, DUMMY_SP};
pub use self::error_reporting::TraitErrorKey; pub use self::error_reporting::TraitErrorKey;
pub use self::error_reporting::recursive_type_with_infinite_size_error;
pub use self::error_reporting::report_fulfillment_errors; pub use self::error_reporting::report_fulfillment_errors;
pub use self::error_reporting::report_overflow_error; pub use self::error_reporting::report_overflow_error;
pub use self::error_reporting::report_overflow_error_cycle;
pub use self::error_reporting::report_selection_error; pub use self::error_reporting::report_selection_error;
pub use self::error_reporting::report_object_safety_error; pub use self::error_reporting::report_object_safety_error;
pub use self::coherence::orphan_check; pub use self::coherence::orphan_check;
@ -356,7 +358,7 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
// this function's result remains infallible, we must confirm // this function's result remains infallible, we must confirm
// that guess. While imperfect, I believe this is sound. // that guess. While imperfect, I believe this is sound.
let mut fulfill_cx = FulfillmentContext::new(false); let mut fulfill_cx = FulfillmentContext::new();
// We can use a dummy node-id here because we won't pay any mind // We can use a dummy node-id here because we won't pay any mind
// to region obligations that arise (there shouldn't really be any // to region obligations that arise (there shouldn't really be any
@ -434,8 +436,9 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi
let elaborated_env = unnormalized_env.with_caller_bounds(predicates); let elaborated_env = unnormalized_env.with_caller_bounds(predicates);
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env), false); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(elaborated_env));
let predicates = match fully_normalize(&infcx, cause, let predicates = match fully_normalize(&infcx,
cause,
&infcx.parameter_environment.caller_bounds) { &infcx.parameter_environment.caller_bounds) {
Ok(predicates) => predicates, Ok(predicates) => predicates,
Err(errors) => { Err(errors) => {
@ -444,6 +447,9 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi
} }
}; };
debug!("normalize_param_env_or_error: normalized predicates={:?}",
predicates);
let free_regions = FreeRegionMap::new(); let free_regions = FreeRegionMap::new();
infcx.resolve_regions_and_report_errors(&free_regions, body_id); infcx.resolve_regions_and_report_errors(&free_regions, body_id);
let predicates = match infcx.fully_resolve(&predicates) { let predicates = match infcx.fully_resolve(&predicates) {
@ -462,6 +468,9 @@ pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvi
} }
}; };
debug!("normalize_param_env_or_error: resolved predicates={:?}",
predicates);
infcx.parameter_environment.with_caller_bounds(predicates) infcx.parameter_environment.with_caller_bounds(predicates)
} }
@ -471,7 +480,7 @@ pub fn fully_normalize<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
-> Result<T, Vec<FulfillmentError<'tcx>>> -> Result<T, Vec<FulfillmentError<'tcx>>>
where T : TypeFoldable<'tcx> where T : TypeFoldable<'tcx>
{ {
debug!("normalize_param_env(value={:?})", value); debug!("fully_normalize(value={:?})", value);
let mut selcx = &mut SelectionContext::new(infcx); let mut selcx = &mut SelectionContext::new(infcx);
// FIXME (@jroesch) ISSUE 26721 // FIXME (@jroesch) ISSUE 26721
@ -487,20 +496,28 @@ pub fn fully_normalize<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
// //
// I think we should probably land this refactor and then come // I think we should probably land this refactor and then come
// back to this is a follow-up patch. // back to this is a follow-up patch.
let mut fulfill_cx = FulfillmentContext::new(false); let mut fulfill_cx = FulfillmentContext::new();
let Normalized { value: normalized_value, obligations } = let Normalized { value: normalized_value, obligations } =
project::normalize(selcx, cause, value); project::normalize(selcx, cause, value);
debug!("normalize_param_env: normalized_value={:?} obligations={:?}", debug!("fully_normalize: normalized_value={:?} obligations={:?}",
normalized_value, normalized_value,
obligations); obligations);
for obligation in obligations { for obligation in obligations {
fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation); fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation);
} }
try!(fulfill_cx.select_all_or_error(infcx)); debug!("fully_normalize: select_all_or_error start");
match fulfill_cx.select_all_or_error(infcx) {
Ok(()) => { }
Err(e) => {
debug!("fully_normalize: error={:?}", e);
return Err(e);
}
}
debug!("fully_normalize: select_all_or_error complete");
let resolved_value = infcx.resolve_type_vars_if_possible(&normalized_value); let resolved_value = infcx.resolve_type_vars_if_possible(&normalized_value);
debug!("normalize_param_env: resolved_value={:?}", resolved_value); debug!("fully_normalize: resolved_value={:?}", resolved_value);
Ok(resolved_value) Ok(resolved_value)
} }

View file

@ -479,7 +479,7 @@ fn project_type<'cx,'tcx>(
let recursion_limit = selcx.tcx().sess.recursion_limit.get(); let recursion_limit = selcx.tcx().sess.recursion_limit.get();
if obligation.recursion_depth >= recursion_limit { if obligation.recursion_depth >= recursion_limit {
debug!("project: overflow!"); debug!("project: overflow!");
report_overflow_error(selcx.infcx(), &obligation); report_overflow_error(selcx.infcx(), &obligation, true);
} }
let obligation_trait_ref = let obligation_trait_ref =

View file

@ -711,7 +711,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// not update) the cache. // not update) the cache.
let recursion_limit = self.infcx.tcx.sess.recursion_limit.get(); let recursion_limit = self.infcx.tcx.sess.recursion_limit.get();
if stack.obligation.recursion_depth >= recursion_limit { if stack.obligation.recursion_depth >= recursion_limit {
report_overflow_error(self.infcx(), &stack.obligation); report_overflow_error(self.infcx(), &stack.obligation, true);
} }
// Check the cache. Note that we skolemize the trait-ref // Check the cache. Note that we skolemize the trait-ref
@ -2124,6 +2124,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
nested: ty::Binder<Vec<Ty<'tcx>>>) nested: ty::Binder<Vec<Ty<'tcx>>>)
-> VtableBuiltinData<PredicateObligation<'tcx>> -> VtableBuiltinData<PredicateObligation<'tcx>>
{ {
debug!("vtable_builtin_data(obligation={:?}, bound={:?}, nested={:?})",
obligation, bound, nested);
let trait_def = match self.tcx().lang_items.from_builtin_kind(bound) { let trait_def = match self.tcx().lang_items.from_builtin_kind(bound) {
Ok(def_id) => def_id, Ok(def_id) => def_id,
Err(_) => { Err(_) => {

View file

@ -182,7 +182,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
let tcx = self.tcx; let tcx = self.tcx;
// FIXME: (@jroesch) float this code up // FIXME: (@jroesch) float this code up
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(self.clone()), false); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(self.clone()));
let adt = match self_type.sty { let adt = match self_type.sty {
ty::TyStruct(struct_def, substs) => { ty::TyStruct(struct_def, substs) => {
@ -655,7 +655,7 @@ impl<'tcx> ty::TyS<'tcx> {
-> bool -> bool
{ {
let tcx = param_env.tcx; let tcx = param_env.tcx;
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env.clone()), false); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env.clone()));
let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, let is_impld = traits::type_known_to_meet_builtin_bound(&infcx,
self, bound, span); self, bound, span);

View file

@ -202,7 +202,7 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
debug!("check_loans(body id={})", body.id); debug!("check_loans(body id={})", body.id);
let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env), false); let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env));
let mut clcx = CheckLoanCtxt { let mut clcx = CheckLoanCtxt {
bccx: bccx, bccx: bccx,

View file

@ -55,7 +55,7 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
}; };
let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id); let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env), false); let infcx = infer::new_infer_ctxt(bccx.tcx, &bccx.tcx.tables, Some(param_env));
{ {
let mut euv = euv::ExprUseVisitor::new(&mut glcx, &infcx); let mut euv = euv::ExprUseVisitor::new(&mut glcx, &infcx);
euv.walk_fn(decl, body); euv.walk_fn(decl, body);
@ -525,7 +525,7 @@ struct StaticInitializerCtxt<'a, 'tcx: 'a> {
impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> { impl<'a, 'tcx, 'v> Visitor<'v> for StaticInitializerCtxt<'a, 'tcx> {
fn visit_expr(&mut self, ex: &Expr) { fn visit_expr(&mut self, ex: &Expr) {
if let hir::ExprAddrOf(mutbl, ref base) = ex.node { if let hir::ExprAddrOf(mutbl, ref base) = ex.node {
let infcx = infer::new_infer_ctxt(self.bccx.tcx, &self.bccx.tcx.tables, None, false); let infcx = infer::new_infer_ctxt(self.bccx.tcx, &self.bccx.tcx.tables, None);
let mc = mc::MemCategorizationContext::new(&infcx); let mc = mc::MemCategorizationContext::new(&infcx);
let base_cmt = mc.cat_expr(&**base).unwrap(); let base_cmt = mc.cat_expr(&**base).unwrap();
let borrow_kind = ty::BorrowKind::from_mutbl(mutbl); let borrow_kind = ty::BorrowKind::from_mutbl(mutbl);

View file

@ -24,17 +24,21 @@
html_favicon_url = "https://www.rust-lang.org/favicon.ico", html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://doc.rust-lang.org/nightly/")] html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(rustc_private, staged_api)]
#![feature(hashmap_hasher)] #![feature(hashmap_hasher)]
#![feature(nonzero)]
#![feature(rustc_private)]
#![feature(staged_api)]
#![cfg_attr(test, feature(test))] #![cfg_attr(test, feature(test))]
extern crate core;
#[macro_use] extern crate log; #[macro_use] extern crate log;
extern crate serialize as rustc_serialize; // used by deriving extern crate serialize as rustc_serialize; // used by deriving
pub mod bitvec; pub mod bitvec;
pub mod graph; pub mod graph;
pub mod ivar; pub mod ivar;
pub mod obligation_forest;
pub mod snapshot_vec; pub mod snapshot_vec;
pub mod transitive_relation; pub mod transitive_relation;
pub mod unify; pub mod unify;

View file

@ -0,0 +1,80 @@
The `ObligationForest` is a utility data structure used in trait
matching to track the set of outstanding obligations (those not yet
resolved to success or error). It also tracks the "backtrace" of each
pending obligation (why we are trying to figure this out in the first
place).
### External view
`ObligationForest` supports two main public operations (there are a
few others not discussed here):
1. Add a new root obligation (`push_root`).
2. Process the pending obligations (`process_obligations`).
When a new obligation `N` is added, it becomes the root of an
obligation tree. This tree is a singleton to start, so `N` is both the
root and the only leaf. Each time the `process_obligations` method is
called, it will invoke its callback with every pending obligation (so
that will include `N`, the first time). The callback shoud process the
obligation `O` that it is given and return one of three results:
- `Ok(None)` -> ambiguous result. Obligation was neither a success
nor a failure. It is assumed that further attempts to process the
obligation will yield the same result unless something in the
surrounding environment changes.
- `Ok(Some(C))` - the obligation was *shallowly successful*. The
vector `C` is a list of subobligations. The meaning of this is that
`O` was successful on the assumption that all the obligations in `C`
are also successful. Therefore, `O` is only considered a "true"
success if `C` is empty. Otherwise, `O` is put into a suspended
state and the obligations in `C` become the new pending
obligations. They will be processed the next time you call
`process_obligations`.
- `Err(E)` -> obligation failed with error `E`. We will collect this
error and return it from `process_obligations`, along with the
"backtrace" of obligations (that is, the list of obligations up to
and including the root of the failed obligation). No further
obligations from that same tree will be processed, since the tree is
now considered to be in error.
When the call to `process_obligations` completes, you get back an `Outcome`,
which includes three bits of information:
- `completed`: a list of obligations where processing was fully
completed without error (meaning that all transitive subobligations
have also been completed). So, for example, if the callback from
`process_obligations` returns `Ok(Some(C))` for some obligation `O`,
then `O` will be considered completed right away if `C` is the
empty vector. Otherwise it will only be considered completed once
all the obligations in `C` have been found completed.
- `errors`: a list of errors that occurred and associated backtraces
at the time of error, which can be used to give context to the user.
- `stalled`: if true, then none of the existing obligations were
*shallowly successful* (that is, no callback returned `Ok(Some(_))`).
This implies that all obligations were either errors or returned an
ambiguous result, which means that any further calls to
`process_obligations` would simply yield back further ambiguous
results. This is used by the `FulfillmentContext` to decide when it
has reached a steady state.
#### Snapshots
The `ObligationForest` supports a limited form of snapshots; see
`start_snapshot`; `commit_snapshot`; and `rollback_snapshot`. In
particular, you can use a snapshot to roll back new root
obligations. However, it is an error to attempt to
`process_obligations` during a snapshot.
### Implementation details
For the most part, comments specific to the implementation are in the
code. This file only contains a very high-level overview. Basically,
the forest is stored in a vector. Each element of the vector is a node
in some tree. Each node in the vector has the index of an (optional)
parent and (for convenience) its root (which may be itself). It also
has a current state, described by `NodeState`. After each
processing step, we compress the vector to remove completed and error
nodes, which aren't needed anymore.

View file

@ -0,0 +1,462 @@
// Copyright 2014 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.
//! The `ObligationForest` is a utility data structure used in trait
//! matching to track the set of outstanding obligations (those not
//! yet resolved to success or error). It also tracks the "backtrace"
//! of each pending obligation (why we are trying to figure this out
//! in the first place). See README.md for a general overview of how
//! to use this class.
use std::fmt::Debug;
use std::mem;
mod node_index;
#[cfg(test)]
mod test;
pub struct ObligationForest<O> {
/// The list of obligations. In between calls to
/// `process_obligations`, this list only contains nodes in the
/// `Pending` or `Success` state (with a non-zero number of
/// incomplete children). During processing, some of those nodes
/// may be changed to the error state, or we may find that they
/// are completed (That is, `num_incomplete_children` drops to 0).
/// At the end of processing, those nodes will be removed by a
/// call to `compress`.
///
/// At all times we maintain the invariant that every node appears
/// at a higher index than its parent. This is needed by the
/// backtrace iterator (which uses `split_at`).
nodes: Vec<Node<O>>,
snapshots: Vec<usize>
}
pub struct Snapshot {
len: usize,
}
pub use self::node_index::NodeIndex;
struct Node<O> {
state: NodeState<O>,
parent: Option<NodeIndex>,
root: NodeIndex, // points to the root, which may be the current node
}
/// The state of one node in some tree within the forest. This
/// represents the current state of processing for the obligation (of
/// type `O`) associated with this node.
#[derive(Debug)]
enum NodeState<O> {
/// Obligation not yet resolved to success or error.
Pending { obligation: O },
/// Obligation resolved to success; `num_incomplete_children`
/// indicates the number of children still in an "incomplete"
/// state. Incomplete means that either the child is still
/// pending, or it has children which are incomplete. (Basically,
/// there is pending work somewhere in the subtree of the child.)
///
/// Once all children have completed, success nodes are removed
/// from the vector by the compression step.
Success { obligation: O, num_incomplete_children: usize },
/// This obligation was resolved to an error. Error nodes are
/// removed from the vector by the compression step.
Error,
}
#[derive(Debug)]
pub struct Outcome<O,E> {
/// Obligations that were completely evaluated, including all
/// (transitive) subobligations.
pub completed: Vec<O>,
/// Backtrace of obligations that were found to be in error.
pub errors: Vec<Error<O,E>>,
/// If true, then we saw no successful obligations, which means
/// there is no point in further iteration. This is based on the
/// assumption that when trait matching returns `Err` or
/// `Ok(None)`, those results do not affect environmental
/// inference state. (Note that if we invoke `process_obligations`
/// with no pending obligations, stalled will be true.)
pub stalled: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub struct Error<O,E> {
pub error: E,
pub backtrace: Vec<O>,
}
impl<O: Debug> ObligationForest<O> {
pub fn new() -> ObligationForest<O> {
ObligationForest {
nodes: vec![],
snapshots: vec![]
}
}
/// Return the total number of nodes in the forest that have not
/// yet been fully resolved.
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn start_snapshot(&mut self) -> Snapshot {
self.snapshots.push(self.nodes.len());
Snapshot { len: self.snapshots.len() }
}
pub fn commit_snapshot(&mut self, snapshot: Snapshot) {
assert_eq!(snapshot.len, self.snapshots.len());
let nodes_len = self.snapshots.pop().unwrap();
assert!(self.nodes.len() >= nodes_len);
}
pub fn rollback_snapshot(&mut self, snapshot: Snapshot) {
// Check that we are obeying stack discipline.
assert_eq!(snapshot.len, self.snapshots.len());
let nodes_len = self.snapshots.pop().unwrap();
// The only action permitted while in a snapshot is to push
// new root obligations. Because no processing will have been
// done, those roots should still be in the pending state.
debug_assert!(self.nodes[nodes_len..].iter().all(|n| match n.state {
NodeState::Pending { .. } => true,
_ => false,
}));
self.nodes.truncate(nodes_len);
}
pub fn in_snapshot(&self) -> bool {
!self.snapshots.is_empty()
}
/// Adds a new tree to the forest.
///
/// This CAN be done during a snapshot.
pub fn push_root(&mut self, obligation: O) {
let index = NodeIndex::new(self.nodes.len());
self.nodes.push(Node::new(index, None, obligation));
}
/// Convert all remaining obligations to the given error.
///
/// This cannot be done during a snapshot.
pub fn to_errors<E:Clone>(&mut self, error: E) -> Vec<Error<O,E>> {
assert!(!self.in_snapshot());
let mut errors = vec![];
for index in 0..self.nodes.len() {
debug_assert!(!self.nodes[index].is_popped());
self.inherit_error(index);
if let NodeState::Pending { .. } = self.nodes[index].state {
let backtrace = self.backtrace(index);
errors.push(Error { error: error.clone(), backtrace: backtrace });
}
}
let successful_obligations = self.compress();
assert!(successful_obligations.is_empty());
errors
}
/// Returns the set of obligations that are in a pending state.
pub fn pending_obligations(&self) -> Vec<O> where O: Clone {
self.nodes.iter()
.filter_map(|n| match n.state {
NodeState::Pending { ref obligation } => Some(obligation),
_ => None,
})
.cloned()
.collect()
}
/// Process the obligations.
///
/// This CANNOT be unrolled (presently, at least).
pub fn process_obligations<E,F>(&mut self, mut action: F) -> Outcome<O,E>
where E: Debug, F: FnMut(&mut O, Backtrace<O>) -> Result<Option<Vec<O>>, E>
{
debug!("process_obligations(len={})", self.nodes.len());
assert!(!self.in_snapshot()); // cannot unroll this action
let mut errors = vec![];
let mut stalled = true;
// We maintain the invariant that the list is in pre-order, so
// parents occur before their children. Also, whenever an
// error occurs, we propagate it from the child all the way to
// the root of the tree. Together, these two facts mean that
// when we visit a node, we can check if its root is in error,
// and we will find out if any prior node within this forest
// encountered an error.
for index in 0..self.nodes.len() {
debug_assert!(!self.nodes[index].is_popped());
self.inherit_error(index);
debug!("process_obligations: node {} == {:?}",
index, self.nodes[index].state);
let result = {
let parent = self.nodes[index].parent;
let (prefix, suffix) = self.nodes.split_at_mut(index);
let backtrace = Backtrace::new(prefix, parent);
match suffix[0].state {
NodeState::Error |
NodeState::Success { .. } =>
continue,
NodeState::Pending { ref mut obligation } =>
action(obligation, backtrace),
}
};
debug!("process_obligations: node {} got result {:?}", index, result);
match result {
Ok(None) => {
// no change in state
}
Ok(Some(children)) => {
// if we saw a Some(_) result, we are not (yet) stalled
stalled = false;
self.success(index, children);
}
Err(err) => {
let backtrace = self.backtrace(index);
errors.push(Error { error: err, backtrace: backtrace });
}
}
}
// Now we have to compress the result
let successful_obligations = self.compress();
debug!("process_obligations: complete");
Outcome {
completed: successful_obligations,
errors: errors,
stalled: stalled,
}
}
/// Indicates that node `index` has been processed successfully,
/// yielding `children` as the derivative work. If children is an
/// empty vector, this will update the ref count on the parent of
/// `index` to indicate that a child has completed
/// successfully. Otherwise, adds new nodes to represent the child
/// work.
fn success(&mut self, index: usize, children: Vec<O>) {
debug!("success(index={}, children={:?})", index, children);
let num_incomplete_children = children.len();
if num_incomplete_children == 0 {
// if there is no work left to be done, decrement parent's ref count
self.update_parent(index);
} else {
// create child work
let root_index = self.nodes[index].root;
let node_index = NodeIndex::new(index);
self.nodes.extend(
children.into_iter()
.map(|o| Node::new(root_index, Some(node_index), o)));
}
// change state from `Pending` to `Success`, temporarily swapping in `Error`
let state = mem::replace(&mut self.nodes[index].state, NodeState::Error);
self.nodes[index].state = match state {
NodeState::Pending { obligation } =>
NodeState::Success { obligation: obligation,
num_incomplete_children: num_incomplete_children },
NodeState::Success { .. } |
NodeState::Error =>
unreachable!()
};
}
/// Decrements the ref count on the parent of `child`; if the
/// parent's ref count then reaches zero, proceeds recursively.
fn update_parent(&mut self, child: usize) {
debug!("update_parent(child={})", child);
if let Some(parent) = self.nodes[child].parent {
let parent = parent.get();
match self.nodes[parent].state {
NodeState::Success { ref mut num_incomplete_children, .. } => {
*num_incomplete_children -= 1;
if *num_incomplete_children > 0 {
return;
}
}
_ => unreachable!(),
}
self.update_parent(parent);
}
}
/// If the root of `child` is in an error state, places `child`
/// into an error state. This is used during processing so that we
/// skip the remaining obligations from a tree once some other
/// node in the tree is found to be in error.
fn inherit_error(&mut self, child: usize) {
let root = self.nodes[child].root.get();
if let NodeState::Error = self.nodes[root].state {
self.nodes[child].state = NodeState::Error;
}
}
/// Returns a vector of obligations for `p` and all of its
/// ancestors, putting them into the error state in the process.
/// The fact that the root is now marked as an error is used by
/// `inherit_error` above to propagate the error state to the
/// remainder of the tree.
fn backtrace(&mut self, mut p: usize) -> Vec<O> {
let mut trace = vec![];
loop {
let state = mem::replace(&mut self.nodes[p].state, NodeState::Error);
match state {
NodeState::Pending { obligation } |
NodeState::Success { obligation, .. } => {
trace.push(obligation);
}
NodeState::Error => {
// we should not encounter an error, because if
// there was an error in the ancestors, it should
// have been propagated down and we should never
// have tried to process this obligation
panic!("encountered error in node {:?} when collecting stack trace", p);
}
}
// loop to the parent
match self.nodes[p].parent {
Some(q) => { p = q.get(); }
None => { return trace; }
}
}
}
/// Compresses the vector, removing all popped nodes. This adjusts
/// the indices and hence invalidates any outstanding
/// indices. Cannot be used during a transaction.
fn compress(&mut self) -> Vec<O> {
assert!(!self.in_snapshot()); // didn't write code to unroll this action
let mut rewrites: Vec<_> = (0..self.nodes.len()).collect();
// Finish propagating error state. Note that in this case we
// only have to check immediate parents, rather than all
// ancestors, because all errors have already occurred that
// are going to occur.
let nodes_len = self.nodes.len();
for i in 0..nodes_len {
if !self.nodes[i].is_popped() {
self.inherit_error(i);
}
}
// Now go through and move all nodes that are either
// successful or which have an error over into to the end of
// the list, preserving the relative order of the survivors
// (which is important for the `inherit_error` logic).
let mut dead = 0;
for i in 0..nodes_len {
if self.nodes[i].is_popped() {
dead += 1;
} else if dead > 0 {
self.nodes.swap(i, i - dead);
rewrites[i] -= dead;
}
}
// Pop off all the nodes we killed and extract the success
// stories.
let successful =
(0 .. dead).map(|_| self.nodes.pop().unwrap())
.flat_map(|node| match node.state {
NodeState::Error => None,
NodeState::Pending { .. } => unreachable!(),
NodeState::Success { obligation, num_incomplete_children } => {
assert_eq!(num_incomplete_children, 0);
Some(obligation)
}
})
.collect();
// Adjust the parent indices, since we compressed things.
for node in &mut self.nodes {
if let Some(ref mut index) = node.parent {
let new_index = rewrites[index.get()];
debug_assert!(new_index < (nodes_len - dead));
*index = NodeIndex::new(new_index);
}
node.root = NodeIndex::new(rewrites[node.root.get()]);
}
successful
}
}
impl<O> Node<O> {
fn new(root: NodeIndex, parent: Option<NodeIndex>, obligation: O) -> Node<O> {
Node {
parent: parent,
state: NodeState::Pending { obligation: obligation },
root: root
}
}
fn is_popped(&self) -> bool {
match self.state {
NodeState::Pending { .. } => false,
NodeState::Success { num_incomplete_children, .. } => num_incomplete_children == 0,
NodeState::Error => true,
}
}
}
#[derive(Clone)]
pub struct Backtrace<'b, O: 'b> {
nodes: &'b [Node<O>],
pointer: Option<NodeIndex>,
}
impl<'b, O> Backtrace<'b, O> {
fn new(nodes: &'b [Node<O>], pointer: Option<NodeIndex>) -> Backtrace<'b, O> {
Backtrace { nodes: nodes, pointer: pointer }
}
}
impl<'b, O> Iterator for Backtrace<'b, O> {
type Item = &'b O;
fn next(&mut self) -> Option<&'b O> {
debug!("Backtrace: self.pointer = {:?}", self.pointer);
if let Some(p) = self.pointer {
self.pointer = self.nodes[p.get()].parent;
match self.nodes[p.get()].state {
NodeState::Pending { ref obligation } |
NodeState::Success { ref obligation, .. } => {
Some(obligation)
}
NodeState::Error => {
panic!("Backtrace encountered an error.");
}
}
} else {
None
}
}
}

View file

@ -0,0 +1,31 @@
// Copyright 2014 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.
use core::nonzero::NonZero;
use std::u32;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct NodeIndex {
index: NonZero<u32>
}
impl NodeIndex {
pub fn new(value: usize) -> NodeIndex {
assert!(value < (u32::MAX as usize));
unsafe {
NodeIndex { index: NonZero::new((value as u32) + 1) }
}
}
pub fn get(self) -> usize {
(*self.index - 1) as usize
}
}

View file

@ -0,0 +1,206 @@
// Copyright 2014 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.
use super::{ObligationForest, Outcome, Error};
#[test]
fn push_pop() {
let mut forest = ObligationForest::new();
forest.push_root("A");
forest.push_root("B");
forest.push_root("C");
// first round, B errors out, A has subtasks, and C completes, creating this:
// A |-> A.1
// |-> A.2
// |-> A.3
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(|obligation, _| {
match *obligation {
"A" => Ok(Some(vec!["A.1", "A.2", "A.3"])),
"B" => Err("B is for broken"),
"C" => Ok(Some(vec![])),
_ => unreachable!(),
}
});
assert_eq!(ok, vec!["C"]);
assert_eq!(err, vec![Error {error: "B is for broken",
backtrace: vec!["B"]}]);
// second round: two delays, one success, creating an uneven set of subtasks:
// A |-> A.1
// |-> A.2
// |-> A.3 |-> A.3.i
// D |-> D.1
// |-> D.2
forest.push_root("D");
let Outcome { completed: ok, errors: err, .. }: Outcome<&'static str, ()> =
forest.process_obligations(|obligation, _| {
match *obligation {
"A.1" => Ok(None),
"A.2" => Ok(None),
"A.3" => Ok(Some(vec!["A.3.i"])),
"D" => Ok(Some(vec!["D.1", "D.2"])),
_ => unreachable!(),
}
});
assert_eq!(ok, Vec::<&'static str>::new());
assert_eq!(err, Vec::new());
// third round: ok in A.1 but trigger an error in A.2. Check that it
// propagates to A.3.i, but not D.1 or D.2.
// D |-> D.1 |-> D.1.i
// |-> D.2 |-> D.2.i
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(|obligation, _| {
match *obligation {
"A.1" => Ok(Some(vec![])),
"A.2" => Err("A is for apple"),
"D.1" => Ok(Some(vec!["D.1.i"])),
"D.2" => Ok(Some(vec!["D.2.i"])),
_ => unreachable!(),
}
});
assert_eq!(ok, vec!["A.1"]);
assert_eq!(err, vec![Error { error: "A is for apple",
backtrace: vec!["A.2", "A"] }]);
// fourth round: error in D.1.i that should propagate to D.2.i
let Outcome { completed: ok, errors: err, .. } = forest.process_obligations(|obligation, _| {
match *obligation {
"D.1.i" => Err("D is for dumb"),
_ => panic!("unexpected obligation {:?}", obligation),
}
});
assert_eq!(ok, Vec::<&'static str>::new());
assert_eq!(err, vec![Error { error: "D is for dumb",
backtrace: vec!["D.1.i", "D.1", "D"] }]);
}
// Test that if a tree with grandchildren succeeds, everything is
// reported as expected:
// A
// A.1
// A.2
// A.2.i
// A.2.ii
// A.3
#[test]
fn success_in_grandchildren() {
let mut forest = ObligationForest::new();
forest.push_root("A");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|obligation, _| {
match *obligation {
"A" => Ok(Some(vec!["A.1", "A.2", "A.3"])),
_ => unreachable!(),
}
});
assert!(ok.is_empty());
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|obligation, _| {
match *obligation {
"A.1" => Ok(Some(vec![])),
"A.2" => Ok(Some(vec!["A.2.i", "A.2.ii"])),
"A.3" => Ok(Some(vec![])),
_ => unreachable!(),
}
});
assert_eq!(ok, vec!["A.3", "A.1"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|obligation, _| {
match *obligation {
"A.2.i" => Ok(Some(vec!["A.2.i.a"])),
"A.2.ii" => Ok(Some(vec![])),
_ => unreachable!(),
}
});
assert_eq!(ok, vec!["A.2.ii"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|obligation, _| {
match *obligation {
"A.2.i.a" => Ok(Some(vec![])),
_ => unreachable!(),
}
});
assert_eq!(ok, vec!["A.2.i.a", "A.2.i", "A.2", "A"]);
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|_, _| unreachable!());
assert!(ok.is_empty());
assert!(err.is_empty());
}
#[test]
fn to_errors_no_throw() {
// check that converting multiple children with common parent (A)
// only yields one of them (and does not panic, in particular).
let mut forest = ObligationForest::new();
forest.push_root("A");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|obligation, _| {
match *obligation {
"A" => Ok(Some(vec!["A.1", "A.2", "A.3"])),
_ => unreachable!(),
}
});
assert_eq!(ok.len(), 0);
assert_eq!(err.len(), 0);
let errors = forest.to_errors(());
assert_eq!(errors.len(), 1);
}
#[test]
fn backtrace() {
// check that converting multiple children with common parent (A)
// only yields one of them (and does not panic, in particular).
let mut forest: ObligationForest<&'static str> = ObligationForest::new();
forest.push_root("A");
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|obligation, mut backtrace| {
assert!(backtrace.next().is_none());
match *obligation {
"A" => Ok(Some(vec!["A.1"])),
_ => unreachable!(),
}
});
assert!(ok.is_empty());
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|obligation, mut backtrace| {
assert!(backtrace.next().unwrap() == &"A");
assert!(backtrace.next().is_none());
match *obligation {
"A.1" => Ok(Some(vec!["A.1.i"])),
_ => unreachable!(),
}
});
assert!(ok.is_empty());
assert!(err.is_empty());
let Outcome { completed: ok, errors: err, .. } =
forest.process_obligations::<(),_>(|obligation, mut backtrace| {
assert!(backtrace.next().unwrap() == &"A.1");
assert!(backtrace.next().unwrap() == &"A");
assert!(backtrace.next().is_none());
match *obligation {
"A.1.i" => Ok(None),
_ => unreachable!(),
}
});
assert_eq!(ok.len(), 0);
assert!(err.is_empty());
}

View file

@ -139,7 +139,7 @@ fn test_env<F>(source_string: &str,
lang_items, lang_items,
stability::Index::new(krate), stability::Index::new(krate),
|tcx| { |tcx| {
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
body(Env { infcx: &infcx }); body(Env { infcx: &infcx });
let free_regions = FreeRegionMap::new(); let free_regions = FreeRegionMap::new();
infcx.resolve_regions_and_report_errors(&free_regions, infcx.resolve_regions_and_report_errors(&free_regions,

View file

@ -869,7 +869,7 @@ impl LateLintPass for UnconditionalRecursion {
let node_id = tcx.map.as_local_node_id(method.def_id).unwrap(); let node_id = tcx.map.as_local_node_id(method.def_id).unwrap();
let param_env = ty::ParameterEnvironment::for_item(tcx, node_id); let param_env = ty::ParameterEnvironment::for_item(tcx, node_id);
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env), false); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
let mut selcx = traits::SelectionContext::new(&infcx); let mut selcx = traits::SelectionContext::new(&infcx);
match selcx.select(&obligation) { match selcx.select(&obligation) {
// The method comes from a `T: Trait` bound. // The method comes from a `T: Trait` bound.

View file

@ -143,7 +143,7 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
let param_env = ty::ParameterEnvironment::for_item(self.tcx, id); let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env), true); let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) { match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
Ok(mut mir) => { Ok(mut mir) => {

View file

@ -141,6 +141,7 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
fulfillment_cx fulfillment_cx
.pending_obligations() .pending_obligations()
.iter() .iter()
.map(|obligation| &obligation.obligation)
.filter_map(|obligation| { .filter_map(|obligation| {
debug!("deduce_expectations_from_obligations: obligation.predicate={:?}", debug!("deduce_expectations_from_obligations: obligation.predicate={:?}",
obligation.predicate); obligation.predicate);
@ -168,6 +169,7 @@ fn deduce_expectations_from_obligations<'a,'tcx>(
fulfillment_cx fulfillment_cx
.pending_obligations() .pending_obligations()
.iter() .iter()
.map(|obligation| &obligation.obligation)
.filter_map(|obligation| { .filter_map(|obligation| {
let opt_trait_ref = match obligation.predicate { let opt_trait_ref = match obligation.predicate {
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()), ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()),

View file

@ -42,7 +42,7 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
debug!("compare_impl_method: impl_trait_ref (liberated) = {:?}", debug!("compare_impl_method: impl_trait_ref (liberated) = {:?}",
impl_trait_ref); impl_trait_ref);
let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, true); let mut infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
let mut fulfillment_cx = infcx.fulfillment_cx.borrow_mut(); let mut fulfillment_cx = infcx.fulfillment_cx.borrow_mut();
let trait_to_impl_substs = &impl_trait_ref.substs; let trait_to_impl_substs = &impl_trait_ref.substs;
@ -416,7 +416,7 @@ pub fn compare_const_impl<'tcx>(tcx: &ty::ctxt<'tcx>,
debug!("compare_const_impl(impl_trait_ref={:?})", debug!("compare_const_impl(impl_trait_ref={:?})",
impl_trait_ref); impl_trait_ref);
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, true); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
let mut fulfillment_cx = infcx.fulfillment_cx.borrow_mut(); let mut fulfillment_cx = infcx.fulfillment_cx.borrow_mut();
// The below is for the most part highly similar to the procedure // The below is for the most part highly similar to the procedure

View file

@ -83,7 +83,7 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
// check that the impl type can be made to match the trait type. // check that the impl type can be made to match the trait type.
let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_node_id); let impl_param_env = ty::ParameterEnvironment::for_item(tcx, self_type_node_id);
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(impl_param_env), true); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(impl_param_env));
let named_type = tcx.lookup_item_type(self_type_did).ty; let named_type = tcx.lookup_item_type(self_type_did).ty;
let named_type = named_type.subst(tcx, &infcx.parameter_environment.free_substs); let named_type = named_type.subst(tcx, &infcx.parameter_environment.free_substs);

View file

@ -261,7 +261,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// FIXME(#18653) -- Try to resolve obligations, giving us more // FIXME(#18653) -- Try to resolve obligations, giving us more
// typing information, which can sometimes be needed to avoid // typing information, which can sometimes be needed to avoid
// pathological region inference failures. // pathological region inference failures.
fcx.select_new_obligations(); fcx.select_obligations_where_possible();
// Insert any adjustments needed (always an autoref of some mutability). // Insert any adjustments needed (always an autoref of some mutability).
match self_expr { match self_expr {

View file

@ -305,7 +305,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
-> Inherited<'a, 'tcx> { -> Inherited<'a, 'tcx> {
Inherited { Inherited {
infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env), true), infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env)),
locals: RefCell::new(NodeMap()), locals: RefCell::new(NodeMap()),
tables: tables, tables: tables,
deferred_call_resolutions: RefCell::new(DefIdMap()), deferred_call_resolutions: RefCell::new(DefIdMap()),
@ -1235,15 +1235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return ty; return ty;
} }
// If not, try resolving any new fcx obligations that have cropped up. // If not, try resolving pending obligations as much as
self.select_new_obligations();
ty = self.infcx().resolve_type_vars_if_possible(&ty);
if !ty.has_infer_types() {
debug!("resolve_type_vars_if_possible: ty={:?}", ty);
return ty;
}
// If not, try resolving *all* pending obligations as much as
// possible. This can help substantially when there are // possible. This can help substantially when there are
// indirect dependencies that don't seem worth tracking // indirect dependencies that don't seem worth tracking
// precisely. // precisely.
@ -2029,22 +2021,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(errors) => { report_fulfillment_errors(self.infcx(), &errors); } Err(errors) => { report_fulfillment_errors(self.infcx(), &errors); }
} }
} }
/// Try to select any fcx obligation that we haven't tried yet, in an effort
/// to improve inference. You could just call
/// `select_obligations_where_possible` except that it leads to repeated
/// work.
fn select_new_obligations(&self) {
match
self.inh.infcx.fulfillment_cx
.borrow_mut()
.select_new_obligations(self.infcx())
{
Ok(()) => { }
Err(errors) => { report_fulfillment_errors(self.infcx(), &errors); }
}
}
} }
impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> { impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> {
@ -2496,7 +2472,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// an "opportunistic" vtable resolution of any trait bounds on // an "opportunistic" vtable resolution of any trait bounds on
// the call. This helps coercions. // the call. This helps coercions.
if check_blocks { if check_blocks {
fcx.select_new_obligations(); fcx.select_obligations_where_possible();
} }
// For variadic functions, we don't have a declared type for all of // For variadic functions, we don't have a declared type for all of
@ -4126,7 +4102,7 @@ fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
pub fn check_representable(tcx: &ty::ctxt, pub fn check_representable(tcx: &ty::ctxt,
sp: Span, sp: Span,
item_id: ast::NodeId, item_id: ast::NodeId,
designation: &str) -> bool { _designation: &str) -> bool {
let rty = tcx.node_id_to_type(item_id); let rty = tcx.node_id_to_type(item_id);
// Check that it is possible to represent this type. This call identifies // Check that it is possible to represent this type. This call identifies
@ -4136,9 +4112,8 @@ pub fn check_representable(tcx: &ty::ctxt,
// caught by case 1. // caught by case 1.
match rty.is_representable(tcx, sp) { match rty.is_representable(tcx, sp) {
Representability::SelfRecursive => { Representability::SelfRecursive => {
struct_span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation) let item_def_id = tcx.map.local_def_id(item_id);
.fileline_help(sp, "wrap the inner value in a box to make it representable") traits::recursive_type_with_infinite_size_error(tcx, item_def_id).emit();
.emit();
return false return false
} }
Representability::Representable | Representability::ContainsRecursive => (), Representability::Representable | Representability::ContainsRecursive => (),

View file

@ -384,7 +384,7 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)", debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)",
source, target); source, target);
let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env), true); let infcx = new_infer_ctxt(tcx, &tcx.tables, Some(param_env));
let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, mt_b: ty::TypeAndMut<'tcx>, let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, mt_b: ty::TypeAndMut<'tcx>,
mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| { mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
@ -528,7 +528,7 @@ fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id:
pub fn check_coherence(crate_context: &CrateCtxt) { pub fn check_coherence(crate_context: &CrateCtxt) {
let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence); let _task = crate_context.tcx.dep_graph.in_task(DepNode::Coherence);
let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None, true); let infcx = new_infer_ctxt(crate_context.tcx, &crate_context.tcx.tables, None);
CoherenceChecker { CoherenceChecker {
crate_context: crate_context, crate_context: crate_context,
inference_context: infcx, inference_context: infcx,

View file

@ -127,7 +127,7 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
impl1_def_id, impl1_def_id,
impl2_def_id); impl2_def_id);
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None, false); let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
if let Some(trait_ref) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) { if let Some(trait_ref) = traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
self.report_overlap_error(impl1_def_id, impl2_def_id, trait_ref); self.report_overlap_error(impl1_def_id, impl2_def_id, trait_ref);
} }

View file

@ -869,43 +869,6 @@ fn main() {
``` ```
"##, "##,
E0072: r##"
When defining a recursive struct or enum, any use of the type being defined
from inside the definition must occur behind a pointer (like `Box` or `&`).
This is because structs and enums must have a well-defined size, and without
the pointer the size of the type would need to be unbounded.
Consider the following erroneous definition of a type for a list of bytes:
```
// error, invalid recursive struct type
struct ListNode {
head: u8,
tail: Option<ListNode>,
}
```
This type cannot have a well-defined size, because it needs to be arbitrarily
large (since we would be able to nest `ListNode`s to any depth). Specifically,
```plain
size of `ListNode` = 1 byte for `head`
+ 1 byte for the discriminant of the `Option`
+ size of `ListNode`
```
One way to fix this is by wrapping `ListNode` in a `Box`, like so:
```
struct ListNode {
head: u8,
tail: Option<Box<ListNode>>,
}
```
This works because `Box` is a pointer, so its size is well-known.
"##,
E0073: r##" E0073: r##"
You cannot define a struct (or enum) `Foo` that requires an instance of `Foo` You cannot define a struct (or enum) `Foo` that requires an instance of `Foo`
in order to make a new `Foo` value. This is because there would be no way a in order to make a new `Foo` value. This is because there would be no way a

View file

@ -193,7 +193,7 @@ fn require_same_types<'a, 'tcx, M>(tcx: &ty::ctxt<'tcx>,
{ {
let result = match maybe_infcx { let result = match maybe_infcx {
None => { None => {
let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None);
infer::mk_eqty(&infcx, t1_is_expected, TypeOrigin::Misc(span), t1, t2) infer::mk_eqty(&infcx, t1_is_expected, TypeOrigin::Misc(span), t1, t2)
} }
Some(infcx) => { Some(infcx) => {

View file

@ -13,5 +13,6 @@ trait Trait {}
pub fn main() { pub fn main() {
let x: Vec<Trait + Sized> = Vec::new(); let x: Vec<Trait + Sized> = Vec::new();
//~^ ERROR the trait `core::marker::Sized` is not implemented //~^ ERROR the trait `core::marker::Sized` is not implemented
//~^^ ERROR the trait `core::marker::Sized` is not implemented //~| ERROR the trait `core::marker::Sized` is not implemented
//~| ERROR the trait `core::marker::Sized` is not implemented
} }

View file

@ -8,9 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: invalid recursive enum type
enum mlist { cons(isize, mlist), nil, } enum mlist { cons(isize, mlist), nil, }
//~^ ERROR recursive type `mlist` has infinite size
fn main() { let a = mlist::cons(10, mlist::cons(11, mlist::nil)); } fn main() { let a = mlist::cons(10, mlist::cons(11, mlist::nil)); }

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
struct Foo { foo: Option<Option<Foo>> } struct Foo { foo: Option<Option<Foo>> }
//~^ ERROR invalid recursive struct type //~^ ERROR recursive type `Foo` has infinite size
impl Foo { fn bar(&self) {} } impl Foo { fn bar(&self) {} }

View file

@ -9,10 +9,9 @@
// except according to those terms. // except according to those terms.
struct Baz { q: Option<Foo> } struct Baz { q: Option<Foo> }
//~^ ERROR invalid recursive struct type
struct Foo { q: Option<Baz> } struct Foo { q: Option<Baz> }
//~^ ERROR invalid recursive struct type //~^ ERROR recursive type `Foo` has infinite size
impl Foo { fn bar(&self) {} } impl Foo { fn bar(&self) {} }

View file

@ -11,7 +11,7 @@
use std::sync::Mutex; use std::sync::Mutex;
struct Foo { foo: Mutex<Option<Foo>> } struct Foo { foo: Mutex<Option<Foo>> }
//~^ ERROR invalid recursive struct type //~^ ERROR recursive type `Foo` has infinite size
impl Foo { fn bar(&self) {} } impl Foo { fn bar(&self) {} }

View file

@ -11,7 +11,7 @@
use std::marker; use std::marker;
struct Foo<T> { foo: Option<Option<Foo<T>>>, marker: marker::PhantomData<T> } struct Foo<T> { foo: Option<Option<Foo<T>>>, marker: marker::PhantomData<T> }
//~^ ERROR invalid recursive struct type //~^ ERROR recursive type `Foo` has infinite size
impl<T> Foo<T> { fn bar(&self) {} } impl<T> Foo<T> { fn bar(&self) {} }

View file

@ -11,8 +11,9 @@
use std::marker; use std::marker;
struct Foo { foo: Bar<Foo> } struct Foo { foo: Bar<Foo> }
struct Bar<T> { x: Bar<Foo> , marker: marker::PhantomData<T> } struct Bar<T> { x: Bar<Foo> , marker: marker::PhantomData<T> }
//~^ ERROR invalid recursive struct type //~^ ERROR recursive type `Bar` has infinite size
impl Foo { fn foo(&self) {} } impl Foo { fn foo(&self) {} }

View file

@ -11,7 +11,7 @@
use std::sync::Mutex; use std::sync::Mutex;
enum Foo { X(Mutex<Option<Foo>>) } enum Foo { X(Mutex<Option<Foo>>) }
//~^ ERROR invalid recursive enum type //~^ ERROR recursive type `Foo` has infinite size
impl Foo { fn bar(self) {} } impl Foo { fn bar(self) {} }

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
enum Foo { Voo(Option<Option<Foo>>) } enum Foo { Voo(Option<Option<Foo>>) }
//~^ ERROR invalid recursive enum type //~^ ERROR recursive type `Foo` has infinite size
impl Foo { fn bar(&self) {} } impl Foo { fn bar(&self) {} }

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
fn main() { fn main() {
for (ref i,) in [].iter() { //~ ERROR: type mismatch resolving for (ref i,) in [].iter() { //~ ERROR mismatched types
i.clone(); i.clone();
//~^ ERROR: the type of this value must be known in this context //~^ ERROR: the type of this value must be known in this context
} }

View file

@ -8,11 +8,10 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: overflow representing the type `S` trait Mirror { type It: ?Sized; }
impl<T: ?Sized> Mirror for T { type It = Self; }
trait Mirror { type It; }
impl<T> Mirror for T { type It = Self; }
struct S(Option<<S as Mirror>::It>); struct S(Option<<S as Mirror>::It>);
//~^ ERROR recursive type `S` has infinite size
fn main() { fn main() {
let _s = S(None); let _s = S(None);

View file

@ -16,7 +16,7 @@ mod pingpong {
use send_packet; use send_packet;
pub type ping = send_packet<pong>; pub type ping = send_packet<pong>;
pub struct pong(send_packet<ping>); pub struct pong(send_packet<ping>);
//~^ ERROR invalid recursive struct type //~^ ERROR recursive type `pingpong::pong` has infinite size
} }
fn main() {} fn main() {}

View file

@ -10,7 +10,7 @@
enum foo { foo_(bar) } enum foo { foo_(bar) }
enum bar { bar_none, bar_some(bar) } enum bar { bar_none, bar_some(bar) }
//~^ ERROR invalid recursive enum type //~^ ERROR recursive type `bar` has infinite size
fn main() { fn main() {
} }

View file

@ -12,7 +12,7 @@
enum foo { foo_(bar) } enum foo { foo_(bar) }
struct bar { x: bar } struct bar { x: bar }
//~^ ERROR invalid recursive struct type //~^ ERROR E0072
fn main() { fn main() {
} }

View file

@ -12,7 +12,7 @@ use std::marker;
enum E1 { V1(E2<E1>), } enum E1 { V1(E2<E1>), }
enum E2<T> { V2(E2<E1>, marker::PhantomData<T>), } enum E2<T> { V2(E2<E1>, marker::PhantomData<T>), }
//~^ ERROR invalid recursive enum type //~^ ERROR recursive type `E2` has infinite size
impl E1 { fn foo(&self) {} } impl E1 { fn foo(&self) {} }

View file

@ -8,8 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
struct S { struct S { //~ ERROR E0072
//~^ ERROR invalid recursive struct type
element: Option<S> element: Option<S>
} }

View file

@ -17,6 +17,5 @@ use std::cell::RefCell;
static boxed: Box<RefCell<isize>> = box RefCell::new(0); static boxed: Box<RefCell<isize>> = box RefCell::new(0);
//~^ ERROR allocations are not allowed in statics //~^ ERROR allocations are not allowed in statics
//~| ERROR the trait `core::marker::Sync` is not implemented for the type //~| ERROR the trait `core::marker::Sync` is not implemented for the type
//~| ERROR the trait `core::marker::Sync` is not implemented for the type
fn main() { } fn main() { }

View file

@ -27,14 +27,12 @@ fn f<T>(val: T) {
let t: S<T> = S(marker::PhantomData); let t: S<T> = S(marker::PhantomData);
let a = &t as &Gettable<T>; let a = &t as &Gettable<T>;
//~^ ERROR the trait `core::marker::Send` is not implemented //~^ ERROR the trait `core::marker::Send` is not implemented
//~^^ ERROR the trait `core::marker::Copy` is not implemented
} }
fn g<T>(val: T) { fn g<T>(val: T) {
let t: S<T> = S(marker::PhantomData); let t: S<T> = S(marker::PhantomData);
let a: &Gettable<T> = &t; let a: &Gettable<T> = &t;
//~^ ERROR the trait `core::marker::Send` is not implemented //~^ ERROR the trait `core::marker::Send` is not implemented
//~^^ ERROR the trait `core::marker::Copy` is not implemented
} }
fn foo<'a>() { fn foo<'a>() {

View file

@ -13,8 +13,8 @@
extern crate libc; extern crate libc;
trait Mirror { type It; } trait Mirror { type It: ?Sized; }
impl<T> Mirror for T { type It = Self; } impl<T: ?Sized> Mirror for T { type It = Self; }
#[repr(C)] #[repr(C)]
pub struct StructWithProjection(*mut <StructWithProjection as Mirror>::It); pub struct StructWithProjection(*mut <StructWithProjection as Mirror>::It);
#[repr(C)] #[repr(C)]

View file

@ -16,5 +16,4 @@ fn main() {
let x = RefCell::new(0); let x = RefCell::new(0);
f(x); f(x);
//~^ ERROR `core::marker::Sync` is not implemented //~^ ERROR `core::marker::Sync` is not implemented
//~^^ ERROR `core::marker::Sync` is not implemented
} }

View file

@ -18,7 +18,6 @@ use std::cell::RefCell;
fn assert<T: RecoverSafe + ?Sized>() {} fn assert<T: RecoverSafe + ?Sized>() {}
fn main() { fn main() {
assert::<Rc<RefCell<i32>>>(); //~ ERROR: is not implemented assert::<Rc<RefCell<i32>>>(); //~ ERROR E0277
//~^ ERROR: is not implemented
} }

View file

@ -19,5 +19,4 @@ fn assert<T: RecoverSafe + ?Sized>() {}
fn main() { fn main() {
assert::<Arc<RefCell<i32>>>(); //~ ERROR: is not implemented assert::<Arc<RefCell<i32>>>(); //~ ERROR: is not implemented
//~^ ERROR: is not implemented
} }

View file

@ -17,6 +17,5 @@ use std::cell::RefCell;
fn assert<T: RecoverSafe + ?Sized>() {} fn assert<T: RecoverSafe + ?Sized>() {}
fn main() { fn main() {
assert::<&RefCell<i32>>(); //~ ERROR: is not implemented assert::<&RefCell<i32>>(); //~ ERROR E0277
//~^ ERROR is not implemented
} }

View file

@ -17,7 +17,6 @@ use std::cell::RefCell;
fn assert<T: RecoverSafe + ?Sized>() {} fn assert<T: RecoverSafe + ?Sized>() {}
fn main() { fn main() {
assert::<*mut RefCell<i32>>(); //~ ERROR: is not implemented assert::<*mut RefCell<i32>>(); //~ ERROR E0277
//~^ ERROR is not implemented
} }

View file

@ -28,6 +28,7 @@ fn make_bar<T:Bar>(t: &T) -> &Bar {
} }
fn make_bar_explicit<T:Bar>(t: &T) -> &Bar { fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
//~^ ERROR E0038
t as &Bar t as &Bar
} }

View file

@ -17,12 +17,11 @@ pub fn main() {
// Bool => does not implement iterator. // Bool => does not implement iterator.
for i in false..true {} for i in false..true {}
//~^ ERROR the trait //~^ ERROR E0277
//~^^ ERROR the trait
//~^^^ ERROR the trait
// Unsized type. // Unsized type.
let arr: &[_] = &[1, 2, 3]; let arr: &[_] = &[1, 2, 3];
let range = *arr..; let range = *arr..;
//~^ ERROR the trait `core::marker::Sized` is not implemented //~^ ERROR the trait `core::marker::Sized` is not implemented
//~| ERROR the trait `core::marker::Sized` is not implemented
} }

View file

@ -19,8 +19,8 @@ impl<T:Dot> Dot for Cons<T> {
self.head * other.head + self.tail.dot(other.tail) self.head * other.head + self.tail.dot(other.tail)
} }
} }
fn test<T:Dot> (n:isize, i:isize, first:T, second:T) ->isize { fn test<T:Dot> (n:isize, i:isize, first:T, second:T) ->isize { //~ ERROR recursion limit
match n { 0 => {first.dot(second)} //~ ERROR overflow match n { 0 => {first.dot(second)}
// FIXME(#4287) Error message should be here. It should be // FIXME(#4287) Error message should be here. It should be
// a type error to instantiate `test` at a type other than T. // a type error to instantiate `test` at a type other than T.
_ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}

View file

@ -8,8 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: invalid recursive enum type
enum list<T> { cons(T, list<T>), nil } enum list<T> { cons(T, list<T>), nil }
//~^ ERROR recursive type `list` has infinite size
fn main() {} fn main() {}

View file

@ -0,0 +1,31 @@
// Copyright 2014 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.
// Test the error message resulting from a cycle in solving `Foo:
// Sized`. The specifics of the message will of course but the main
// thing we want to preserve is that:
//
// 1. the message should appear attached to one of the structs
// defined in this file;
// 2. it should elaborate the steps that led to the cycle.
struct Baz { q: Option<Foo> }
struct Foo { q: Option<Baz> }
//~^ ERROR recursive type `Foo` has infinite size
//~| type `Foo` is embedded within `core::option::Option<Foo>`...
//~| ...which in turn is embedded within `core::option::Option<Foo>`...
//~| ...which in turn is embedded within `Baz`...
//~| ...which in turn is embedded within `core::option::Option<Baz>`...
//~| ...which in turn is embedded within `Foo`, completing the cycle.
impl Foo { fn bar(&self) {} }
fn main() {}

View file

@ -22,6 +22,6 @@ fn main() {
x: 3 x: 3
}; };
let baz: Foo<usize> = panic!(); let baz: Foo<usize> = loop { };
//~^ ERROR not implemented //~^ ERROR not implemented
} }

View file

@ -21,5 +21,7 @@ fn main() {
(box 10 as Box<bar>).dup(); (box 10 as Box<bar>).dup();
//~^ ERROR E0038 //~^ ERROR E0038
//~| ERROR E0038 //~| ERROR E0038
//~| ERROR E0038
//~| ERROR E0038
//~| ERROR E0277 //~| ERROR E0277
} }

View file

@ -0,0 +1,28 @@
// 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.
// OIBIT-based version of #29859, supertrait version. Test that using
// a simple OIBIT `..` impl alone still doesn't allow arbitary bounds
// to be synthesized.
#![feature(optin_builtin_traits)]
trait Magic: Copy {}
impl Magic for .. {}
fn copy<T: Magic>(x: T) -> (T, T) { (x, x) }
#[derive(Debug)]
struct NoClone;
fn main() {
let (a, b) = copy(NoClone); //~ ERROR E0277
println!("{:?} {:?}", a, b);
}

View file

@ -0,0 +1,25 @@
// 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 #29859, supertrait version. This example
// allowed arbitrary trait bounds to be synthesized.
trait Magic: Copy {}
impl<T: Magic> Magic for T {}
fn copy<T: Magic>(x: T) -> (T, T) { (x, x) }
#[derive(Debug)]
struct NoClone;
fn main() {
let (a, b) = copy(NoClone); //~ ERROR E0275
println!("{:?} {:?}", a, b);
}

View file

@ -0,0 +1,31 @@
// 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 #29859, initial version. This example allowed
// arbitrary trait bounds to be synthesized.
// Trait that you want all types to implement.
use std::marker::{Sync as Trait};
pub trait Magic {
type X: Trait;
}
impl<T: Magic> Magic for T {
type X = Self;
}
fn check<T: Trait>() {}
fn wizard<T: Magic>() { check::<<T as Magic>::X>(); }
fn main() {
wizard::<*mut ()>(); //~ ERROR E0275
// check::<*mut ()>();
}

View file

@ -8,8 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern:invalid recursive struct type struct t1 { //~ ERROR E0072
struct t1 {
foo: isize, foo: isize,
foolish: t1 foolish: t1
} }