diff options
| author | Nadrieril <nadrieril+git@gmail.com> | 2023-12-11 13:32:34 +0100 |
|---|---|---|
| committer | Nadrieril <nadrieril+git@gmail.com> | 2023-12-15 16:57:36 +0100 |
| commit | b111b2e8390c4fe0b1a5c1c8a7e75f7ca7df9fcf (patch) | |
| tree | b8680383ff6d19cdea4194ec7d39c7fe7454a90e /compiler/rustc_pattern_analysis/src | |
| parent | 4d1bd0db7f489b22c6d8aa2385937a95412c015b (diff) | |
| download | rust-b111b2e8390c4fe0b1a5c1c8a7e75f7ca7df9fcf.tar.gz rust-b111b2e8390c4fe0b1a5c1c8a7e75f7ca7df9fcf.zip | |
Split `Single` ctor into more specific variants
Diffstat (limited to 'compiler/rustc_pattern_analysis/src')
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/constructor.rs | 48 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/cx.rs | 73 | ||||
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/usefulness.rs | 9 |
3 files changed, 79 insertions, 51 deletions
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 3bca7894a29..95ffece54f3 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -631,11 +631,16 @@ impl OpaqueId { /// `Fields`. #[derive(Clone, Debug, PartialEq)] pub enum Constructor<'tcx> { - /// The constructor for patterns that have a single constructor, like tuples, struct patterns, - /// and references. Fixed-length arrays are treated separately with `Slice`. - Single, + /// Tuples and structs. + Struct, /// Enum variants. Variant(VariantIdx), + /// References + Ref, + /// Array and slice patterns. + Slice(Slice), + /// Union field accesses. + UnionField, /// Booleans Bool(bool), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). @@ -645,8 +650,6 @@ pub enum Constructor<'tcx> { F64Range(IeeeFloat<DoubleS>, IeeeFloat<DoubleS>, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. Str(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. @@ -723,7 +726,9 @@ impl<'tcx> Constructor<'tcx> { // Only a wildcard pattern can match these special constructors. (Missing { .. } | NonExhaustive | Hidden, _) => false, - (Single, Single) => true, + (Struct, Struct) => true, + (Ref, Ref) => true, + (UnionField, UnionField) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, (Bool(self_b), Bool(other_b)) => self_b == other_b, @@ -786,12 +791,15 @@ pub enum VariantVisibility { /// `exhaustive_patterns` feature. #[derive(Debug)] pub enum ConstructorSet { - /// The type has a single constructor, e.g. `&T` or a struct. `empty` tracks whether the - /// constructor is empty. - Single { empty: bool }, + /// The type is a tuple or struct. `empty` tracks whether the type is empty. + Struct { empty: bool }, /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. Variants { variants: IndexVec<VariantIdx, VariantVisibility>, non_exhaustive: bool }, + /// The type is `&T`. + Ref, + /// The type is a union. + Union, /// Booleans. Bool, /// The type is spanned by integer values. The range or ranges give the set of allowed values. @@ -866,13 +874,27 @@ impl ConstructorSet { } match self { - ConstructorSet::Single { empty } => { + ConstructorSet::Struct { empty } => { if !seen.is_empty() { - present.push(Single); + present.push(Struct); } else if *empty { - missing_empty.push(Single); + missing_empty.push(Struct); + } else { + missing.push(Struct); + } + } + ConstructorSet::Ref => { + if !seen.is_empty() { + present.push(Ref); + } else { + missing.push(Ref); + } + } + ConstructorSet::Union => { + if !seen.is_empty() { + present.push(UnionField); } else { - missing.push(Single); + missing.push(UnionField); } } ConstructorSet::Variants { variants, non_exhaustive } => { diff --git a/compiler/rustc_pattern_analysis/src/cx.rs b/compiler/rustc_pattern_analysis/src/cx.rs index 8a4f39a1f4a..81c836b878e 100644 --- a/compiler/rustc_pattern_analysis/src/cx.rs +++ b/compiler/rustc_pattern_analysis/src/cx.rs @@ -105,7 +105,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { ) -> VariantIdx { match *ctor { Variant(idx) => idx, - Single => { + Struct | UnionField => { assert!(!adt.is_enum()); FIRST_VARIANT } @@ -123,9 +123,8 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { ) -> &'p [DeconstructedPat<'p, 'tcx>] { let cx = self; match ctor { - Single | Variant(_) => match ty.kind() { + Struct | Variant(_) | UnionField => match ty.kind() { ty::Tuple(fs) => cx.alloc_wildcard_slice(fs.iter()), - ty::Ref(_, rty, _) => cx.alloc_wildcard_slice(once(*rty)), ty::Adt(adt, args) => { if adt.is_box() { // The only legal patterns of type `Box` (outside `std`) are `_` and box @@ -138,7 +137,11 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { cx.alloc_wildcard_slice(tys) } } - _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), + _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), + }, + Ref => match ty.kind() { + ty::Ref(_, rty, _) => cx.alloc_wildcard_slice(once(*rty)), + _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"), }, Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { @@ -167,9 +170,8 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { /// `Fields::wildcards`. pub(crate) fn ctor_arity(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> usize { match ctor { - Single | Variant(_) => match ty.kind() { + Struct | Variant(_) | UnionField => match ty.kind() { ty::Tuple(fs) => fs.len(), - ty::Ref(..) => 1, ty::Adt(adt, ..) => { if adt.is_box() { // The only legal patterns of type `Box` (outside `std`) are `_` and box @@ -181,8 +183,9 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { self.list_variant_nonhidden_fields(ty, variant).count() } } - _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), + _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), }, + Ref => 1, Slice(slice) => slice.arity(), Bool(..) | IntRange(..) @@ -298,9 +301,9 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive } } } - ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => { - ConstructorSet::Single { empty: cx.is_uninhabited(ty) } - } + ty::Adt(def, _) if def.is_union() => ConstructorSet::Union, + ty::Adt(..) | ty::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(ty) }, + ty::Ref(..) => ConstructorSet::Ref, ty::Never => ConstructorSet::NoConstructors, // This type is one for which we cannot list constructors, like `str` or `f64`. // FIXME(Nadrieril): which of these are actually allowed? @@ -359,13 +362,18 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { fields = &[]; } PatKind::Deref { subpattern } => { - ctor = Single; fields = singleton(self.lower_pat(subpattern)); + ctor = match pat.ty.kind() { + // This is a box pattern. + ty::Adt(adt, ..) if adt.is_box() => Struct, + ty::Ref(..) => Ref, + _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty), + }; } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { match pat.ty.kind() { ty::Tuple(fs) => { - ctor = Single; + ctor = Struct; let mut wilds: SmallVec<[_; 2]> = fs.iter().map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect(); for pat in subpatterns { @@ -380,7 +388,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { // _)` or a box pattern. As a hack to avoid an ICE with the former, we // ignore other fields than the first one. This will trigger an error later // anyway. - // See https://github.com/rust-lang/rust/issues/82772 , + // See https://github.com/rust-lang/rust/issues/82772, // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 // The problem is that we can't know from the type whether we'll match // normally or through box-patterns. We'll have to figure out a proper @@ -392,12 +400,13 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { } else { DeconstructedPat::wildcard(args.type_at(0), pat.span) }; - ctor = Single; + ctor = Struct; fields = singleton(pat); } ty::Adt(adt, _) => { ctor = match pat.kind { - PatKind::Leaf { .. } => Single, + PatKind::Leaf { .. } if adt.is_union() => UnionField, + PatKind::Leaf { .. } => Struct, PatKind::Variant { variant_index, .. } => Variant(variant_index), _ => bug!(), }; @@ -477,11 +486,11 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { // with other `Deref` patterns. This could have been done in `const_to_pat`, // but that causes issues with the rest of the matching code. // So here, the constructor for a `"foo"` pattern is `&` (represented by - // `Single`), and has one field. That field has constructor `Str(value)` and no - // fields. + // `Ref`), and has one field. That field has constructor `Str(value)` and no + // subfields. // Note: `t` is `str`, not `&str`. let subpattern = DeconstructedPat::new(Str(*value), &[], *t, pat.span); - ctor = Single; + ctor = Ref; fields = singleton(subpattern) } // All constants that can be structurally matched have already been expanded @@ -657,7 +666,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { let kind = match pat.ctor() { Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) }, IntRange(range) => return self.hoist_pat_range(range, pat.ty()), - Single | Variant(_) => match pat.ty().kind() { + Struct | Variant(_) | UnionField => match pat.ty().kind() { ty::Tuple(..) => PatKind::Leaf { subpatterns: subpatterns .enumerate() @@ -686,13 +695,13 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { PatKind::Leaf { subpatterns } } } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to reconstruct the correct constant pattern here. However a string - // literal pattern will never be reported as a non-exhaustiveness witness, so we - // ignore this issue. - ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()), }, + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to reconstruct the correct constant pattern here. However a string + // literal pattern will never be reported as a non-exhaustiveness witness, so we + // ignore this issue. + Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, Slice(slice) => { match slice.kind { SliceKind::FixedLen(_) => PatKind::Slice { @@ -758,7 +767,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { let mut start_or_comma = || start_or_continue(", "); match pat.ctor() { - Single | Variant(_) => match pat.ty().kind() { + Struct | Variant(_) | UnionField => match pat.ty().kind() { ty::Adt(def, _) if def.is_box() => { // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside // of `std`). So this branch is only reachable when the feature is enabled and @@ -789,15 +798,15 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { } write!(f, ")") } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to detect strings here. However a string literal pattern will never - // be reported as a non-exhaustiveness witness, so we can ignore this issue. - ty::Ref(_, _, mutbl) => { - let subpattern = pat.iter_fields().next().unwrap(); - write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern) - } _ => write!(f, "_"), }, + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to detect strings here. However a string literal pattern will never + // be reported as a non-exhaustiveness witness, so we can ignore this issue. + Ref => { + let subpattern = pat.iter_fields().next().unwrap(); + write!(f, "&{:?}", subpattern) + } Slice(slice) => { let mut subpatterns = pat.iter_fields(); write!(f, "[")?; diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index f268a551547..a2467ae6a39 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -629,12 +629,9 @@ impl ValidityConstraint { /// /// Pending further opsem decisions, the current behavior is: validity is preserved, except /// inside `&` and union fields where validity is reset to `MaybeInvalid`. - fn specialize<'tcx>(self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) -> Self { + fn specialize(self, ctor: &Constructor<'_>) -> Self { // We preserve validity except when we go inside a reference or a union field. - if matches!(ctor, Constructor::Single) - && (matches!(pcx.ty.kind(), ty::Ref(..)) - || matches!(pcx.ty.kind(), ty::Adt(def, ..) if def.is_union())) - { + if matches!(ctor, Constructor::Ref | Constructor::UnionField) { // Validity of `x: &T` does not imply validity of `*x: T`. MaybeInvalid } else { @@ -902,7 +899,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { ctor: &Constructor<'tcx>, ) -> Matrix<'p, 'tcx> { let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor); - let new_validity = self.place_validity[0].specialize(pcx, ctor); + let new_validity = self.place_validity[0].specialize(ctor); let new_place_validity = std::iter::repeat(new_validity) .take(ctor.arity(pcx)) .chain(self.place_validity[1..].iter().copied()) |
