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:
commit
c14b615534
66 changed files with 1467 additions and 356 deletions
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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: ¶m_env };
|
let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: ¶m_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);
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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 } =
|
||||||
|
|
|
@ -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>)
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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(_) => {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
80
src/librustc_data_structures/obligation_forest/README.md
Normal file
80
src/librustc_data_structures/obligation_forest/README.md
Normal 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.
|
||||||
|
|
||||||
|
|
462
src/librustc_data_structures/obligation_forest/mod.rs
Normal file
462
src/librustc_data_structures/obligation_forest/mod.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/librustc_data_structures/obligation_forest/node_index.rs
Normal file
31
src/librustc_data_structures/obligation_forest/node_index.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
206
src/librustc_data_structures/obligation_forest/test.rs
Normal file
206
src/librustc_data_structures/obligation_forest/test.rs
Normal 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());
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 => (),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)); }
|
||||||
|
|
|
@ -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) {} }
|
||||||
|
|
||||||
|
|
|
@ -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) {} }
|
||||||
|
|
||||||
|
|
|
@ -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) {} }
|
||||||
|
|
||||||
|
|
|
@ -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) {} }
|
||||||
|
|
||||||
|
|
|
@ -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) {} }
|
||||||
|
|
||||||
|
|
|
@ -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) {} }
|
||||||
|
|
||||||
|
|
|
@ -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) {} }
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {}
|
||||||
|
|
|
@ -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() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {} }
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() { }
|
||||||
|
|
|
@ -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>() {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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})}
|
||||||
|
|
|
@ -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() {}
|
||||||
|
|
31
src/test/compile-fail/sized-cycle-note.rs
Normal file
31
src/test/compile-fail/sized-cycle-note.rs
Normal 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() {}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 ()>();
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue