]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_mir/hair/pattern/_match.rs
Introduce Constructor::NonExhaustive
[rust.git] / src / librustc_mir / hair / pattern / _match.rs
index 15f88334e8d5e2e6f36273bf489d61231ab94998..fbf073d9423d931262e6f1f4b28b72ad581482af 100644 (file)
@@ -586,10 +586,12 @@ enum Constructor<'tcx> {
     ConstantValue(&'tcx ty::Const<'tcx>, Span),
     /// Ranges of literal values (`2..=5` and `2..5`).
     ConstantRange(u128, u128, Ty<'tcx>, RangeEnd, Span),
-    /// Array patterns of length n.
+    /// Array patterns of length `n`.
     FixedLenSlice(u64),
-    /// Slice patterns. Captures any array constructor of length >= i+j.
+    /// Slice patterns. Captures any array constructor of `length >= i + j`.
     VarLenSlice(u64, u64),
+    /// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
+    NonExhaustive,
 }
 
 // Ignore spans when comparing, they don't carry semantic information as they are only for lints.
@@ -597,6 +599,7 @@ impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> {
     fn eq(&self, other: &Self) -> bool {
         match (self, other) {
             (Constructor::Single, Constructor::Single) => true,
+            (Constructor::NonExhaustive, Constructor::NonExhaustive) => true,
             (Constructor::Variant(a), Constructor::Variant(b)) => a == b,
             (Constructor::ConstantValue(a, _), Constructor::ConstantValue(b, _)) => a == b,
             (
@@ -616,8 +619,7 @@ fn eq(&self, other: &Self) -> bool {
 impl<'tcx> Constructor<'tcx> {
     fn is_slice(&self) -> bool {
         match self {
-            FixedLenSlice { .. } => true,
-            VarLenSlice { .. } => true,
+            FixedLenSlice { .. } | VarLenSlice { .. } => true,
             _ => false,
         }
     }
@@ -687,13 +689,13 @@ fn subtract_ctors(
 
                 // For each used ctor, subtract from the current set of constructors.
                 // Naming: we remove the "neg" constructors from the "pos" ones.
-                // Remember, VarLenSlice(i, j) covers the union of FixedLenSlice from
-                // i+j to infinity.
+                // Remember, `VarLenSlice(i, j)` covers the union of `FixedLenSlice` from
+                // `i + j` to infinity.
                 for neg_ctor in other_ctors {
                     remaining_ctors = remaining_ctors
                         .into_iter()
                         .flat_map(|pos_ctor| -> SmallVec<[Constructor<'tcx>; 1]> {
-                            // Compute pos_ctor \ neg_ctor
+                            // Compute `pos_ctor \ neg_ctor`.
                             match (&pos_ctor, neg_ctor) {
                                 (&FixedLenSlice(pos_len), &VarLenSlice(neg_prefix, neg_suffix)) => {
                                     let neg_len = neg_prefix + neg_suffix;
@@ -722,7 +724,7 @@ fn subtract_ctors(
                                     } else {
                                         (pos_len..neg_len)
                                             .map(FixedLenSlice)
-                                            // We know neg_len + 1 >= pos_len >= pos_suffix
+                                            // We know that `neg_len + 1 >= pos_len >= pos_suffix`.
                                             .chain(Some(VarLenSlice(
                                                 neg_len + 1 - pos_suffix,
                                                 pos_suffix,
@@ -772,6 +774,8 @@ fn subtract_ctors(
                 // ranges have been omitted.
                 remaining_ctors
             }
+            // This constructor is never covered by anything else
+            NonExhaustive => vec![NonExhaustive],
         }
     }
 
@@ -780,12 +784,71 @@ fn wildcard_subpatterns<'a>(
         &self,
         cx: &MatchCheckCtxt<'a, 'tcx>,
         ty: Ty<'tcx>,
-    ) -> impl Iterator<Item = Pat<'tcx>> + DoubleEndedIterator {
-        constructor_sub_pattern_tys(cx, self, ty).into_iter().map(|ty| Pat {
-            ty,
-            span: DUMMY_SP,
-            kind: box PatKind::Wild,
-        })
+    ) -> Vec<Pat<'tcx>> {
+        debug!("wildcard_subpatterns({:#?}, {:?})", self, ty);
+
+        match self {
+            Single | Variant(_) => match ty.kind {
+                ty::Tuple(ref fs) => {
+                    fs.into_iter().map(|t| t.expect_ty()).map(Pat::wildcard_from_ty).collect()
+                }
+                ty::Ref(_, rty, _) => vec![Pat::wildcard_from_ty(rty)],
+                ty::Adt(adt, substs) => {
+                    if adt.is_box() {
+                        // Use T as the sub pattern type of Box<T>.
+                        vec![Pat::wildcard_from_ty(substs.type_at(0))]
+                    } else {
+                        let variant = &adt.variants[self.variant_index_for_adt(cx, adt)];
+                        let is_non_exhaustive =
+                            variant.is_field_list_non_exhaustive() && !cx.is_local(ty);
+                        variant
+                            .fields
+                            .iter()
+                            .map(|field| {
+                                let is_visible = adt.is_enum()
+                                    || field.vis.is_accessible_from(cx.module, cx.tcx);
+                                let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs));
+                                match (is_visible, is_non_exhaustive, is_uninhabited) {
+                                    // Treat all uninhabited types in non-exhaustive variants as
+                                    // `TyErr`.
+                                    (_, true, true) => cx.tcx.types.err,
+                                    // Treat all non-visible fields as `TyErr`. They can't appear
+                                    // in any other pattern from this match (because they are
+                                    // private), so their type does not matter - but we don't want
+                                    // to know they are uninhabited.
+                                    (false, ..) => cx.tcx.types.err,
+                                    (true, ..) => {
+                                        let ty = field.ty(cx.tcx, substs);
+                                        match ty.kind {
+                                            // If the field type returned is an array of an unknown
+                                            // size return an TyErr.
+                                            ty::Array(_, len)
+                                                if len
+                                                    .try_eval_usize(cx.tcx, cx.param_env)
+                                                    .is_none() =>
+                                            {
+                                                cx.tcx.types.err
+                                            }
+                                            _ => ty,
+                                        }
+                                    }
+                                }
+                            })
+                            .map(Pat::wildcard_from_ty)
+                            .collect()
+                    }
+                }
+                _ => vec![],
+            },
+            FixedLenSlice(_) | VarLenSlice(..) => match ty.kind {
+                ty::Slice(ty) | ty::Array(ty, _) => {
+                    let arity = self.arity(cx, ty);
+                    (0..arity).map(|_| Pat::wildcard_from_ty(ty)).collect()
+                }
+                _ => bug!("bad slice pattern {:?} {:?}", self, ty),
+            },
+            ConstantValue(..) | ConstantRange(..) | NonExhaustive => vec![],
+        }
     }
 
     /// This computes the arity of a constructor. The arity of a constructor
@@ -795,19 +858,19 @@ fn wildcard_subpatterns<'a>(
     /// A struct pattern's arity is the number of fields it contains, etc.
     fn arity<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> u64 {
         debug!("Constructor::arity({:#?}, {:?})", self, ty);
-        match ty.kind {
-            ty::Tuple(ref fs) => fs.len() as u64,
-            ty::Slice(..) | ty::Array(..) => match *self {
-                FixedLenSlice(length) => length,
-                VarLenSlice(prefix, suffix) => prefix + suffix,
-                ConstantValue(..) => 0,
-                _ => bug!("bad slice pattern {:?} {:?}", self, ty),
+        match self {
+            Single | Variant(_) => match ty.kind {
+                ty::Tuple(ref fs) => fs.len() as u64,
+                ty::Slice(..) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty),
+                ty::Ref(..) => 1,
+                ty::Adt(adt, _) => {
+                    adt.variants[self.variant_index_for_adt(cx, adt)].fields.len() as u64
+                }
+                _ => 0,
             },
-            ty::Ref(..) => 1,
-            ty::Adt(adt, _) => {
-                adt.variants[self.variant_index_for_adt(cx, adt)].fields.len() as u64
-            }
-            _ => 0,
+            FixedLenSlice(length) => *length,
+            VarLenSlice(prefix, suffix) => prefix + suffix,
+            ConstantValue(..) | ConstantRange(..) | NonExhaustive => 0,
         }
     }
 
@@ -831,53 +894,50 @@ fn apply<'a>(
         pats: impl IntoIterator<Item = Pat<'tcx>>,
     ) -> Pat<'tcx> {
         let mut subpatterns = pats.into_iter();
-        let pat = match ty.kind {
-            ty::Adt(..) | ty::Tuple(..) => {
-                let subpatterns = subpatterns
-                    .enumerate()
-                    .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p })
-                    .collect();
-
-                if let ty::Adt(adt, substs) = ty.kind {
-                    if adt.is_enum() {
-                        PatKind::Variant {
-                            adt_def: adt,
-                            substs,
-                            variant_index: self.variant_index_for_adt(cx, adt),
-                            subpatterns,
+
+        let pat = match self {
+            Single | Variant(_) => match ty.kind {
+                ty::Adt(..) | ty::Tuple(..) => {
+                    let subpatterns = subpatterns
+                        .enumerate()
+                        .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p })
+                        .collect();
+
+                    if let ty::Adt(adt, substs) = ty.kind {
+                        if adt.is_enum() {
+                            PatKind::Variant {
+                                adt_def: adt,
+                                substs,
+                                variant_index: self.variant_index_for_adt(cx, adt),
+                                subpatterns,
+                            }
+                        } else {
+                            PatKind::Leaf { subpatterns }
                         }
                     } else {
                         PatKind::Leaf { subpatterns }
                     }
-                } else {
-                    PatKind::Leaf { subpatterns }
-                }
-            }
-
-            ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.nth(0).unwrap() },
-
-            ty::Slice(_) | ty::Array(..) => match self {
-                FixedLenSlice(_) => {
-                    PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
-                }
-                VarLenSlice(prefix_len, _suffix_len) => {
-                    let prefix = subpatterns.by_ref().take(*prefix_len as usize).collect();
-                    let suffix = subpatterns.collect();
-                    let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) };
-                    PatKind::Slice { prefix, slice: Some(wild), suffix }
                 }
-                _ => bug!("bad slice pattern {:?} {:?}", self, ty),
-            },
-
-            _ => match *self {
-                ConstantValue(value, _) => PatKind::Constant { value },
-                ConstantRange(lo, hi, ty, end, _) => PatKind::Range(PatRange {
-                    lo: ty::Const::from_bits(cx.tcx, lo, ty::ParamEnv::empty().and(ty)),
-                    hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)),
-                    end,
-                }),
+                ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.nth(0).unwrap() },
+                ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty),
                 _ => PatKind::Wild,
             },
+            FixedLenSlice(_) => {
+                PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
+            }
+            &VarLenSlice(prefix_len, _) => {
+                let prefix = subpatterns.by_ref().take(prefix_len as usize).collect();
+                let suffix = subpatterns.collect();
+                let wild = Pat::wildcard_from_ty(ty);
+                PatKind::Slice { prefix, slice: Some(wild), suffix }
+            }
+            &ConstantValue(value, _) => PatKind::Constant { value },
+            &ConstantRange(lo, hi, ty, end, _) => PatKind::Range(PatRange {
+                lo: ty::Const::from_bits(cx.tcx, lo, ty::ParamEnv::empty().and(ty)),
+                hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)),
+                end,
+            }),
+            NonExhaustive => PatKind::Wild,
         };
 
         Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }
@@ -885,7 +945,7 @@ fn apply<'a>(
 
     /// Like `apply`, but where all the subpatterns are wildcards `_`.
     fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
-        let subpatterns = self.wildcard_subpatterns(cx, ty).rev();
+        let subpatterns = self.wildcard_subpatterns(cx, ty).into_iter().rev();
         self.apply(cx, ty, subpatterns)
     }
 }
@@ -932,7 +992,7 @@ fn apply_constructor(
     fn apply_wildcard(self, ty: Ty<'tcx>) -> Self {
         match self {
             UsefulWithWitness(witnesses) => {
-                let wild = Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild };
+                let wild = Pat::wildcard_from_ty(ty);
                 UsefulWithWitness(
                     witnesses
                         .into_iter()
@@ -1139,6 +1199,36 @@ fn all_constructors<'a, 'tcx>(
             }
         }
     };
+
+    // FIXME: currently the only way I know of something can
+    // be a privately-empty enum is when the exhaustive_patterns
+    // feature flag is not present, so this is only
+    // needed for that case.
+    let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
+    let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
+    let is_non_exhaustive = is_privately_empty
+        || is_declared_nonexhaustive
+        || (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching);
+    if is_non_exhaustive {
+        // If our scrutinee is *privately* an empty enum, we must treat it as though it had an
+        // "unknown" constructor (in that case, all other patterns obviously can't be variants) to
+        // avoid exposing its emptyness. See the `match_privately_empty` test for details.
+        //
+        // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an additionnal
+        // "unknown" constructor. However there is no point in enumerating all possible variants,
+        // because the user can't actually match against them themselves. So we return only the
+        // fictitious constructor.
+        // E.g., in an example like:
+        // ```
+        //     let err: io::ErrorKind = ...;
+        //     match err {
+        //         io::ErrorKind::NotFound => {},
+        //     }
+        // ```
+        // we don't want to show every possible IO error, but instead have only `_` as the witness.
+        return vec![NonExhaustive];
+    }
+
     ctors
 }
 
@@ -1537,9 +1627,6 @@ pub fn is_useful<'p, 'a, 'tcx>(
         let all_ctors = all_constructors(cx, pcx);
         debug!("all_ctors = {:#?}", all_ctors);
 
-        let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
-        let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
-
         // `missing_ctors` is the set of constructors from the same type as the
         // first column of `matrix` that are matched only by wildcard patterns
         // from the first column.
@@ -1547,38 +1634,15 @@ pub fn is_useful<'p, 'a, 'tcx>(
         // Therefore, if there is some pattern that is unmatched by `matrix`,
         // it will still be unmatched if the first constructor is replaced by
         // any of the constructors in `missing_ctors`
-        //
-        // However, if our scrutinee is *privately* an empty enum, we
-        // must treat it as though it had an "unknown" constructor (in
-        // that case, all other patterns obviously can't be variants)
-        // to avoid exposing its emptyness. See the `match_privately_empty`
-        // test for details.
-        //
-        // FIXME: currently the only way I know of something can
-        // be a privately-empty enum is when the exhaustive_patterns
-        // feature flag is not present, so this is only
-        // needed for that case.
-
-        // Missing constructors are those that are not matched by any
-        // non-wildcard patterns in the current column. To determine if
-        // the set is empty, we can check that `.peek().is_none()`, so
-        // we only fully construct them on-demand, because they're rarely used and can be big.
-        let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors);
 
-        debug!(
-            "missing_ctors.empty()={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
-            missing_ctors.is_empty(),
-            is_privately_empty,
-            is_declared_nonexhaustive
-        );
+        // Missing constructors are those that are not matched by any non-wildcard patterns in the
+        // current column. We only fully construct them on-demand, because they're rarely used and
+        // can be big.
+        let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors);
 
-        // For privately empty and non-exhaustive enums, we work as if there were an "extra"
-        // `_` constructor for the type, so we can never match over all constructors.
-        let is_non_exhaustive = is_privately_empty
-            || is_declared_nonexhaustive
-            || (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching);
+        debug!("missing_ctors.empty()={:#?}", missing_ctors.is_empty(),);
 
-        if missing_ctors.is_empty() && !is_non_exhaustive {
+        if missing_ctors.is_empty() {
             let (all_ctors, _) = missing_ctors.into_inner();
             split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None)
                 .into_iter()
@@ -1607,26 +1671,9 @@ pub fn is_useful<'p, 'a, 'tcx>(
             //
             // we can report 3 witnesses: `S`, `E`, and `W`.
             //
-            // However, there are 2 cases where we don't want
+            // However, there is a case where we don't want
             // to do this and instead report a single `_` witness:
-            //
-            // 1) If the user is matching against a non-exhaustive
-            // enum, there is no point in enumerating all possible
-            // variants, because the user can't actually match
-            // against them themselves, e.g., in an example like:
-            // ```
-            //     let err: io::ErrorKind = ...;
-            //     match err {
-            //         io::ErrorKind::NotFound => {},
-            //     }
-            // ```
-            // we don't want to show every possible IO error,
-            // but instead have `_` as the witness (this is
-            // actually *required* if the user specified *all*
-            // IO errors, but is probably what we want in every
-            // case).
-            //
-            // 2) If the user didn't actually specify a constructor
+            // if the user didn't actually specify a constructor
             // in this arm, e.g., in
             // ```
             //     let x: (Direction, Direction, bool) = ...;
@@ -1636,7 +1683,7 @@ pub fn is_useful<'p, 'a, 'tcx>(
             // `(<direction-1>, <direction-2>, true)` - we are
             // satisfied with `(_, _, true)`. In this case,
             // `used_ctors` is empty.
-            if is_non_exhaustive || missing_ctors.all_ctors_are_missing() {
+            if missing_ctors.all_ctors_are_missing() {
                 // All constructors are unused. Add a wild pattern
                 // rather than each individual constructor.
                 usefulness.apply_wildcard(pcx.ty)
@@ -1664,7 +1711,7 @@ fn is_useful_specialized<'p, 'a, 'tcx>(
 ) -> Usefulness<'tcx> {
     debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty);
 
-    let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty).collect();
+    let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty);
     let ctor_wild_subpatterns: Vec<_> = ctor_wild_subpatterns_owned.iter().collect();
     let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns);
     v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns)
@@ -1714,69 +1761,6 @@ fn pat_constructor<'tcx>(
     }
 }
 
-/// This computes the types of the sub patterns that a constructor should be
-/// expanded to.
-///
-/// For instance, a tuple pattern (43u32, 'a') has sub pattern types [u32, char].
-fn constructor_sub_pattern_tys<'a, 'tcx>(
-    cx: &MatchCheckCtxt<'a, 'tcx>,
-    ctor: &Constructor<'tcx>,
-    ty: Ty<'tcx>,
-) -> Vec<Ty<'tcx>> {
-    debug!("constructor_sub_pattern_tys({:#?}, {:?})", ctor, ty);
-    match ty.kind {
-        ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(),
-        ty::Slice(ty) | ty::Array(ty, _) => match *ctor {
-            FixedLenSlice(length) => (0..length).map(|_| ty).collect(),
-            VarLenSlice(prefix, suffix) => (0..prefix + suffix).map(|_| ty).collect(),
-            ConstantValue(..) => vec![],
-            _ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
-        },
-        ty::Ref(_, rty, _) => vec![rty],
-        ty::Adt(adt, substs) => {
-            if adt.is_box() {
-                // Use T as the sub pattern type of Box<T>.
-                vec![substs.type_at(0)]
-            } else {
-                let variant = &adt.variants[ctor.variant_index_for_adt(cx, adt)];
-                let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(ty);
-                variant
-                    .fields
-                    .iter()
-                    .map(|field| {
-                        let is_visible =
-                            adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
-                        let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs));
-                        match (is_visible, is_non_exhaustive, is_uninhabited) {
-                            // Treat all uninhabited types in non-exhaustive variants as `TyErr`.
-                            (_, true, true) => cx.tcx.types.err,
-                            // Treat all non-visible fields as `TyErr`. They can't appear in any
-                            // other pattern from this match (because they are private), so their
-                            // type does not matter - but we don't want to know they are
-                            // uninhabited.
-                            (false, ..) => cx.tcx.types.err,
-                            (true, ..) => {
-                                let ty = field.ty(cx.tcx, substs);
-                                match ty.kind {
-                                    // If the field type returned is an array of an unknown size
-                                    // return an TyErr.
-                                    ty::Array(_, len)
-                                        if len.try_eval_usize(cx.tcx, cx.param_env).is_none() =>
-                                    {
-                                        cx.tcx.types.err
-                                    }
-                                    _ => ty,
-                                }
-                            }
-                        }
-                    })
-                    .collect()
-            }
-        }
-        _ => vec![],
-    }
-}
-
 // 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.
@@ -2042,7 +2026,7 @@ fn range_borders(r: IntRange<'_>) -> impl Iterator<Item = Border> {
                 //
                 // Of course, if fixed-length patterns exist, we must be sure
                 // that our length is large enough to miss them all, so
-                // we can pick `L = max(FIXED_LEN+1 ∪ {max(PREFIX_LEN) + max(SUFFIX_LEN)})`
+                // we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))`
                 //
                 // for example, with the above pair of patterns, all elements
                 // but the first and last can be added/removed, so any
@@ -2080,9 +2064,24 @@ fn range_borders(r: IntRange<'_>) -> impl Iterator<Item = Border> {
                     }
                 }
 
-                let max_slice_length = cmp::max(max_fixed_len + 1, max_prefix_len + max_suffix_len);
-                split_ctors
-                    .extend((self_prefix + self_suffix..=max_slice_length).map(FixedLenSlice))
+                // For diagnostics, we keep the prefix and suffix lengths separate, so in the case
+                // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly,
+                // so that `L = max_prefix_len + max_suffix_len`.
+                if max_fixed_len + 1 >= max_prefix_len + max_suffix_len {
+                    // The subtraction can't overflow thanks to the above check.
+                    // The new `max_prefix_len` is also guaranteed to be larger than its previous
+                    // value.
+                    max_prefix_len = max_fixed_len + 1 - max_suffix_len;
+                }
+
+                // `ctor` originally covered the range `(self_prefix + self_suffix..infinity)`. We
+                // now split it into two: lengths smaller than `max_prefix_len + max_suffix_len`
+                // are treated independently as fixed-lengths slices, and lengths above are
+                // captured by a final VarLenSlice constructor.
+                split_ctors.extend(
+                    (self_prefix + self_suffix..max_prefix_len + max_suffix_len).map(FixedLenSlice),
+                );
+                split_ctors.push(VarLenSlice(max_prefix_len, max_suffix_len));
             }
             // Any other constructor can be used unchanged.
             _ => split_ctors.push(ctor),
@@ -2211,13 +2210,21 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>(
 /// fields filled with wild patterns.
 fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>(
     cx: &mut MatchCheckCtxt<'a, 'tcx>,
-    pat: &'q Pat<'tcx>,
+    mut pat: &'q Pat<'tcx>,
     constructor: &Constructor<'tcx>,
     ctor_wild_subpatterns: &[&'p Pat<'tcx>],
 ) -> Option<PatStack<'p, 'tcx>> {
+    while let PatKind::AscribeUserType { ref subpattern, .. } = *pat.kind {
+        pat = subpattern;
+    }
+
+    if let NonExhaustive = constructor {
+        // Only a wildcard pattern can match the special extra constructor
+        return if pat.is_wildcard() { Some(PatStack::default()) } else { None };
+    }
+
     let result = match *pat.kind {
-        PatKind::AscribeUserType { ref subpattern, .. } => PatStack::from_pattern(subpattern)
-            .specialize_constructor(cx, constructor, ctor_wild_subpatterns),
+        PatKind::AscribeUserType { .. } => bug!(), // Handled above
 
         PatKind::Binding { .. } | PatKind::Wild => {
             Some(PatStack::from_slice(ctor_wild_subpatterns))