about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcore/macros.rs4
-rw-r--r--src/libfmt_macros/lib.rs24
-rw-r--r--src/libfmt_macros/tests.rs43
-rw-r--r--src/librustc/query/mod.rs2
-rw-r--r--src/librustc/traits/error_reporting.rs9
-rw-r--r--src/librustc/traits/mod.rs9
-rw-r--r--src/librustc/traits/structural_impls.rs2
-rw-r--r--src/librustc/ty/query/mod.rs1
-rw-r--r--src/librustc/ty/wf.rs101
-rw-r--r--src/librustc_driver/args.rs12
-rw-r--r--src/librustc_driver/lib.rs6
-rw-r--r--src/librustc_metadata/rmeta/decoder/cstore_impl.rs5
-rw-r--r--src/librustc_metadata/rmeta/encoder.rs6
-rw-r--r--src/librustc_mir/transform/check_consts/qualifs.rs2
-rw-r--r--src/librustc_mir/transform/mod.rs7
-rw-r--r--src/librustc_mir/transform/promote_consts.rs140
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs173
-rw-r--r--src/librustc_target/abi/call/mod.rs2
-rw-r--r--src/librustc_target/abi/call/powerpc.rs41
-rw-r--r--src/libsyntax_ext/format.rs88
-rw-r--r--src/test/mir-opt/match_false_edges.rs12
-rw-r--r--src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs16
-rw-r--r--src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr28
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn.rs1
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn.stderr19
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs1
-rw-r--r--src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr14
-rw-r--r--src/test/ui/if/ifmt-bad-arg.stderr4
-rw-r--r--src/test/ui/if/ifmt-unknown-trait.stderr4
29 files changed, 453 insertions, 323 deletions
diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
index 131fb52e2d2..726d187d2e9 100644
--- a/src/libcore/macros.rs
+++ b/src/libcore/macros.rs
@@ -1274,6 +1274,10 @@ pub(crate) mod builtin {
     }
 
     /// Inline assembly.
+    ///
+    /// Read the [unstable book] for the usage.
+    ///
+    /// [unstable book]: ../unstable-book/library-features/asm.html
     #[unstable(feature = "asm", issue = "29722",
                reason = "inline assembly is not stable enough for use and is subject to change")]
     #[rustc_builtin_macro]
diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs
index d22420e76dc..24b19028ac1 100644
--- a/src/libfmt_macros/lib.rs
+++ b/src/libfmt_macros/lib.rs
@@ -35,7 +35,7 @@ impl InnerOffset {
 
 /// A piece is a portion of the format string which represents the next part
 /// to emit. These are emitted as a stream by the `Parser` class.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub enum Piece<'a> {
     /// A literal string which should directly be emitted
     String(&'a str),
@@ -45,7 +45,7 @@ pub enum Piece<'a> {
 }
 
 /// Representation of an argument specification.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub struct Argument<'a> {
     /// Where to find this argument
     pub position: Position,
@@ -54,7 +54,7 @@ pub struct Argument<'a> {
 }
 
 /// Specification for the formatting of an argument in the format string.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub struct FormatSpec<'a> {
     /// Optionally specified character to fill alignment with.
     pub fill: Option<char>,
@@ -74,10 +74,12 @@ pub struct FormatSpec<'a> {
     /// this argument, this can be empty or any number of characters, although
     /// it is required to be one word.
     pub ty: &'a str,
+    /// The span of the descriptor string (for diagnostics).
+    pub ty_span: Option<InnerSpan>,
 }
 
 /// Enum describing where an argument for a format can be located.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub enum Position {
     /// The argument is implied to be located at an index
     ArgumentImplicitlyIs(usize),
@@ -97,7 +99,7 @@ impl Position {
 }
 
 /// Enum of alignments which are supported.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub enum Alignment {
     /// The value will be aligned to the left.
     AlignLeft,
@@ -111,7 +113,7 @@ pub enum Alignment {
 
 /// Various flags which can be applied to format strings. The meaning of these
 /// flags is defined by the formatters themselves.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub enum Flag {
     /// A `+` will be used to denote positive numbers.
     FlagSignPlus,
@@ -131,7 +133,7 @@ pub enum Flag {
 
 /// A count is used for the precision and width parameters of an integer, and
 /// can reference either an argument or a literal integer.
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub enum Count {
     /// The count is specified explicitly.
     CountIs(usize),
@@ -475,6 +477,7 @@ impl<'a> Parser<'a> {
             width: CountImplied,
             width_span: None,
             ty: &self.input[..0],
+            ty_span: None,
         };
         if !self.consume(':') {
             return spec;
@@ -548,6 +551,7 @@ impl<'a> Parser<'a> {
                 spec.precision_span = sp;
             }
         }
+        let ty_span_start = self.cur.peek().map(|(pos, _)| *pos);
         // Optional radix followed by the actual format specifier
         if self.consume('x') {
             if self.consume('?') {
@@ -567,6 +571,12 @@ impl<'a> Parser<'a> {
             spec.ty = "?";
         } else {
             spec.ty = self.word();
+            let ty_span_end = self.cur.peek().map(|(pos, _)| *pos);
+            if !spec.ty.is_empty() {
+                spec.ty_span = ty_span_start
+                    .and_then(|s| ty_span_end.map(|e| (s, e)))
+                    .map(|(start, end)| self.to_span_index(start).to(self.to_span_index(end)));
+            }
         }
         spec
     }
diff --git a/src/libfmt_macros/tests.rs b/src/libfmt_macros/tests.rs
index e2ddb8810e9..81359033eda 100644
--- a/src/libfmt_macros/tests.rs
+++ b/src/libfmt_macros/tests.rs
@@ -2,7 +2,7 @@ use super::*;
 
 fn same(fmt: &'static str, p: &[Piece<'static>]) {
     let parser = Parser::new(fmt, None, vec![], false);
-    assert!(parser.collect::<Vec<Piece<'static>>>() == p);
+    assert_eq!(parser.collect::<Vec<Piece<'static>>>(), p);
 }
 
 fn fmtdflt() -> FormatSpec<'static> {
@@ -15,6 +15,7 @@ fn fmtdflt() -> FormatSpec<'static> {
         precision_span: None,
         width_span: None,
         ty: "",
+        ty_span: None,
     };
 }
 
@@ -82,7 +83,7 @@ fn format_position_nothing_else() {
 #[test]
 fn format_type() {
     same(
-        "{3:a}",
+        "{3:x}",
         &[NextArgument(Argument {
             position: ArgumentIs(3),
             format: FormatSpec {
@@ -93,7 +94,8 @@ fn format_type() {
                 width: CountImplied,
                 precision_span: None,
                 width_span: None,
-                ty: "a",
+                ty: "x",
+                ty_span: None,
             },
         })]);
 }
@@ -112,6 +114,7 @@ fn format_align_fill() {
                 precision_span: None,
                 width_span: None,
                 ty: "",
+                ty_span: None,
             },
         })]);
     same(
@@ -127,6 +130,7 @@ fn format_align_fill() {
                 precision_span: None,
                 width_span: None,
                 ty: "",
+                ty_span: None,
             },
         })]);
     same(
@@ -142,6 +146,7 @@ fn format_align_fill() {
                 precision_span: None,
                 width_span: None,
                 ty: "abcd",
+                ty_span: Some(InnerSpan::new(6, 10)),
             },
         })]);
 }
@@ -150,7 +155,7 @@ fn format_counts() {
     use syntax_pos::{GLOBALS, Globals, edition};
     GLOBALS.set(&Globals::new(edition::DEFAULT_EDITION), || {
     same(
-        "{:10s}",
+        "{:10x}",
         &[NextArgument(Argument {
             position: ArgumentImplicitlyIs(0),
             format: FormatSpec {
@@ -161,11 +166,12 @@ fn format_counts() {
                 width: CountIs(10),
                 precision_span: None,
                 width_span: None,
-                ty: "s",
+                ty: "x",
+                ty_span: None,
             },
         })]);
     same(
-        "{:10$.10s}",
+        "{:10$.10x}",
         &[NextArgument(Argument {
             position: ArgumentImplicitlyIs(0),
             format: FormatSpec {
@@ -176,11 +182,12 @@ fn format_counts() {
                 width: CountIsParam(10),
                 precision_span: None,
                 width_span: Some(InnerSpan::new(3, 6)),
-                ty: "s",
+                ty: "x",
+                ty_span: None,
             },
         })]);
     same(
-        "{:.*s}",
+        "{:.*x}",
         &[NextArgument(Argument {
             position: ArgumentImplicitlyIs(1),
             format: FormatSpec {
@@ -191,11 +198,12 @@ fn format_counts() {
                 width: CountImplied,
                 precision_span: Some(InnerSpan::new(3, 5)),
                 width_span: None,
-                ty: "s",
+                ty: "x",
+                ty_span: None,
             },
         })]);
     same(
-        "{:.10$s}",
+        "{:.10$x}",
         &[NextArgument(Argument {
             position: ArgumentImplicitlyIs(0),
             format: FormatSpec {
@@ -206,11 +214,12 @@ fn format_counts() {
                 width: CountImplied,
                 precision_span: Some(InnerSpan::new(3, 7)),
                 width_span: None,
-                ty: "s",
+                ty: "x",
+                ty_span: None,
             },
         })]);
     same(
-        "{:a$.b$s}",
+        "{:a$.b$?}",
         &[NextArgument(Argument {
             position: ArgumentImplicitlyIs(0),
             format: FormatSpec {
@@ -221,7 +230,8 @@ fn format_counts() {
                 width: CountIsName(Symbol::intern("a")),
                 precision_span: None,
                 width_span: None,
-                ty: "s",
+                ty: "?",
+                ty_span: None,
             },
         })]);
     });
@@ -241,6 +251,7 @@ fn format_flags() {
                 precision_span: None,
                 width_span: None,
                 ty: "",
+                ty_span: None,
             },
         })]);
     same(
@@ -256,13 +267,14 @@ fn format_flags() {
                 precision_span: None,
                 width_span: None,
                 ty: "",
+                ty_span: None,
             },
         })]);
 }
 #[test]
 fn format_mixture() {
     same(
-        "abcd {3:a} efg",
+        "abcd {3:x} efg",
         &[
             String("abcd "),
             NextArgument(Argument {
@@ -275,7 +287,8 @@ fn format_mixture() {
                     width: CountImplied,
                     precision_span: None,
                     width_span: None,
-                    ty: "a",
+                    ty: "x",
+                    ty_span: None,
                 },
             }),
             String(" efg"),
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index 12ae2c32015..bd7b77b0abb 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -93,7 +93,7 @@ rustc_queries! {
         /// Maps DefId's that have an associated `mir::Body` to the result
         /// of the MIR qualify_consts pass. The actual meaning of
         /// the value isn't known except to the pass itself.
-        query mir_const_qualif(key: DefId) -> (u8, &'tcx BitSet<mir::Local>) {
+        query mir_const_qualif(key: DefId) -> u8 {
             desc { |tcx| "const checking `{}`", tcx.def_path_str(key) }
             cache_on_disk_if { key.is_local() }
         }
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 54c01034221..23c4ec062ea 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -2287,11 +2287,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     );
                 }
             }
-            ObligationCauseCode::AssocTypeBound(impl_span, orig) => {
-                err.span_label(orig, "associated type defined here");
-                if let Some(sp) = impl_span {
+            ObligationCauseCode::AssocTypeBound(ref data) => {
+                err.span_label(data.original, "associated type defined here");
+                if let Some(sp) = data.impl_span {
                     err.span_label(sp, "in this `impl` item");
                 }
+                for sp in &data.bounds {
+                    err.span_label(*sp, "restricted in this bound");
+                }
             }
         }
     }
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index b8275299562..a29d8c66d81 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -276,7 +276,14 @@ pub enum ObligationCauseCode<'tcx> {
     /// #[feature(trivial_bounds)] is not enabled
     TrivialBound,
 
-    AssocTypeBound(/*impl*/ Option<Span>, /*original*/ Span),
+    AssocTypeBound(Box<AssocTypeBoundData>),
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct AssocTypeBoundData {
+    pub impl_span: Option<Span>,
+    pub original: Span,
+    pub bounds: Vec<Span>,
 }
 
 // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index 109e884f8bd..59f2bb37548 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -549,7 +549,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
             super::MethodReceiver => Some(super::MethodReceiver),
             super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)),
             super::TrivialBound => Some(super::TrivialBound),
-            super::AssocTypeBound(impl_sp, sp) => Some(super::AssocTypeBound(impl_sp, sp)),
+            super::AssocTypeBound(ref data) => Some(super::AssocTypeBound(data.clone())),
         }
     }
 }
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index 9b15ad560b5..0615004125b 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -42,7 +42,6 @@ use crate::util::common::ErrorReported;
 use crate::util::profiling::ProfileCategory::*;
 
 use rustc_data_structures::svh::Svh;
-use rustc_index::bit_set::BitSet;
 use rustc_index::vec::IndexVec;
 use rustc_data_structures::fx::{FxIndexMap, FxHashMap, FxHashSet};
 use rustc_data_structures::stable_hasher::StableVec;
diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs
index 4ea01bf9647..f9e7a8030a6 100644
--- a/src/librustc/ty/wf.rs
+++ b/src/librustc/ty/wf.rs
@@ -2,9 +2,10 @@ use crate::hir;
 use crate::hir::def_id::DefId;
 use crate::infer::InferCtxt;
 use crate::ty::subst::SubstsRef;
-use crate::traits;
+use crate::traits::{self, AssocTypeBoundData};
 use crate::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use std::iter::once;
+use syntax::symbol::{kw, Ident};
 use syntax_pos::Span;
 use crate::middle::lang_items;
 use crate::mir::interpret::ConstValue;
@@ -176,6 +177,23 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
             pred: &ty::Predicate<'_>,
             trait_assoc_items: ty::AssocItemsIterator<'_>,
         | {
+            let trait_item = tcx.hir().as_local_hir_id(trait_ref.def_id).and_then(|trait_id| {
+                tcx.hir().find(trait_id)
+            });
+            let (trait_name, trait_generics) = match trait_item {
+                Some(hir::Node::Item(hir::Item {
+                    ident,
+                    kind: hir::ItemKind::Trait(.., generics, _, _),
+                    ..
+                })) |
+                Some(hir::Node::Item(hir::Item {
+                    ident,
+                    kind: hir::ItemKind::TraitAlias(generics, _),
+                    ..
+                })) => (Some(ident), Some(generics)),
+                _ => (None, None),
+            };
+
             let item_span = item.map(|i| tcx.sess.source_map().def_span(i.span));
             match pred {
                 ty::Predicate::Projection(proj) => {
@@ -226,10 +244,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                             item.ident == trait_assoc_item.ident
                         }).next() {
                             cause.span = impl_item.span;
-                            cause.code = traits::AssocTypeBound(
-                                item_span,
-                                trait_assoc_item.ident.span,
-                            );
+                            cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
+                                impl_span: item_span,
+                                original: trait_assoc_item.ident.span,
+                                bounds: vec![],
+                            }));
                         }
                     }
                 }
@@ -251,14 +270,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                     //   LL |     type Assoc = bool;
                     //      |     ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
                     //
-                    // FIXME: if the obligation comes from the where clause in the `trait`, we
-                    // should point at it:
+                    // If the obligation comes from the where clause in the `trait`, we point at it:
                     //
                     //   error[E0277]: the trait bound `bool: Bar` is not satisfied
                     //     --> $DIR/point-at-type-on-obligation-failure-2.rs:8:5
                     //      |
                     //      | trait Foo where <Self as Foo>>::Assoc: Bar {
-                    //      |                 -------------------------- obligation set here
+                    //      |                 -------------------------- restricted in this bound
                     //   LL |     type Assoc;
                     //      |          ----- associated type defined here
                     //   ...
@@ -278,11 +296,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                                 .next()
                                 .map(|impl_item| (impl_item, trait_assoc_item)))
                         {
+                            let bounds = trait_generics.map(|generics| get_generic_bound_spans(
+                                &generics,
+                                trait_name,
+                                trait_assoc_item.ident,
+                            )).unwrap_or_else(Vec::new);
                             cause.span = impl_item.span;
-                            cause.code = traits::AssocTypeBound(
-                                item_span,
-                                trait_assoc_item.ident.span,
-                            );
+                            cause.code = traits::AssocTypeBound(Box::new(AssocTypeBoundData {
+                                impl_span: item_span,
+                                original: trait_assoc_item.ident.span,
+                                bounds,
+                            }));
                         }
                     }
                 }
@@ -666,3 +690,56 @@ pub fn object_region_bounds<'tcx>(
 
     tcx.required_region_bounds(open_ty, predicates)
 }
+
+/// Find the span of a generic bound affecting an associated type.
+fn get_generic_bound_spans(
+    generics: &hir::Generics,
+    trait_name: Option<&Ident>,
+    assoc_item_name: Ident,
+) -> Vec<Span> {
+    let mut bounds = vec![];
+    for clause in generics.where_clause.predicates.iter() {
+        if let hir::WherePredicate::BoundPredicate(pred) = clause {
+            match &pred.bounded_ty.kind {
+                hir::TyKind::Path(hir::QPath::Resolved(Some(ty), path)) => {
+                    let mut s = path.segments.iter();
+                    if let (a, Some(b), None) = (s.next(), s.next(), s.next()) {
+                        if a.map(|s| &s.ident) == trait_name
+                            && b.ident == assoc_item_name
+                            && is_self_path(&ty.kind)
+                        {
+                            // `<Self as Foo>::Bar`
+                            bounds.push(pred.span);
+                        }
+                    }
+                }
+                hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => {
+                    if segment.ident == assoc_item_name {
+                        if is_self_path(&ty.kind) {
+                            // `Self::Bar`
+                            bounds.push(pred.span);
+                        }
+                    }
+                }
+                _ => {}
+            }
+        }
+    }
+    bounds
+}
+
+fn is_self_path(kind: &hir::TyKind) -> bool {
+    match kind {
+        hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
+            let mut s = path.segments.iter();
+            if let (Some(segment), None) = (s.next(), s.next()) {
+                if segment.ident.name == kw::SelfUpper {
+                    // `type(Self)`
+                    return true;
+                }
+            }
+        }
+        _ => {}
+    }
+    false
+}
diff --git a/src/librustc_driver/args.rs b/src/librustc_driver/args.rs
index 0906d358bad..339a10f9140 100644
--- a/src/librustc_driver/args.rs
+++ b/src/librustc_driver/args.rs
@@ -3,22 +3,12 @@ use std::fmt;
 use std::fs;
 use std::io;
 use std::str;
-use std::sync::atomic::{AtomicBool, Ordering};
-
-static USED_ARGSFILE_FEATURE: AtomicBool = AtomicBool::new(false);
-
-pub fn used_unstable_argsfile() -> bool {
-    USED_ARGSFILE_FEATURE.load(Ordering::Relaxed)
-}
 
 pub fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
     if arg.starts_with("@") {
         let path = &arg[1..];
         let file = match fs::read_to_string(path) {
-            Ok(file) => {
-                USED_ARGSFILE_FEATURE.store(true, Ordering::Relaxed);
-                file
-            }
+            Ok(file) => file,
             Err(ref err) if err.kind() == io::ErrorKind::InvalidData => {
                 return Err(Error::Utf8Error(Some(path.to_string())));
             }
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 611b891d99a..47acf387f35 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -1044,12 +1044,6 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
     //   (unstable option being used on stable)
     nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
 
-    // Late check to see if @file was used without unstable options enabled
-    if crate::args::used_unstable_argsfile() && !nightly_options::is_unstable_enabled(&matches) {
-        early_error(ErrorOutputType::default(),
-            "@path is unstable - use -Z unstable-options to enable its use");
-    }
-
     if matches.opt_present("h") || matches.opt_present("help") {
         // Only show unstable options in --help if we accept unstable options.
         usage(matches.opt_present("verbose"), nightly_options::is_unstable_enabled(&matches));
diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
index 6eacfc28de2..1118162bdeb 100644
--- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
+++ b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
@@ -32,7 +32,6 @@ use syntax::parse::parser::emit_unclosed_delims;
 use syntax::source_map::Spanned;
 use syntax::symbol::Symbol;
 use syntax_pos::{Span, FileName};
-use rustc_index::bit_set::BitSet;
 
 macro_rules! provide {
     (<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
@@ -122,9 +121,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
     }
     optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
     promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
-    mir_const_qualif => {
-        (cdata.mir_const_qualif(def_id.index), tcx.arena.alloc(BitSet::new_empty(0)))
-    }
+    mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
     fn_sig => { cdata.fn_sig(def_id.index, tcx) }
     inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
     is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }
diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs
index c6677ea3534..ad1ab16a410 100644
--- a/src/librustc_metadata/rmeta/encoder.rs
+++ b/src/librustc_metadata/rmeta/encoder.rs
@@ -955,7 +955,7 @@ impl EncodeContext<'tcx> {
         record!(self.per_def.kind[def_id] <- match impl_item.kind {
             ty::AssocKind::Const => {
                 if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind {
-                    let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id).0;
+                    let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id);
 
                     EntryKind::AssocConst(container,
                         ConstQualif { mir },
@@ -1089,7 +1089,7 @@ impl EncodeContext<'tcx> {
             hir::ItemKind::Static(_, hir::MutMutable, _) => EntryKind::MutStatic,
             hir::ItemKind::Static(_, hir::MutImmutable, _) => EntryKind::ImmStatic,
             hir::ItemKind::Const(_, body_id) => {
-                let mir = self.tcx.at(item.span).mir_const_qualif(def_id).0;
+                let mir = self.tcx.at(item.span).mir_const_qualif(def_id);
                 EntryKind::Const(
                     ConstQualif { mir },
                     self.encode_rendered_const_for_body(body_id)
@@ -1368,7 +1368,7 @@ impl EncodeContext<'tcx> {
         let id = self.tcx.hir().as_local_hir_id(def_id).unwrap();
         let body_id = self.tcx.hir().body_owned_by(id);
         let const_data = self.encode_rendered_const_for_body(body_id);
-        let mir = self.tcx.mir_const_qualif(def_id).0;
+        let mir = self.tcx.mir_const_qualif(def_id);
 
         record!(self.per_def.kind[def_id] <- EntryKind::Const(ConstQualif { mir }, const_data));
         record!(self.per_def.visibility[def_id] <- ty::Visibility::Public);
diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs
index 840ad303016..496a5679067 100644
--- a/src/librustc_mir/transform/check_consts/qualifs.rs
+++ b/src/librustc_mir/transform/check_consts/qualifs.rs
@@ -123,7 +123,7 @@ pub trait Qualif {
                     if cx.tcx.trait_of_item(def_id).is_some() {
                         Self::in_any_value_of_ty(cx, constant.literal.ty)
                     } else {
-                        let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
+                        let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
 
                         let qualif = QualifSet(bits).contains::<Self>();
 
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index dbe6c784592..6d6d6bea2a0 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -210,13 +210,14 @@ fn mir_validated(
     }
 
     let mut body = tcx.mir_const(def_id).steal();
-    let qualify_and_promote_pass = qualify_consts::QualifyAndPromoteConstants::default();
+    let promote_pass = promote_consts::PromoteTemps::default();
     run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[
         // What we need to run borrowck etc.
-        &qualify_and_promote_pass,
+        &qualify_consts::QualifyAndPromoteConstants::default(),
+        &promote_pass,
         &simplify::SimplifyCfg::new("qualify-consts"),
     ]);
-    let promoted = qualify_and_promote_pass.promoted.into_inner();
+    let promoted = promote_pass.promoted_fragments.into_inner();
     (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
 }
 
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index f5e49e32834..48a58f1d0ee 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -17,7 +17,7 @@ use rustc::mir::*;
 use rustc::mir::interpret::ConstValue;
 use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor};
 use rustc::mir::traversal::ReversePostorder;
-use rustc::ty::{self, List, TyCtxt};
+use rustc::ty::{self, List, TyCtxt, TypeFoldable};
 use rustc::ty::subst::InternalSubsts;
 use rustc::ty::cast::CastTy;
 use syntax::ast::LitKind;
@@ -25,12 +25,68 @@ use syntax::symbol::sym;
 use syntax_pos::{Span, DUMMY_SP};
 
 use rustc_index::vec::{IndexVec, Idx};
+use rustc_index::bit_set::HybridBitSet;
 use rustc_target::spec::abi::Abi;
 
+use std::cell::Cell;
 use std::{iter, mem, usize};
 
+use crate::transform::{MirPass, MirSource};
 use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn};
 
+/// A `MirPass` for promotion.
+///
+/// In this case, "promotion" entails the following:
+/// - Extract promotable temps in `fn` and `const fn` into their own MIR bodies.
+/// - Extend lifetimes in `const` and `static` by removing `Drop` and `StorageDead`.
+/// - Emit errors if the requirements of `#[rustc_args_required_const]` are not met.
+///
+/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
+/// newly created `StaticKind::Promoted`.
+#[derive(Default)]
+pub struct PromoteTemps<'tcx> {
+    pub promoted_fragments: Cell<IndexVec<Promoted, Body<'tcx>>>,
+}
+
+impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+        // There's not really any point in promoting errorful MIR.
+        //
+        // This does not include MIR that failed const-checking, which we still try to promote.
+        if body.return_ty().references_error() {
+            tcx.sess.delay_span_bug(body.span, "PromoteTemps: MIR had errors");
+            return;
+        }
+
+        if src.promoted.is_some() {
+            return;
+        }
+
+        let def_id = src.def_id();
+
+        let item = Item::new(tcx, def_id, body);
+        let mut rpo = traversal::reverse_postorder(body);
+        let (temps, all_candidates) = collect_temps_and_candidates(tcx, body, &mut rpo);
+
+        let promotable_candidates = validate_candidates(tcx, body, def_id, &temps, &all_candidates);
+
+        // For now, lifetime extension is done in `const` and `static`s without creating promoted
+        // MIR fragments by removing `Drop` and `StorageDead` for each referent. However, this will
+        // not work inside loops when they are allowed in `const`s.
+        //
+        // FIXME: use promoted MIR fragments everywhere?
+        let promoted_fragments = if should_create_promoted_mir_fragments(item.const_kind) {
+            promote_candidates(def_id, body, tcx, temps, promotable_candidates)
+        } else {
+            // FIXME: promote const array initializers in consts.
+            remove_drop_and_storage_dead_on_promoted_locals(tcx, body, &promotable_candidates);
+            IndexVec::new()
+        };
+
+        self.promoted_fragments.set(promoted_fragments);
+    }
+}
+
 /// State of a temporary during collection and promotion.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum TempState {
@@ -538,7 +594,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                         // is gone - we can always promote constants even if they
                         // fail to pass const-checking, as compilation would've
                         // errored independently and promotion can't change that.
-                        let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id);
+                        let bits = self.tcx.at(constant.span).mir_const_qualif(def_id);
                         if bits == super::qualify_consts::QUALIF_ERROR_BIT {
                             self.tcx.sess.delay_span_bug(
                                 constant.span,
@@ -1154,3 +1210,83 @@ crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
             should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag);
     should_promote && !feature_flag
 }
+
+fn should_create_promoted_mir_fragments(const_kind: Option<ConstKind>) -> bool {
+    match const_kind {
+        Some(ConstKind::ConstFn) | None => true,
+        Some(ConstKind::Const) | Some(ConstKind::Static) | Some(ConstKind::StaticMut) => false,
+    }
+}
+
+/// In `const` and `static` everything without `StorageDead`
+/// is `'static`, we don't have to create promoted MIR fragments,
+/// just remove `Drop` and `StorageDead` on "promoted" locals.
+fn remove_drop_and_storage_dead_on_promoted_locals(
+    tcx: TyCtxt<'tcx>,
+    body: &mut Body<'tcx>,
+    promotable_candidates: &[Candidate],
+) {
+    debug!("run_pass: promotable_candidates={:?}", promotable_candidates);
+
+    // Removing `StorageDead` will cause errors for temps declared inside a loop body. For now we
+    // simply skip promotion if a loop exists, since loops are not yet allowed in a `const`.
+    //
+    // FIXME: Just create MIR fragments for `const`s instead of using this hackish approach?
+    if body.is_cfg_cyclic() {
+        tcx.sess.delay_span_bug(body.span, "Control-flow cycle detected in `const`");
+        return;
+    }
+
+    // The underlying local for promotion contexts like `&temp` and `&(temp.proj)`.
+    let mut requires_lifetime_extension = HybridBitSet::new_empty(body.local_decls.len());
+
+    promotable_candidates
+        .iter()
+        .filter_map(|c| {
+            match c {
+                Candidate::Ref(loc) => Some(loc),
+                Candidate::Repeat(_) | Candidate::Argument { .. } => None,
+            }
+        })
+        .map(|&Location { block, statement_index }| {
+            // FIXME: store the `Local` for each `Candidate` when it is created.
+            let place = match &body[block].statements[statement_index].kind {
+                StatementKind::Assign(box ( _, Rvalue::Ref(_, _, place))) => place,
+                _ => bug!("`Candidate::Ref` without corresponding assignment"),
+            };
+
+            match place.base {
+                PlaceBase::Local(local) => local,
+                PlaceBase::Static(_) => bug!("`Candidate::Ref` for a non-local"),
+            }
+        })
+        .for_each(|local| {
+            requires_lifetime_extension.insert(local);
+        });
+
+    // Remove `Drop` terminators and `StorageDead` statements for all promotable temps that require
+    // lifetime extension.
+    for block in body.basic_blocks_mut() {
+        block.statements.retain(|statement| {
+            match statement.kind {
+                StatementKind::StorageDead(index) => !requires_lifetime_extension.contains(index),
+                _ => true
+            }
+        });
+        let terminator = block.terminator_mut();
+        match &terminator.kind {
+            TerminatorKind::Drop {
+                location,
+                target,
+                ..
+            } => {
+                if let Some(index) = location.as_local() {
+                    if requires_lifetime_extension.contains(index) {
+                        terminator.kind = TerminatorKind::Goto { target: *target };
+                    }
+                }
+            }
+            _ => {}
+        }
+    }
+}
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 6a7058f1930..5ad5363768d 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -15,7 +15,6 @@ use rustc::ty::cast::CastTy;
 use rustc::ty::query::Providers;
 use rustc::mir::*;
 use rustc::mir::interpret::ConstValue;
-use rustc::mir::traversal::ReversePostorder;
 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
 use rustc::middle::lang_items;
 use rustc::session::config::nightly_options;
@@ -31,7 +30,6 @@ use std::usize;
 
 use rustc::hir::HirId;
 use crate::transform::{MirPass, MirSource};
-use super::promote_consts::{self, Candidate, TempState};
 use crate::transform::check_consts::ops::{self, NonConstOp};
 
 /// What kind of item we are in.
@@ -258,7 +256,7 @@ trait Qualif {
                     if cx.tcx.trait_of_item(def_id).is_some() {
                         Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
                     } else {
-                        let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
+                        let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
 
                         let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
 
@@ -477,10 +475,6 @@ struct Checker<'a, 'tcx> {
 
     span: Span,
     def_id: DefId,
-    rpo: ReversePostorder<'a, 'tcx>,
-
-    temp_promotion_state: IndexVec<Local, TempState>,
-    unchecked_promotion_candidates: Vec<Candidate>,
 
     /// If `true`, do not emit errors to the user, merely collect them in `errors`.
     suppress_errors: bool,
@@ -509,10 +503,6 @@ impl Deref for Checker<'a, 'tcx> {
 impl<'a, 'tcx> Checker<'a, 'tcx> {
     fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
         assert!(def_id.is_local());
-        let mut rpo = traversal::reverse_postorder(body);
-        let (temps, unchecked_promotion_candidates) =
-            promote_consts::collect_temps_and_candidates(tcx, body, &mut rpo);
-        rpo.reset();
 
         let param_env = tcx.param_env(def_id);
 
@@ -539,9 +529,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
             cx,
             span: body.span,
             def_id,
-            rpo,
-            temp_promotion_state: temps,
-            unchecked_promotion_candidates,
             errors: vec![],
             suppress_errors: false,
         }
@@ -662,14 +649,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
         let kind = self.body.local_kind(index);
         debug!("store to {:?} {:?}", kind, index);
 
-        // Only handle promotable temps in non-const functions.
-        if self.mode == Mode::NonConstFn {
-            if kind != LocalKind::Temp ||
-               !self.temp_promotion_state[index].is_promotable() {
-                return;
-            }
-        }
-
         // this is overly restrictive, because even full assignments do not clear the qualif
         // While we could special case full assignments, this would be inconsistent with
         // aggregates where we overwrite all fields via assignments, which would not get
@@ -682,7 +661,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
     }
 
     /// Check a whole const, static initializer or const fn.
-    fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) {
+    fn check_const(&mut self) -> u8 {
         use crate::transform::check_consts as new_checker;
 
         debug!("const-checking {} {:?}", self.mode, self.def_id);
@@ -704,7 +683,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
 
         let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
         let mut bb = START_BLOCK;
-        let mut has_controlflow_error = false;
         loop {
             seen_blocks.insert(bb.index());
 
@@ -745,7 +723,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                     bb = target;
                 }
                 _ => {
-                    has_controlflow_error = true;
                     self.not_const(ops::Loop);
                     validator.check_op(ops::Loop);
                     break;
@@ -772,51 +749,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
             }
         }
 
-        // Collect all the temps we need to promote.
-        let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
-
-        // HACK: if parts of the control-flow graph were skipped due to an error, don't try to
-        // promote anything, since that can cause errors in a `const` if e.g. rvalue static
-        // promotion is attempted within a loop body.
-        let unleash_miri = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
-        let promotion_candidates = if has_controlflow_error && !unleash_miri {
-            self.tcx.sess.delay_span_bug(
-                body.span,
-                "check_const: expected control-flow error(s)",
-            );
-
-            vec![]
-        } else {
-            promote_consts::validate_candidates(
-                self.tcx,
-                self.body,
-                self.def_id,
-                &self.temp_promotion_state,
-                &self.unchecked_promotion_candidates,
-            )
-        };
-
-        debug!("qualify_const: promotion_candidates={:?}", promotion_candidates);
-        for candidate in promotion_candidates {
-            match candidate {
-                Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
-                    if let StatementKind::Assign(box( _, Rvalue::Ref(_, _, place)))
-                        = &self.body[bb].statements[stmt_idx].kind
-                    {
-                        if let PlaceBase::Local(local) = place.base {
-                            promoted_temps.insert(local);
-                        }
-                    }
-                }
-
-                // Only rvalue-static promotion requires extending the lifetime of the promoted
-                // local.
-                Candidate::Argument { .. } | Candidate::Repeat(_) => {}
-            }
-        }
-
-        let qualifs = self.qualifs_in_local(RETURN_PLACE);
-        (qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
+        self.qualifs_in_local(RETURN_PLACE).encode_to_bits()
     }
 }
 
@@ -1346,7 +1279,7 @@ pub fn provide(providers: &mut Providers<'_>) {
 // in `promote_consts`, see the comment in `validate_operand`.
 pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2;
 
-fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
+fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
     // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
     // cannot yet be stolen), because `mir_validated()`, which steals
     // from `mir_const(), forces this query to execute before
@@ -1355,7 +1288,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
 
     if body.return_ty().references_error() {
         tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
-        return (QUALIF_ERROR_BIT, tcx.arena.alloc(BitSet::new_empty(0)));
+        return QUALIF_ERROR_BIT;
     }
 
     Checker::new(tcx, def_id, body, Mode::Const).check_const()
@@ -1391,56 +1324,34 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
         let mode = determine_mode(tcx, hir_id, def_id);
 
         debug!("run_pass: mode={:?}", mode);
-        if let Mode::NonConstFn | Mode::ConstFn = mode {
+        if let Mode::NonConstFn = mode {
+            // No need to const-check a non-const `fn` now that we don't do promotion here.
+            return;
+        } else if let Mode::ConstFn = mode {
             let mut checker = Checker::new(tcx, def_id, body, mode);
-            if let Mode::ConstFn = mode {
-                let use_min_const_fn_checks =
-                    !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
-                    tcx.is_min_const_fn(def_id);
-                if use_min_const_fn_checks {
-                    // Enforce `min_const_fn` for stable `const fn`s.
-                    use super::qualify_min_const_fn::is_min_const_fn;
-                    if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
-                        error_min_const_fn_violation(tcx, span, err);
-                        return;
-                    }
-
-                    // `check_const` should not produce any errors, but better safe than sorry
-                    // FIXME(#53819)
-                    // NOTE(eddyb) `check_const` is actually needed for promotion inside
-                    // `min_const_fn` functions.
-                }
-
-                // Enforce a constant-like CFG for `const fn`.
-                checker.check_const();
-            } else {
-                while let Some((bb, data)) = checker.rpo.next() {
-                    checker.visit_basic_block_data(bb, data);
+            let use_min_const_fn_checks =
+                !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
+                tcx.is_min_const_fn(def_id);
+            if use_min_const_fn_checks {
+                // Enforce `min_const_fn` for stable `const fn`s.
+                use super::qualify_min_const_fn::is_min_const_fn;
+                if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
+                    error_min_const_fn_violation(tcx, span, err);
+                    return;
                 }
             }
 
-            // Promote only the promotable candidates.
-            let temps = checker.temp_promotion_state;
-            let candidates = promote_consts::validate_candidates(
-                tcx,
-                body,
-                def_id,
-                &temps,
-                &checker.unchecked_promotion_candidates,
-            );
-
-            // Do the actual promotion, now that we know what's viable.
-            self.promoted.set(
-                promote_consts::promote_candidates(def_id, body, tcx, temps, candidates)
-            );
+            // `check_const` should not produce any errors, but better safe than sorry
+            // FIXME(#53819)
+            // Enforce a constant-like CFG for `const fn`.
+            checker.check_const();
         } else {
             check_short_circuiting_in_const_local(tcx, body, mode);
 
-            let promoted_temps = match mode {
-                Mode::Const => tcx.mir_const_qualif(def_id).1,
-                _ => Checker::new(tcx, def_id, body, mode).check_const().1,
+            match mode {
+                Mode::Const => tcx.mir_const_qualif(def_id),
+                _ => Checker::new(tcx, def_id, body, mode).check_const(),
             };
-            remove_drop_and_storage_dead_on_promoted_locals(body, promoted_temps);
         }
 
         if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
@@ -1501,40 +1412,6 @@ fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>,
     }
 }
 
-/// In `const` and `static` everything without `StorageDead`
-/// is `'static`, we don't have to create promoted MIR fragments,
-/// just remove `Drop` and `StorageDead` on "promoted" locals.
-fn remove_drop_and_storage_dead_on_promoted_locals(
-    body: &mut Body<'tcx>,
-    promoted_temps: &BitSet<Local>,
-) {
-    debug!("run_pass: promoted_temps={:?}", promoted_temps);
-
-    for block in body.basic_blocks_mut() {
-        block.statements.retain(|statement| {
-            match statement.kind {
-                StatementKind::StorageDead(index) => !promoted_temps.contains(index),
-                _ => true
-            }
-        });
-        let terminator = block.terminator_mut();
-        match &terminator.kind {
-            TerminatorKind::Drop {
-                location,
-                target,
-                ..
-            } => {
-                if let Some(index) = location.as_local() {
-                    if promoted_temps.contains(index) {
-                        terminator.kind = TerminatorKind::Goto { target: *target };
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-}
-
 fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
     let ty = body.return_ty();
     tcx.infer_ctxt().enter(|infcx| {
diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs
index 6029b00aa31..aced12aa32a 100644
--- a/src/librustc_target/abi/call/mod.rs
+++ b/src/librustc_target/abi/call/mod.rs
@@ -554,7 +554,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
             "arm" => arm::compute_abi_info(cx, self),
             "mips" => mips::compute_abi_info(cx, self),
             "mips64" => mips64::compute_abi_info(cx, self),
-            "powerpc" => powerpc::compute_abi_info(cx, self),
+            "powerpc" => powerpc::compute_abi_info(self),
             "powerpc64" => powerpc64::compute_abi_info(cx, self),
             "s390x" => s390x::compute_abi_info(cx, self),
             "msp430" => msp430::compute_abi_info(self),
diff --git a/src/librustc_target/abi/call/powerpc.rs b/src/librustc_target/abi/call/powerpc.rs
index b2c8d26ff1f..740bd7222f2 100644
--- a/src/librustc_target/abi/call/powerpc.rs
+++ b/src/librustc_target/abi/call/powerpc.rs
@@ -1,49 +1,28 @@
-use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform};
-use crate::abi::{HasDataLayout, LayoutOf, Size, TyLayoutMethods};
+use crate::abi::call::{ArgAbi, FnAbi};
 
-fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size)
-    where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> + HasDataLayout
-{
-    if !ret.layout.is_aggregate() {
-        ret.extend_integer_width_to(32);
-    } else {
+fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
+    if ret.layout.is_aggregate() {
         ret.make_indirect();
-        *offset += cx.data_layout().pointer_size;
+    } else {
+        ret.extend_integer_width_to(32);
     }
 }
 
-fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size)
-    where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> + HasDataLayout
-{
-    let dl = cx.data_layout();
-    let size = arg.layout.size;
-    let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi;
-
+fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
     if arg.layout.is_aggregate() {
-        arg.cast_to(Uniform {
-            unit: Reg::i32(),
-            total: size
-        });
-        if !offset.is_aligned(align) {
-            arg.pad_with(Reg::i32());
-        }
+        arg.make_indirect();
     } else {
         arg.extend_integer_width_to(32);
     }
-
-    *offset = offset.align_to(align) + size.align_to(align);
 }
 
-pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, Ty>)
-    where Ty: TyLayoutMethods<'a, C>, C: LayoutOf<Ty = Ty> + HasDataLayout
-{
-    let mut offset = Size::ZERO;
+pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
     if !fn_abi.ret.is_ignore() {
-        classify_ret(cx, &mut fn_abi.ret, &mut offset);
+        classify_ret(&mut fn_abi.ret);
     }
 
     for arg in &mut fn_abi.args {
         if arg.is_ignore() { continue; }
-        classify_arg(cx, arg, &mut offset);
+        classify_arg(arg);
     }
 }
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index e25ba7b1783..25daca9237f 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -21,7 +21,7 @@ use std::collections::hash_map::Entry;
 
 #[derive(PartialEq)]
 enum ArgumentType {
-    Placeholder(String),
+    Placeholder(&'static str),
     Count,
 }
 
@@ -244,7 +244,57 @@ impl<'a, 'b> Context<'a, 'b> {
                     parse::ArgumentNamed(s) => Named(s),
                 };
 
-                let ty = Placeholder(arg.format.ty.to_string());
+                let ty = Placeholder(match &arg.format.ty[..] {
+                    "" => "Display",
+                    "?" => "Debug",
+                    "e" => "LowerExp",
+                    "E" => "UpperExp",
+                    "o" => "Octal",
+                    "p" => "Pointer",
+                    "b" => "Binary",
+                    "x" => "LowerHex",
+                    "X" => "UpperHex",
+                    _ => {
+                        let fmtsp = self.fmtsp;
+                        let sp = arg.format.ty_span.map(|sp| fmtsp.from_inner(sp));
+                        let mut err = self.ecx.struct_span_err(
+                            sp.unwrap_or(fmtsp),
+                            &format!("unknown format trait `{}`", arg.format.ty),
+                        );
+                        err.note("the only appropriate formatting traits are:\n\
+                                - ``, which uses the `Display` trait\n\
+                                - `?`, which uses the `Debug` trait\n\
+                                - `e`, which uses the `LowerExp` trait\n\
+                                - `E`, which uses the `UpperExp` trait\n\
+                                - `o`, which uses the `Octal` trait\n\
+                                - `p`, which uses the `Pointer` trait\n\
+                                - `b`, which uses the `Binary` trait\n\
+                                - `x`, which uses the `LowerHex` trait\n\
+                                - `X`, which uses the `UpperHex` trait");
+                        if let Some(sp) = sp {
+                            for (fmt, name) in &[
+                                ("", "Display"),
+                                ("?", "Debug"),
+                                ("e", "LowerExp"),
+                                ("E", "UpperExp"),
+                                ("o", "Octal"),
+                                ("p", "Pointer"),
+                                ("b", "Binary"),
+                                ("x", "LowerHex"),
+                                ("X", "UpperHex"),
+                            ] {
+                                err.tool_only_span_suggestion(
+                                    sp,
+                                    &format!("use the `{}` trait", name),
+                                    fmt.to_string(),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                        }
+                        err.emit();
+                        "<invalid>"
+                    }
+                });
                 self.verify_arg_type(pos, ty);
                 self.curpiece += 1;
             }
@@ -590,6 +640,7 @@ impl<'a, 'b> Context<'a, 'b> {
                         width: parse::CountImplied,
                         width_span: None,
                         ty: arg.format.ty,
+                        ty_span: arg.format.ty_span,
                     },
                 };
 
@@ -761,37 +812,8 @@ impl<'a, 'b> Context<'a, 'b> {
         sp = ecx.with_def_site_ctxt(sp);
         let arg = ecx.expr_ident(sp, arg);
         let trait_ = match *ty {
-            Placeholder(ref tyname) => {
-                match &tyname[..] {
-                    "" => "Display",
-                    "?" => "Debug",
-                    "e" => "LowerExp",
-                    "E" => "UpperExp",
-                    "o" => "Octal",
-                    "p" => "Pointer",
-                    "b" => "Binary",
-                    "x" => "LowerHex",
-                    "X" => "UpperHex",
-                    _ => {
-                        let mut err = ecx.struct_span_err(
-                            sp,
-                            &format!("unknown format trait `{}`", *tyname),
-                        );
-                        err.note("the only appropriate formatting traits are:\n\
-                                  - ``, which uses the `Display` trait\n\
-                                  - `?`, which uses the `Debug` trait\n\
-                                  - `e`, which uses the `LowerExp` trait\n\
-                                  - `E`, which uses the `UpperExp` trait\n\
-                                  - `o`, which uses the `Octal` trait\n\
-                                  - `p`, which uses the `Pointer` trait\n\
-                                  - `b`, which uses the `Binary` trait\n\
-                                  - `x`, which uses the `LowerHex` trait\n\
-                                  - `X`, which uses the `UpperHex` trait");
-                        err.emit();
-                        return DummyResult::raw_expr(sp, true);
-                    }
-                }
-            }
+            Placeholder(trait_) if trait_ == "<invalid>" => return DummyResult::raw_expr(sp, true),
+            Placeholder(trait_) => trait_,
             Count => {
                 let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, sym::from_usize]);
                 return ecx.expr_call_global(macsp, path, vec![arg]);
diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs
index b275c06e05c..648856b5523 100644
--- a/src/test/mir-opt/match_false_edges.rs
+++ b/src/test/mir-opt/match_false_edges.rs
@@ -39,7 +39,7 @@ fn main() {
 
 // END RUST SOURCE
 //
-// START rustc.full_tested_match.QualifyAndPromoteConstants.after.mir
+// START rustc.full_tested_match.PromoteTemps.after.mir
 //  bb0: {
 //      ...
 //      _2 = std::option::Option::<i32>::Some(const 42i32,);
@@ -108,9 +108,9 @@ fn main() {
 //      _0 = ();
 //      return;
 //  }
-// END rustc.full_tested_match.QualifyAndPromoteConstants.after.mir
+// END rustc.full_tested_match.PromoteTemps.after.mir
 //
-// START rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir
+// START rustc.full_tested_match2.PromoteTemps.before.mir
 //  bb0: {
 //      ...
 //      _2 = std::option::Option::<i32>::Some(const 42i32,);
@@ -179,9 +179,9 @@ fn main() {
 //      _0 = ();
 //      return;
 //  }
-// END rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir
+// END rustc.full_tested_match2.PromoteTemps.before.mir
 //
-// START rustc.main.QualifyAndPromoteConstants.before.mir
+// START rustc.main.PromoteTemps.before.mir
 //  bb0: {
 //     ...
 //      _2 = std::option::Option::<i32>::Some(const 1i32,);
@@ -276,4 +276,4 @@ fn main() {
 //      _0 = ();
 //      return;
 //  }
-// END rustc.main.QualifyAndPromoteConstants.before.mir
+// END rustc.main.PromoteTemps.before.mir
diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs
index 9360d96f05e..67b7c78071c 100644
--- a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs
+++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.rs
@@ -8,4 +8,20 @@ impl Foo for () {
     type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
 }
 
+trait Baz where Self::Assoc: Bar {
+    type Assoc;
+}
+
+impl Baz for () {
+    type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
+}
+
+trait Bat where <Self as Bat>::Assoc: Bar {
+    type Assoc;
+}
+
+impl Bat for () {
+    type Assoc = bool; //~ ERROR the trait bound `bool: Bar` is not satisfied
+}
+
 fn main() {}
diff --git a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr
index f1a2e343a7e..072e9dad062 100644
--- a/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr
+++ b/src/test/ui/associated-types/point-at-type-on-obligation-failure-2.stderr
@@ -9,6 +9,32 @@ LL | impl Foo for () {
 LL |     type Assoc = bool;
    |     ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
 
-error: aborting due to previous error
+error[E0277]: the trait bound `bool: Bar` is not satisfied
+  --> $DIR/point-at-type-on-obligation-failure-2.rs:16:5
+   |
+LL | trait Baz where Self::Assoc: Bar {
+   |                 ---------------- restricted in this bound
+LL |     type Assoc;
+   |          ----- associated type defined here
+...
+LL | impl Baz for () {
+   | --------------- in this `impl` item
+LL |     type Assoc = bool;
+   |     ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
+
+error[E0277]: the trait bound `bool: Bar` is not satisfied
+  --> $DIR/point-at-type-on-obligation-failure-2.rs:24:5
+   |
+LL | trait Bat where <Self as Bat>::Assoc: Bar {
+   |                 ------------------------- restricted in this bound
+LL |     type Assoc;
+   |          ----- associated type defined here
+...
+LL | impl Bat for () {
+   | --------------- in this `impl` item
+LL |     type Assoc = bool;
+   |     ^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `bool`
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn.rs b/src/test/ui/consts/min_const_fn/min_const_fn.rs
index d0f63b148ff..db68a05905a 100644
--- a/src/test/ui/consts/min_const_fn/min_const_fn.rs
+++ b/src/test/ui/consts/min_const_fn/min_const_fn.rs
@@ -136,7 +136,6 @@ const fn no_rpit() -> impl std::fmt::Debug {} //~ ERROR `impl Trait` in const fn
 const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
 const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
 //~^ ERROR trait bounds other than `Sized`
-//~| ERROR cannot return reference to temporary value
 
 const fn no_unsafe() { unsafe {} }
 
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn.stderr b/src/test/ui/consts/min_const_fn/min_const_fn.stderr
index 3158b6284db..64b2ce83da2 100644
--- a/src/test/ui/consts/min_const_fn/min_const_fn.stderr
+++ b/src/test/ui/consts/min_const_fn/min_const_fn.stderr
@@ -286,17 +286,8 @@ LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
    = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
    = help: add `#![feature(const_fn)]` to the crate attributes to enable
 
-error[E0515]: cannot return reference to temporary value
-  --> $DIR/min_const_fn.rs:137:63
-   |
-LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
-   |                                                               ^--
-   |                                                               ||
-   |                                                               |temporary value created here
-   |                                                               returns a reference to data owned by the current function
-
 error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
-  --> $DIR/min_const_fn.rs:143:41
+  --> $DIR/min_const_fn.rs:142:41
    |
 LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 }
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -305,7 +296,7 @@ LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1
    = help: add `#![feature(const_fn)]` to the crate attributes to enable
 
 error[E0723]: function pointers in const fn are unstable
-  --> $DIR/min_const_fn.rs:146:21
+  --> $DIR/min_const_fn.rs:145:21
    |
 LL | const fn no_fn_ptrs(_x: fn()) {}
    |                     ^^
@@ -314,7 +305,7 @@ LL | const fn no_fn_ptrs(_x: fn()) {}
    = help: add `#![feature(const_fn)]` to the crate attributes to enable
 
 error[E0723]: function pointers in const fn are unstable
-  --> $DIR/min_const_fn.rs:148:27
+  --> $DIR/min_const_fn.rs:147:27
    |
 LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
    |                           ^^^^
@@ -322,7 +313,7 @@ LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
    = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
    = help: add `#![feature(const_fn)]` to the crate attributes to enable
 
-error: aborting due to 37 previous errors
+error: aborting due to 36 previous errors
 
-Some errors have detailed explanations: E0493, E0515, E0723.
+Some errors have detailed explanations: E0493, E0723.
 For more information about an error, try `rustc --explain E0493`.
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs b/src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs
index 3833510c0b3..6ca1e59b3af 100644
--- a/src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs
+++ b/src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs
@@ -11,6 +11,5 @@ const fn no_inner_dyn_trait2(x: Hide) {
 }
 const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
 //~^ ERROR trait bounds other than `Sized`
-//~| ERROR temporary value dropped while borrowed
 
 fn main() {}
diff --git a/src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr b/src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr
index 0ea950d678f..e20b4f9dcb4 100644
--- a/src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr
+++ b/src/test/ui/consts/min_const_fn/min_const_fn_dyn.stderr
@@ -16,16 +16,6 @@ LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
    = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
    = help: add `#![feature(const_fn)]` to the crate attributes to enable
 
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/min_const_fn_dyn.rs:12:67
-   |
-LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
-   |                                                                  -^    - temporary value is freed at the end of this statement
-   |                                                                  ||
-   |                                                                  |creates a temporary which is freed while still in use
-   |                                                                  cast requires that borrow lasts for `'static`
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0716, E0723.
-For more information about an error, try `rustc --explain E0716`.
+For more information about this error, try `rustc --explain E0723`.
diff --git a/src/test/ui/if/ifmt-bad-arg.stderr b/src/test/ui/if/ifmt-bad-arg.stderr
index 11dcc3a6d23..d65ffd85060 100644
--- a/src/test/ui/if/ifmt-bad-arg.stderr
+++ b/src/test/ui/if/ifmt-bad-arg.stderr
@@ -257,10 +257,10 @@ LL |     println!("{} {:07$} {}", 1, 3.2, 4);
    = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
 
 error: unknown format trait `foo`
-  --> $DIR/ifmt-bad-arg.rs:86:24
+  --> $DIR/ifmt-bad-arg.rs:86:17
    |
 LL |     println!("{:foo}", 1);
-   |                        ^
+   |                 ^^^
    |
    = note: the only appropriate formatting traits are:
            - ``, which uses the `Display` trait
diff --git a/src/test/ui/if/ifmt-unknown-trait.stderr b/src/test/ui/if/ifmt-unknown-trait.stderr
index 7853b5ca0c9..459432bf4e4 100644
--- a/src/test/ui/if/ifmt-unknown-trait.stderr
+++ b/src/test/ui/if/ifmt-unknown-trait.stderr
@@ -1,8 +1,8 @@
 error: unknown format trait `notimplemented`
-  --> $DIR/ifmt-unknown-trait.rs:2:34
+  --> $DIR/ifmt-unknown-trait.rs:2:16
    |
 LL |     format!("{:notimplemented}", "3");
-   |                                  ^^^
+   |                ^^^^^^^^^^^^^^
    |
    = note: the only appropriate formatting traits are:
            - ``, which uses the `Display` trait