about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcollections/range.rs28
-rw-r--r--src/libcollections/string.rs9
-rw-r--r--src/libcollectionstest/btree/map.rs37
-rw-r--r--src/libcollectionstest/lib.rs1
-rw-r--r--src/libcollectionstest/vec.rs50
-rw-r--r--src/librustc/infer/error_reporting/mod.rs (renamed from src/librustc/infer/error_reporting.rs)470
-rw-r--r--src/librustc/infer/error_reporting/note.rs432
-rw-r--r--src/librustc/infer/mod.rs10
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/librustdoc/html/format.rs116
-rw-r--r--src/librustdoc/html/highlight.rs6
-rw-r--r--src/librustdoc/html/render.rs64
-rw-r--r--src/librustdoc/html/static/main.js2
-rw-r--r--src/librustdoc/html/static/rustdoc.css8
-rw-r--r--src/libstd/ffi/c_str.rs14
-rw-r--r--src/test/rustdoc/assoc-consts.rs8
-rw-r--r--src/test/rustdoc/issue-28478.rs3
-rw-r--r--src/test/rustdoc/issue-33302.rs30
18 files changed, 759 insertions, 531 deletions
diff --git a/src/libcollections/range.rs b/src/libcollections/range.rs
index 1df4ace3777..e4b94a1d70e 100644
--- a/src/libcollections/range.rs
+++ b/src/libcollections/range.rs
@@ -14,7 +14,7 @@
 
 //! Range syntax.
 
-use core::ops::{RangeFull, Range, RangeTo, RangeFrom};
+use core::ops::{RangeFull, Range, RangeTo, RangeFrom, RangeInclusive, RangeToInclusive};
 use Bound::{self, Excluded, Included, Unbounded};
 
 /// **RangeArgument** is implemented by Rust's built-in range types, produced
@@ -105,6 +105,32 @@ impl<T> RangeArgument<T> for Range<T> {
     }
 }
 
+#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
+impl<T> RangeArgument<T> for RangeInclusive<T> {
+    fn start(&self) -> Bound<&T> {
+        match *self {
+            RangeInclusive::Empty{ ref at }            => Included(at),
+            RangeInclusive::NonEmpty { ref start, .. } => Included(start),
+        }
+    }
+    fn end(&self) -> Bound<&T> {
+        match *self {
+            RangeInclusive::Empty{ ref at }            => Excluded(at),
+            RangeInclusive::NonEmpty { ref end, .. }   => Included(end),
+        }
+    }
+}
+
+#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
+impl<T> RangeArgument<T> for RangeToInclusive<T> {
+    fn start(&self) -> Bound<&T> {
+        Unbounded
+    }
+    fn end(&self) -> Bound<&T> {
+        Included(&self.end)
+    }
+}
+
 impl<T> RangeArgument<T> for (Bound<T>, Bound<T>) {
     fn start(&self) -> Bound<&T> {
         match *self {
diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs
index 6839b698a56..4b37aef860d 100644
--- a/src/libcollections/string.rs
+++ b/src/libcollections/string.rs
@@ -1483,6 +1483,15 @@ impl FromIterator<char> for String {
     }
 }
 
+#[stable(feature = "string_from_iter_by_ref", since = "1.17.0")]
+impl<'a> FromIterator<&'a char> for String {
+    fn from_iter<I: IntoIterator<Item = &'a char>>(iter: I) -> String {
+        let mut buf = String::new();
+        buf.extend(iter);
+        buf
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a> FromIterator<&'a str> for String {
     fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> String {
diff --git a/src/libcollectionstest/btree/map.rs b/src/libcollectionstest/btree/map.rs
index f33923f9963..2c899d96940 100644
--- a/src/libcollectionstest/btree/map.rs
+++ b/src/libcollectionstest/btree/map.rs
@@ -179,6 +179,43 @@ fn test_range_small() {
 }
 
 #[test]
+fn test_range_inclusive() {
+    let size = 500;
+
+    let map: BTreeMap<_, _> = (0...size).map(|i| (i, i)).collect();
+
+    fn check<'a, L, R>(lhs: L, rhs: R)
+        where L: IntoIterator<Item=(&'a i32, &'a i32)>,
+              R: IntoIterator<Item=(&'a i32, &'a i32)>,
+    {
+        let lhs: Vec<_> = lhs.into_iter().collect();
+        let rhs: Vec<_> = rhs.into_iter().collect();
+        assert_eq!(lhs, rhs);
+    }
+
+    check(map.range(size + 1...size + 1), vec![]);
+    check(map.range(size...size), vec![(&size, &size)]);
+    check(map.range(size...size + 1), vec![(&size, &size)]);
+    check(map.range(0...0), vec![(&0, &0)]);
+    check(map.range(0...size - 1), map.range(..size));
+    check(map.range(-1...-1), vec![]);
+    check(map.range(-1...size), map.range(..));
+    check(map.range(...size), map.range(..));
+    check(map.range(...200), map.range(..201));
+    check(map.range(5...8), vec![(&5, &5), (&6, &6), (&7, &7), (&8, &8)]);
+    check(map.range(-1...0), vec![(&0, &0)]);
+    check(map.range(-1...2), vec![(&0, &0), (&1, &1), (&2, &2)]);
+}
+
+#[test]
+fn test_range_inclusive_max_value() {
+    let max = ::std::usize::MAX;
+    let map: BTreeMap<_, _> = vec![(max, 0)].into_iter().collect();
+
+    assert_eq!(map.range(max...max).collect::<Vec<_>>(), &[(&max, &0)]);
+}
+
+#[test]
 fn test_range_equal_empty_cases() {
     let map: BTreeMap<_, _> = (0..5).map(|i| (i, i)).collect();
     assert_eq!(map.range((Included(2), Excluded(2))).next(), None);
diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs
index 57e3c2df059..849d2401691 100644
--- a/src/libcollectionstest/lib.rs
+++ b/src/libcollectionstest/lib.rs
@@ -14,6 +14,7 @@
 #![feature(binary_heap_peek_mut_pop)]
 #![feature(box_syntax)]
 #![feature(btree_range)]
+#![feature(inclusive_range_syntax)]
 #![feature(collection_placement)]
 #![feature(collections)]
 #![feature(collections_bound)]
diff --git a/src/libcollectionstest/vec.rs b/src/libcollectionstest/vec.rs
index edeedf1d40b..06d70800d39 100644
--- a/src/libcollectionstest/vec.rs
+++ b/src/libcollectionstest/vec.rs
@@ -508,6 +508,56 @@ fn test_drain_range() {
 }
 
 #[test]
+fn test_drain_inclusive_range() {
+    let mut v = vec!['a', 'b', 'c', 'd', 'e'];
+    for _ in v.drain(1...3) {
+    }
+    assert_eq!(v, &['a', 'e']);
+
+    let mut v: Vec<_> = (0...5).map(|x| x.to_string()).collect();
+    for _ in v.drain(1...5) {
+    }
+    assert_eq!(v, &["0".to_string()]);
+
+    let mut v: Vec<String> = (0...5).map(|x| x.to_string()).collect();
+    for _ in v.drain(0...5) {
+    }
+    assert_eq!(v, Vec::<String>::new());
+
+    let mut v: Vec<_> = (0...5).map(|x| x.to_string()).collect();
+    for _ in v.drain(0...3) {
+    }
+    assert_eq!(v, &["4".to_string(), "5".to_string()]);
+
+    let mut v: Vec<_> = (0...1).map(|x| x.to_string()).collect();
+    for _ in v.drain(...0) {
+    }
+    assert_eq!(v, &["1".to_string()]);
+}
+
+#[test]
+fn test_drain_max_vec_size() {
+    let mut v = Vec::<()>::with_capacity(usize::max_value());
+    unsafe { v.set_len(usize::max_value()); }
+    for _ in v.drain(usize::max_value() - 1..) {
+    }
+    assert_eq!(v.len(), usize::max_value() - 1);
+
+    let mut v = Vec::<()>::with_capacity(usize::max_value());
+    unsafe { v.set_len(usize::max_value()); }
+    for _ in v.drain(usize::max_value() - 1...usize::max_value() - 1) {
+    }
+    assert_eq!(v.len(), usize::max_value() - 1);
+}
+
+#[test]
+#[should_panic]
+fn test_drain_inclusive_out_of_bounds() {
+    let mut v = vec![1, 2, 3, 4, 5];
+    v.drain(5...5);
+}
+
+#[test]
 fn test_into_boxed_slice() {
     let xs = vec![1, 2, 3];
     let ys = xs.into_boxed_slice();
diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting/mod.rs
index f48ff87689f..21139c8dde2 100644
--- a/src/librustc/infer/error_reporting.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -55,32 +55,25 @@
 //! ported to this system, and which relies on string concatenation at the
 //! time of error detection.
 
-use super::InferCtxt;
-use super::TypeTrace;
-use super::SubregionOrigin;
-use super::RegionVariableOrigin;
-use super::ValuePairs;
-use super::region_inference::RegionResolutionError;
-use super::region_inference::ConcreteFailure;
-use super::region_inference::SubSupConflict;
-use super::region_inference::GenericBoundFailure;
-use super::region_inference::GenericKind;
+use infer;
+use super::{InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs};
+use super::region_inference::{RegionResolutionError, ConcreteFailure, SubSupConflict,
+                              GenericBoundFailure, GenericKind};
 
-use hir::map as hir_map;
+use std::fmt;
 use hir;
-
+use hir::map as hir_map;
 use hir::def_id::DefId;
-use infer;
 use middle::region;
 use traits::{ObligationCause, ObligationCauseCode};
 use ty::{self, TyCtxt, TypeFoldable};
 use ty::{Region, Issue32330};
 use ty::error::TypeError;
-
-use std::fmt;
 use syntax_pos::{Pos, Span};
 use errors::DiagnosticBuilder;
 
+mod note;
+
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn note_and_explain_region(self,
                                    err: &mut DiagnosticBuilder,
@@ -584,289 +577,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         err.emit();
     }
 
-    fn report_concrete_failure(&self,
-                               origin: SubregionOrigin<'tcx>,
-                               sub: &'tcx Region,
-                               sup: &'tcx Region)
-                                -> DiagnosticBuilder<'tcx> {
-        match origin {
-            infer::Subtype(trace) => {
-                let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
-                self.report_and_explain_type_error(trace, &terr)
-            }
-            infer::Reborrow(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0312,
-                    "lifetime of reference outlives \
-                     lifetime of borrowed content...");
-                self.tcx.note_and_explain_region(&mut err,
-                    "...the reference is valid for ",
-                    sub,
-                    "...");
-                self.tcx.note_and_explain_region(&mut err,
-                    "...but the borrowed content is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::ReborrowUpvar(span, ref upvar_id) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0313,
-                    "lifetime of borrowed pointer outlives \
-                            lifetime of captured variable `{}`...",
-                            self.tcx.local_var_name_str(upvar_id.var_id));
-                self.tcx.note_and_explain_region(&mut err,
-                    "...the borrowed pointer is valid for ",
-                    sub,
-                    "...");
-                self.tcx.note_and_explain_region(&mut err,
-                    &format!("...but `{}` is only valid for ",
-                             self.tcx.local_var_name_str(upvar_id.var_id)),
-                    sup,
-                    "");
-                err
-            }
-            infer::InfStackClosure(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0314,
-                    "closure outlives stack frame");
-                self.tcx.note_and_explain_region(&mut err,
-                    "...the closure must be valid for ",
-                    sub,
-                    "...");
-                self.tcx.note_and_explain_region(&mut err,
-                    "...but the closure's stack frame is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::InvokeClosure(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0315,
-                    "cannot invoke closure outside of its lifetime");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the closure is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::DerefPointer(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0473,
-                          "dereference of reference outside its lifetime");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the reference is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::FreeVariable(span, id) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0474,
-                          "captured variable `{}` does not outlive the enclosing closure",
-                          self.tcx.local_var_name_str(id));
-                self.tcx.note_and_explain_region(&mut err,
-                    "captured variable is valid for ",
-                    sup,
-                    "");
-                self.tcx.note_and_explain_region(&mut err,
-                    "closure is valid for ",
-                    sub,
-                    "");
-                err
-            }
-            infer::IndexSlice(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0475,
-                          "index of slice outside its lifetime");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the slice is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::RelateObjectBound(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0476,
-                          "lifetime of the source pointer does not outlive \
-                           lifetime bound of the object type");
-                self.tcx.note_and_explain_region(&mut err,
-                    "object type is valid for ",
-                    sub,
-                    "");
-                self.tcx.note_and_explain_region(&mut err,
-                    "source pointer is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::RelateParamBound(span, ty) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0477,
-                          "the type `{}` does not fulfill the required lifetime",
-                          self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(&mut err,
-                                        "type must outlive ",
-                                        sub,
-                                        "");
-                err
-            }
-            infer::RelateRegionParamBound(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0478,
-                          "lifetime bound not satisfied");
-                self.tcx.note_and_explain_region(&mut err,
-                    "lifetime parameter instantiated with ",
-                    sup,
-                    "");
-                self.tcx.note_and_explain_region(&mut err,
-                    "but lifetime parameter must outlive ",
-                    sub,
-                    "");
-                err
-            }
-            infer::RelateDefaultParamBound(span, ty) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0479,
-                          "the type `{}` (provided as the value of \
-                           a type parameter) is not valid at this point",
-                          self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(&mut err,
-                                        "type must outlive ",
-                                        sub,
-                                        "");
-                err
-            }
-            infer::CallRcvr(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0480,
-                          "lifetime of method receiver does not outlive \
-                           the method call");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the receiver is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::CallArg(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0481,
-                          "lifetime of function argument does not outlive \
-                           the function call");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the function argument is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::CallReturn(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0482,
-                          "lifetime of return value does not outlive \
-                           the function call");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the return value is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::Operand(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0483,
-                          "lifetime of operand does not outlive \
-                           the operation");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the operand is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::AddrOf(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0484,
-                          "reference is not valid at the time of borrow");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the borrow is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::AutoBorrow(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0485,
-                          "automatically reference is not valid \
-                           at the time of borrow");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the automatic borrow is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::ExprTypeIsNotInScope(t, span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0486,
-                          "type of expression contains references \
-                           that are not valid during the expression: `{}`",
-                          self.ty_to_string(t));
-                self.tcx.note_and_explain_region(&mut err,
-                    "type is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::SafeDestructor(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0487,
-                          "unsafe use of destructor: destructor might be called \
-                           while references are dead");
-                // FIXME (22171): terms "super/subregion" are suboptimal
-                self.tcx.note_and_explain_region(&mut err,
-                    "superregion: ",
-                    sup,
-                    "");
-                self.tcx.note_and_explain_region(&mut err,
-                    "subregion: ",
-                    sub,
-                    "");
-                err
-            }
-            infer::BindingTypeIsNotValidAtDecl(span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0488,
-                          "lifetime of variable does not enclose its declaration");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the variable is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::ParameterInScope(_, span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0489,
-                          "type/lifetime parameter not in scope here");
-                self.tcx.note_and_explain_region(&mut err,
-                    "the parameter is only valid for ",
-                    sub,
-                    "");
-                err
-            }
-            infer::DataBorrowed(ty, span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0490,
-                          "a value of type `{}` is borrowed for too long",
-                          self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(&mut err, "the type is valid for ", sub, "");
-                self.tcx.note_and_explain_region(&mut err, "but the borrow lasts for ", sup, "");
-                err
-            }
-            infer::ReferenceOutlivesReferent(ty, span) => {
-                let mut err = struct_span_err!(self.tcx.sess, span, E0491,
-                          "in type `{}`, reference has a longer lifetime \
-                           than the data it references",
-                          self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(&mut err,
-                    "the pointer is valid for ",
-                    sub,
-                    "");
-                self.tcx.note_and_explain_region(&mut err,
-                    "but the referenced data is only valid for ",
-                    sup,
-                    "");
-                err
-            }
-            infer::CompareImplMethodObligation { span,
-                                                 item_name,
-                                                 impl_item_def_id,
-                                                 trait_item_def_id,
-                                                 lint_id } => {
-                self.report_extra_impl_obligation(span,
-                                                  item_name,
-                                                  impl_item_def_id,
-                                                  trait_item_def_id,
-                                                  &format!("`{}: {}`", sup, sub),
-                                                  lint_id)
-            }
-        }
-    }
-
     fn report_sub_sup_conflict(&self,
                                var_origin: RegionVariableOrigin,
                                sub_origin: SubregionOrigin<'tcx>,
@@ -939,170 +649,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                    due to conflicting requirements",
                   var_description)
     }
-
-    fn note_region_origin(&self, err: &mut DiagnosticBuilder, origin: &SubregionOrigin<'tcx>) {
-        match *origin {
-            infer::Subtype(ref trace) => {
-                if let Some((expected, found)) = self.values_str(&trace.values) {
-                    // FIXME: do we want a "the" here?
-                    err.span_note(
-                        trace.cause.span,
-                        &format!("...so that {} (expected {}, found {})",
-                                 trace.cause.as_requirement_str(), expected, found));
-                } else {
-                    // FIXME: this really should be handled at some earlier stage. Our
-                    // handling of region checking when type errors are present is
-                    // *terrible*.
-
-                    err.span_note(
-                        trace.cause.span,
-                        &format!("...so that {}",
-                                 trace.cause.as_requirement_str()));
-                }
-            }
-            infer::Reborrow(span) => {
-                err.span_note(
-                    span,
-                    "...so that reference does not outlive \
-                    borrowed content");
-            }
-            infer::ReborrowUpvar(span, ref upvar_id) => {
-                err.span_note(
-                    span,
-                    &format!(
-                        "...so that closure can access `{}`",
-                        self.tcx.local_var_name_str(upvar_id.var_id)
-                            .to_string()));
-            }
-            infer::InfStackClosure(span) => {
-                err.span_note(
-                    span,
-                    "...so that closure does not outlive its stack frame");
-            }
-            infer::InvokeClosure(span) => {
-                err.span_note(
-                    span,
-                    "...so that closure is not invoked outside its lifetime");
-            }
-            infer::DerefPointer(span) => {
-                err.span_note(
-                    span,
-                    "...so that pointer is not dereferenced \
-                    outside its lifetime");
-            }
-            infer::FreeVariable(span, id) => {
-                err.span_note(
-                    span,
-                    &format!("...so that captured variable `{}` \
-                            does not outlive the enclosing closure",
-                            self.tcx.local_var_name_str(id)));
-            }
-            infer::IndexSlice(span) => {
-                err.span_note(
-                    span,
-                    "...so that slice is not indexed outside the lifetime");
-            }
-            infer::RelateObjectBound(span) => {
-                err.span_note(
-                    span,
-                    "...so that it can be closed over into an object");
-            }
-            infer::CallRcvr(span) => {
-                err.span_note(
-                    span,
-                    "...so that method receiver is valid for the method call");
-            }
-            infer::CallArg(span) => {
-                err.span_note(
-                    span,
-                    "...so that argument is valid for the call");
-            }
-            infer::CallReturn(span) => {
-                err.span_note(
-                    span,
-                    "...so that return value is valid for the call");
-            }
-            infer::Operand(span) => {
-                err.span_note(
-                    span,
-                    "...so that operand is valid for operation");
-            }
-            infer::AddrOf(span) => {
-                err.span_note(
-                    span,
-                    "...so that reference is valid \
-                     at the time of borrow");
-            }
-            infer::AutoBorrow(span) => {
-                err.span_note(
-                    span,
-                    "...so that auto-reference is valid \
-                     at the time of borrow");
-            }
-            infer::ExprTypeIsNotInScope(t, span) => {
-                err.span_note(
-                    span,
-                    &format!("...so type `{}` of expression is valid during the \
-                             expression",
-                            self.ty_to_string(t)));
-            }
-            infer::BindingTypeIsNotValidAtDecl(span) => {
-                err.span_note(
-                    span,
-                    "...so that variable is valid at time of its declaration");
-            }
-            infer::ParameterInScope(_, span) => {
-                err.span_note(
-                    span,
-                    "...so that a type/lifetime parameter is in scope here");
-            }
-            infer::DataBorrowed(ty, span) => {
-                err.span_note(
-                    span,
-                    &format!("...so that the type `{}` is not borrowed for too long",
-                             self.ty_to_string(ty)));
-            }
-            infer::ReferenceOutlivesReferent(ty, span) => {
-                err.span_note(
-                    span,
-                    &format!("...so that the reference type `{}` \
-                             does not outlive the data it points at",
-                            self.ty_to_string(ty)));
-            }
-            infer::RelateParamBound(span, t) => {
-                err.span_note(
-                    span,
-                    &format!("...so that the type `{}` \
-                             will meet its required lifetime bounds",
-                            self.ty_to_string(t)));
-            }
-            infer::RelateDefaultParamBound(span, t) => {
-                err.span_note(
-                    span,
-                    &format!("...so that type parameter \
-                             instantiated with `{}`, \
-                             will meet its declared lifetime bounds",
-                            self.ty_to_string(t)));
-            }
-            infer::RelateRegionParamBound(span) => {
-                err.span_note(
-                    span,
-                    "...so that the declared lifetime parameter bounds \
-                                are satisfied");
-            }
-            infer::SafeDestructor(span) => {
-                err.span_note(
-                    span,
-                    "...so that references are valid when the destructor \
-                     runs");
-            }
-            infer::CompareImplMethodObligation { span, .. } => {
-                err.span_note(
-                    span,
-                    "...so that the definition in impl matches the definition from the trait");
-            }
-        }
-    }
 }
 
 impl<'tcx> ObligationCause<'tcx> {
diff --git a/src/librustc/infer/error_reporting/note.rs b/src/librustc/infer/error_reporting/note.rs
new file mode 100644
index 00000000000..8f8b2603dad
--- /dev/null
+++ b/src/librustc/infer/error_reporting/note.rs
@@ -0,0 +1,432 @@
+// Copyright 2017 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 infer::{self, InferCtxt, SubregionOrigin};
+use ty::Region;
+use ty::error::TypeError;
+use errors::DiagnosticBuilder;
+
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    pub(super) fn note_region_origin(&self,
+                                     err: &mut DiagnosticBuilder,
+                                     origin: &SubregionOrigin<'tcx>) {
+        match *origin {
+            infer::Subtype(ref trace) => {
+                if let Some((expected, found)) = self.values_str(&trace.values) {
+                    // FIXME: do we want a "the" here?
+                    err.span_note(trace.cause.span,
+                                  &format!("...so that {} (expected {}, found {})",
+                                           trace.cause.as_requirement_str(),
+                                           expected,
+                                           found));
+                } else {
+                    // FIXME: this really should be handled at some earlier stage. Our
+                    // handling of region checking when type errors are present is
+                    // *terrible*.
+
+                    err.span_note(trace.cause.span,
+                                  &format!("...so that {}", trace.cause.as_requirement_str()));
+                }
+            }
+            infer::Reborrow(span) => {
+                err.span_note(span,
+                              "...so that reference does not outlive borrowed content");
+            }
+            infer::ReborrowUpvar(span, ref upvar_id) => {
+                err.span_note(span,
+                              &format!("...so that closure can access `{}`",
+                                       self.tcx
+                                           .local_var_name_str(upvar_id.var_id)
+                                           .to_string()));
+            }
+            infer::InfStackClosure(span) => {
+                err.span_note(span, "...so that closure does not outlive its stack frame");
+            }
+            infer::InvokeClosure(span) => {
+                err.span_note(span,
+                              "...so that closure is not invoked outside its lifetime");
+            }
+            infer::DerefPointer(span) => {
+                err.span_note(span,
+                              "...so that pointer is not dereferenced outside its lifetime");
+            }
+            infer::FreeVariable(span, id) => {
+                err.span_note(span,
+                              &format!("...so that captured variable `{}` does not outlive the \
+                                        enclosing closure",
+                                       self.tcx.local_var_name_str(id)));
+            }
+            infer::IndexSlice(span) => {
+                err.span_note(span, "...so that slice is not indexed outside the lifetime");
+            }
+            infer::RelateObjectBound(span) => {
+                err.span_note(span, "...so that it can be closed over into an object");
+            }
+            infer::CallRcvr(span) => {
+                err.span_note(span,
+                              "...so that method receiver is valid for the method call");
+            }
+            infer::CallArg(span) => {
+                err.span_note(span, "...so that argument is valid for the call");
+            }
+            infer::CallReturn(span) => {
+                err.span_note(span, "...so that return value is valid for the call");
+            }
+            infer::Operand(span) => {
+                err.span_note(span, "...so that operand is valid for operation");
+            }
+            infer::AddrOf(span) => {
+                err.span_note(span, "...so that reference is valid at the time of borrow");
+            }
+            infer::AutoBorrow(span) => {
+                err.span_note(span,
+                              "...so that auto-reference is valid at the time of borrow");
+            }
+            infer::ExprTypeIsNotInScope(t, span) => {
+                err.span_note(span,
+                              &format!("...so type `{}` of expression is valid during the \
+                                        expression",
+                                       self.ty_to_string(t)));
+            }
+            infer::BindingTypeIsNotValidAtDecl(span) => {
+                err.span_note(span,
+                              "...so that variable is valid at time of its declaration");
+            }
+            infer::ParameterInScope(_, span) => {
+                err.span_note(span,
+                              "...so that a type/lifetime parameter is in scope here");
+            }
+            infer::DataBorrowed(ty, span) => {
+                err.span_note(span,
+                              &format!("...so that the type `{}` is not borrowed for too long",
+                                       self.ty_to_string(ty)));
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                err.span_note(span,
+                              &format!("...so that the reference type `{}` does not outlive the \
+                                        data it points at",
+                                       self.ty_to_string(ty)));
+            }
+            infer::RelateParamBound(span, t) => {
+                err.span_note(span,
+                              &format!("...so that the type `{}` will meet its required \
+                                        lifetime bounds",
+                                       self.ty_to_string(t)));
+            }
+            infer::RelateDefaultParamBound(span, t) => {
+                err.span_note(span,
+                              &format!("...so that type parameter instantiated with `{}`, will \
+                                        meet its declared lifetime bounds",
+                                       self.ty_to_string(t)));
+            }
+            infer::RelateRegionParamBound(span) => {
+                err.span_note(span,
+                              "...so that the declared lifetime parameter bounds are satisfied");
+            }
+            infer::SafeDestructor(span) => {
+                err.span_note(span,
+                              "...so that references are valid when the destructor runs");
+            }
+            infer::CompareImplMethodObligation { span, .. } => {
+                err.span_note(span,
+                              "...so that the definition in impl matches the definition from the \
+                               trait");
+            }
+        }
+    }
+
+    pub(super) fn report_concrete_failure(&self,
+                                          origin: SubregionOrigin<'tcx>,
+                                          sub: &'tcx Region,
+                                          sup: &'tcx Region)
+                                          -> DiagnosticBuilder<'tcx> {
+        match origin {
+            infer::Subtype(trace) => {
+                let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
+                self.report_and_explain_type_error(trace, &terr)
+            }
+            infer::Reborrow(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0312,
+                                               "lifetime of reference outlives lifetime of \
+                                                borrowed content...");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "...the reference is valid for ",
+                                                 sub,
+                                                 "...");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "...but the borrowed content is only valid for ",
+                                                 sup,
+                                                 "");
+                err
+            }
+            infer::ReborrowUpvar(span, ref upvar_id) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0313,
+                                               "lifetime of borrowed pointer outlives lifetime \
+                                                of captured variable `{}`...",
+                                               self.tcx.local_var_name_str(upvar_id.var_id));
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "...the borrowed pointer is valid for ",
+                                                 sub,
+                                                 "...");
+                self.tcx
+                    .note_and_explain_region(&mut err,
+                                             &format!("...but `{}` is only valid for ",
+                                                      self.tcx
+                                                          .local_var_name_str(upvar_id.var_id)),
+                                             sup,
+                                             "");
+                err
+            }
+            infer::InfStackClosure(span) => {
+                let mut err =
+                    struct_span_err!(self.tcx.sess, span, E0314, "closure outlives stack frame");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "...the closure must be valid for ",
+                                                 sub,
+                                                 "...");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "...but the closure's stack frame is only valid \
+                                                  for ",
+                                                 sup,
+                                                 "");
+                err
+            }
+            infer::InvokeClosure(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0315,
+                                               "cannot invoke closure outside of its lifetime");
+                self.tcx
+                    .note_and_explain_region(&mut err, "the closure is only valid for ", sup, "");
+                err
+            }
+            infer::DerefPointer(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0473,
+                                               "dereference of reference outside its lifetime");
+                self.tcx
+                    .note_and_explain_region(&mut err, "the reference is only valid for ", sup, "");
+                err
+            }
+            infer::FreeVariable(span, id) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0474,
+                                               "captured variable `{}` does not outlive the \
+                                                enclosing closure",
+                                               self.tcx.local_var_name_str(id));
+                self.tcx
+                    .note_and_explain_region(&mut err, "captured variable is valid for ", sup, "");
+                self.tcx.note_and_explain_region(&mut err, "closure is valid for ", sub, "");
+                err
+            }
+            infer::IndexSlice(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0475,
+                                               "index of slice outside its lifetime");
+                self.tcx.note_and_explain_region(&mut err, "the slice is only valid for ", sup, "");
+                err
+            }
+            infer::RelateObjectBound(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0476,
+                                               "lifetime of the source pointer does not outlive \
+                                                lifetime bound of the object type");
+                self.tcx.note_and_explain_region(&mut err, "object type is valid for ", sub, "");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "source pointer is only valid for ",
+                                                 sup,
+                                                 "");
+                err
+            }
+            infer::RelateParamBound(span, ty) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0477,
+                                               "the type `{}` does not fulfill the required \
+                                                lifetime",
+                                               self.ty_to_string(ty));
+                self.tcx.note_and_explain_region(&mut err, "type must outlive ", sub, "");
+                err
+            }
+            infer::RelateRegionParamBound(span) => {
+                let mut err =
+                    struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "lifetime parameter instantiated with ",
+                                                 sup,
+                                                 "");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "but lifetime parameter must outlive ",
+                                                 sub,
+                                                 "");
+                err
+            }
+            infer::RelateDefaultParamBound(span, ty) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0479,
+                                               "the type `{}` (provided as the value of a type \
+                                                parameter) is not valid at this point",
+                                               self.ty_to_string(ty));
+                self.tcx.note_and_explain_region(&mut err, "type must outlive ", sub, "");
+                err
+            }
+            infer::CallRcvr(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0480,
+                                               "lifetime of method receiver does not outlive the \
+                                                method call");
+                self.tcx
+                    .note_and_explain_region(&mut err, "the receiver is only valid for ", sup, "");
+                err
+            }
+            infer::CallArg(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0481,
+                                               "lifetime of function argument does not outlive \
+                                                the function call");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "the function argument is only valid for ",
+                                                 sup,
+                                                 "");
+                err
+            }
+            infer::CallReturn(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0482,
+                                               "lifetime of return value does not outlive the \
+                                                function call");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "the return value is only valid for ",
+                                                 sup,
+                                                 "");
+                err
+            }
+            infer::Operand(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0483,
+                                               "lifetime of operand does not outlive the \
+                                                operation");
+                self.tcx
+                    .note_and_explain_region(&mut err, "the operand is only valid for ", sup, "");
+                err
+            }
+            infer::AddrOf(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0484,
+                                               "reference is not valid at the time of borrow");
+                self.tcx
+                    .note_and_explain_region(&mut err, "the borrow is only valid for ", sup, "");
+                err
+            }
+            infer::AutoBorrow(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0485,
+                                               "automatically reference is not valid at the time \
+                                                of borrow");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "the automatic borrow is only valid for ",
+                                                 sup,
+                                                 "");
+                err
+            }
+            infer::ExprTypeIsNotInScope(t, span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0486,
+                                               "type of expression contains references that are \
+                                                not valid during the expression: `{}`",
+                                               self.ty_to_string(t));
+                self.tcx.note_and_explain_region(&mut err, "type is only valid for ", sup, "");
+                err
+            }
+            infer::SafeDestructor(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0487,
+                                               "unsafe use of destructor: destructor might be \
+                                                called while references are dead");
+                // FIXME (22171): terms "super/subregion" are suboptimal
+                self.tcx.note_and_explain_region(&mut err, "superregion: ", sup, "");
+                self.tcx.note_and_explain_region(&mut err, "subregion: ", sub, "");
+                err
+            }
+            infer::BindingTypeIsNotValidAtDecl(span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0488,
+                                               "lifetime of variable does not enclose its \
+                                                declaration");
+                self.tcx
+                    .note_and_explain_region(&mut err, "the variable is only valid for ", sup, "");
+                err
+            }
+            infer::ParameterInScope(_, span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0489,
+                                               "type/lifetime parameter not in scope here");
+                self.tcx
+                    .note_and_explain_region(&mut err, "the parameter is only valid for ", sub, "");
+                err
+            }
+            infer::DataBorrowed(ty, span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0490,
+                                               "a value of type `{}` is borrowed for too long",
+                                               self.ty_to_string(ty));
+                self.tcx.note_and_explain_region(&mut err, "the type is valid for ", sub, "");
+                self.tcx.note_and_explain_region(&mut err, "but the borrow lasts for ", sup, "");
+                err
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               span,
+                                               E0491,
+                                               "in type `{}`, reference has a longer lifetime \
+                                                than the data it references",
+                                               self.ty_to_string(ty));
+                self.tcx.note_and_explain_region(&mut err, "the pointer is valid for ", sub, "");
+                self.tcx.note_and_explain_region(&mut err,
+                                                 "but the referenced data is only valid for ",
+                                                 sup,
+                                                 "");
+                err
+            }
+            infer::CompareImplMethodObligation { span,
+                                                 item_name,
+                                                 impl_item_def_id,
+                                                 trait_item_def_id,
+                                                 lint_id } => {
+                self.report_extra_impl_obligation(span,
+                                                  item_name,
+                                                  impl_item_def_id,
+                                                  trait_item_def_id,
+                                                  &format!("`{}: {}`", sup, sub),
+                                                  lint_id)
+            }
+        }
+    }
+}
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index a929060cf98..b07ef4dfd44 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -210,7 +210,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 /// region that each late-bound region was replaced with.
 pub type SkolemizationMap<'tcx> = FxHashMap<ty::BoundRegion, &'tcx ty::Region>;
 
-/// See `error_reporting.rs` for more details
+/// See `error_reporting` module for more details
 #[derive(Clone, Debug)]
 pub enum ValuePairs<'tcx> {
     Types(ExpectedFound<Ty<'tcx>>),
@@ -221,7 +221,7 @@ pub enum ValuePairs<'tcx> {
 /// The trace designates the path through inference that we took to
 /// encounter an error or subtyping constraint.
 ///
-/// See `error_reporting.rs` for more details.
+/// See `error_reporting` module for more details.
 #[derive(Clone)]
 pub struct TypeTrace<'tcx> {
     cause: ObligationCause<'tcx>,
@@ -230,7 +230,7 @@ pub struct TypeTrace<'tcx> {
 
 /// The origin of a `r1 <= r2` constraint.
 ///
-/// See `error_reporting.rs` for more details
+/// See `error_reporting` module for more details
 #[derive(Clone, Debug)]
 pub enum SubregionOrigin<'tcx> {
     // Arose from a subtyping relation
@@ -348,7 +348,7 @@ pub enum LateBoundRegionConversionTime {
 
 /// Reasons to create a region inference variable
 ///
-/// See `error_reporting.rs` for more details
+/// See `error_reporting` module for more details
 #[derive(Clone, Debug)]
 pub enum RegionVariableOrigin {
     // Region variables created for ill-categorized reasons,
@@ -1295,7 +1295,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             // this infcx was in use.  This is totally hokey but
             // otherwise we have a hard time separating legit region
             // errors from silly ones.
-            self.report_region_errors(&errors); // see error_reporting.rs
+            self.report_region_errors(&errors); // see error_reporting module
         }
     }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 73b82fbad5d..1294296840e 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1476,7 +1476,7 @@ pub struct PolyTrait {
 /// A representation of a Type suitable for hyperlinking purposes. Ideally one can get the original
 /// type out of the AST/TyCtxt given one of these, if more information is needed. Most importantly
 /// it does not preserve mutability or boxes.
-#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]
+#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq)]
 pub enum Type {
     /// structs/enums/traits (most that'd be an hir::TyPath)
     ResolvedPath {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 6f8c6aa7094..23507dc889b 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -90,6 +90,16 @@ impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> {
     }
 }
 
+impl<'a, T: fmt::Debug> fmt::Debug for CommaSep<'a, T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        for (i, item) in self.0.iter().enumerate() {
+            if i != 0 { write!(f, ", ")?; }
+            fmt::Debug::fmt(item, f)?;
+        }
+        Ok(())
+    }
+}
+
 impl<'a> fmt::Display for TyParamBounds<'a> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let &TyParamBounds(bounds) = self;
@@ -165,7 +175,7 @@ impl<'a> fmt::Display for WhereClause<'a> {
         if f.alternate() {
             clause.push_str(" where ");
         } else {
-            clause.push_str(" <span class='where fmt-newline'>where ");
+            clause.push_str(" <span class=\"where fmt-newline\">where ");
         }
         for (i, pred) in gens.where_predicates.iter().enumerate() {
             if i > 0 {
@@ -449,8 +459,8 @@ fn resolved_path(w: &mut fmt::Formatter, did: DefId, path: &clean::Path,
                     } else {
                         root.push_str(&seg.name);
                         root.push_str("/");
-                        write!(w, "<a class='mod'
-                                       href='{}index.html'>{}</a>::",
+                        write!(w, "<a class=\"mod\"
+                                       href=\"{}index.html\">{}</a>::",
                                  root,
                                  seg.name)?;
                     }
@@ -491,7 +501,7 @@ fn primitive_link(f: &mut fmt::Formatter,
             Some(&def_id) if def_id.is_local() => {
                 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
                 let len = if len == 0 {0} else {len - 1};
-                write!(f, "<a class='primitive' href='{}primitive.{}.html'>",
+                write!(f, "<a class=\"primitive\" href=\"{}primitive.{}.html\">",
                        repeat("../").take(len).collect::<String>(),
                        prim.to_url_str())?;
                 needs_termination = true;
@@ -508,7 +518,7 @@ fn primitive_link(f: &mut fmt::Formatter,
                     (.., render::Unknown) => None,
                 };
                 if let Some((cname, root)) = loc {
-                    write!(f, "<a class='primitive' href='{}{}/primitive.{}.html'>",
+                    write!(f, "<a class=\"primitive\" href=\"{}{}/primitive.{}.html\">",
                            root,
                            cname,
                            prim.to_url_str())?;
@@ -550,7 +560,7 @@ impl<'a> fmt::Display for HRef<'a> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match href(self.did) {
             Some((url, shortty, fqp)) => if !f.alternate() {
-                write!(f, "<a class='{}' href='{}' title='{} {}'>{}</a>",
+                write!(f, "<a class=\"{}\" href=\"{}\" title=\"{} {}\">{}</a>",
                        shortty, url, shortty, fqp.join("::"), self.text)
             } else {
                 write!(f, "{}", self.text)
@@ -560,7 +570,8 @@ impl<'a> fmt::Display for HRef<'a> {
     }
 }
 
-fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt::Result {
+fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool,
+            is_not_debug: bool) -> fmt::Result {
     match *t {
         clean::Generic(ref name) => {
             f.write_str(name)
@@ -571,7 +582,8 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
             tybounds(f, typarams)
         }
         clean::Infer => write!(f, "_"),
-        clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()),
+        clean::Primitive(prim) if is_not_debug => primitive_link(f, prim, prim.as_str()),
+        clean::Primitive(prim) => write!(f, "{}", prim.as_str()),
         clean::BareFunction(ref decl) => {
             if f.alternate() {
                 write!(f, "{}{}fn{:#}{:#}",
@@ -589,26 +601,30 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
         }
         clean::Tuple(ref typs) => {
             match &typs[..] {
-                &[] => primitive_link(f, PrimitiveType::Tuple, "()"),
-                &[ref one] => {
+                &[] if is_not_debug => primitive_link(f, PrimitiveType::Tuple, "()"),
+                &[] => write!(f, "()"),
+                &[ref one] if is_not_debug => {
                     primitive_link(f, PrimitiveType::Tuple, "(")?;
                     //carry f.alternate() into this display w/o branching manually
                     fmt::Display::fmt(one, f)?;
                     primitive_link(f, PrimitiveType::Tuple, ",)")
                 }
-                many => {
+                &[ref one] => write!(f, "({:?},)", one),
+                many if is_not_debug => {
                     primitive_link(f, PrimitiveType::Tuple, "(")?;
                     fmt::Display::fmt(&CommaSep(&many), f)?;
                     primitive_link(f, PrimitiveType::Tuple, ")")
                 }
+                many => write!(f, "({:?})", &CommaSep(&many)),
             }
         }
-        clean::Vector(ref t) => {
+        clean::Vector(ref t) if is_not_debug => {
             primitive_link(f, PrimitiveType::Slice, &format!("["))?;
             fmt::Display::fmt(t, f)?;
             primitive_link(f, PrimitiveType::Slice, &format!("]"))
         }
-        clean::FixedVector(ref t, ref s) => {
+        clean::Vector(ref t) => write!(f, "[{:?}]", t),
+        clean::FixedVector(ref t, ref s) if is_not_debug => {
             primitive_link(f, PrimitiveType::Array, "[")?;
             fmt::Display::fmt(t, f)?;
             if f.alternate() {
@@ -619,10 +635,17 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
                                &format!("; {}]", Escape(s)))
             }
         }
+        clean::FixedVector(ref t, ref s) => {
+            if f.alternate() {
+                write!(f, "[{:?}; {}]", t, s)
+            } else {
+                write!(f, "[{:?}; {}]", t, Escape(s))
+            }
+        }
         clean::Never => f.write_str("!"),
         clean::RawPointer(m, ref t) => {
             match **t {
-                clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
+                clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} if is_not_debug => {
                     if f.alternate() {
                         primitive_link(f, clean::PrimitiveType::RawPointer,
                                        &format!("*{}{:#}", RawMutableSpace(m), t))
@@ -631,11 +654,21 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
                                        &format!("*{}{}", RawMutableSpace(m), t))
                     }
                 }
-                _ => {
+                clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
+                    if f.alternate() {
+                        write!(f, "*{}{:#?}", RawMutableSpace(m), t)
+                    } else {
+                        write!(f, "*{}{:?}", RawMutableSpace(m), t)
+                    }
+                }
+                _ if is_not_debug => {
                     primitive_link(f, clean::PrimitiveType::RawPointer,
                                    &format!("*{}", RawMutableSpace(m)))?;
                     fmt::Display::fmt(t, f)
                 }
+                _ => {
+                    write!(f, "*{}{:?}", RawMutableSpace(m), t)
+                }
             }
         }
         clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
@@ -647,15 +680,23 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
             match **ty {
                 clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
                     match **bt {
-                        clean::Generic(_) =>
+                        clean::Generic(_) if is_not_debug => {
                             if f.alternate() {
                                 primitive_link(f, PrimitiveType::Slice,
                                     &format!("&{}{}[{:#}]", lt, m, **bt))
                             } else {
                                 primitive_link(f, PrimitiveType::Slice,
                                     &format!("&amp;{}{}[{}]", lt, m, **bt))
-                            },
-                        _ => {
+                            }
+                        }
+                        clean::Generic(_) => {
+                            if f.alternate() {
+                                write!(f, "&{}{}[{:#?}]", lt, m, **bt)
+                            } else {
+                                write!(f, "&{}{}[{:?}]", lt, m, **bt)
+                            }
+                        }
+                        _ if is_not_debug => {
                             if f.alternate() {
                                 primitive_link(f, PrimitiveType::Slice,
                                                &format!("&{}{}[", lt, m))?;
@@ -667,15 +708,26 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
                             }
                             primitive_link(f, PrimitiveType::Slice, "]")
                         }
+                        _ => {
+                            if f.alternate() {
+                                write!(f, "&{}{}[{:#?}]", lt, m, **bt)
+                            } else {
+                                write!(f, "&{}{}[{:?}]", lt, m, **bt)
+                            }
+                        }
                     }
                 }
                 _ => {
                     if f.alternate() {
                         write!(f, "&{}{}", lt, m)?;
-                        fmt_type(&ty, f, use_absolute)
+                        fmt_type(&ty, f, use_absolute, is_not_debug)
                     } else {
-                        write!(f, "&amp;{}{}", lt, m)?;
-                        fmt_type(&ty, f, use_absolute)
+                        if is_not_debug {
+                            write!(f, "&amp;{}{}", lt, m)?;
+                        } else {
+                            write!(f, "&{}{}", lt, m)?;
+                        }
+                        fmt_type(&ty, f, use_absolute, is_not_debug)
                     }
                 }
             }
@@ -723,9 +775,17 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
         }
         clean::QPath { ref name, ref self_type, ref trait_ } => {
             if f.alternate() {
-                write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name)
+                if is_not_debug {
+                    write!(f, "<{:#} as {:#}>::{}", self_type, trait_, name)
+                } else {
+                    write!(f, "<{:#?} as {:#?}>::{}", self_type, trait_, name)
+                }
             } else {
-                write!(f, "&lt;{} as {}&gt;::{}", self_type, trait_, name)
+                if is_not_debug {
+                    write!(f, "&lt;{} as {}&gt;::{}", self_type, trait_, name)
+                } else {
+                    write!(f, "<{:?} as {:?}>::{}", self_type, trait_, name)
+                }
             }
         }
         clean::Unique(..) => {
@@ -736,7 +796,13 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
 
 impl fmt::Display for clean::Type {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt_type(self, f, false)
+        fmt_type(self, f, false, true)
+    }
+}
+
+impl fmt::Debug for clean::Type {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt_type(self, f, false, false)
     }
 }
 
@@ -777,7 +843,7 @@ fn fmt_impl(i: &clean::Impl,
         plain.push_str(" for ");
     }
 
-    fmt_type(&i.for_, f, use_absolute)?;
+    fmt_type(&i.for_, f, use_absolute, true)?;
     plain.push_str(&format!("{:#}", i.for_));
 
     fmt::Display::fmt(&WhereClause(&i.generics, plain.len() + 1), f)?;
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 2c15dd92323..0dafc4225a3 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -144,12 +144,12 @@ impl<U: Write> Writer for U {
                           -> io::Result<()> {
         match klass {
             Class::None => write!(self, "{}", text),
-            klass => write!(self, "<span class='{}'>{}</span>", klass.rustdoc_class(), text),
+            klass => write!(self, "<span class=\"{}\">{}</span>", klass.rustdoc_class(), text),
         }
     }
 
     fn enter_span(&mut self, klass: Class) -> io::Result<()> {
-        write!(self, "<span class='{}'>", klass.rustdoc_class())
+        write!(self, "<span class=\"{}\">", klass.rustdoc_class())
     }
 
     fn exit_span(&mut self) -> io::Result<()> {
@@ -363,7 +363,7 @@ fn write_header(class: Option<&str>,
     if let Some(id) = id {
         write!(out, "id='{}' ", id)?;
     }
-    write!(out, "class='rust {}'>\n", class.unwrap_or(""))
+    write!(out, "class=\"rust {}\">\n", class.unwrap_or(""))
 }
 
 fn write_footer(out: &mut Write) -> io::Result<()> {
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index bb39c8c4f22..44f71d89529 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -1547,7 +1547,7 @@ impl<'a> fmt::Display for Item<'a> {
                        component)?;
             }
         }
-        write!(fmt, "<a class='{}' href=''>{}</a>",
+        write!(fmt, "<a class=\"{}\" href=''>{}</a>",
                self.item.type_(), self.item.name.as_ref().unwrap())?;
 
         write!(fmt, "</span>")?; // in-band
@@ -1654,9 +1654,35 @@ fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLin
     Ok(())
 }
 
+fn md_render_assoc_item(item: &clean::Item) -> String {
+    match item.inner {
+        clean::AssociatedConstItem(ref ty, ref default) => {
+            if let Some(default) = default.as_ref() {
+                format!("```\n{}: {:?} = {}\n```\n\n", item.name.as_ref().unwrap(), ty, default)
+            } else {
+                format!("```\n{}: {:?}\n```\n\n", item.name.as_ref().unwrap(), ty)
+            }
+        }
+        _ => String::new(),
+    }
+}
+
+fn get_doc_value(item: &clean::Item) -> Option<&str> {
+    let x = item.doc_value();
+    if x.is_none() {
+        match item.inner {
+            clean::AssociatedConstItem(_, _) => Some(""),
+            _ => None,
+        }
+    } else {
+        x
+    }
+}
+
 fn document_full(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
-    if let Some(s) = item.doc_value() {
-        write!(w, "<div class='docblock'>{}</div>", Markdown(s))?;
+    if let Some(s) = get_doc_value(item) {
+        write!(w, "<div class='docblock'>{}</div>",
+               Markdown(&format!("{}{}", md_render_assoc_item(item), s)))?;
     }
     Ok(())
 }
@@ -1817,7 +1843,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
                 let doc_value = myitem.doc_value().unwrap_or("");
                 write!(w, "
                        <tr class='{stab} module-item'>
-                           <td><a class='{class}' href='{href}'
+                           <td><a class=\"{class}\" href=\"{href}\"
                                   title='{title_type} {title}'>{name}</a>{unsafety_flag}</td>
                            <td class='docblock-short'>
                                {stab_docs} {docs}
@@ -2215,16 +2241,12 @@ fn naive_assoc_href(it: &clean::Item, link: AssocItemLink) -> String {
 fn assoc_const(w: &mut fmt::Formatter,
                it: &clean::Item,
                ty: &clean::Type,
-               default: Option<&String>,
+               _default: Option<&String>,
                link: AssocItemLink) -> fmt::Result {
-    write!(w, "const <a href='{}' class='constant'>{}</a>",
+    write!(w, "const <a href='{}' class=\"constant\"><b>{}</b></a>: {}",
            naive_assoc_href(it, link),
-           it.name.as_ref().unwrap())?;
-
-    write!(w, ": {}", ty)?;
-    if let Some(default) = default {
-        write!(w, " = {}", Escape(default))?;
-    }
+           it.name.as_ref().unwrap(),
+           ty)?;
     Ok(())
 }
 
@@ -2232,7 +2254,7 @@ fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item,
               bounds: &Vec<clean::TyParamBound>,
               default: Option<&clean::Type>,
               link: AssocItemLink) -> fmt::Result {
-    write!(w, "type <a href='{}' class='type'>{}</a>",
+    write!(w, "type <a href='{}' class=\"type\">{}</a>",
            naive_assoc_href(it, link),
            it.name.as_ref().unwrap())?;
     if !bounds.is_empty() {
@@ -2375,7 +2397,7 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
                 let ns_id = derive_id(format!("{}.{}",
                                               field.name.as_ref().unwrap(),
                                               ItemType::StructField.name_space()));
-                write!(w, "<span id='{id}' class='{item_type}'>
+                write!(w, "<span id='{id}' class=\"{item_type}\">
                            <span id='{ns_id}' class='invisible'>
                            <code>{name}: {ty}</code>
                            </span></span>",
@@ -2417,7 +2439,7 @@ fn item_union(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     if fields.peek().is_some() {
         write!(w, "<h2 class='fields'>Fields</h2>")?;
         for (field, ty) in fields {
-            write!(w, "<span id='{shortty}.{name}' class='{shortty}'><code>{name}: {ty}</code>
+            write!(w, "<span id='{shortty}.{name}' class=\"{shortty}\"><code>{name}: {ty}</code>
                        </span>",
                    shortty = ItemType::StructField,
                    name = field.name.as_ref().unwrap(),
@@ -2902,7 +2924,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
                 if render_method_item {
                     let id = derive_id(format!("{}.{}", item_type, name));
                     let ns_id = derive_id(format!("{}.{}", name, item_type.name_space()));
-                    write!(w, "<h4 id='{}' class='{}'>", id, item_type)?;
+                    write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?;
                     write!(w, "<span id='{}' class='invisible'>", ns_id)?;
                     write!(w, "<code>")?;
                     render_assoc_item(w, item, link.anchor(&id), ItemType::Impl)?;
@@ -2914,7 +2936,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
             clean::TypedefItem(ref tydef, _) => {
                 let id = derive_id(format!("{}.{}", ItemType::AssociatedType, name));
                 let ns_id = derive_id(format!("{}.{}", name, item_type.name_space()));
-                write!(w, "<h4 id='{}' class='{}'>", id, item_type)?;
+                write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?;
                 write!(w, "<span id='{}' class='invisible'><code>", ns_id)?;
                 assoc_type(w, item, &Vec::new(), Some(&tydef.type_), link.anchor(&id))?;
                 write!(w, "</code></span></h4>\n")?;
@@ -2922,7 +2944,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
             clean::AssociatedConstItem(ref ty, ref default) => {
                 let id = derive_id(format!("{}.{}", item_type, name));
                 let ns_id = derive_id(format!("{}.{}", name, item_type.name_space()));
-                write!(w, "<h4 id='{}' class='{}'>", id, item_type)?;
+                write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?;
                 write!(w, "<span id='{}' class='invisible'><code>", ns_id)?;
                 assoc_const(w, item, ty, default.as_ref(), link.anchor(&id))?;
                 write!(w, "</code></span></h4>\n")?;
@@ -2930,7 +2952,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
             clean::ConstantItem(ref c) => {
                 let id = derive_id(format!("{}.{}", item_type, name));
                 let ns_id = derive_id(format!("{}.{}", name, item_type.name_space()));
-                write!(w, "<h4 id='{}' class='{}'>", id, item_type)?;
+                write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?;
                 write!(w, "<span id='{}' class='invisible'><code>", ns_id)?;
                 assoc_const(w, item, &c.type_, Some(&c.expr), link.anchor(&id))?;
                 write!(w, "</code></span></h4>\n")?;
@@ -2938,7 +2960,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
             clean::AssociatedTypeItem(ref bounds, ref default) => {
                 let id = derive_id(format!("{}.{}", item_type, name));
                 let ns_id = derive_id(format!("{}.{}", name, item_type.name_space()));
-                write!(w, "<h4 id='{}' class='{}'>", id, item_type)?;
+                write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?;
                 write!(w, "<span id='{}' class='invisible'><code>", ns_id)?;
                 assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id))?;
                 write!(w, "</code></span></h4>\n")?;
@@ -2956,7 +2978,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
                         // We need the stability of the item from the trait
                         // because impls can't have a stability.
                         document_stability(w, cx, it)?;
-                        if item.doc_value().is_some() {
+                        if get_doc_value(item).is_some() {
                             document_full(w, item)?;
                         } else {
                             // In case the item isn't documented,
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index c12e1e7d608..20028586227 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -979,7 +979,7 @@
             .html("[<span class='inner'></span>]");
         toggle.children(".inner").text(labelForToggleButton(false));
 
-        $(".method").each(function() {
+        $(".method, .impl-items > .associatedconstant").each(function() {
             if ($(this).next().is(".docblock") ||
                 ($(this).next().is(".stability") && $(this).next().next().is(".docblock"))) {
                     $(this).children().last().after(toggle.clone());
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 681d2354056..b0bf69b0181 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -89,7 +89,7 @@ h2 {
 h3 {
 	font-size: 1.3em;
 }
-h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {
+h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) {
 	font-weight: 500;
 	margin: 20px 0 15px 0;
 	padding-bottom: 6px;
@@ -99,10 +99,10 @@ h1.fqn {
 	margin-top: 0;
 	position: relative;
 }
-h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {
+h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) {
 	border-bottom: 1px solid;
 }
-h3.impl, h3.method, h4.method, h3.type, h4.type {
+h3.impl, h3.method, h4.method, h3.type, h4.type, h4.associatedconstant {
 	font-weight: 600;
 	margin-top: 10px;
 	margin-bottom: 10px;
@@ -382,7 +382,7 @@ h4 > code, h3 > code, .invisible > code {
 .content .impl-items .docblock, .content .impl-items .stability {
 	margin-left: 40px;
 }
-.content .impl-items .method, .content .impl-items > .type {
+.content .impl-items .method, .content .impl-items > .type, .impl-items > .associatedconstant {
 	margin-left: 20px;
 }
 
diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs
index dc3855367ae..bc678fcb838 100644
--- a/src/libstd/ffi/c_str.rs
+++ b/src/libstd/ffi/c_str.rs
@@ -455,6 +455,20 @@ impl From<NulError> for io::Error {
     }
 }
 
+#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
+impl Error for FromBytesWithNulError {
+    fn description(&self) -> &str {
+        "data provided is not null terminated or contains an interior nul byte"
+    }
+}
+
+#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
+impl fmt::Display for FromBytesWithNulError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.description().fmt(f)
+    }
+}
+
 impl IntoStringError {
     /// Consumes this error, returning original `CString` which generated the
     /// error.
diff --git a/src/test/rustdoc/assoc-consts.rs b/src/test/rustdoc/assoc-consts.rs
index 8d3f9b59bb2..d4119f5d351 100644
--- a/src/test/rustdoc/assoc-consts.rs
+++ b/src/test/rustdoc/assoc-consts.rs
@@ -13,14 +13,16 @@
 pub trait Foo {
     // @has assoc_consts/trait.Foo.html '//*[@class="rust trait"]' \
     //      'const FOO: usize;'
-    // @has - '//*[@id="associatedconstant.FOO"]' 'const FOO'
-    const FOO: usize;
+    // @has - '//*[@id="associatedconstant.FOO"]' 'const FOO: usize'
+    // @has - '//*[@class="docblock"]' 'FOO: usize = 12'
+    const FOO: usize = 12;
 }
 
 pub struct Bar;
 
 impl Bar {
     // @has assoc_consts/struct.Bar.html '//*[@id="associatedconstant.BAR"]' \
-    //      'const BAR: usize = 3'
+    //      'const BAR: usize'
+    // @has - '//*[@class="docblock"]' 'BAR: usize = 3'
     pub const BAR: usize = 3;
 }
diff --git a/src/test/rustdoc/issue-28478.rs b/src/test/rustdoc/issue-28478.rs
index 0db92a491ed..493c08693e9 100644
--- a/src/test/rustdoc/issue-28478.rs
+++ b/src/test/rustdoc/issue-28478.rs
@@ -16,7 +16,8 @@ pub trait Bar {
     // @has - '//*[@id="associatedtype.Bar"]' 'type Bar = ()'
     // @has - '//*[@href="#associatedtype.Bar"]' 'Bar'
     type Bar = ();
-    // @has - '//*[@id="associatedconstant.Baz"]' 'const Baz: usize = 7'
+    // @has - '//*[@id="associatedconstant.Baz"]' 'const Baz: usize'
+    // @has - '//*[@class="docblock"]' 'Baz: usize = 7'
     // @has - '//*[@href="#associatedconstant.Baz"]' 'Baz'
     const Baz: usize = 7;
     // @has - '//*[@id="tymethod.bar"]' 'fn bar'
diff --git a/src/test/rustdoc/issue-33302.rs b/src/test/rustdoc/issue-33302.rs
index c6da6b0575b..a34ee908ef2 100644
--- a/src/test/rustdoc/issue-33302.rs
+++ b/src/test/rustdoc/issue-33302.rs
@@ -28,18 +28,40 @@ macro_rules! make {
             fn ignore(_: &X) {}
             const C: X;
             // @has issue_33302/trait.T.html \
-            //        '//*[@class="rust trait"]' 'const D: i32 = 4 * 4;'
-            // @has - '//*[@id="associatedconstant.D"]' 'const D: i32 = 4 * 4'
+            //        '//*[@class="rust trait"]' 'const D: i32'
+            // @has - '//*[@class="docblock"]' 'D: i32 = 4 * 4'
+            // @has - '//*[@id="associatedconstant.D"]' 'const D: i32'
             const D: i32 = ($n * $n);
         }
 
         // @has issue_33302/struct.S.html \
         //        '//h3[@class="impl"]' 'impl T<[i32; 16]> for S'
-        // @has - '//*[@id="associatedconstant.C"]' 'const C: [i32; 16] = [0; 4 * 4]'
-        // @has - '//*[@id="associatedconstant.D"]' 'const D: i32 = 4 * 4'
+        // @has - '//*[@id="associatedconstant.C"]' 'const C: [i32; 16]'
+        // @has - '//*[@id="associatedconstant.D"]' 'const D: i32'
+        // @has - '//*[@class="docblock"]' 'C: [i32; 16] = [0; 4 * 4]'
         impl T<[i32; ($n * $n)]> for S {
             const C: [i32; ($n * $n)] = [0; ($n * $n)];
         }
+
+        // @has issue_33302/struct.S.html \
+        //        '//h3[@class="impl"]' 'impl T<[i32; 16]> for S'
+        // @has - '//*[@id="associatedconstant.C-1"]' 'const C: (i32,)'
+        // @has - '//*[@id="associatedconstant.D-1"]' 'const D: i32'
+        // @has - '//*[@class="docblock"]' 'C: (i32,) = (4,)'
+        impl T<(i32,)> for S {
+            const C: (i32,) = ($n,);
+        }
+
+        // @has issue_33302/struct.S.html \
+        //        '//h3[@class="impl"]' 'impl T<(i32, i32)> for S'
+        // @has - '//*[@id="associatedconstant.C-2"]' 'const C: (i32, i32)'
+        // @has - '//*[@id="associatedconstant.D-2"]' 'const D: i32'
+        // @has - '//*[@class="docblock"]' 'C: (i32, i32) = (4, 4)'
+        // @has - '//*[@class="docblock"]' 'D: i32 = 4 / 4'
+        impl T<(i32, i32)> for S {
+            const C: (i32, i32) = ($n, $n);
+            const D: i32 = ($n / $n);
+        }
     }
 }