about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_data_structures/src/graph/iterate/mod.rs5
-rw-r--r--compiler/rustc_data_structures/src/profiling.rs11
-rw-r--r--compiler/rustc_hir/src/lang_items.rs1
-rw-r--r--compiler/rustc_macros/src/query.rs72
-rw-r--r--compiler/rustc_middle/src/ty/context.rs8
-rw-r--r--compiler/rustc_middle/src/ty/query/mod.rs70
-rw-r--r--compiler/rustc_middle/src/ty/query/plumbing.rs20
-rw-r--r--compiler/rustc_middle/src/ty/query/stats.rs10
-rw-r--r--compiler/rustc_mir/src/const_eval/machine.rs3
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/mod.rs4
-rw-r--r--compiler/rustc_mir/src/transform/match_branches.rs30
-rw-r--r--compiler/rustc_mir_build/src/lints.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/_match.rs450
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs12
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs7
-rw-r--r--compiler/rustc_query_system/src/query/config.rs2
-rw-r--r--compiler/rustc_resolve/src/macros.rs8
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/codegen.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs85
21 files changed, 306 insertions, 503 deletions
diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
index bc3d1ce53ba..5f42d46e285 100644
--- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs
@@ -1,6 +1,7 @@
 use super::{DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors};
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::IndexVec;
+use std::ops::ControlFlow;
 
 #[cfg(test)]
 mod tests;
@@ -86,10 +87,6 @@ where
     }
 }
 
-/// Allows searches to terminate early with a value.
-// FIXME (#75744): remove the alias once the generics are in a better order and `C=()`.
-pub type ControlFlow<T> = std::ops::ControlFlow<(), T>;
-
 /// The status of a node in the depth-first search.
 ///
 /// See the documentation of `TriColorDepthFirstSearch` to see how a node's status is updated
diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs
index 363879cbb1d..0ae3c75fcc2 100644
--- a/compiler/rustc_data_structures/src/profiling.rs
+++ b/compiler/rustc_data_structures/src/profiling.rs
@@ -111,17 +111,6 @@ cfg_if! {
 
 type Profiler = measureme::Profiler<SerializationSink>;
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
-pub enum ProfileCategory {
-    Parsing,
-    Expansion,
-    TypeChecking,
-    BorrowChecking,
-    Codegen,
-    Linking,
-    Other,
-}
-
 bitflags::bitflags! {
     struct EventFilter: u32 {
         const GENERIC_ACTIVITIES = 1 << 0;
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 5e4c03bec83..3e4eb9eafd7 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -263,6 +263,7 @@ language_item_table! {
     // is required to define it somewhere. Additionally, there are restrictions on crates that use
     // a weak lang item, but do not have it defined.
     Panic,                   sym::panic,               panic_fn,                   Target::Fn;
+    PanicStr,                sym::panic_str,           panic_str,                  Target::Fn;
     PanicBoundsCheck,        sym::panic_bounds_check,  panic_bounds_check_fn,      Target::Fn;
     PanicInfo,               sym::panic_info,          panic_info,                 Target::Struct;
     PanicLocation,           sym::panic_location,      panic_location,             Target::Struct;
diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs
index e7c054653ac..fd859196369 100644
--- a/compiler/rustc_macros/src/query.rs
+++ b/compiler/rustc_macros/src/query.rs
@@ -190,7 +190,11 @@ impl<T: Parse> Parse for List<T> {
 }
 
 /// A named group containing queries.
+///
+/// For now, the name is not used any more, but the capability remains interesting for future
+/// developments of the query system.
 struct Group {
+    #[allow(unused)]
     name: Ident,
     queries: List<Query>,
 }
@@ -417,12 +421,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
     let mut query_stream = quote! {};
     let mut query_description_stream = quote! {};
     let mut dep_node_def_stream = quote! {};
-    let mut dep_node_force_stream = quote! {};
-    let mut try_load_from_on_disk_cache_stream = quote! {};
     let mut cached_queries = quote! {};
 
     for group in groups.0 {
-        let mut group_stream = quote! {};
         for mut query in group.queries.0 {
             let modifiers = process_modifiers(&mut query);
             let name = &query.name;
@@ -437,22 +438,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
                 cached_queries.extend(quote! {
                     #name,
                 });
-
-                try_load_from_on_disk_cache_stream.extend(quote! {
-                    ::rustc_middle::dep_graph::DepKind::#name => {
-                        if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
-                            debug_assert!($tcx.dep_graph
-                                            .node_color($dep_node)
-                                            .map(|c| c.is_green())
-                                            .unwrap_or(false));
-
-                            let key = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
-                            if queries::#name::cache_on_disk($tcx, &key, None) {
-                                let _ = $tcx.#name(key);
-                            }
-                        }
-                    }
-                });
             }
 
             let mut attributes = Vec::new();
@@ -485,9 +470,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
             let attribute_stream = quote! {#(#attributes),*};
             let doc_comments = query.doc_comments.iter();
             // Add the query to the group
-            group_stream.extend(quote! {
+            query_stream.extend(quote! {
                 #(#doc_comments)*
-                [#attribute_stream] fn #name: #name(#arg) #result,
+                [#attribute_stream] fn #name(#arg) #result,
             });
 
             // Create a dep node for the query
@@ -495,37 +480,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
                 [#attribute_stream] #name(#arg),
             });
 
-            // Add a match arm to force the query given the dep node
-            dep_node_force_stream.extend(quote! {
-                ::rustc_middle::dep_graph::DepKind::#name => {
-                    if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
-                        if let Some(key) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
-                            force_query::<crate::ty::query::queries::#name<'_>, _>(
-                                $tcx,
-                                key,
-                                DUMMY_SP,
-                                *$dep_node
-                            );
-                            return true;
-                        }
-                    }
-                }
-            });
-
             add_query_description_impl(&query, modifiers, &mut query_description_stream);
         }
-        let name = &group.name;
-        query_stream.extend(quote! {
-            #name { #group_stream },
-        });
     }
 
-    dep_node_force_stream.extend(quote! {
-        ::rustc_middle::dep_graph::DepKind::Null => {
-            bug!("Cannot force dep node: {:?}", $dep_node)
-        }
-    });
-
     TokenStream::from(quote! {
         macro_rules! rustc_query_append {
             ([$($macro:tt)*][$($other:tt)*]) => {
@@ -546,15 +504,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
                 );
             }
         }
-        macro_rules! rustc_dep_node_force {
-            ([$dep_node:expr, $tcx:expr] $($other:tt)*) => {
-                match $dep_node.kind {
-                    $($other)*
-
-                    #dep_node_force_stream
-                }
-            }
-        }
         macro_rules! rustc_cached_queries {
             ($($macro:tt)*) => {
                 $($macro)*(#cached_queries);
@@ -562,14 +511,5 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
         }
 
         #query_description_stream
-
-        macro_rules! rustc_dep_node_try_load_from_on_disk_cache {
-            ($dep_node:expr, $tcx:expr) => {
-                match $dep_node.kind {
-                    #try_load_from_on_disk_cache_stream
-                    _ => (),
-                }
-            }
-        }
     })
 }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index ab92e5b745b..f6ea6743a0e 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -2036,13 +2036,13 @@ direct_interners! {
 
 macro_rules! slice_interners {
     ($($field:ident: $method:ident($ty:ty)),+ $(,)?) => (
-        $(impl<'tcx> TyCtxt<'tcx> {
-            pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> {
+        impl<'tcx> TyCtxt<'tcx> {
+            $(pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> {
                 self.interners.$field.intern_ref(v, || {
                     Interned(List::from_arena(&*self.arena, v))
                 }).0
-            }
-        })+
+            })+
+        }
     );
 }
 
diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs
index d3a7412ef14..7ba4d5a14df 100644
--- a/compiler/rustc_middle/src/ty/query/mod.rs
+++ b/compiler/rustc_middle/src/ty/query/mod.rs
@@ -34,7 +34,6 @@ use crate::ty::util::AlwaysRequiresDrop;
 use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
-use rustc_data_structures::profiling::ProfileCategory::*;
 use rustc_data_structures::stable_hasher::StableVec;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::Lrc;
@@ -169,26 +168,71 @@ pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool
         return false;
     }
 
-    rustc_dep_node_force!([dep_node, tcx]
-        // These are inputs that are expected to be pre-allocated and that
-        // should therefore always be red or green already.
-        DepKind::CrateMetadata |
+    macro_rules! force_from_dep_node {
+        ($($(#[$attr:meta])* [$($modifiers:tt)*] $name:ident($K:ty),)*) => {
+            match dep_node.kind {
+                // These are inputs that are expected to be pre-allocated and that
+                // should therefore always be red or green already.
+                DepKind::CrateMetadata |
 
-        // These are anonymous nodes.
-        DepKind::TraitSelect |
+                // These are anonymous nodes.
+                DepKind::TraitSelect |
 
-        // We don't have enough information to reconstruct the query key of
-        // these.
-        DepKind::CompileCodegenUnit => {
-            bug!("force_from_dep_node: encountered {:?}", dep_node)
+                // We don't have enough information to reconstruct the query key of
+                // these.
+                DepKind::CompileCodegenUnit |
+
+                // Forcing this makes no sense.
+                DepKind::Null => {
+                    bug!("force_from_dep_node: encountered {:?}", dep_node)
+                }
+
+                $(DepKind::$name => {
+                    debug_assert!(<$K as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key());
+
+                    if let Some(key) = <$K as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node) {
+                        force_query::<queries::$name<'_>, _>(
+                            tcx,
+                            key,
+                            DUMMY_SP,
+                            *dep_node
+                        );
+                        return true;
+                    }
+                })*
+            }
         }
-    );
+    }
+
+    rustc_dep_node_append! { [force_from_dep_node!][] }
 
     false
 }
 
 pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) {
-    rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx)
+    macro_rules! try_load_from_on_disk_cache {
+        ($($name:ident,)*) => {
+            match dep_node.kind {
+                $(DepKind::$name => {
+                    if <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
+                        debug_assert!(tcx.dep_graph
+                                         .node_color(dep_node)
+                                         .map(|c| c.is_green())
+                                         .unwrap_or(false));
+
+                        let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap();
+                        if queries::$name::cache_on_disk(tcx, &key, None) {
+                            let _ = tcx.$name(key);
+                        }
+                    }
+                })*
+
+                _ => (),
+            }
+        }
+    }
+
+    rustc_cached_queries!(try_load_from_on_disk_cache!);
 }
 
 mod sealed {
diff --git a/compiler/rustc_middle/src/ty/query/plumbing.rs b/compiler/rustc_middle/src/ty/query/plumbing.rs
index 1a8aacc4869..d038695283c 100644
--- a/compiler/rustc_middle/src/ty/query/plumbing.rs
+++ b/compiler/rustc_middle/src/ty/query/plumbing.rs
@@ -242,25 +242,15 @@ macro_rules! hash_result {
     };
 }
 
-macro_rules! define_queries {
-    (<$tcx:tt> $($category:tt {
-        $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*
-    },)*) => {
-        define_queries_inner! { <$tcx>
-            $($( $(#[$attr])* category<$category> [$($modifiers)*] fn $name: $node($($K)*) -> $V,)*)*
-        }
-    }
-}
-
 macro_rules! query_helper_param_ty {
     (DefId) => { impl IntoQueryParam<DefId> };
     ($K:ty) => { $K };
 }
 
-macro_rules! define_queries_inner {
+macro_rules! define_queries {
     (<$tcx:tt>
-     $($(#[$attr:meta])* category<$category:tt>
-        [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*) => {
+     $($(#[$attr:meta])*
+        [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
 
         use std::mem;
         use crate::{
@@ -268,7 +258,6 @@ macro_rules! define_queries_inner {
             rustc_data_structures::stable_hasher::StableHasher,
             ich::StableHashingContext
         };
-        use rustc_data_structures::profiling::ProfileCategory;
 
         define_queries_struct! {
             tcx: $tcx,
@@ -362,13 +351,12 @@ macro_rules! define_queries_inner {
                 as QueryStorage
             >::Stored;
             const NAME: &'static str = stringify!($name);
-            const CATEGORY: ProfileCategory = $category;
         }
 
         impl<$tcx> QueryAccessors<TyCtxt<$tcx>> for queries::$name<$tcx> {
             const ANON: bool = is_anon!([$($modifiers)*]);
             const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]);
-            const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$node;
+            const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$name;
 
             type Cache = query_storage!([$($modifiers)*][$($K)*, $V]);
 
diff --git a/compiler/rustc_middle/src/ty/query/stats.rs b/compiler/rustc_middle/src/ty/query/stats.rs
index 877f88d380a..e0b44ce23c9 100644
--- a/compiler/rustc_middle/src/ty/query/stats.rs
+++ b/compiler/rustc_middle/src/ty/query/stats.rs
@@ -120,13 +120,13 @@ pub fn print_stats(tcx: TyCtxt<'_>) {
 }
 
 macro_rules! print_stats {
-    (<$tcx:tt> $($category:tt {
-        $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)*
-    },)*) => {
+    (<$tcx:tt>
+        $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($K:ty) -> $V:ty,)*
+    ) => {
         fn query_stats(tcx: TyCtxt<'_>) -> Vec<QueryStats> {
             let mut queries = Vec::new();
 
-            $($(
+            $(
                 queries.push(stats::<
                     crate::dep_graph::DepKind,
                     <TyCtxt<'_> as QueryContext>::Query,
@@ -135,7 +135,7 @@ macro_rules! print_stats {
                     stringify!($name),
                     &tcx.queries.$name,
                 ));
-            )*)*
+            )*
 
             queries
         }
diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs
index 73ca7e0d471..7e2cae09481 100644
--- a/compiler/rustc_mir/src/const_eval/machine.rs
+++ b/compiler/rustc_mir/src/const_eval/machine.rs
@@ -70,9 +70,10 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
     ) -> InterpResult<'tcx> {
         let def_id = instance.def_id();
         if Some(def_id) == self.tcx.lang_items().panic_fn()
+            || Some(def_id) == self.tcx.lang_items().panic_str()
             || Some(def_id) == self.tcx.lang_items().begin_panic_fn()
         {
-            // &'static str
+            // &str
             assert!(args.len() == 1);
 
             let msg_place = self.deref_operand(args[0])?;
diff --git a/compiler/rustc_mir/src/transform/check_consts/mod.rs b/compiler/rustc_mir/src/transform/check_consts/mod.rs
index 33815ceba62..b93d63b4fdd 100644
--- a/compiler/rustc_mir/src/transform/check_consts/mod.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/mod.rs
@@ -74,7 +74,9 @@ impl ConstCx<'mir, 'tcx> {
 
 /// Returns `true` if this `DefId` points to one of the official `panic` lang items.
 pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
-    Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn()
+    Some(def_id) == tcx.lang_items().panic_fn()
+        || Some(def_id) == tcx.lang_items().panic_str()
+        || Some(def_id) == tcx.lang_items().begin_panic_fn()
 }
 
 pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
diff --git a/compiler/rustc_mir/src/transform/match_branches.rs b/compiler/rustc_mir/src/transform/match_branches.rs
index 0f9b2ff5ab8..06690dcbf6e 100644
--- a/compiler/rustc_mir/src/transform/match_branches.rs
+++ b/compiler/rustc_mir/src/transform/match_branches.rs
@@ -38,19 +38,16 @@ pub struct MatchBranchSimplification;
 
 impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-        // FIXME: This optimization can result in unsoundness, because it introduces
-        // additional uses of a place holding the discriminant value without ensuring that
-        // it is valid to do so.
-        if !tcx.sess.opts.debugging_opts.unsound_mir_opts {
+        if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 {
             return;
         }
 
         let param_env = tcx.param_env(body.source.def_id());
-        let bbs = body.basic_blocks_mut();
+        let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut();
         'outer: for bb_idx in bbs.indices() {
             let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind {
                 TerminatorKind::SwitchInt {
-                    discr: Operand::Copy(ref place) | Operand::Move(ref place),
+                    discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)),
                     switch_ty,
                     ref targets,
                     ..
@@ -59,7 +56,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
                     if target == targets.otherwise() {
                         continue;
                     }
-                    (place, value, switch_ty, target, targets.otherwise())
+                    (discr, value, switch_ty, target, targets.otherwise())
                 }
                 // Only optimize switch int statements
                 _ => continue,
@@ -99,6 +96,10 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
             // Take ownership of items now that we know we can optimize.
             let discr = discr.clone();
 
+            // Introduce a temporary for the discriminant value.
+            let source_info = bbs[bb_idx].terminator().source_info;
+            let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span));
+
             // We already checked that first and second are different blocks,
             // and bb_idx has a different terminator from both of them.
             let (from, first, second) = bbs.pick3_mut(bb_idx, first, second);
@@ -127,7 +128,11 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
                                 rustc_span::DUMMY_SP,
                             );
                             let op = if f_b { BinOp::Eq } else { BinOp::Ne };
-                            let rhs = Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp);
+                            let rhs = Rvalue::BinaryOp(
+                                op,
+                                Operand::Copy(Place::from(discr_local)),
+                                const_cmp,
+                            );
                             Statement {
                                 source_info: f.source_info,
                                 kind: StatementKind::Assign(box (*lhs, rhs)),
@@ -138,7 +143,16 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
                     _ => unreachable!(),
                 }
             });
+
+            from.statements
+                .push(Statement { source_info, kind: StatementKind::StorageLive(discr_local) });
+            from.statements.push(Statement {
+                source_info,
+                kind: StatementKind::Assign(box (Place::from(discr_local), Rvalue::Use(discr))),
+            });
             from.statements.extend(new_stmts);
+            from.statements
+                .push(Statement { source_info, kind: StatementKind::StorageDead(discr_local) });
             from.terminator_mut().kind = first.terminator().kind.clone();
         }
     }
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs
index a9620b83124..576b537c017 100644
--- a/compiler/rustc_mir_build/src/lints.rs
+++ b/compiler/rustc_mir_build/src/lints.rs
@@ -1,5 +1,5 @@
 use rustc_data_structures::graph::iterate::{
-    ControlFlow, NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
+    NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
 };
 use rustc_hir::intravisit::FnKind;
 use rustc_middle::hir::map::blocks::FnLikeNode;
@@ -8,6 +8,7 @@ use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
 use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt};
 use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
 use rustc_span::Span;
+use std::ops::ControlFlow;
 
 crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
     let def_id = body.source.def_id().expect_local();
diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
index 7216d1305fd..0449e149831 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs
@@ -302,10 +302,9 @@ use super::{FieldPat, Pat, PatKind, PatRange};
 
 use rustc_arena::TypedArena;
 use rustc_attr::{SignedInt, UnsignedInt};
-use rustc_errors::ErrorReported;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{HirId, RangeEnd};
-use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar};
+use rustc_middle::mir::interpret::{truncate, ConstValue};
 use rustc_middle::mir::Field;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{self, Const, Ty, TyCtxt};
@@ -314,108 +313,21 @@ use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{Integer, Size, VariantIdx};
 
 use smallvec::{smallvec, SmallVec};
-use std::borrow::Cow;
 use std::cmp::{self, max, min, Ordering};
-use std::convert::TryInto;
 use std::fmt;
 use std::iter::{FromIterator, IntoIterator};
 use std::ops::RangeInclusive;
 
-crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> {
-    LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat)
+crate fn expand_pattern<'tcx>(pat: Pat<'tcx>) -> Pat<'tcx> {
+    LiteralExpander.fold_pattern(&pat)
 }
 
-struct LiteralExpander<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-}
+struct LiteralExpander;
 
-impl<'tcx> LiteralExpander<'tcx> {
-    /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice.
-    ///
-    /// `crty` and `rty` can differ because you can use array constants in the presence of slice
-    /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert
-    /// the array to a slice in that case.
-    fn fold_const_value_deref(
-        &mut self,
-        val: ConstValue<'tcx>,
-        // the pattern's pointee type
-        rty: Ty<'tcx>,
-        // the constant's pointee type
-        crty: Ty<'tcx>,
-    ) -> ConstValue<'tcx> {
-        debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
-        match (val, &crty.kind(), &rty.kind()) {
-            // the easy case, deref a reference
-            (ConstValue::Scalar(p), x, y) if x == y => {
-                match p {
-                    Scalar::Ptr(p) => {
-                        let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory();
-                        ConstValue::ByRef { alloc, offset: p.offset }
-                    }
-                    Scalar::Raw { .. } => {
-                        let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap();
-                        if layout.is_zst() {
-                            // Deref of a reference to a ZST is a nop.
-                            ConstValue::Scalar(Scalar::zst())
-                        } else {
-                            // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;`
-                            bug!("cannot deref {:#?}, {} -> {}", val, crty, rty);
-                        }
-                    }
-                }
-            }
-            // unsize array to slice if pattern is array but match value or other patterns are slice
-            (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
-                assert_eq!(t, u);
-                ConstValue::Slice {
-                    data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(),
-                    start: p.offset.bytes().try_into().unwrap(),
-                    end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(),
-                }
-            }
-            // fat pointers stay the same
-            (ConstValue::Slice { .. }, _, _)
-            | (_, ty::Slice(_), ty::Slice(_))
-            | (_, ty::Str, ty::Str) => val,
-            // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used
-            _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
-        }
-    }
-}
-
-impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> {
+impl<'tcx> PatternFolder<'tcx> for LiteralExpander {
     fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> {
         debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind);
         match (pat.ty.kind(), &*pat.kind) {
-            (&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } })
-                if const_ty.is_ref() =>
-            {
-                let crty =
-                    if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() };
-                if let ty::ConstKind::Value(val) = val {
-                    Pat {
-                        ty: pat.ty,
-                        span: pat.span,
-                        kind: box PatKind::Deref {
-                            subpattern: Pat {
-                                ty: rty,
-                                span: pat.span,
-                                kind: box PatKind::Constant {
-                                    value: Const::from_value(
-                                        self.tcx,
-                                        self.fold_const_value_deref(*val, rty, crty),
-                                        rty,
-                                    ),
-                                },
-                            },
-                        },
-                    }
-                } else {
-                    bug!("cannot deref {:#?}, {} -> {}", val, crty, rty)
-                }
-            }
-
             (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self),
             (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self),
             _ => pat.super_fold_with(self),
@@ -500,9 +412,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
         cx: &mut MatchCheckCtxt<'p, 'tcx>,
         constructor: &Constructor<'tcx>,
         ctor_wild_subpatterns: &Fields<'p, 'tcx>,
+        is_my_head_ctor: bool,
     ) -> Option<PatStack<'p, 'tcx>> {
-        let new_fields =
-            specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?;
+        let new_fields = specialize_one_pattern(
+            cx,
+            self.head(),
+            constructor,
+            ctor_wild_subpatterns,
+            is_my_head_ctor,
+        )?;
         Some(new_fields.push_on_patstack(&self.0[1..]))
     }
 }
@@ -680,6 +598,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
                                 cx,
                                 constructor,
                                 ctor_wild_subpatterns,
+                                false,
                             )
                         })
                         .collect()
@@ -705,7 +624,9 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
             SpecializationCache::Incompatible => self
                 .patterns
                 .iter()
-                .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
+                .filter_map(|r| {
+                    r.specialize_constructor(cx, constructor, ctor_wild_subpatterns, false)
+                })
                 .collect(),
         }
     }
@@ -725,6 +646,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
 /// +++++++++++++++++++++++++++++
 /// + _     + [_, _, tail @ ..] +
 /// +++++++++++++++++++++++++++++
+/// ```
 impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "\n")?;
@@ -926,42 +848,30 @@ enum Constructor<'tcx> {
     Single,
     /// Enum variants.
     Variant(DefId),
-    /// Literal values.
-    ConstantValue(&'tcx ty::Const<'tcx>),
     /// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
     IntRange(IntRange<'tcx>),
     /// Ranges of floating-point literal values (`2.0..=5.2`).
     FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
+    /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
+    Str(&'tcx ty::Const<'tcx>),
     /// Array and slice patterns.
     Slice(Slice),
+    /// Constants that must not be matched structurally. They are treated as black
+    /// boxes for the purposes of exhaustiveness: we must not inspect them, and they
+    /// don't count towards making a match exhaustive.
+    Opaque,
     /// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
     NonExhaustive,
 }
 
 impl<'tcx> Constructor<'tcx> {
-    fn is_slice(&self) -> bool {
-        match self {
-            Slice(_) => true,
-            _ => false,
-        }
-    }
-
-    fn variant_index_for_adt<'a>(
-        &self,
-        cx: &MatchCheckCtxt<'a, 'tcx>,
-        adt: &'tcx ty::AdtDef,
-    ) -> VariantIdx {
+    fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx {
         match *self {
             Variant(id) => adt.variant_index_with_id(id),
             Single => {
                 assert!(!adt.is_enum());
                 VariantIdx::new(0)
             }
-            ConstantValue(c) => cx
-                .tcx
-                .destructure_const(cx.param_env.and(c))
-                .variant
-                .expect("destructed const of adt without variant id"),
             _ => bug!("bad constructor {:?} for adt {:?}", self, adt),
         }
     }
@@ -975,7 +885,7 @@ impl<'tcx> Constructor<'tcx> {
 
         match self {
             // Those constructors can only match themselves.
-            Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
+            Single | Variant(_) | Str(..) | FloatRange(..) => {
                 if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] }
             }
             &Slice(slice) => {
@@ -983,8 +893,6 @@ impl<'tcx> Constructor<'tcx> {
                     .iter()
                     .filter_map(|c: &Constructor<'_>| match c {
                         Slice(slice) => Some(*slice),
-                        // FIXME(oli-obk): implement `deref` for `ConstValue`
-                        ConstantValue(..) => None,
                         _ => bug!("bad slice pattern constructor {:?}", c),
                     })
                     .map(Slice::value_kind);
@@ -1048,6 +956,7 @@ impl<'tcx> Constructor<'tcx> {
             }
             // This constructor is never covered by anything else
             NonExhaustive => vec![NonExhaustive],
+            Opaque => bug!("unexpected opaque ctor {:?} found in all_ctors", self),
         }
     }
 
@@ -1087,7 +996,7 @@ impl<'tcx> Constructor<'tcx> {
                             PatKind::Variant {
                                 adt_def: adt,
                                 substs,
-                                variant_index: self.variant_index_for_adt(cx, adt),
+                                variant_index: self.variant_index_for_adt(adt),
                                 subpatterns,
                             }
                         } else {
@@ -1126,10 +1035,11 @@ impl<'tcx> Constructor<'tcx> {
                     PatKind::Slice { prefix, slice: Some(wild), suffix }
                 }
             },
-            &ConstantValue(value) => PatKind::Constant { value },
+            &Str(value) => PatKind::Constant { value },
             &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
             IntRange(range) => return range.to_pat(cx.tcx),
             NonExhaustive => PatKind::Wild,
+            Opaque => bug!("we should not try to apply an opaque constructor {:?}", self),
         };
 
         Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }
@@ -1204,12 +1114,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
         Fields::Slice(std::slice::from_ref(pat))
     }
 
-    /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't
-    /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`.
-    fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self {
-        Fields::Slice(pats)
-    }
-
     /// Convenience; internal use.
     fn wildcards_from_tys(
         cx: &MatchCheckCtxt<'p, 'tcx>,
@@ -1239,7 +1143,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
                         // Use T as the sub pattern type of Box<T>.
                         Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0)))
                     } else {
-                        let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)];
+                        let variant = &adt.variants[constructor.variant_index_for_adt(adt)];
                         // Whether we must not match the fields of this variant exhaustively.
                         let is_non_exhaustive =
                             variant.is_field_list_non_exhaustive() && !adt.did.is_local();
@@ -1287,7 +1191,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
                 }
                 _ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
             },
-            ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(),
+            Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque => Fields::empty(),
         };
         debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
         ret
@@ -1600,9 +1504,7 @@ fn all_constructors<'a, 'tcx>(
         )
     };
     match *pcx.ty.kind() {
-        ty::Bool => {
-            [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect()
-        }
+        ty::Bool => vec![make_range(0, 1)],
         ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
             let len = len.eval_usize(cx.tcx, cx.param_env);
             if len != 0 && cx.is_uninhabited(sub_ty) {
@@ -1717,7 +1619,7 @@ impl<'tcx> IntRange<'tcx> {
     #[inline]
     fn is_integral(ty: Ty<'_>) -> bool {
         match ty.kind() {
-            ty::Char | ty::Int(_) | ty::Uint(_) => true,
+            ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool => true,
             _ => false,
         }
     }
@@ -1739,6 +1641,7 @@ impl<'tcx> IntRange<'tcx> {
     #[inline]
     fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> {
         match *ty.kind() {
+            ty::Bool => Some((Size::from_bytes(1), 0)),
             ty::Char => Some((Size::from_bytes(4), 0)),
             ty::Int(ity) => {
                 let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
@@ -2230,7 +2133,7 @@ fn is_useful_specialized<'p, 'tcx>(
     // We cache the result of `Fields::wildcards` because it is used a lot.
     let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty);
     let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns);
-    v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns)
+    v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns, true)
         .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false))
         .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns))
         .unwrap_or(NotUseful)
@@ -2255,18 +2158,13 @@ fn pat_constructor<'tcx>(
             if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {
                 Some(IntRange(int_range))
             } else {
-                match (value.val, &value.ty.kind()) {
-                    (_, ty::Array(_, n)) => {
-                        let len = n.eval_usize(tcx, param_env);
-                        Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) }))
-                    }
-                    (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => {
-                        let len = (end - start) as u64;
-                        Some(Slice(Slice { array_len: None, kind: FixedLen(len) }))
-                    }
-                    // FIXME(oli-obk): implement `deref` for `ConstValue`
-                    // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... }
-                    _ => Some(ConstantValue(value)),
+                match value.ty.kind() {
+                    ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)),
+                    ty::Ref(_, t, _) if t.is_str() => Some(Str(value)),
+                    // All constants that can be structurally matched have already been expanded
+                    // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
+                    // opaque.
+                    _ => Some(Opaque),
                 }
             }
         }
@@ -2302,75 +2200,6 @@ fn pat_constructor<'tcx>(
     }
 }
 
-// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices,
-// meaning all other types will compare unequal and thus equal patterns often do not cause the
-// second pattern to lint about unreachable match arms.
-fn slice_pat_covered_by_const<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    _span: Span,
-    const_val: &'tcx ty::Const<'tcx>,
-    prefix: &[Pat<'tcx>],
-    slice: &Option<Pat<'tcx>>,
-    suffix: &[Pat<'tcx>],
-    param_env: ty::ParamEnv<'tcx>,
-) -> Result<bool, ErrorReported> {
-    let const_val_val = if let ty::ConstKind::Value(val) = const_val.val {
-        val
-    } else {
-        bug!(
-            "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
-            const_val,
-            prefix,
-            slice,
-            suffix,
-        )
-    };
-
-    let data: &[u8] = match (const_val_val, &const_val.ty.kind()) {
-        (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => {
-            assert_eq!(*t, tcx.types.u8);
-            let n = n.eval_usize(tcx, param_env);
-            let ptr = Pointer::new(AllocId(0), offset);
-            alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
-        }
-        (ConstValue::Slice { data, start, end }, ty::Slice(t)) => {
-            assert_eq!(*t, tcx.types.u8);
-            let ptr = Pointer::new(AllocId(0), Size::from_bytes(start));
-            data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap()
-        }
-        // FIXME(oli-obk): create a way to extract fat pointers from ByRef
-        (_, ty::Slice(_)) => return Ok(false),
-        _ => bug!(
-            "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
-            const_val,
-            prefix,
-            slice,
-            suffix,
-        ),
-    };
-
-    let pat_len = prefix.len() + suffix.len();
-    if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) {
-        return Ok(false);
-    }
-
-    for (ch, pat) in data[..prefix.len()]
-        .iter()
-        .zip(prefix)
-        .chain(data[data.len() - suffix.len()..].iter().zip(suffix))
-    {
-        if let box PatKind::Constant { value } = pat.kind {
-            let b = value.eval_bits(tcx, param_env, pat.ty);
-            assert_eq!(b as u8 as u128, b);
-            if b as u8 != *ch {
-                return Ok(false);
-            }
-        }
-    }
-
-    Ok(true)
-}
-
 /// For exhaustive integer matching, some constructors are grouped within other constructors
 /// (namely integer typed values are grouped within ranges). However, when specialising these
 /// constructors, we want to be specialising for the underlying constructors (the integers), not
@@ -2668,35 +2497,6 @@ fn lint_overlapping_patterns<'tcx>(
     }
 }
 
-fn constructor_covered_by_range<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    ctor: &Constructor<'tcx>,
-    pat: &Pat<'tcx>,
-) -> Option<()> {
-    if let Single = ctor {
-        return Some(());
-    }
-
-    let (pat_from, pat_to, pat_end, ty) = match *pat.kind {
-        PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
-        PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty),
-        _ => bug!("`constructor_covered_by_range` called with {:?}", pat),
-    };
-    let (ctor_from, ctor_to, ctor_end) = match *ctor {
-        ConstantValue(value) => (value, value, RangeEnd::Included),
-        FloatRange(from, to, ctor_end) => (from, to, ctor_end),
-        _ => bug!("`constructor_covered_by_range` called with {:?}", ctor),
-    };
-    trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty);
-
-    let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?;
-    let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?;
-    let intersects = (from == Ordering::Greater || from == Ordering::Equal)
-        && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal));
-    if intersects { Some(()) } else { None }
-}
-
 /// This is the main specialization step. It expands the pattern
 /// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
 /// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
@@ -2713,15 +2513,43 @@ fn specialize_one_pattern<'p, 'tcx>(
     pat: &'p Pat<'tcx>,
     constructor: &Constructor<'tcx>,
     ctor_wild_subpatterns: &Fields<'p, 'tcx>,
+    is_its_own_ctor: bool, // Whether `ctor` is known to be derived from `pat`
 ) -> Option<Fields<'p, 'tcx>> {
     if let NonExhaustive = constructor {
-        // Only a wildcard pattern can match the special extra constructor
+        // Only a wildcard pattern can match the special extra constructor.
         if !pat.is_wildcard() {
             return None;
         }
         return Some(Fields::empty());
     }
 
+    if let Opaque = constructor {
+        // Only a wildcard pattern can match an opaque constant, unless we're specializing the
+        // value against its own constructor. That happens when we call
+        // `v.specialize_constructor(ctor)` with `ctor` obtained from `pat_constructor(v.head())`.
+        // For example, in the following match, when we are dealing with the third branch, we will
+        // specialize with an `Opaque` ctor. We want to ignore the second branch because opaque
+        // constants should not be inspected, but we don't want to ignore the current (third)
+        // branch, as that would cause us to always conclude that such a branch is unreachable.
+        // ```rust
+        // #[derive(PartialEq)]
+        // struct Foo(i32);
+        // impl Eq for Foo {}
+        // const FOO: Foo = Foo(42);
+        //
+        // match (Foo(0), true) {
+        //     (_, true) => {}
+        //     (FOO, true) => {}
+        //     (FOO, false) => {}
+        // }
+        // ```
+        if is_its_own_ctor || pat.is_wildcard() {
+            return Some(Fields::empty());
+        } else {
+            return None;
+        }
+    }
+
     let result = match *pat.kind {
         PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
 
@@ -2741,93 +2569,52 @@ fn specialize_one_pattern<'p, 'tcx>(
 
         PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)),
 
-        PatKind::Constant { value } if constructor.is_slice() => {
-            // We extract an `Option` for the pointer because slices of zero
-            // elements don't necessarily point to memory, they are usually
-            // just integers. The only time they should be pointing to memory
-            // is when they are subslices of nonzero slices.
-            let (alloc, offset, n, ty) = match value.ty.kind() {
-                ty::Array(t, n) => {
-                    let n = n.eval_usize(cx.tcx, cx.param_env);
-                    // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce,
-                    // the result would be exactly what we early return here.
-                    if n == 0 {
-                        if ctor_wild_subpatterns.len() as u64 != n {
-                            return None;
-                        }
-                        return Some(Fields::empty());
-                    }
-                    match value.val {
-                        ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => {
-                            (Cow::Borrowed(alloc), offset, n, t)
-                        }
-                        _ => span_bug!(pat.span, "array pattern is {:?}", value,),
+        PatKind::Constant { .. } | PatKind::Range { .. } => {
+            match constructor {
+                IntRange(ctor) => {
+                    let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?;
+                    ctor.intersection(cx.tcx, &pat)?;
+                    // Constructor splitting should ensure that all intersections we encounter
+                    // are actually inclusions.
+                    assert!(ctor.is_subrange(&pat));
+                }
+                FloatRange(ctor_from, ctor_to, ctor_end) => {
+                    let (pat_from, pat_to, pat_end, ty) = match *pat.kind {
+                        PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
+                        PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty),
+                        _ => unreachable!(), // This is ensured by the branch we're in
+                    };
+                    let to = compare_const_vals(cx.tcx, ctor_to, pat_to, cx.param_env, ty)?;
+                    let from = compare_const_vals(cx.tcx, ctor_from, pat_from, cx.param_env, ty)?;
+                    let intersects = (from == Ordering::Greater || from == Ordering::Equal)
+                        && (to == Ordering::Less
+                            || (pat_end == *ctor_end && to == Ordering::Equal));
+                    if !intersects {
+                        return None;
                     }
                 }
-                ty::Slice(t) => {
-                    match value.val {
-                        ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => {
-                            let offset = Size::from_bytes(start);
-                            let n = (end - start) as u64;
-                            (Cow::Borrowed(data), offset, n, t)
-                        }
-                        ty::ConstKind::Value(ConstValue::ByRef { .. }) => {
-                            // FIXME(oli-obk): implement `deref` for `ConstValue`
-                            return None;
-                        }
+                Str(ctor_value) => {
+                    let pat_value = match *pat.kind {
+                        PatKind::Constant { value } => value,
                         _ => span_bug!(
                             pat.span,
-                            "slice pattern constant must be scalar pair but is {:?}",
-                            value,
+                            "unexpected range pattern {:?} for constant value ctor",
+                            pat
                         ),
+                    };
+
+                    // FIXME: there's probably a more direct way of comparing for equality
+                    if compare_const_vals(cx.tcx, ctor_value, pat_value, cx.param_env, pat.ty)?
+                        != Ordering::Equal
+                    {
+                        return None;
                     }
                 }
-                _ => span_bug!(
-                    pat.span,
-                    "unexpected const-val {:?} with ctor {:?}",
-                    value,
-                    constructor,
-                ),
-            };
-            if ctor_wild_subpatterns.len() as u64 != n {
-                return None;
-            }
-
-            // Convert a constant slice/array pattern to a list of patterns.
-            let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?;
-            let ptr = Pointer::new(AllocId(0), offset);
-            let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| {
-                let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?;
-                let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?;
-                let scalar = scalar.check_init().ok()?;
-                let value = ty::Const::from_scalar(cx.tcx, scalar, ty);
-                let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } };
-                Some(pattern)
-            }));
-            // Ensure none of the dereferences failed.
-            if pats.len() as u64 != n {
-                return None;
-            }
-            Some(Fields::from_slice_unfiltered(pats))
-        }
-
-        PatKind::Constant { .. } | PatKind::Range { .. } => {
-            // If the constructor is a:
-            // - Single value: add a row if the pattern contains the constructor.
-            // - Range: add a row if the constructor intersects the pattern.
-            if let IntRange(ctor) = constructor {
-                let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?;
-                ctor.intersection(cx.tcx, &pat)?;
-                // Constructor splitting should ensure that all intersections we encounter
-                // are actually inclusions.
-                assert!(ctor.is_subrange(&pat));
-            } else {
-                // Fallback for non-ranges and ranges that involve
-                // floating-point numbers, which are not conveniently handled
-                // by `IntRange`. For these cases, the constructor may not be a
-                // range so intersection actually devolves into being covered
-                // by the pattern.
-                constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?;
+                _ => {
+                    // If we reach here, we must be trying to inspect an opaque constant. Thus we skip
+                    // the row.
+                    return None;
+                }
             }
             Some(Fields::empty())
         }
@@ -2850,21 +2637,6 @@ fn specialize_one_pattern<'p, 'tcx>(
                 let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p));
                 Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix)))
             }
-            ConstantValue(cv) => {
-                match slice_pat_covered_by_const(
-                    cx.tcx,
-                    pat.span,
-                    cv,
-                    prefix,
-                    slice,
-                    suffix,
-                    cx.param_env,
-                ) {
-                    Ok(true) => Some(Fields::empty()),
-                    Ok(false) => None,
-                    Err(ErrorReported) => None,
-                }
-            }
             _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor),
         },
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 69de7c7e2ee..30b700a1d4f 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -137,7 +137,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         patcx.include_lint_checks();
         let pattern = patcx.lower_pattern(pat);
         let pattern_ty = pattern.ty;
-        let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
+        let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern));
         if !patcx.errors.is_empty() {
             *have_errors = true;
             patcx.report_inlining_errors(pat.span);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index a203b3a1428..6370f8c375b 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -387,14 +387,16 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
                 // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
                 // optimization for now.
                 ty::Str => PatKind::Constant { value: cv },
-                ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
                 // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
                 // matching against references, you can only use byte string literals.
-                // FIXME: clean this up, likely by permitting array patterns when matching on slices
-                ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
+                // The typechecker has a special case for byte string literals, by treating them
+                // as slices. This means we turn `&[T; N]` constants into slice patterns, which
+                // has no negative effects on pattern matching, even if we're actually matching on
+                // arrays.
+                ty::Array(..) |
                 // Cannot merge this with the catch all branch below, because the `const_deref`
-                // changes the type from slice to array, and slice patterns behave differently from
-                // array patterns.
+                // changes the type from slice to array, we need to keep the original type in the
+                // pattern.
                 ty::Slice(..) => {
                     let old = self.behind_reference.replace(true);
                     let array = tcx.deref_const(self.param_env.and(cv));
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index b05ddb3b464..25e18724341 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -158,6 +158,13 @@ crate enum PatKind<'tcx> {
         subpattern: Pat<'tcx>,
     },
 
+    /// One of the following:
+    /// * `&str`, which will be handled as a string pattern and thus exhaustiveness
+    ///   checking will detect if you use the same string twice in different patterns.
+    /// * integer, bool, char or float, which will be handled by exhaustivenes to cover exactly
+    ///   its own value, similar to `&str`, but these values are much simpler.
+    /// * Opaque constants, that must not be matched structurally. So anything that does not derive
+    ///   `PartialEq` and `Eq`.
     Constant {
         value: &'tcx ty::Const<'tcx>,
     },
diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs
index 6c9849e8708..0f0684b3547 100644
--- a/compiler/rustc_query_system/src/query/config.rs
+++ b/compiler/rustc_query_system/src/query/config.rs
@@ -5,7 +5,6 @@ use crate::dep_graph::SerializedDepNodeIndex;
 use crate::query::caches::QueryCache;
 use crate::query::plumbing::CycleError;
 use crate::query::{QueryContext, QueryState};
-use rustc_data_structures::profiling::ProfileCategory;
 
 use rustc_data_structures::fingerprint::Fingerprint;
 use std::borrow::Cow;
@@ -14,7 +13,6 @@ use std::hash::Hash;
 
 pub trait QueryConfig {
     const NAME: &'static str;
-    const CATEGORY: ProfileCategory;
 
     type Key: Eq + Hash + Clone + Debug;
     type Value;
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index bea71389647..b5b281b93bc 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -19,7 +19,7 @@ use rustc_feature::is_builtin_attr_name;
 use rustc_hir::def::{self, DefKind, NonMacroAttrKind};
 use rustc_hir::def_id;
 use rustc_middle::middle::stability;
-use rustc_middle::{span_bug, ty};
+use rustc_middle::ty;
 use rustc_session::lint::builtin::UNUSED_MACROS;
 use rustc_session::Session;
 use rustc_span::edition::Edition;
@@ -885,11 +885,11 @@ impl<'a> Resolver<'a> {
                                  initial_res: Option<Res>,
                                  res: Res| {
             if let Some(initial_res) = initial_res {
-                if res != initial_res && res != Res::Err && this.ambiguity_errors.is_empty() {
+                if res != initial_res {
                     // Make sure compilation does not succeed if preferred macro resolution
                     // has changed after the macro had been expanded. In theory all such
-                    // situations should be reported as ambiguity errors, so this is a bug.
-                    span_bug!(span, "inconsistent resolution for a macro");
+                    // situations should be reported as errors, so this is a bug.
+                    this.session.delay_span_bug(span, "inconsistent resolution for a macro");
                 }
             } else {
                 // It's possible that the macro was unresolved (indeterminate) and silently
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 9cf530d57c0..3133090575e 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -777,6 +777,7 @@ symbols! {
         panic_info,
         panic_location,
         panic_runtime,
+        panic_str,
         panic_unwind,
         param_attrs,
         parent_trait,
diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs
index 05e6c4804ff..3cb6ec86261 100644
--- a/compiler/rustc_trait_selection/src/traits/codegen.rs
+++ b/compiler/rustc_trait_selection/src/traits/codegen.rs
@@ -121,7 +121,10 @@ where
     // contains unbound type parameters. It could be a slight
     // optimization to stop iterating early.
     if let Err(errors) = fulfill_cx.select_all_or_error(infcx) {
-        bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors);
+        infcx.tcx.sess.delay_span_bug(
+            rustc_span::DUMMY_SP,
+            &format!("Encountered errors `{:?}` resolving bounds after type-checking", errors),
+        );
     }
 
     let result = infcx.resolve_vars_if_possible(result);
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 1e1eb16faf4..c79b2624f8c 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -223,11 +223,23 @@ impl AbstractConst<'tcx> {
     }
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+struct WorkNode<'tcx> {
+    node: Node<'tcx>,
+    span: Span,
+    used: bool,
+}
+
 struct AbstractConstBuilder<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     body: &'a mir::Body<'tcx>,
     /// The current WIP node tree.
-    nodes: IndexVec<NodeId, Node<'tcx>>,
+    ///
+    /// We require all nodes to be used in the final abstract const,
+    /// so we store this here. Note that we also consider nodes as used
+    /// if they are mentioned in an assert, so some used nodes are never
+    /// actually reachable by walking the [`AbstractConst`].
+    nodes: IndexVec<NodeId, WorkNode<'tcx>>,
     locals: IndexVec<mir::Local, NodeId>,
     /// We only allow field accesses if they access
     /// the result of a checked operation.
@@ -274,6 +286,27 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
         Ok(Some(builder))
     }
 
+    fn add_node(&mut self, node: Node<'tcx>, span: Span) -> NodeId {
+        // Mark used nodes.
+        match node {
+            Node::Leaf(_) => (),
+            Node::Binop(_, lhs, rhs) => {
+                self.nodes[lhs].used = true;
+                self.nodes[rhs].used = true;
+            }
+            Node::UnaryOp(_, input) => {
+                self.nodes[input].used = true;
+            }
+            Node::FunctionCall(func, nodes) => {
+                self.nodes[func].used = true;
+                nodes.iter().for_each(|&n| self.nodes[n].used = true);
+            }
+        }
+
+        // Nodes start as unused.
+        self.nodes.push(WorkNode { node, span, used: false })
+    }
+
     fn place_to_local(
         &mut self,
         span: Span,
@@ -311,7 +344,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
                 let local = self.place_to_local(span, p)?;
                 Ok(self.locals[local])
             }
-            mir::Operand::Constant(ct) => Ok(self.nodes.push(Node::Leaf(ct.literal))),
+            mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal), span)),
         }
     }
 
@@ -336,19 +369,19 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
 
     fn build_statement(&mut self, stmt: &mir::Statement<'tcx>) -> Result<(), ErrorReported> {
         debug!("AbstractConstBuilder: stmt={:?}", stmt);
+        let span = stmt.source_info.span;
         match stmt.kind {
             StatementKind::Assign(box (ref place, ref rvalue)) => {
-                let local = self.place_to_local(stmt.source_info.span, place)?;
+                let local = self.place_to_local(span, place)?;
                 match *rvalue {
                     Rvalue::Use(ref operand) => {
-                        self.locals[local] =
-                            self.operand_to_node(stmt.source_info.span, operand)?;
+                        self.locals[local] = self.operand_to_node(span, operand)?;
                         Ok(())
                     }
                     Rvalue::BinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => {
-                        let lhs = self.operand_to_node(stmt.source_info.span, lhs)?;
-                        let rhs = self.operand_to_node(stmt.source_info.span, rhs)?;
-                        self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs));
+                        let lhs = self.operand_to_node(span, lhs)?;
+                        let rhs = self.operand_to_node(span, rhs)?;
+                        self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span);
                         if op.is_checkable() {
                             bug!("unexpected unchecked checkable binary operation");
                         } else {
@@ -356,18 +389,18 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
                         }
                     }
                     Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => {
-                        let lhs = self.operand_to_node(stmt.source_info.span, lhs)?;
-                        let rhs = self.operand_to_node(stmt.source_info.span, rhs)?;
-                        self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs));
+                        let lhs = self.operand_to_node(span, lhs)?;
+                        let rhs = self.operand_to_node(span, rhs)?;
+                        self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span);
                         self.checked_op_locals.insert(local);
                         Ok(())
                     }
                     Rvalue::UnaryOp(op, ref operand) if Self::check_unop(op) => {
-                        let operand = self.operand_to_node(stmt.source_info.span, operand)?;
-                        self.locals[local] = self.nodes.push(Node::UnaryOp(op, operand));
+                        let operand = self.operand_to_node(span, operand)?;
+                        self.locals[local] = self.add_node(Node::UnaryOp(op, operand), span);
                         Ok(())
                     }
-                    _ => self.error(Some(stmt.source_info.span), "unsupported rvalue")?,
+                    _ => self.error(Some(span), "unsupported rvalue")?,
                 }
             }
             // These are not actually relevant for us here, so we can ignore them.
@@ -415,13 +448,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
                         .map(|arg| self.operand_to_node(terminator.source_info.span, arg))
                         .collect::<Result<Vec<NodeId>, _>>()?,
                 );
-                self.locals[local] = self.nodes.push(Node::FunctionCall(func, args));
+                self.locals[local] = self.add_node(Node::FunctionCall(func, args), fn_span);
                 Ok(Some(target))
             }
-            // We only allow asserts for checked operations.
-            //
-            // These asserts seem to all have the form `!_local.0` so
-            // we only allow exactly that.
             TerminatorKind::Assert { ref cond, expected: false, target, .. } => {
                 let p = match cond {
                     mir::Operand::Copy(p) | mir::Operand::Move(p) => p,
@@ -430,7 +459,15 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
 
                 const ONE_FIELD: mir::Field = mir::Field::from_usize(1);
                 debug!("proj: {:?}", p.projection);
-                if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() {
+                if let Some(p) = p.as_local() {
+                    debug_assert!(!self.checked_op_locals.contains(p));
+                    // Mark locals directly used in asserts as used.
+                    //
+                    // This is needed because division does not use `CheckedBinop` but instead
+                    // adds an explicit assert for `divisor != 0`.
+                    self.nodes[self.locals[p]].used = true;
+                    return Ok(Some(target));
+                } else if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() {
                     // Only allow asserts checking the result of a checked operation.
                     if self.checked_op_locals.contains(p.local) {
                         return Ok(Some(target));
@@ -457,7 +494,13 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
             if let Some(next) = self.build_terminator(block.terminator())? {
                 block = &self.body.basic_blocks()[next];
             } else {
-                return Ok(self.tcx.arena.alloc_from_iter(self.nodes));
+                assert_eq!(self.locals[mir::RETURN_PLACE], self.nodes.last().unwrap());
+                self.nodes[self.locals[mir::RETURN_PLACE]].used = true;
+                if let Some(&unused) = self.nodes.iter().find(|n| !n.used) {
+                    self.error(Some(unused.span), "dead code")?;
+                }
+
+                return Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter().map(|n| n.node)));
             }
         }
     }