about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-01-25 23:27:20 +0000
committerbors <bors@rust-lang.org>2019-01-25 23:27:20 +0000
commit37d51aa8f3bca674a50eb7c6204deed6fb4dff80 (patch)
tree497061fd0419b43366c4a601081cd782d358b15d
parentbf669d1e3295bc688f71b8c91f48a6beaf895f67 (diff)
parent0ae3d87d814d28457c143813beb526733e400570 (diff)
downloadrust-37d51aa8f3bca674a50eb7c6204deed6fb4dff80.tar.gz
rust-37d51aa8f3bca674a50eb7c6204deed6fb4dff80.zip
Auto merge of #57898 - Centril:rollup, r=Centril
Rollup of 5 pull requests

Successful merges:

 - #56233 (Miri and miri-related code contains repetitions of `(n << amt) >> amt`)
 - #57645 (distinguish "no data" from "heterogeneous" in ABI)
 - #57734 (Fix evaluating trivial drop glue in constants)
 - #57886 (Add suggestion for moving type declaration before associated type bindings in generic arguments.)
 - #57890 (Fix wording in diagnostics page)

Failed merges:

r? @ghost
-rw-r--r--src/librustc/ty/mod.rs10
-rw-r--r--src/librustc_driver/driver.rs5
-rw-r--r--src/librustc_lint/types.rs10
-rw-r--r--src/librustc_mir/const_eval.rs29
-rw-r--r--src/librustc_mir/interpret/eval_context.rs10
-rw-r--r--src/librustc_passes/layout_test.rs132
-rw-r--r--src/librustc_passes/lib.rs1
-rw-r--r--src/librustc_target/abi/call/aarch64.rs2
-rw-r--r--src/librustc_target/abi/call/arm.rs2
-rw-r--r--src/librustc_target/abi/call/asmjs.rs2
-rw-r--r--src/librustc_target/abi/call/mod.rs79
-rw-r--r--src/librustc_target/abi/call/powerpc64.rs2
-rw-r--r--src/librustc_target/abi/call/sparc64.rs2
-rw-r--r--src/librustc_target/abi/call/x86.rs2
-rw-r--r--src/librustc_typeck/diagnostics.rs2
-rw-r--r--src/libsyntax/feature_gate.rs7
-rw-r--r--src/libsyntax/parse/parser.rs134
-rw-r--r--src/test/ui/consts/drop_none.rs13
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs36
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr14
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs73
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr32
-rw-r--r--src/test/ui/layout/zero-sized-array-union.rs95
-rw-r--r--src/test/ui/layout/zero-sized-array-union.stderr26
-rw-r--r--src/test/ui/parser/issue-32214.stderr4
-rw-r--r--src/test/ui/static/static-drop-scope.nll.stderr14
-rw-r--r--src/test/ui/static/static-drop-scope.rs8
-rw-r--r--src/test/ui/static/static-drop-scope.stderr14
-rw-r--r--src/test/ui/suggestions/suggest-move-types.rs85
-rw-r--r--src/test/ui/suggestions/suggest-move-types.stderr107
30 files changed, 870 insertions, 82 deletions
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 930bbc08885..c9089428b23 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -2937,16 +2937,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    /// Given the DefId of an item, returns its MIR, borrowed immutably.
-    /// Returns None if there is no MIR for the DefId
-    pub fn maybe_optimized_mir(self, did: DefId) -> Option<&'gcx Mir<'gcx>> {
-        if self.is_mir_available(did) {
-            Some(self.optimized_mir(did))
-        } else {
-            None
-        }
-    }
-
     /// Get the attributes of a definition.
     pub fn get_attrs(self, did: DefId) -> Attributes<'gcx> {
         if let Some(id) = self.hir().as_local_node_id(did) {
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 142d60c8d00..55f9d8a1109 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -22,7 +22,7 @@ use rustc_incremental;
 use rustc_metadata::creader::CrateLoader;
 use rustc_metadata::cstore::{self, CStore};
 use rustc_mir as mir;
-use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion};
+use rustc_passes::{self, ast_validation, hir_stats, loops, rvalue_promotion, layout_test};
 use rustc_plugin as plugin;
 use rustc_plugin::registry::Registry;
 use rustc_privacy;
@@ -1287,6 +1287,9 @@ where
                     mir::transform::check_unsafety::check_unsafety(tcx, def_id)
                 }
             });
+
+            time(sess, "layout testing", || layout_test::test_layout(tcx));
+
             // Avoid overwhelming user with errors if type checking failed.
             // I'm not sure how helpful this is, to be honest, but it avoids
             // a
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index 642681a73a8..9d3275ffde2 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -21,6 +21,8 @@ use syntax::source_map;
 
 use rustc::hir;
 
+use rustc::mir::interpret::{sign_extend, truncate};
+
 declare_lint! {
     UNUSED_COMPARISONS,
     Warn,
@@ -368,14 +370,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
             let (t, actually) = match ty {
                 ty::Int(t) => {
                     let ity = attr::IntType::SignedInt(t);
-                    let bits = layout::Integer::from_attr(&cx.tcx, ity).size().bits();
-                    let actually = (val << (128 - bits)) as i128 >> (128 - bits);
+                    let size = layout::Integer::from_attr(&cx.tcx, ity).size();
+                    let actually = sign_extend(val, size) as i128;
                     (format!("{:?}", t), actually.to_string())
                 }
                 ty::Uint(t) => {
                     let ity = attr::IntType::UnsignedInt(t);
-                    let bits = layout::Integer::from_attr(&cx.tcx, ity).size().bits();
-                    let actually = (val << (128 - bits)) >> (128 - bits);
+                    let size = layout::Integer::from_attr(&cx.tcx, ity).size();
+                    let actually = truncate(val, size);
                     (format!("{:?}", t), actually.to_string())
                 }
                 _ => bug!(),
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 45c6c1b4249..9ed480a9af5 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -340,19 +340,22 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
         ret: Option<mir::BasicBlock>,
     ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
         debug!("eval_fn_call: {:?}", instance);
-        // Execution might have wandered off into other crates, so we cannot to a stability-
-        // sensitive check here.  But we can at least rule out functions that are not const
-        // at all.
-        if !ecx.tcx.is_const_fn_raw(instance.def_id()) {
-            // Some functions we support even if they are non-const -- but avoid testing
-            // that for const fn!  We certainly do *not* want to actually call the fn
-            // though, so be sure we return here.
-            return if ecx.hook_fn(instance, args, dest)? {
-                ecx.goto_block(ret)?; // fully evaluated and done
-                Ok(None)
-            } else {
-                err!(MachineError(format!("calling non-const function `{}`", instance)))
-            };
+        // Only check non-glue functions
+        if let ty::InstanceDef::Item(def_id) = instance.def {
+            // Execution might have wandered off into other crates, so we cannot to a stability-
+            // sensitive check here.  But we can at least rule out functions that are not const
+            // at all.
+            if !ecx.tcx.is_const_fn_raw(def_id) {
+                // Some functions we support even if they are non-const -- but avoid testing
+                // that for const fn!  We certainly do *not* want to actually call the fn
+                // though, so be sure we return here.
+                return if ecx.hook_fn(instance, args, dest)? {
+                    ecx.goto_block(ret)?; // fully evaluated and done
+                    Ok(None)
+                } else {
+                    err!(MachineError(format!("calling non-const function `{}`", instance)))
+                };
+            }
         }
         // This is a const fn. Call it.
         Ok(Some(match ecx.load_mir(instance.def) {
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 132b753eb9a..34443bb353e 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -273,11 +273,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
         }
         trace!("load mir {:?}", instance);
         match instance {
-            ty::InstanceDef::Item(def_id) => {
-                self.tcx.maybe_optimized_mir(def_id).ok_or_else(||
-                    EvalErrorKind::NoMirFor(self.tcx.item_path_str(def_id)).into()
-                )
-            }
+            ty::InstanceDef::Item(def_id) => if self.tcx.is_mir_available(did) {
+                Ok(self.tcx.optimized_mir(did))
+            } else {
+                err!(NoMirFor(self.tcx.item_path_str(def_id)))
+            },
             _ => Ok(self.tcx.instance_mir(instance)),
         }
     }
diff --git a/src/librustc_passes/layout_test.rs b/src/librustc_passes/layout_test.rs
new file mode 100644
index 00000000000..d21707c578b
--- /dev/null
+++ b/src/librustc_passes/layout_test.rs
@@ -0,0 +1,132 @@
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::hir::itemlikevisit::ItemLikeVisitor;
+use rustc::hir::ItemKind;
+use rustc::ty::layout::HasDataLayout;
+use rustc::ty::layout::HasTyCtxt;
+use rustc::ty::layout::LayoutOf;
+use rustc::ty::layout::TargetDataLayout;
+use rustc::ty::layout::TyLayout;
+use rustc::ty::ParamEnv;
+use rustc::ty::Ty;
+use rustc::ty::TyCtxt;
+use syntax::ast::Attribute;
+
+pub fn test_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
+    if tcx.features().rustc_attrs {
+        // if the `rustc_attrs` feature is not enabled, don't bother testing layout
+        tcx.hir()
+            .krate()
+            .visit_all_item_likes(&mut VarianceTest { tcx });
+    }
+}
+
+struct VarianceTest<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+}
+
+impl<'a, 'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'a, 'tcx> {
+    fn visit_item(&mut self, item: &'tcx hir::Item) {
+        let item_def_id = self.tcx.hir().local_def_id(item.id);
+
+        if let ItemKind::Ty(..) = item.node {
+            for attr in self.tcx.get_attrs(item_def_id).iter() {
+                if attr.check_name("rustc_layout") {
+                    self.dump_layout_of(item_def_id, item, attr);
+                }
+            }
+        }
+    }
+
+    fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {}
+    fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {}
+}
+
+impl<'a, 'tcx> VarianceTest<'a, 'tcx> {
+    fn dump_layout_of(&self, item_def_id: DefId, item: &hir::Item, attr: &Attribute) {
+        let tcx = self.tcx;
+        let param_env = self.tcx.param_env(item_def_id);
+        let ty = self.tcx.type_of(item_def_id);
+        match self.tcx.layout_of(param_env.and(ty)) {
+            Ok(ty_layout) => {
+                // Check out the `#[rustc_layout(..)]` attribute to tell what to dump.
+                // The `..` are the names of fields to dump.
+                let meta_items = attr.meta_item_list().unwrap_or_default();
+                for meta_item in meta_items {
+                    let name = meta_item.word().map(|mi| mi.name().as_str());
+                    let name = name.as_ref().map(|s| &s[..]).unwrap_or("");
+
+                    match name {
+                        "abi" => {
+                            self.tcx
+                                .sess
+                                .span_err(item.span, &format!("abi: {:?}", ty_layout.abi));
+                        }
+
+                        "align" => {
+                            self.tcx
+                                .sess
+                                .span_err(item.span, &format!("align: {:?}", ty_layout.align));
+                        }
+
+                        "size" => {
+                            self.tcx
+                                .sess
+                                .span_err(item.span, &format!("size: {:?}", ty_layout.size));
+                        }
+
+                        "homogeneous_aggregate" => {
+                            self.tcx.sess.span_err(
+                                item.span,
+                                &format!(
+                                    "homogeneous_aggregate: {:?}",
+                                    ty_layout
+                                        .homogeneous_aggregate(&UnwrapLayoutCx { tcx, param_env }),
+                                ),
+                            );
+                        }
+
+                        _ => {
+                            self.tcx.sess.span_err(
+                                meta_item.span,
+                                &format!("unrecognized field name `{}`", name),
+                            );
+                        }
+                    }
+                }
+            }
+
+            Err(layout_error) => {
+                self.tcx
+                    .sess
+                    .span_err(item.span, &format!("layout error: {:?}", layout_error));
+            }
+        }
+    }
+}
+
+struct UnwrapLayoutCx<'me, 'tcx> {
+    tcx: TyCtxt<'me, 'tcx, 'tcx>,
+    param_env: ParamEnv<'tcx>,
+}
+
+impl<'me, 'tcx> LayoutOf for UnwrapLayoutCx<'me, 'tcx> {
+    type Ty = Ty<'tcx>;
+    type TyLayout = TyLayout<'tcx>;
+
+    fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
+        self.tcx.layout_of(self.param_env.and(ty)).unwrap()
+    }
+}
+
+impl<'me, 'tcx> HasTyCtxt<'tcx> for UnwrapLayoutCx<'me, 'tcx> {
+    fn tcx<'a>(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
+        self.tcx
+    }
+}
+
+impl<'me, 'tcx> HasDataLayout for UnwrapLayoutCx<'me, 'tcx> {
+    fn data_layout(&self) -> &TargetDataLayout {
+        self.tcx.data_layout()
+    }
+}
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index 829d4b34cf7..a181bc7e9b4 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -32,6 +32,7 @@ mod diagnostics;
 pub mod ast_validation;
 pub mod rvalue_promotion;
 pub mod hir_stats;
+pub mod layout_test;
 pub mod loops;
 
 __build_diagnostic_array! { librustc_passes, DIAGNOSTICS }
diff --git a/src/librustc_target/abi/call/aarch64.rs b/src/librustc_target/abi/call/aarch64.rs
index 121f80c75b9..9f9bba14b96 100644
--- a/src/librustc_target/abi/call/aarch64.rs
+++ b/src/librustc_target/abi/call/aarch64.rs
@@ -6,7 +6,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
         let size = arg.layout.size;
 
         // Ensure we have at most four uniquely addressable members.
diff --git a/src/librustc_target/abi/call/arm.rs b/src/librustc_target/abi/call/arm.rs
index 2b3588d31bf..228dd362161 100644
--- a/src/librustc_target/abi/call/arm.rs
+++ b/src/librustc_target/abi/call/arm.rs
@@ -7,7 +7,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
         let size = arg.layout.size;
 
         // Ensure we have at most four uniquely addressable members.
diff --git a/src/librustc_target/abi/call/asmjs.rs b/src/librustc_target/abi/call/asmjs.rs
index 32c6634b3d2..85444500c5e 100644
--- a/src/librustc_target/abi/call/asmjs.rs
+++ b/src/librustc_target/abi/call/asmjs.rs
@@ -11,7 +11,7 @@ fn classify_ret_ty<'a, Ty, C>(cx: &C, ret: &mut ArgType<'a, Ty>)
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
     if ret.layout.is_aggregate() {
-        if let Some(unit) = ret.layout.homogeneous_aggregate(cx) {
+        if let Some(unit) = ret.layout.homogeneous_aggregate(cx).unit() {
             let size = ret.layout.size;
             if unit.size == size {
                 ret.cast_to(Uniform {
diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs
index 37bf6e68247..0d50439c67e 100644
--- a/src/librustc_target/abi/call/mod.rs
+++ b/src/librustc_target/abi/call/mod.rs
@@ -228,6 +228,33 @@ impl CastTarget {
     }
 }
 
+/// Return value from the `homogeneous_aggregate` test function.
+#[derive(Copy, Clone, Debug)]
+pub enum HomogeneousAggregate {
+    /// Yes, all the "leaf fields" of this struct are passed in the
+    /// same way (specified in the `Reg` value).
+    Homogeneous(Reg),
+
+    /// There are distinct leaf fields passed in different ways,
+    /// or this is uninhabited.
+    Heterogeneous,
+
+    /// There are no leaf fields at all.
+    NoData,
+}
+
+impl HomogeneousAggregate {
+    /// If this is a homogeneous aggregate, returns the homogeneous
+    /// unit, else `None`.
+    pub fn unit(self) -> Option<Reg> {
+        if let HomogeneousAggregate::Homogeneous(r) = self {
+            Some(r)
+        } else {
+            None
+        }
+    }
+}
+
 impl<'a, Ty> TyLayout<'a, Ty> {
     fn is_aggregate(&self) -> bool {
         match self.abi {
@@ -239,11 +266,21 @@ impl<'a, Ty> TyLayout<'a, Ty> {
         }
     }
 
-    fn homogeneous_aggregate<C>(&self, cx: &C) -> Option<Reg>
+    /// True if this layout is an aggregate containing fields of only
+    /// a single type (e.g., `(u32, u32)`). Such aggregates are often
+    /// special-cased in ABIs.
+    ///
+    /// Note: We generally ignore fields of zero-sized type when computing
+    /// this value (cc #56877).
+    ///
+    /// This is public so that it can be used in unit tests, but
+    /// should generally only be relevant to the ABI details of
+    /// specific targets.
+    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> HomogeneousAggregate
         where Ty: TyLayoutMethods<'a, C> + Copy, C: LayoutOf<Ty = Ty, TyLayout = Self>
     {
         match self.abi {
-            Abi::Uninhabited => None,
+            Abi::Uninhabited => HomogeneousAggregate::Heterogeneous,
 
             // The primitive for this algorithm.
             Abi::Scalar(ref scalar) => {
@@ -252,14 +289,15 @@ impl<'a, Ty> TyLayout<'a, Ty> {
                     abi::Pointer => RegKind::Integer,
                     abi::Float(_) => RegKind::Float,
                 };
-                Some(Reg {
+                HomogeneousAggregate::Homogeneous(Reg {
                     kind,
                     size: self.size
                 })
             }
 
             Abi::Vector { .. } => {
-                Some(Reg {
+                assert!(!self.is_zst());
+                HomogeneousAggregate::Homogeneous(Reg {
                     kind: RegKind::Vector,
                     size: self.size
                 })
@@ -275,7 +313,7 @@ impl<'a, Ty> TyLayout<'a, Ty> {
                         if count > 0 {
                             return self.field(cx, 0).homogeneous_aggregate(cx);
                         } else {
-                            return None;
+                            return HomogeneousAggregate::NoData;
                         }
                     }
                     FieldPlacement::Union(_) => true,
@@ -284,21 +322,27 @@ impl<'a, Ty> TyLayout<'a, Ty> {
 
                 for i in 0..self.fields.count() {
                     if !is_union && total != self.fields.offset(i) {
-                        return None;
+                        return HomogeneousAggregate::Heterogeneous;
                     }
 
                     let field = self.field(cx, i);
+
                     match (result, field.homogeneous_aggregate(cx)) {
-                        // The field itself must be a homogeneous aggregate.
-                        (_, None) => return None,
+                        (_, HomogeneousAggregate::NoData) => {
+                            // Ignore fields that have no data
+                        }
+                        (_, HomogeneousAggregate::Heterogeneous) => {
+                            // The field itself must be a homogeneous aggregate.
+                            return HomogeneousAggregate::Heterogeneous;
+                        }
                         // If this is the first field, record the unit.
-                        (None, Some(unit)) => {
+                        (None, HomogeneousAggregate::Homogeneous(unit)) => {
                             result = Some(unit);
                         }
                         // For all following fields, the unit must be the same.
-                        (Some(prev_unit), Some(unit)) => {
+                        (Some(prev_unit), HomogeneousAggregate::Homogeneous(unit)) => {
                             if prev_unit != unit {
-                                return None;
+                                return HomogeneousAggregate::Heterogeneous;
                             }
                         }
                     }
@@ -314,9 +358,18 @@ impl<'a, Ty> TyLayout<'a, Ty> {
 
                 // There needs to be no padding.
                 if total != self.size {
-                    None
+                    HomogeneousAggregate::Heterogeneous
                 } else {
-                    result
+                    match result {
+                        Some(reg) => {
+                            assert_ne!(total, Size::ZERO);
+                            HomogeneousAggregate::Homogeneous(reg)
+                        }
+                        None => {
+                            assert_eq!(total, Size::ZERO);
+                            HomogeneousAggregate::NoData
+                        }
+                    }
                 }
             }
         }
diff --git a/src/librustc_target/abi/call/powerpc64.rs b/src/librustc_target/abi/call/powerpc64.rs
index 3ae9ab9ac5e..305a2d42250 100644
--- a/src/librustc_target/abi/call/powerpc64.rs
+++ b/src/librustc_target/abi/call/powerpc64.rs
@@ -18,7 +18,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>, abi: A
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
         // ELFv1 only passes one-member aggregates transparently.
         // ELFv2 passes up to eight uniquely addressable members.
         if (abi == ELFv1 && arg.layout.size > unit.size)
diff --git a/src/librustc_target/abi/call/sparc64.rs b/src/librustc_target/abi/call/sparc64.rs
index bb0c9b7967a..150b48a8d02 100644
--- a/src/librustc_target/abi/call/sparc64.rs
+++ b/src/librustc_target/abi/call/sparc64.rs
@@ -8,7 +8,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    arg.layout.homogeneous_aggregate(cx).and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
         // Ensure we have at most eight uniquely addressable members.
         if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
             return None;
diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs
index 748bef4de66..648a4b5bb9d 100644
--- a/src/librustc_target/abi/call/x86.rs
+++ b/src/librustc_target/abi/call/x86.rs
@@ -99,7 +99,7 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>, flavor: Fla
             };
 
             // At this point we know this must be a primitive of sorts.
-            let unit = arg.layout.homogeneous_aggregate(cx).unwrap();
+            let unit = arg.layout.homogeneous_aggregate(cx).unit().unwrap();
             assert_eq!(unit.size, arg.layout.size);
             if unit.kind == RegKind::Float {
                 continue;
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index c0a8dd87ee2..1f260d317e4 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -763,7 +763,7 @@ function's return type and the value being returned.
 "##,
 
 E0070: r##"
-The left-hand side of an assignment operator must be a place expression. An
+The left-hand side of an assignment operator must be a place expression. A
 place expression represents a memory location and can be a variable (with
 optional namespacing), a dereference, an indexing expression or a field
 reference.
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 9e107fee5ba..85e80f7bdaf 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -938,6 +938,13 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
                                       is just used for rustc unit tests \
                                       and will never be stable",
                                      cfg_fn!(rustc_attrs))),
+    ("rustc_layout", Normal, template!(List: "field1, field2, ..."),
+     Gated(Stability::Unstable,
+           "rustc_attrs",
+           "the `#[rustc_layout]` attribute \
+            is just used for rustc unit tests \
+            and will never be stable",
+           cfg_fn!(rustc_attrs))),
     ("rustc_regions", Normal, template!(Word), Gated(Stability::Unstable,
                                     "rustc_attrs",
                                     "the `#[rustc_regions]` attribute \
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index fb832afb748..57dcc42d9e6 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -5543,22 +5543,31 @@ impl<'a> Parser<'a> {
     fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
         let mut args = Vec::new();
         let mut bindings = Vec::new();
+
         let mut seen_type = false;
         let mut seen_binding = false;
+
+        let mut last_comma_span = None;
         let mut first_type_or_binding_span: Option<Span> = None;
+        let mut first_binding_span: Option<Span> = None;
+
         let mut bad_lifetime_pos = vec![];
-        let mut last_comma_span = None;
-        let mut suggestions = vec![];
+        let mut bad_type_pos = vec![];
+
+        let mut lifetime_suggestions = vec![];
+        let mut type_suggestions = vec![];
         loop {
             if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
                 // Parse lifetime argument.
                 args.push(GenericArg::Lifetime(self.expect_lifetime()));
+
                 if seen_type || seen_binding {
                     let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
                     bad_lifetime_pos.push(self.prev_span);
+
                     if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
-                        suggestions.push((remove_sp, String::new()));
-                        suggestions.push((
+                        lifetime_suggestions.push((remove_sp, String::new()));
+                        lifetime_suggestions.push((
                             first_type_or_binding_span.unwrap().shrink_to_lo(),
                             format!("{}, ", snippet)));
                     }
@@ -5576,24 +5585,29 @@ impl<'a> Parser<'a> {
                     ty,
                     span,
                 });
+
                 seen_binding = true;
                 if first_type_or_binding_span.is_none() {
                     first_type_or_binding_span = Some(span);
                 }
+                if first_binding_span.is_none() {
+                    first_binding_span = Some(span);
+                }
             } else if self.check_type() {
                 // Parse type argument.
                 let ty_param = self.parse_ty()?;
                 if seen_binding {
-                    self.struct_span_err(
-                        ty_param.span,
-                        "type parameters must be declared prior to associated type bindings"
-                    )
-                        .span_label(
-                            ty_param.span,
-                            "must be declared prior to associated type bindings",
-                        )
-                        .emit();
+                    let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
+                    bad_type_pos.push(self.prev_span);
+
+                    if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
+                        type_suggestions.push((remove_sp, String::new()));
+                        type_suggestions.push((
+                            first_binding_span.unwrap().shrink_to_lo(),
+                            format!("{}, ", snippet)));
+                    }
                 }
+
                 if first_type_or_binding_span.is_none() {
                     first_type_or_binding_span = Some(ty_param.span);
                 }
@@ -5609,27 +5623,93 @@ impl<'a> Parser<'a> {
                 last_comma_span = Some(self.prev_span);
             }
         }
-        if !bad_lifetime_pos.is_empty() {
-            let mut err = self.struct_span_err(
+
+        self.maybe_report_incorrect_generic_argument_order(
+            bad_lifetime_pos, bad_type_pos, lifetime_suggestions, type_suggestions
+        );
+
+        Ok((args, bindings))
+    }
+
+    /// Maybe report an error about incorrect generic argument order - "lifetime parameters
+    /// must be declared before type parameters", "type parameters must be declared before
+    /// associated type bindings" or both.
+    fn maybe_report_incorrect_generic_argument_order(
+        &self,
+        bad_lifetime_pos: Vec<Span>,
+        bad_type_pos: Vec<Span>,
+        lifetime_suggestions: Vec<(Span, String)>,
+        type_suggestions: Vec<(Span, String)>,
+    ) {
+        let mut err = if !bad_lifetime_pos.is_empty() && !bad_type_pos.is_empty() {
+            let mut positions = bad_lifetime_pos.clone();
+            positions.extend_from_slice(&bad_type_pos);
+
+            self.struct_span_err(
+                positions,
+                "generic arguments must declare lifetimes, types and associated type bindings in \
+                 that order",
+            )
+        } else if !bad_lifetime_pos.is_empty() {
+            self.struct_span_err(
                 bad_lifetime_pos.clone(),
                 "lifetime parameters must be declared prior to type parameters"
-            );
+            )
+        } else if !bad_type_pos.is_empty() {
+            self.struct_span_err(
+                bad_type_pos.clone(),
+                "type parameters must be declared prior to associated type bindings"
+            )
+        } else {
+            return;
+        };
+
+        if !bad_lifetime_pos.is_empty() {
             for sp in &bad_lifetime_pos {
                 err.span_label(*sp, "must be declared prior to type parameters");
             }
-            if !suggestions.is_empty() {
-                err.multipart_suggestion_with_applicability(
-                    &format!(
-                        "move the lifetime parameter{} prior to the first type parameter",
-                        if bad_lifetime_pos.len() > 1 { "s" } else { "" },
-                    ),
-                    suggestions,
-                    Applicability::MachineApplicable,
-                );
+        }
+
+        if !bad_type_pos.is_empty() {
+            for sp in &bad_type_pos {
+                err.span_label(*sp, "must be declared prior to associated type bindings");
             }
-            err.emit();
         }
-        Ok((args, bindings))
+
+        if !lifetime_suggestions.is_empty() && !type_suggestions.is_empty() {
+            let mut suggestions = lifetime_suggestions;
+            suggestions.extend_from_slice(&type_suggestions);
+
+            let plural = bad_lifetime_pos.len() + bad_type_pos.len() > 1;
+            err.multipart_suggestion_with_applicability(
+                &format!(
+                    "move the parameter{}",
+                    if plural { "s" } else { "" },
+                ),
+                suggestions,
+                Applicability::MachineApplicable,
+            );
+        } else if !lifetime_suggestions.is_empty() {
+            err.multipart_suggestion_with_applicability(
+                &format!(
+                    "move the lifetime parameter{} prior to the first type parameter",
+                    if bad_lifetime_pos.len() > 1 { "s" } else { "" },
+                ),
+                lifetime_suggestions,
+                Applicability::MachineApplicable,
+            );
+        } else if !type_suggestions.is_empty() {
+            err.multipart_suggestion_with_applicability(
+                &format!(
+                    "move the type parameter{} prior to the first associated type binding",
+                    if bad_type_pos.len() > 1 { "s" } else { "" },
+                ),
+                type_suggestions,
+                Applicability::MachineApplicable,
+            );
+        }
+
+        err.emit();
     }
 
     /// Parses an optional `where` clause and places it in `generics`.
diff --git a/src/test/ui/consts/drop_none.rs b/src/test/ui/consts/drop_none.rs
new file mode 100644
index 00000000000..86a197ffb99
--- /dev/null
+++ b/src/test/ui/consts/drop_none.rs
@@ -0,0 +1,13 @@
+// compile-pass
+#![allow(dead_code)]
+struct A;
+impl Drop for A {
+    fn drop(&mut self) {}
+}
+
+const FOO: Option<A> = None;
+
+const BAR: () = (FOO, ()).1;
+
+
+fn main() {}
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
new file mode 100644
index 00000000000..622709e7de5
--- /dev/null
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
@@ -0,0 +1,36 @@
+#![feature(rustc_attrs)]
+
+// Show that `homogeneous_aggregate` code ignores zero-length C
+// arrays.  This matches the recent C standard, though not the
+// behavior of all older compilers, which somtimes consider `T[0]` to
+// be a "flexible array member" (see discussion on #56877 for
+// details).
+
+#[repr(C)]
+pub struct Foo {
+    x: u32
+}
+
+#[repr(C)]
+pub struct Middle {
+    pub a: f32,
+    pub foo: [Foo; 0],
+    pub b: f32,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type TestMiddle = Middle;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+#[repr(C)]
+pub struct Final {
+    pub a: f32,
+    pub b: f32,
+    pub foo: [Foo; 0],
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type TestFinal = Final;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+fn main() { }
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr
new file mode 100644
index 00000000000..0d442606351
--- /dev/null
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr
@@ -0,0 +1,14 @@
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:22:1
+   |
+LL | pub type TestMiddle = Middle;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:33:1
+   |
+LL | pub type TestFinal = Final;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
new file mode 100644
index 00000000000..4b429412aeb
--- /dev/null
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
@@ -0,0 +1,73 @@
+#![feature(rustc_attrs)]
+
+// Regression test for #56877. We want to ensure that the presence of
+// `PhantomData` does not prevent `Bar` from being considered a
+// homogeneous aggregate.
+
+#[repr(C)]
+pub struct BaseCase {
+    pub a: f32,
+    pub b: f32,
+}
+
+#[repr(C)]
+pub struct WithPhantomData {
+    pub a: f32,
+    pub b: f32,
+    pub _unit: std::marker::PhantomData<()>,
+}
+
+pub struct EmptyRustStruct {
+}
+
+#[repr(C)]
+pub struct WithEmptyRustStruct {
+    pub a: f32,
+    pub b: f32,
+    pub _unit: EmptyRustStruct,
+}
+
+pub struct TransitivelyEmptyRustStruct {
+    field: EmptyRustStruct,
+    array: [u32; 0],
+}
+
+#[repr(C)]
+pub struct WithTransitivelyEmptyRustStruct {
+    pub a: f32,
+    pub b: f32,
+    pub _unit: TransitivelyEmptyRustStruct,
+}
+
+pub enum EmptyRustEnum {
+    Dummy,
+}
+
+#[repr(C)]
+pub struct WithEmptyRustEnum {
+    pub a: f32,
+    pub b: f32,
+    pub _unit: EmptyRustEnum,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test1 = BaseCase;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test2 = WithPhantomData;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test3 = WithEmptyRustStruct;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test4 = WithTransitivelyEmptyRustStruct;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+#[rustc_layout(homogeneous_aggregate)]
+pub type Test5 = WithEmptyRustEnum;
+//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+
+fn main() { }
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr
new file mode 100644
index 00000000000..be04ba3e7f6
--- /dev/null
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr
@@ -0,0 +1,32 @@
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:54:1
+   |
+LL | pub type Test1 = BaseCase;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:58:1
+   |
+LL | pub type Test2 = WithPhantomData;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:62:1
+   |
+LL | pub type Test3 = WithEmptyRustStruct;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:66:1
+   |
+LL | pub type Test4 = WithTransitivelyEmptyRustStruct;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:70:1
+   |
+LL | pub type Test5 = WithEmptyRustEnum;
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
diff --git a/src/test/ui/layout/zero-sized-array-union.rs b/src/test/ui/layout/zero-sized-array-union.rs
new file mode 100644
index 00000000000..68b218249eb
--- /dev/null
+++ b/src/test/ui/layout/zero-sized-array-union.rs
@@ -0,0 +1,95 @@
+#![feature(rustc_attrs)]
+
+// Various tests around the behavior of zero-sized arrays and
+// unions. This matches the behavior of modern C compilers, though
+// older compilers (and sometimes clang) treat `T[0]` as a "flexible
+// array member". See more
+// details in #56877.
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Empty { }
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Empty2 {
+    e: Empty
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Empty3 {
+    z: [f32; 0],
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+struct Empty4 {
+    e: Empty3
+}
+
+#[repr(C)]
+union U1 {
+    s: Empty
+}
+
+#[repr(C)]
+union U2 {
+    s: Empty2
+}
+
+#[repr(C)]
+union U3 {
+    s: Empty3
+}
+
+#[repr(C)]
+union U4 {
+    s: Empty4
+}
+
+#[repr(C)]
+struct Baz1 {
+    x: f32,
+    y: f32,
+    u: U1,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+type TestBaz1 = Baz1;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+#[repr(C)]
+struct Baz2 {
+    x: f32,
+    y: f32,
+    u: U2,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+type TestBaz2 = Baz2;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+#[repr(C)]
+struct Baz3 {
+    x: f32,
+    y: f32,
+    u: U3,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+type TestBaz3 = Baz3;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+#[repr(C)]
+struct Baz4 {
+    x: f32,
+    y: f32,
+    u: U4,
+}
+
+#[rustc_layout(homogeneous_aggregate)]
+type TestBaz4 = Baz4;
+//~^ ERROR homogeneous_aggregate: Homogeneous
+
+fn main() { }
diff --git a/src/test/ui/layout/zero-sized-array-union.stderr b/src/test/ui/layout/zero-sized-array-union.stderr
new file mode 100644
index 00000000000..1bb31aaf7b7
--- /dev/null
+++ b/src/test/ui/layout/zero-sized-array-union.stderr
@@ -0,0 +1,26 @@
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/zero-sized-array-union.rs:59:1
+   |
+LL | type TestBaz1 = Baz1;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/zero-sized-array-union.rs:70:1
+   |
+LL | type TestBaz2 = Baz2;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/zero-sized-array-union.rs:81:1
+   |
+LL | type TestBaz3 = Baz3;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+  --> $DIR/zero-sized-array-union.rs:92:1
+   |
+LL | type TestBaz4 = Baz4;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/parser/issue-32214.stderr b/src/test/ui/parser/issue-32214.stderr
index a889513eaee..660e517c85a 100644
--- a/src/test/ui/parser/issue-32214.stderr
+++ b/src/test/ui/parser/issue-32214.stderr
@@ -3,6 +3,10 @@ error: type parameters must be declared prior to associated type bindings
    |
 LL | pub fn test<W, I: Trait<Item=(), W> >() {}
    |                                  ^ must be declared prior to associated type bindings
+help: move the type parameter prior to the first associated type binding
+   |
+LL | pub fn test<W, I: Trait<W, Item=()> >() {}
+   |                         ^^       --
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/static/static-drop-scope.nll.stderr b/src/test/ui/static/static-drop-scope.nll.stderr
index 3c01f694bff..df6383b4fc2 100644
--- a/src/test/ui/static/static-drop-scope.nll.stderr
+++ b/src/test/ui/static/static-drop-scope.nll.stderr
@@ -54,7 +54,19 @@ error[E0493]: destructors cannot be evaluated at compile-time
 LL |     (x, ()).1
    |     ^^^^^^^ constant functions cannot evaluate destructors
 
-error: aborting due to 8 previous errors
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/static-drop-scope.rs:31:34
+   |
+LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1;
+   |                                  ^^^^^^^^^^^^^^^^^^^ constants cannot evaluate destructors
+
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/static-drop-scope.rs:36:43
+   |
+LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1;
+   |                                           ^^^^^^^^^^^ constants cannot evaluate destructors
+
+error: aborting due to 10 previous errors
 
 Some errors occurred: E0493, E0716.
 For more information about an error, try `rustc --explain E0493`.
diff --git a/src/test/ui/static/static-drop-scope.rs b/src/test/ui/static/static-drop-scope.rs
index a11a9f020e0..e5a9f2a4056 100644
--- a/src/test/ui/static/static-drop-scope.rs
+++ b/src/test/ui/static/static-drop-scope.rs
@@ -28,4 +28,12 @@ const fn const_drop2<T>(x: T) {
     //~^ ERROR destructors cannot be evaluated at compile-time
 }
 
+const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1;
+//~^ ERROR destructors cannot be evaluated at compile-time
+
+const HELPER: Option<WithDtor> = Some(WithDtor);
+
+const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1;
+//~^ ERROR destructors cannot be evaluated at compile-time
+
 fn main () {}
diff --git a/src/test/ui/static/static-drop-scope.stderr b/src/test/ui/static/static-drop-scope.stderr
index 89b31d95a2a..3e3032eb4fb 100644
--- a/src/test/ui/static/static-drop-scope.stderr
+++ b/src/test/ui/static/static-drop-scope.stderr
@@ -54,7 +54,19 @@ error[E0493]: destructors cannot be evaluated at compile-time
 LL |     (x, ()).1
    |     ^^^^^^^ constant functions cannot evaluate destructors
 
-error: aborting due to 8 previous errors
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/static-drop-scope.rs:31:34
+   |
+LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1;
+   |                                  ^^^^^^^^^^^^^^^^^^^ constants cannot evaluate destructors
+
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/static-drop-scope.rs:36:43
+   |
+LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1;
+   |                                           ^^^^^^^^^^^ constants cannot evaluate destructors
+
+error: aborting due to 10 previous errors
 
 Some errors occurred: E0493, E0597.
 For more information about an error, try `rustc --explain E0493`.
diff --git a/src/test/ui/suggestions/suggest-move-types.rs b/src/test/ui/suggestions/suggest-move-types.rs
new file mode 100644
index 00000000000..fd10ba4350c
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-move-types.rs
@@ -0,0 +1,85 @@
+// ignore-tidy-linelength
+
+#![allow(warnings)]
+
+// This test verifies that the suggestion to move types before associated type bindings
+// is correct.
+
+trait One<T> {
+  type A;
+}
+
+trait OneWithLifetime<'a, T> {
+  type A;
+}
+
+trait Three<T, U, V> {
+  type A;
+  type B;
+  type C;
+}
+
+trait ThreeWithLifetime<'a, 'b, 'c, T, U, V> {
+  type A;
+  type B;
+  type C;
+}
+
+struct A<T, M: One<A=(), T>> { //~ ERROR type parameters must be declared
+    m: M,
+    t: T,
+}
+
+
+struct Al<'a, T, M: OneWithLifetime<A=(), T, 'a>> {
+//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
+    m: M,
+    t: &'a T,
+}
+
+struct B<T, U, V, M: Three<A=(), B=(), C=(), T, U, V>> { //~ ERROR type parameters must be declared
+    m: M,
+    t: T,
+    u: U,
+    v: V,
+}
+
+struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<A=(), B=(), C=(), T, U, V, 'a, 'b, 'c>> {
+//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
+    m: M,
+    t: &'a T,
+    u: &'b U,
+    v: &'c V,
+}
+
+struct C<T, U, V, M: Three<T, A=(), B=(), C=(), U, V>> { //~ ERROR type parameters must be declared
+    m: M,
+    t: T,
+    u: U,
+    v: V,
+}
+
+struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), C=(), U, 'b, V, 'c>> {
+//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
+    m: M,
+    t: &'a T,
+    u: &'b U,
+    v: &'c V,
+}
+
+struct D<T, U, V, M: Three<T, A=(), B=(), U, C=(), V>> { //~ ERROR type parameters must be declared
+    m: M,
+    t: T,
+    u: U,
+    v: V,
+}
+
+struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), U, 'b, C=(), V, 'c>> {
+//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
+    m: M,
+    t: &'a T,
+    u: &'b U,
+    v: &'c V,
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr
new file mode 100644
index 00000000000..3643d9a9124
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-move-types.stderr
@@ -0,0 +1,107 @@
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:28:26
+   |
+LL | struct A<T, M: One<A=(), T>> { //~ ERROR type parameters must be declared
+   |                          ^ must be declared prior to associated type bindings
+help: move the type parameter prior to the first associated type binding
+   |
+LL | struct A<T, M: One<T, A=()>> { //~ ERROR type parameters must be declared
+   |                    ^^    --
+
+error: generic arguments must declare lifetimes, types and associated type bindings in that order
+  --> $DIR/suggest-move-types.rs:34:46
+   |
+LL | struct Al<'a, T, M: OneWithLifetime<A=(), T, 'a>> {
+   |                                           ^  ^^ must be declared prior to type parameters
+   |                                           |
+   |                                           must be declared prior to associated type bindings
+help: move the parameters
+   |
+LL | struct Al<'a, T, M: OneWithLifetime<'a, T, A=()>> {
+   |                                     ^^^ ^^    --
+
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:40:46
+   |
+LL | struct B<T, U, V, M: Three<A=(), B=(), C=(), T, U, V>> { //~ ERROR type parameters must be declared
+   |                                              ^  ^  ^ must be declared prior to associated type bindings
+   |                                              |  |
+   |                                              |  must be declared prior to associated type bindings
+   |                                              must be declared prior to associated type bindings
+help: move the type parameters prior to the first associated type binding
+   |
+LL | struct B<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
+   |                            ^^ ^^ ^^                --
+
+error: generic arguments must declare lifetimes, types and associated type bindings in that order
+  --> $DIR/suggest-move-types.rs:47:80
+   |
+LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<A=(), B=(), C=(), T, U, V, 'a, 'b, 'c>> {
+   |                                                                       ^  ^  ^  ^^  ^^  ^^ must be declared prior to type parameters
+   |                                                                       |  |  |  |   |
+   |                                                                       |  |  |  |   must be declared prior to type parameters
+   |                                                                       |  |  |  must be declared prior to type parameters
+   |                                                                       |  |  must be declared prior to associated type bindings
+   |                                                                       |  must be declared prior to associated type bindings
+   |                                                                       must be declared prior to associated type bindings
+help: move the parameters
+   |
+LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
+   |                                                     ^^^ ^^^ ^^^ ^^ ^^ ^^                --
+
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:55:49
+   |
+LL | struct C<T, U, V, M: Three<T, A=(), B=(), C=(), U, V>> { //~ ERROR type parameters must be declared
+   |                                                 ^  ^ must be declared prior to associated type bindings
+   |                                                 |
+   |                                                 must be declared prior to associated type bindings
+help: move the type parameters prior to the first associated type binding
+   |
+LL | struct C<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
+   |                               ^^ ^^                --
+
+error: generic arguments must declare lifetimes, types and associated type bindings in that order
+  --> $DIR/suggest-move-types.rs:62:56
+   |
+LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), C=(), U, 'b, V, 'c>> {
+   |                                                        ^^                    ^  ^^  ^  ^^ must be declared prior to type parameters
+   |                                                        |                     |  |   |
+   |                                                        |                     |  |   must be declared prior to associated type bindings
+   |                                                        |                     |  must be declared prior to type parameters
+   |                                                        |                     must be declared prior to associated type bindings
+   |                                                        must be declared prior to type parameters
+help: move the parameters
+   |
+LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
+   |                                                     ^^^ ^^^ ^^^ -- ^^ ^^                --
+
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:70:43
+   |
+LL | struct D<T, U, V, M: Three<T, A=(), B=(), U, C=(), V>> { //~ ERROR type parameters must be declared
+   |                                           ^        ^ must be declared prior to associated type bindings
+   |                                           |
+   |                                           must be declared prior to associated type bindings
+help: move the type parameters prior to the first associated type binding
+   |
+LL | struct D<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
+   |                               ^^ ^^          --    --
+
+error: generic arguments must declare lifetimes, types and associated type bindings in that order
+  --> $DIR/suggest-move-types.rs:77:56
+   |
+LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), U, 'b, C=(), V, 'c>> {
+   |                                                        ^^              ^  ^^        ^  ^^ must be declared prior to type parameters
+   |                                                        |               |  |         |
+   |                                                        |               |  |         must be declared prior to associated type bindings
+   |                                                        |               |  must be declared prior to type parameters
+   |                                                        |               must be declared prior to associated type bindings
+   |                                                        must be declared prior to type parameters
+help: move the parameters
+   |
+LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
+   |                                                     ^^^ ^^^ ^^^ -- ^^ ^^          --    --
+
+error: aborting due to 8 previous errors
+