about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-01-08 21:41:37 -0500
committerNiko Matsakis <niko@alum.mit.edu>2016-01-16 05:22:32 -0500
commit3046ac217fbc8c8962ee83025b47cdf4048c39f0 (patch)
tree85e3402da08722ddbb6231793988c34441744aa3
parent4bbe532737630eb2a5e07b73f01875c936e2ec57 (diff)
downloadrust-3046ac217fbc8c8962ee83025b47cdf4048c39f0.tar.gz
rust-3046ac217fbc8c8962ee83025b47cdf4048c39f0.zip
Detect cycles and specialize error reporting for Sized. It is important
to get the `Sized` error usable, since that hits new users
frequently. Further work is needed for the error reporting for non-Sized
cycle cases; those currently just fallback to the old path. Also adjust tests.
-rw-r--r--src/librustc/diagnostics.rs37
-rw-r--r--src/librustc/middle/traits/error_reporting.rs142
-rw-r--r--src/librustc/middle/traits/fulfill.rs43
-rw-r--r--src/librustc/middle/traits/mod.rs2
-rw-r--r--src/librustc/middle/traits/project.rs2
-rw-r--r--src/librustc/middle/traits/select.rs5
-rw-r--r--src/librustc_typeck/check/mod.rs6
-rw-r--r--src/librustc_typeck/diagnostics.rs37
-rw-r--r--src/test/compile-fail/bad-sized.rs3
-rw-r--r--src/test/compile-fail/infinite-tag-type-recursion.rs4
-rw-r--r--src/test/compile-fail/issue-17431-1.rs2
-rw-r--r--src/test/compile-fail/issue-17431-2.rs3
-rw-r--r--src/test/compile-fail/issue-17431-3.rs2
-rw-r--r--src/test/compile-fail/issue-17431-4.rs2
-rw-r--r--src/test/compile-fail/issue-17431-5.rs3
-rw-r--r--src/test/compile-fail/issue-17431-6.rs2
-rw-r--r--src/test/compile-fail/issue-17431-7.rs2
-rw-r--r--src/test/compile-fail/issue-20261.rs2
-rw-r--r--src/test/compile-fail/issue-20831-debruijn.rs1
-rw-r--r--src/test/compile-fail/issue-26548.rs7
-rw-r--r--src/test/compile-fail/issue-2718-a.rs2
-rw-r--r--src/test/compile-fail/issue-3008-1.rs2
-rw-r--r--src/test/compile-fail/issue-3008-2.rs2
-rw-r--r--src/test/compile-fail/issue-3008-3.rs2
-rw-r--r--src/test/compile-fail/issue-3779.rs3
-rw-r--r--src/test/compile-fail/issue-7364.rs1
-rw-r--r--src/test/compile-fail/kindck-impl-type-params.rs2
-rw-r--r--src/test/compile-fail/lint-ctypes.rs4
-rw-r--r--src/test/compile-fail/mut-not-freeze.rs1
-rw-r--r--src/test/compile-fail/not-panic-safe-2.rs3
-rw-r--r--src/test/compile-fail/not-panic-safe-3.rs1
-rw-r--r--src/test/compile-fail/not-panic-safe-4.rs3
-rw-r--r--src/test/compile-fail/not-panic-safe-6.rs3
-rw-r--r--src/test/compile-fail/object-safety-generics.rs1
-rw-r--r--src/test/compile-fail/range-1.rs5
-rw-r--r--src/test/compile-fail/recursive-enum.rs3
-rw-r--r--src/test/compile-fail/sized-cycle-note.rs31
-rw-r--r--src/test/compile-fail/trait-bounds-on-structs-and-enums-locals.rs2
-rw-r--r--src/test/compile-fail/trait-test-2.rs2
-rw-r--r--src/test/compile-fail/type-recursive.rs3
40 files changed, 291 insertions, 92 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 3aabe4b4931..2a2936e7363 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -712,6 +712,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>`.
 "####,
 
+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##"
 You tried to give a type parameter to a type which doesn't need it. Erroneous
 code example:
diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs
index 883c5e7bb40..ef97c4c8a12 100644
--- a/src/librustc/middle/traits/error_reporting.rs
+++ b/src/librustc/middle/traits/error_reporting.rs
@@ -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
 /// occurrences in any case.
 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>
 {
@@ -192,7 +193,9 @@ pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
                                    "overflow evaluating the requirement `{}`",
                                    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);
 
@@ -201,6 +204,141 @@ pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
     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>,
                                         obligation: &PredicateObligation<'tcx>,
                                         error: &SelectionError<'tcx>)
diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs
index 0e1c9c3843b..2fdf93e6e35 100644
--- a/src/librustc/middle/traits/fulfill.rs
+++ b/src/librustc/middle/traits/fulfill.rs
@@ -11,10 +11,10 @@
 use middle::infer::InferCtxt;
 use middle::ty::{self, Ty, TypeFoldable};
 use rustc_data_structures::obligation_forest::{Backtrace, ObligationForest, Error};
-
+use std::iter;
 use syntax::ast;
 use util::common::ErrorReported;
-use util::nodemap::{FnvHashSet, NodeMap};
+use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
 
 use super::CodeAmbiguity;
 use super::CodeProjectionError;
@@ -25,6 +25,7 @@ use super::FulfillmentErrorCode;
 use super::ObligationCause;
 use super::PredicateObligation;
 use super::project;
+use super::report_overflow_error_cycle;
 use super::select::SelectionContext;
 use super::Unimplemented;
 use super::util::predicate_for_builtin_bound;
@@ -357,6 +358,17 @@ fn process_predicate1<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
     }
 
     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 {
         ty::Predicate::Trait(ref data) => {
             if coinductive_match(selcx, obligation, data, &backtrace) {
@@ -488,11 +500,15 @@ fn coinductive_match<'a,'tcx>(selcx: &mut SelectionContext<'a,'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;
                     }
                 }
@@ -501,7 +517,7 @@ fn coinductive_match<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
 
             // And we must find a recursive match.
             if bt_obligation.obligation.predicate == top_obligation.predicate {
-                debug!("process_predicate: found a match in the backtrace");
+                debug!("coinductive_match: found a match in the backtrace");
                 return true;
             }
         }
@@ -510,6 +526,27 @@ fn coinductive_match<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
     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>,
                                     r_b: ty::Region,
                                     cause: ObligationCause<'tcx>,
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index 4a92a8a9d58..8fecffcea9f 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -28,8 +28,10 @@ use syntax::ast;
 use syntax::codemap::{Span, DUMMY_SP};
 
 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_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_object_safety_error;
 pub use self::coherence::orphan_check;
diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs
index e9d7b330d07..c363425db85 100644
--- a/src/librustc/middle/traits/project.rs
+++ b/src/librustc/middle/traits/project.rs
@@ -479,7 +479,7 @@ fn project_type<'cx,'tcx>(
     let recursion_limit = selcx.tcx().sess.recursion_limit.get();
     if obligation.recursion_depth >= recursion_limit {
         debug!("project: overflow!");
-        report_overflow_error(selcx.infcx(), &obligation);
+        report_overflow_error(selcx.infcx(), &obligation, true);
     }
 
     let obligation_trait_ref =
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index f6d0da904a4..75992b6849b 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -711,7 +711,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // not update) the cache.
         let recursion_limit = self.infcx.tcx.sess.recursion_limit.get();
         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
@@ -2124,6 +2124,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                            nested: ty::Binder<Vec<Ty<'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) {
             Ok(def_id) => def_id,
             Err(_) => {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index e9184a12d72..eafa1e4304d 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -4132,7 +4132,7 @@ fn check_const_with_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 pub fn check_representable(tcx: &ty::ctxt,
                            sp: Span,
                            item_id: ast::NodeId,
-                           designation: &str) -> bool {
+                           _designation: &str) -> bool {
     let rty = tcx.node_id_to_type(item_id);
 
     // Check that it is possible to represent this type. This call identifies
@@ -4142,9 +4142,7 @@ pub fn check_representable(tcx: &ty::ctxt,
     // caught by case 1.
     match rty.is_representable(tcx, sp) {
         Representability::SelfRecursive => {
-            struct_span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation)
-                .fileline_help(sp, "wrap the inner value in a box to make it representable")
-                .emit();
+            traits::recursive_type_with_infinite_size_error(tcx, tcx.map.local_def_id(item_id)).emit();
             return false
         }
         Representability::Representable | Representability::ContainsRecursive => (),
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 5f2582a548b..55a1021f0fb 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -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##"
 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
diff --git a/src/test/compile-fail/bad-sized.rs b/src/test/compile-fail/bad-sized.rs
index 8c13ff70515..bfe9d740339 100644
--- a/src/test/compile-fail/bad-sized.rs
+++ b/src/test/compile-fail/bad-sized.rs
@@ -13,5 +13,6 @@ trait Trait {}
 pub fn main() {
     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
 }
diff --git a/src/test/compile-fail/infinite-tag-type-recursion.rs b/src/test/compile-fail/infinite-tag-type-recursion.rs
index 7dbf75feda0..c9a7f731aea 100644
--- a/src/test/compile-fail/infinite-tag-type-recursion.rs
+++ b/src/test/compile-fail/infinite-tag-type-recursion.rs
@@ -8,9 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
-// error-pattern: invalid recursive enum type
-
 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)); }
diff --git a/src/test/compile-fail/issue-17431-1.rs b/src/test/compile-fail/issue-17431-1.rs
index bd3f2835058..260cc366fae 100644
--- a/src/test/compile-fail/issue-17431-1.rs
+++ b/src/test/compile-fail/issue-17431-1.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 struct Foo { foo: Option<Option<Foo>> }
-//~^ ERROR invalid recursive struct type
+//~^ ERROR recursive type `Foo` has infinite size
 
 impl Foo { fn bar(&self) {} }
 
diff --git a/src/test/compile-fail/issue-17431-2.rs b/src/test/compile-fail/issue-17431-2.rs
index 4e1c0d6571d..edbc8c82432 100644
--- a/src/test/compile-fail/issue-17431-2.rs
+++ b/src/test/compile-fail/issue-17431-2.rs
@@ -9,10 +9,9 @@
 // except according to those terms.
 
 struct Baz { q: Option<Foo> }
-//~^ ERROR invalid recursive struct type
 
 struct Foo { q: Option<Baz> }
-//~^ ERROR invalid recursive struct type
+//~^ ERROR recursive type `Foo` has infinite size
 
 impl Foo { fn bar(&self) {} }
 
diff --git a/src/test/compile-fail/issue-17431-3.rs b/src/test/compile-fail/issue-17431-3.rs
index 07c5f106456..9ba085591f0 100644
--- a/src/test/compile-fail/issue-17431-3.rs
+++ b/src/test/compile-fail/issue-17431-3.rs
@@ -11,7 +11,7 @@
 use std::sync::Mutex;
 
 struct Foo { foo: Mutex<Option<Foo>> }
-//~^ ERROR invalid recursive struct type
+//~^ ERROR recursive type `Foo` has infinite size
 
 impl Foo { fn bar(&self) {} }
 
diff --git a/src/test/compile-fail/issue-17431-4.rs b/src/test/compile-fail/issue-17431-4.rs
index 74952d9ca2b..665c3cf8fe6 100644
--- a/src/test/compile-fail/issue-17431-4.rs
+++ b/src/test/compile-fail/issue-17431-4.rs
@@ -11,7 +11,7 @@
 use std::marker;
 
 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) {} }
 
diff --git a/src/test/compile-fail/issue-17431-5.rs b/src/test/compile-fail/issue-17431-5.rs
index 157b5ed434e..85ed4d5d634 100644
--- a/src/test/compile-fail/issue-17431-5.rs
+++ b/src/test/compile-fail/issue-17431-5.rs
@@ -11,8 +11,9 @@
 use std::marker;
 
 struct Foo { foo: Bar<Foo> }
+
 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) {} }
 
diff --git a/src/test/compile-fail/issue-17431-6.rs b/src/test/compile-fail/issue-17431-6.rs
index b2037378d37..4c1e82c3d6a 100644
--- a/src/test/compile-fail/issue-17431-6.rs
+++ b/src/test/compile-fail/issue-17431-6.rs
@@ -11,7 +11,7 @@
 use std::sync::Mutex;
 
 enum Foo { X(Mutex<Option<Foo>>) }
-//~^ ERROR invalid recursive enum type
+//~^ ERROR recursive type `Foo` has infinite size
 
 impl Foo { fn bar(self) {} }
 
diff --git a/src/test/compile-fail/issue-17431-7.rs b/src/test/compile-fail/issue-17431-7.rs
index 9ad81e030aa..71b85da29fc 100644
--- a/src/test/compile-fail/issue-17431-7.rs
+++ b/src/test/compile-fail/issue-17431-7.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 enum Foo { Voo(Option<Option<Foo>>) }
-//~^ ERROR invalid recursive enum type
+//~^ ERROR recursive type `Foo` has infinite size
 
 impl Foo { fn bar(&self) {} }
 
diff --git a/src/test/compile-fail/issue-20261.rs b/src/test/compile-fail/issue-20261.rs
index 42fd856ad87..09044b5b505 100644
--- a/src/test/compile-fail/issue-20261.rs
+++ b/src/test/compile-fail/issue-20261.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn main() {
-    for (ref i,) in [].iter() { //~ ERROR: type mismatch resolving
+    for (ref i,) in [].iter() { //~ ERROR mismatched types
         i.clone();
         //~^ ERROR: the type of this value must be known in this context
     }
diff --git a/src/test/compile-fail/issue-20831-debruijn.rs b/src/test/compile-fail/issue-20831-debruijn.rs
index 3f96a9c3422..48ea344c7c2 100644
--- a/src/test/compile-fail/issue-20831-debruijn.rs
+++ b/src/test/compile-fail/issue-20831-debruijn.rs
@@ -40,6 +40,7 @@ impl<'a> Publisher<'a> for MyStruct<'a> {
         //~^^ ERROR cannot infer
         //~|  ERROR cannot infer
         //~|  ERROR cannot infer
+        //~|  ERROR cannot infer
         //
         // The fact that `Publisher` is using an implicit lifetime is
         // what was causing the debruijn accounting to be off, so
diff --git a/src/test/compile-fail/issue-26548.rs b/src/test/compile-fail/issue-26548.rs
index 8b02e8e7046..28080ae09e5 100644
--- a/src/test/compile-fail/issue-26548.rs
+++ b/src/test/compile-fail/issue-26548.rs
@@ -8,11 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: overflow representing the type `S`
-
-trait Mirror { type It; }
-impl<T> Mirror for T { type It = Self; }
+trait Mirror { type It: ?Sized; }
+impl<T: ?Sized> Mirror for T { type It = Self; }
 struct S(Option<<S as Mirror>::It>);
+//~^ ERROR recursive type `S` has infinite size
 
 fn main() {
     let _s = S(None);
diff --git a/src/test/compile-fail/issue-2718-a.rs b/src/test/compile-fail/issue-2718-a.rs
index 37daf76c0b9..6de28cbbf35 100644
--- a/src/test/compile-fail/issue-2718-a.rs
+++ b/src/test/compile-fail/issue-2718-a.rs
@@ -16,7 +16,7 @@ mod pingpong {
     use send_packet;
     pub type ping = send_packet<pong>;
     pub struct pong(send_packet<ping>);
-    //~^ ERROR invalid recursive struct type
+    //~^ ERROR recursive type `pingpong::pong` has infinite size
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/issue-3008-1.rs b/src/test/compile-fail/issue-3008-1.rs
index eb684208326..d3c15763eb0 100644
--- a/src/test/compile-fail/issue-3008-1.rs
+++ b/src/test/compile-fail/issue-3008-1.rs
@@ -10,7 +10,7 @@
 
 enum foo { foo_(bar) }
 enum bar { bar_none, bar_some(bar) }
-//~^ ERROR invalid recursive enum type
+//~^ ERROR recursive type `bar` has infinite size
 
 fn main() {
 }
diff --git a/src/test/compile-fail/issue-3008-2.rs b/src/test/compile-fail/issue-3008-2.rs
index f934e0771c2..e6cc29634a1 100644
--- a/src/test/compile-fail/issue-3008-2.rs
+++ b/src/test/compile-fail/issue-3008-2.rs
@@ -12,7 +12,7 @@
 
 enum foo { foo_(bar) }
 struct bar { x: bar }
-//~^ ERROR invalid recursive struct type
+//~^ ERROR E0072
 
 fn main() {
 }
diff --git a/src/test/compile-fail/issue-3008-3.rs b/src/test/compile-fail/issue-3008-3.rs
index f8756b83f23..66bfab003e9 100644
--- a/src/test/compile-fail/issue-3008-3.rs
+++ b/src/test/compile-fail/issue-3008-3.rs
@@ -12,7 +12,7 @@ use std::marker;
 
 enum E1 { V1(E2<E1>), }
 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) {} }
 
diff --git a/src/test/compile-fail/issue-3779.rs b/src/test/compile-fail/issue-3779.rs
index 66d8fb40cd1..d96b1a1cbe3 100644
--- a/src/test/compile-fail/issue-3779.rs
+++ b/src/test/compile-fail/issue-3779.rs
@@ -8,8 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-struct S {
-    //~^ ERROR invalid recursive struct type
+struct S { //~ ERROR E0072
     element: Option<S>
 }
 
diff --git a/src/test/compile-fail/issue-7364.rs b/src/test/compile-fail/issue-7364.rs
index 7d0a9007829..87b7b73d57d 100644
--- a/src/test/compile-fail/issue-7364.rs
+++ b/src/test/compile-fail/issue-7364.rs
@@ -17,6 +17,5 @@ use std::cell::RefCell;
 static boxed: Box<RefCell<isize>> = box RefCell::new(0);
 //~^ 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
 
 fn main() { }
diff --git a/src/test/compile-fail/kindck-impl-type-params.rs b/src/test/compile-fail/kindck-impl-type-params.rs
index 3474a73b31f..aec40c1a73a 100644
--- a/src/test/compile-fail/kindck-impl-type-params.rs
+++ b/src/test/compile-fail/kindck-impl-type-params.rs
@@ -27,14 +27,12 @@ fn f<T>(val: T) {
     let t: S<T> = S(marker::PhantomData);
     let a = &t as &Gettable<T>;
     //~^ ERROR the trait `core::marker::Send` is not implemented
-    //~^^ ERROR the trait `core::marker::Copy` is not implemented
 }
 
 fn g<T>(val: T) {
     let t: S<T> = S(marker::PhantomData);
     let a: &Gettable<T> = &t;
     //~^ ERROR the trait `core::marker::Send` is not implemented
-    //~^^ ERROR the trait `core::marker::Copy` is not implemented
 }
 
 fn foo<'a>() {
diff --git a/src/test/compile-fail/lint-ctypes.rs b/src/test/compile-fail/lint-ctypes.rs
index 5c49098d870..731c1edbfc0 100644
--- a/src/test/compile-fail/lint-ctypes.rs
+++ b/src/test/compile-fail/lint-ctypes.rs
@@ -13,8 +13,8 @@
 
 extern crate libc;
 
-trait Mirror { type It; }
-impl<T> Mirror for T { type It = Self; }
+trait Mirror { type It: ?Sized; }
+impl<T: ?Sized> Mirror for T { type It = Self; }
 #[repr(C)]
 pub struct StructWithProjection(*mut <StructWithProjection as Mirror>::It);
 #[repr(C)]
diff --git a/src/test/compile-fail/mut-not-freeze.rs b/src/test/compile-fail/mut-not-freeze.rs
index 2269c58c97d..db19132b2c4 100644
--- a/src/test/compile-fail/mut-not-freeze.rs
+++ b/src/test/compile-fail/mut-not-freeze.rs
@@ -16,5 +16,4 @@ fn main() {
     let x = RefCell::new(0);
     f(x);
     //~^ ERROR `core::marker::Sync` is not implemented
-    //~^^ ERROR `core::marker::Sync` is not implemented
 }
diff --git a/src/test/compile-fail/not-panic-safe-2.rs b/src/test/compile-fail/not-panic-safe-2.rs
index 47a65505d8a..922d70b8013 100644
--- a/src/test/compile-fail/not-panic-safe-2.rs
+++ b/src/test/compile-fail/not-panic-safe-2.rs
@@ -18,7 +18,6 @@ use std::cell::RefCell;
 fn assert<T: RecoverSafe + ?Sized>() {}
 
 fn main() {
-    assert::<Rc<RefCell<i32>>>(); //~ ERROR: is not implemented
-    //~^ ERROR: is not implemented
+    assert::<Rc<RefCell<i32>>>(); //~ ERROR E0277
 }
 
diff --git a/src/test/compile-fail/not-panic-safe-3.rs b/src/test/compile-fail/not-panic-safe-3.rs
index a0c7865eeb0..50a69543f7d 100644
--- a/src/test/compile-fail/not-panic-safe-3.rs
+++ b/src/test/compile-fail/not-panic-safe-3.rs
@@ -19,5 +19,4 @@ fn assert<T: RecoverSafe + ?Sized>() {}
 
 fn main() {
     assert::<Arc<RefCell<i32>>>(); //~ ERROR: is not implemented
-    //~^ ERROR: is not implemented
 }
diff --git a/src/test/compile-fail/not-panic-safe-4.rs b/src/test/compile-fail/not-panic-safe-4.rs
index 9e716131525..c50e4b9d87e 100644
--- a/src/test/compile-fail/not-panic-safe-4.rs
+++ b/src/test/compile-fail/not-panic-safe-4.rs
@@ -17,6 +17,5 @@ use std::cell::RefCell;
 fn assert<T: RecoverSafe + ?Sized>() {}
 
 fn main() {
-    assert::<&RefCell<i32>>(); //~ ERROR: is not implemented
-    //~^ ERROR is not implemented
+    assert::<&RefCell<i32>>(); //~ ERROR E0277
 }
diff --git a/src/test/compile-fail/not-panic-safe-6.rs b/src/test/compile-fail/not-panic-safe-6.rs
index 90c730d3758..0fc912dc95f 100644
--- a/src/test/compile-fail/not-panic-safe-6.rs
+++ b/src/test/compile-fail/not-panic-safe-6.rs
@@ -17,7 +17,6 @@ use std::cell::RefCell;
 fn assert<T: RecoverSafe + ?Sized>() {}
 
 fn main() {
-    assert::<*mut RefCell<i32>>(); //~ ERROR: is not implemented
-    //~^ ERROR is not implemented
+    assert::<*mut RefCell<i32>>(); //~ ERROR E0277
 }
 
diff --git a/src/test/compile-fail/object-safety-generics.rs b/src/test/compile-fail/object-safety-generics.rs
index 341736f7ab5..8e3161ef884 100644
--- a/src/test/compile-fail/object-safety-generics.rs
+++ b/src/test/compile-fail/object-safety-generics.rs
@@ -28,6 +28,7 @@ fn make_bar<T:Bar>(t: &T) -> &Bar {
 }
 
 fn make_bar_explicit<T:Bar>(t: &T) -> &Bar {
+    //~^ ERROR E0038
     t as &Bar
 }
 
diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs
index 826e4283ef8..b839902c683 100644
--- a/src/test/compile-fail/range-1.rs
+++ b/src/test/compile-fail/range-1.rs
@@ -17,12 +17,11 @@ pub fn main() {
 
     // Bool => does not implement iterator.
     for i in false..true {}
-    //~^ ERROR the trait
-    //~^^ ERROR the trait
-    //~^^^ ERROR the trait
+    //~^ ERROR E0277
 
     // Unsized type.
     let arr: &[_] = &[1, 2, 3];
     let range = *arr..;
     //~^ ERROR the trait `core::marker::Sized` is not implemented
+    //~| ERROR the trait `core::marker::Sized` is not implemented
 }
diff --git a/src/test/compile-fail/recursive-enum.rs b/src/test/compile-fail/recursive-enum.rs
index 33dcbdf74d2..555755cdb96 100644
--- a/src/test/compile-fail/recursive-enum.rs
+++ b/src/test/compile-fail/recursive-enum.rs
@@ -8,8 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern: invalid recursive enum type
-
 enum list<T> { cons(T, list<T>), nil }
+//~^ ERROR recursive type `list` has infinite size
 
 fn main() {}
diff --git a/src/test/compile-fail/sized-cycle-note.rs b/src/test/compile-fail/sized-cycle-note.rs
new file mode 100644
index 00000000000..bb1ab2eafb3
--- /dev/null
+++ b/src/test/compile-fail/sized-cycle-note.rs
@@ -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() {}
diff --git a/src/test/compile-fail/trait-bounds-on-structs-and-enums-locals.rs b/src/test/compile-fail/trait-bounds-on-structs-and-enums-locals.rs
index d39b7e15edc..520691fbecc 100644
--- a/src/test/compile-fail/trait-bounds-on-structs-and-enums-locals.rs
+++ b/src/test/compile-fail/trait-bounds-on-structs-and-enums-locals.rs
@@ -22,6 +22,6 @@ fn main() {
         x: 3
     };
 
-    let baz: Foo<usize> = panic!();
+    let baz: Foo<usize> = loop { };
     //~^ ERROR not implemented
 }
diff --git a/src/test/compile-fail/trait-test-2.rs b/src/test/compile-fail/trait-test-2.rs
index 2d4df77f960..0cfcf6bb3f9 100644
--- a/src/test/compile-fail/trait-test-2.rs
+++ b/src/test/compile-fail/trait-test-2.rs
@@ -21,5 +21,7 @@ fn main() {
     (box 10 as Box<bar>).dup();
     //~^ ERROR E0038
     //~| ERROR E0038
+    //~| ERROR E0038
+    //~| ERROR E0038
     //~| ERROR E0277
 }
diff --git a/src/test/compile-fail/type-recursive.rs b/src/test/compile-fail/type-recursive.rs
index 3b08d900733..4bb739800df 100644
--- a/src/test/compile-fail/type-recursive.rs
+++ b/src/test/compile-fail/type-recursive.rs
@@ -8,8 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:invalid recursive struct type
-struct t1 {
+struct t1 { //~ ERROR E0072
     foo: isize,
     foolish: t1
 }