From dc6c89aee9347d509e720d907dbed7bbb13f1637 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Sat, 18 Sep 2021 16:52:43 -0400 Subject: [PATCH 01/26] Filter unstable and doc hidden variants in exhaustive check --- .../src/thir/pattern/deconstruct_pat.rs | 21 +++++++++- .../src/thir/pattern/usefulness.rs | 39 ++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index dfcbd0da3a6e1..6d4828fb2dec3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -48,6 +48,7 @@ use self::SliceKind::*; use super::compare_const_vals; use super::usefulness::{MatchCheckCtxt, PatCtxt}; +use rustc_attr::{Stability, StabilityLevel}; use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; @@ -941,8 +942,24 @@ impl<'tcx> SplitWildcard<'tcx> { smallvec![NonExhaustive] } else if is_declared_nonexhaustive { def.variants - .indices() - .map(|idx| Variant(idx)) + .iter_enumerated() + .filter_map(|(idx, def)| { + let x = cx.tcx.lookup_stability(def.def_id); + if let Some(Stability { + feature, + level: StabilityLevel::Unstable { .. }, + }) = x + { + // The unstable feature is not activated so ignore this variant + if !cx.tcx.stability().active_features.contains(&feature) { + None + } else { + Some(Variant(idx)) + } + } else { + Some(Variant(idx)) + } + }) .chain(Some(NonExhaustive)) .collect() } else if cx.tcx.features().exhaustive_patterns { diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 43adef3d03bed..22fac8dd50fba 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -293,7 +293,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::fmt; @@ -589,11 +589,38 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { // constructor, that matches everything that can be built with // it. For example, if `ctor` is a `Constructor::Variant` for // `Option::Some`, we get the pattern `Some(_)`. - split_wildcard - .iter_missing(pcx) - .cloned() - .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) - .collect() + let mut new = vec![]; + for missing_ctor in split_wildcard.iter_missing(pcx) { + if let Constructor::Variant(idx) = missing_ctor { + if let ty::Adt(adt, _) = pcx.ty.kind() { + if pcx + .cx + .tcx + .get_attrs(adt.variants[*idx].def_id) + .iter() + .find_map(|attr| { + if attr.has_name(sym::doc) { + attr.meta_item_list() + } else { + None + } + }) + .map(|items| { + items + .iter() + .find(|item| item.has_name(sym::hidden)) + .is_some() + }) + .unwrap_or_default() + { + new = vec![DeconstructedPat::wildcard(pcx.ty)]; + break; + } + } + } + new.push(DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) + } + new }; witnesses From 068325b17b0368be38750cdc25e8a594daa4a8b1 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 20 Sep 2021 17:10:53 -0400 Subject: [PATCH 02/26] Add test cases for unstable variants --- .../auxiliary/unstable.rs | 13 ++++++++++ .../stable-reachable.rs | 22 ++++++++++++++++ .../unstable-reachable.rs | 25 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs create mode 100644 src/test/ui/rfc-2008-non-exhaustive/stable-reachable.rs create mode 100644 src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.rs diff --git a/src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs b/src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs new file mode 100644 index 0000000000000..4341b98e45273 --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs @@ -0,0 +1,13 @@ +#![feature(staged_api)] +#![stable(feature = "stable_test_feature", since = "1.0.0")] + +#[stable(feature = "stable_test_feature", since = "1.0.0")] +#[non_exhaustive] +pub enum Foo { + #[stable(feature = "stable_test_feature", since = "1.0.0")] + Stable, + #[stable(feature = "stable_test_feature", since = "1.0.0")] + Stable2, + #[unstable(feature = "unstable_test_feature", issue = "none")] + Unstable, +} diff --git a/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.rs b/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.rs new file mode 100644 index 0000000000000..f6a73bf5cbe7e --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.rs @@ -0,0 +1,22 @@ +// aux-build:unstable.rs + +extern crate unstable; + +use unstable::Foo; + +fn main() { + #[deny(non_exhaustive_omitted_patterns)] + match Foo::Stable { + Foo::Stable => {} + _ => {} + } + //~^^ some variants are not matched explicitly + + // Ok: all variants are explicitly matched + #[deny(non_exhaustive_omitted_patterns)] + match Foo::Stable { + Foo::Stable => {} + Foo::Stable2 => {} + _ => {} + } +} diff --git a/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.rs b/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.rs new file mode 100644 index 0000000000000..8ffaba479261b --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.rs @@ -0,0 +1,25 @@ +#![feature(unstable_test_feature)] + +// aux-build:unstable.rs + +extern crate unstable; + +use unstable::Foo; + +fn main() { + #[deny(non_exhaustive_omitted_patterns)] + match Foo::Stable { + Foo::Stable => {} + _ => {} + } + //~^^ some variants are not matched explicitly + + // Ok: all variants are explicitly matched + #[deny(non_exhaustive_omitted_patterns)] + match Foo::Stable { + Foo::Stable => {} + Foo::Stable2 => {} + Foo::Unstable => {} + _ => {} + } +} From 6f86964d6ab7fc76873ce062722aa2fa63cb4eeb Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 20 Sep 2021 16:54:30 -0400 Subject: [PATCH 03/26] Diagnostic output for unstable variant tests --- .../stable-reachable.stderr | 16 ++++++++++++++++ .../unstable-reachable.stderr | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/test/ui/rfc-2008-non-exhaustive/stable-reachable.stderr create mode 100644 src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.stderr diff --git a/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.stderr b/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.stderr new file mode 100644 index 0000000000000..fcfe793a1edec --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.stderr @@ -0,0 +1,16 @@ +error: some variants are not matched explicitly + --> $DIR/stable-reachable.rs:11:9 + | +LL | _ => {} + | ^ pattern `Stable2` not covered + | +note: the lint level is defined here + --> $DIR/stable-reachable.rs:8:12 + | +LL | #[deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: ensure that all variants are matched explicitly by adding the suggested match arms + = note: the matched value is of type `Foo` and the `non_exhaustive_omitted_patterns` attribute was found + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.stderr b/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.stderr new file mode 100644 index 0000000000000..b6b69eccaefba --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.stderr @@ -0,0 +1,16 @@ +error: some variants are not matched explicitly + --> $DIR/unstable-reachable.rs:13:9 + | +LL | _ => {} + | ^ patterns `Stable2` and `Unstable` not covered + | +note: the lint level is defined here + --> $DIR/unstable-reachable.rs:10:12 + | +LL | #[deny(non_exhaustive_omitted_patterns)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: ensure that all variants are matched explicitly by adding the suggested match arms + = note: the matched value is of type `Foo` and the `non_exhaustive_omitted_patterns` attribute was found + +error: aborting due to previous error + From 3dd5ad23a362289420eb1667064b007bb1a0a6c7 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 20 Sep 2021 16:55:04 -0400 Subject: [PATCH 04/26] Add test cases for doc hidden variants --- .../ui/pattern/usefulness/auxiliary/hidden.rs | 6 ++++ .../usefulness/doc-hidden-non-exhaustive.rs | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/test/ui/pattern/usefulness/auxiliary/hidden.rs create mode 100644 src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs diff --git a/src/test/ui/pattern/usefulness/auxiliary/hidden.rs b/src/test/ui/pattern/usefulness/auxiliary/hidden.rs new file mode 100644 index 0000000000000..742b7e82c16b0 --- /dev/null +++ b/src/test/ui/pattern/usefulness/auxiliary/hidden.rs @@ -0,0 +1,6 @@ +pub enum Foo { + A, + B, + #[doc(hidden)] + C, +} diff --git a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs new file mode 100644 index 0000000000000..54bef73c66090 --- /dev/null +++ b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs @@ -0,0 +1,30 @@ +// aux-build:hidden.rs + +extern crate hidden; + +use hidden::Foo; + +fn main() { + match Foo::A { + Foo::A => {} + Foo::B => {} + } + //~^^^^ non-exhaustive patterns: `_` not covered + + match Foo::A { + Foo::A => {} + Foo::C => {} + } + //~^^^^ non-exhaustive patterns: `B` not covered + + match Foo::A { + Foo::A => {} + } + //~^^^ non-exhaustive patterns: `_` not covered + + match None { + None => {} + Some(Foo::A) => {} + } + //~^^^^ non-exhaustive patterns: `Some(_)` not covered +} From 0d58694d6767bb68a4228fe6bbc277b0186ea0a1 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 20 Sep 2021 16:55:36 -0400 Subject: [PATCH 05/26] Diagnostic output for doc hidden variants --- .../usefulness/doc-hidden-non-exhaustive.rs | 4 +- .../doc-hidden-non-exhaustive.stderr | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr diff --git a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs index 54bef73c66090..a1dcab0931423 100644 --- a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs +++ b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs @@ -20,11 +20,11 @@ fn main() { match Foo::A { Foo::A => {} } - //~^^^ non-exhaustive patterns: `_` not covered + //~^^^ non-exhaustive patterns: `B` and `_` not covered match None { None => {} Some(Foo::A) => {} } - //~^^^^ non-exhaustive patterns: `Some(_)` not covered + //~^^^^ non-exhaustive patterns: `Some(B)` and `Some(_)` not covered } diff --git a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr new file mode 100644 index 0000000000000..0c8239c75365d --- /dev/null +++ b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr @@ -0,0 +1,49 @@ +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/doc-hidden-non-exhaustive.rs:8:11 + | +LL | match Foo::A { + | ^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error[E0004]: non-exhaustive patterns: `B` not covered + --> $DIR/doc-hidden-non-exhaustive.rs:14:11 + | +LL | match Foo::A { + | ^^^^^^ pattern `B` not covered + | + ::: $DIR/auxiliary/hidden.rs:3:5 + | +LL | B, + | - not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/doc-hidden-non-exhaustive.rs:20:11 + | +LL | match Foo::A { + | ^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/doc-hidden-non-exhaustive.rs:25:11 + | +LL | match None { + | ^^^^ pattern `Some(_)` not covered + | + ::: $SRC_DIR/core/src/option.rs:LL:COL + | +LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), + | ---- not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Option` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0004`. From 311333b589631d6f1d698472ef8e90e6c818d647 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 20 Sep 2021 17:19:33 -0400 Subject: [PATCH 06/26] Remove io::ErrorKind and update reachable-patterns diagnostic output --- .../reachable-patterns.rs | 29 ---------------- .../reachable-patterns.stderr | 34 ++++++------------- 2 files changed, 10 insertions(+), 53 deletions(-) diff --git a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs index c196ded404ddc..21c9dc28994f3 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs @@ -92,35 +92,6 @@ fn main() { //~^^ some variants are not matched explicitly //~^^^^^ some variants are not matched explicitly - // The io::ErrorKind has many `unstable` fields how do they interact with this - // lint - #[deny(non_exhaustive_omitted_patterns)] - match std::io::ErrorKind::Other { - std::io::ErrorKind::NotFound => {} - std::io::ErrorKind::PermissionDenied => {} - std::io::ErrorKind::ConnectionRefused => {} - std::io::ErrorKind::ConnectionReset => {} - std::io::ErrorKind::ConnectionAborted => {} - std::io::ErrorKind::NotConnected => {} - std::io::ErrorKind::AddrInUse => {} - std::io::ErrorKind::AddrNotAvailable => {} - std::io::ErrorKind::BrokenPipe => {} - std::io::ErrorKind::AlreadyExists => {} - std::io::ErrorKind::WouldBlock => {} - std::io::ErrorKind::InvalidInput => {} - std::io::ErrorKind::InvalidData => {} - std::io::ErrorKind::TimedOut => {} - std::io::ErrorKind::WriteZero => {} - std::io::ErrorKind::Interrupted => {} - std::io::ErrorKind::Other => {} - std::io::ErrorKind::UnexpectedEof => {} - std::io::ErrorKind::Unsupported => {} - std::io::ErrorKind::OutOfMemory => {} - // All stable variants are above and unstable in `_` - _ => {} - } - //~^^ some variants are not matched explicitly - #[warn(non_exhaustive_omitted_patterns)] match VariantNonExhaustive::Baz(1, 2) { VariantNonExhaustive::Baz(_, _) => {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr index e66fd8008a10b..4bce850eed0d9 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr @@ -1,11 +1,11 @@ warning: some fields are not explicitly listed - --> $DIR/reachable-patterns.rs:127:9 + --> $DIR/reachable-patterns.rs:98:9 | LL | VariantNonExhaustive::Bar { x, .. } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `y` not listed | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:124:12 + --> $DIR/reachable-patterns.rs:95:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,13 +13,13 @@ LL | #[warn(non_exhaustive_omitted_patterns)] = note: the pattern is of type `VariantNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found warning: some fields are not explicitly listed - --> $DIR/reachable-patterns.rs:132:9 + --> $DIR/reachable-patterns.rs:103:9 | LL | let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `third_field` not listed | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:131:12 + --> $DIR/reachable-patterns.rs:102:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,13 +27,13 @@ LL | #[warn(non_exhaustive_omitted_patterns)] = note: the pattern is of type `FunctionalRecord` and the `non_exhaustive_omitted_patterns` attribute was found warning: some fields are not explicitly listed - --> $DIR/reachable-patterns.rs:140:29 + --> $DIR/reachable-patterns.rs:111:29 | LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `second_field` not listed | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:139:12 + --> $DIR/reachable-patterns.rs:110:12 | LL | #[warn(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | #[warn(non_exhaustive_omitted_patterns)] = note: the pattern is of type `NormalStruct` and the `non_exhaustive_omitted_patterns` attribute was found warning: some fields are not explicitly listed - --> $DIR/reachable-patterns.rs:140:9 + --> $DIR/reachable-patterns.rs:111:9 | LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `foo` not listed @@ -115,32 +115,18 @@ LL | _ => {} = note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/reachable-patterns.rs:120:9 - | -LL | _ => {} - | ^ patterns `HostUnreachable`, `NetworkUnreachable`, `NetworkDown` and 18 more not covered - | -note: the lint level is defined here - --> $DIR/reachable-patterns.rs:97:12 - | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `ErrorKind` and the `non_exhaustive_omitted_patterns` attribute was found - -error: some variants are not matched explicitly - --> $DIR/reachable-patterns.rs:157:9 + --> $DIR/reachable-patterns.rs:124:9 | LL | _ => {} | ^ pattern `A(_)` not covered | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:155:12 + --> $DIR/reachable-patterns.rs:122:12 | LL | #[deny(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: ensure that all variants are matched explicitly by adding the suggested match arms = note: the matched value is of type `NonExhaustiveSingleVariant` and the `non_exhaustive_omitted_patterns` attribute was found -error: aborting due to 7 previous errors; 4 warnings emitted +error: aborting due to 6 previous errors; 4 warnings emitted From 9927c1f786894d381838d929a2cad42341e11267 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 20 Sep 2021 17:09:15 -0400 Subject: [PATCH 07/26] Fix behavior of doc hidden filtering --- .../src/thir/pattern/usefulness.rs | 67 +++++++++++-------- .../doc-hidden-non-exhaustive.stderr | 13 ++-- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 22fac8dd50fba..5926e64660a7a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -585,41 +585,52 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { } else { let mut split_wildcard = SplitWildcard::new(pcx); split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); + + // This lets us know if we skipped any variants because they are marked + // `doc(hidden)`. + let mut doc_hidden_variant = false; // Construct for each missing constructor a "wild" version of this // constructor, that matches everything that can be built with // it. For example, if `ctor` is a `Constructor::Variant` for // `Option::Some`, we get the pattern `Some(_)`. - let mut new = vec![]; - for missing_ctor in split_wildcard.iter_missing(pcx) { - if let Constructor::Variant(idx) = missing_ctor { - if let ty::Adt(adt, _) = pcx.ty.kind() { - if pcx - .cx - .tcx - .get_attrs(adt.variants[*idx].def_id) - .iter() - .find_map(|attr| { - if attr.has_name(sym::doc) { - attr.meta_item_list() - } else { - None - } - }) - .map(|items| { - items - .iter() - .find(|item| item.has_name(sym::hidden)) - .is_some() - }) - .unwrap_or_default() - { - new = vec![DeconstructedPat::wildcard(pcx.ty)]; - break; + let mut new: Vec> = split_wildcard + .iter_missing(pcx) + .filter_map(|missing_ctor| { + if let Constructor::Variant(idx) = missing_ctor { + if let ty::Adt(adt, _) = pcx.ty.kind() { + if pcx + .cx + .tcx + .get_attrs(adt.variants[*idx].def_id) + .iter() + .find_map(|attr| { + if attr.has_name(sym::doc) { + attr.meta_item_list() + } else { + None + } + }) + .map(|items| { + items + .iter() + .find(|item| item.has_name(sym::hidden)) + .is_some() + }) + .unwrap_or_default() + { + doc_hidden_variant = true; + return None; + } } } - } - new.push(DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) + Some(DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone())) + }) + .collect(); + + if doc_hidden_variant { + new.push(DeconstructedPat::wildcard(pcx.ty)); } + new }; diff --git a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr index 0c8239c75365d..6c9539822b3dd 100644 --- a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr +++ b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr @@ -21,20 +21,25 @@ LL | B, = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `Foo` -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `B` and `_` not covered --> $DIR/doc-hidden-non-exhaustive.rs:20:11 | LL | match Foo::A { - | ^^^^^^ pattern `_` not covered + | ^^^^^^ patterns `B` and `_` not covered + | + ::: $DIR/auxiliary/hidden.rs:3:5 + | +LL | B, + | - not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `Foo` -error[E0004]: non-exhaustive patterns: `Some(_)` not covered +error[E0004]: non-exhaustive patterns: `Some(B)` and `Some(_)` not covered --> $DIR/doc-hidden-non-exhaustive.rs:25:11 | LL | match None { - | ^^^^ pattern `Some(_)` not covered + | ^^^^ patterns `Some(B)` and `Some(_)` not covered | ::: $SRC_DIR/core/src/option.rs:LL:COL | From b02590de7c1fa4e1dfd1e99546d866babf78beb1 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Tue, 5 Oct 2021 08:56:06 -0400 Subject: [PATCH 08/26] Cleanup check for unstable attributes on variants --- .../src/thir/pattern/deconstruct_pat.rs | 70 ++++++++----------- .../src/thir/pattern/usefulness.rs | 3 +- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 6d4828fb2dec3..bd4ab020a4544 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -48,11 +48,11 @@ use self::SliceKind::*; use super::compare_const_vals; use super::usefulness::{MatchCheckCtxt, PatCtxt}; -use rustc_attr::{Stability, StabilityLevel}; use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; use rustc_hir::{HirId, RangeEnd}; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::mir::Field; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; @@ -930,52 +930,38 @@ impl<'tcx> SplitWildcard<'tcx> { // witness. let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); + let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns; + // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it // as though it had an "unknown" constructor to avoid exposing its emptiness. The // exception is if the pattern is at the top level, because we want empty matches to be // considered exhaustive. - let is_secretly_empty = def.variants.is_empty() - && !cx.tcx.features().exhaustive_patterns - && !pcx.is_top_level; - - if is_secretly_empty { - smallvec![NonExhaustive] - } else if is_declared_nonexhaustive { - def.variants - .iter_enumerated() - .filter_map(|(idx, def)| { - let x = cx.tcx.lookup_stability(def.def_id); - if let Some(Stability { - feature, - level: StabilityLevel::Unstable { .. }, - }) = x - { - // The unstable feature is not activated so ignore this variant - if !cx.tcx.stability().active_features.contains(&feature) { - None - } else { - Some(Variant(idx)) - } - } else { - Some(Variant(idx)) - } - }) - .chain(Some(NonExhaustive)) - .collect() - } else if cx.tcx.features().exhaustive_patterns { - // If `exhaustive_patterns` is enabled, we exclude variants known to be - // uninhabited. - def.variants - .iter_enumerated() - .filter(|(_, v)| { - !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) - .contains(cx.tcx, cx.module) - }) - .map(|(idx, _)| Variant(idx)) - .collect() - } else { - def.variants.indices().map(|idx| Variant(idx)).collect() + let is_secretly_empty = + def.variants.is_empty() && !is_exhaustive_pat_feature && !pcx.is_top_level; + + let mut ctors: SmallVec<[_; 1]> = def + .variants + .iter_enumerated() + .filter(|(_, v)| { + // Filter variants that depend on a disabled unstalbe feature. + let is_enabled = !matches!( + cx.tcx.eval_stability(v.def_id, None, DUMMY_SP, None), + EvalResult::Deny { .. } + ); + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + let is_uninhabited = is_exhaustive_pat_feature + && v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) + .contains(cx.tcx, cx.module); + is_enabled && !is_uninhabited + }) + .map(|(idx, _)| Variant(idx)) + .collect(); + + if is_secretly_empty || is_declared_nonexhaustive { + ctors.push(NonExhaustive); } + ctors } ty::Char => { smallvec![ diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 5926e64660a7a..afc842a6c44e5 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -596,6 +596,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { let mut new: Vec> = split_wildcard .iter_missing(pcx) .filter_map(|missing_ctor| { + // Check if this variant is marked `doc(hidden)` if let Constructor::Variant(idx) = missing_ctor { if let ty::Adt(adt, _) = pcx.ty.kind() { if pcx @@ -616,7 +617,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { .find(|item| item.has_name(sym::hidden)) .is_some() }) - .unwrap_or_default() + .unwrap_or(false) { doc_hidden_variant = true; return None; From 5d5b381e998fd23367477006aa2c0134021af8af Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Tue, 5 Oct 2021 09:16:17 -0400 Subject: [PATCH 09/26] Move unstable patterns test to patterns/usefulness --- .../usefulness}/auxiliary/unstable.rs | 1 - .../usefulness/stable-gated-patterns.rs} | 6 +----- .../usefulness/stable-gated-patterns.stderr | 17 +++++++++++++++++ .../usefulness/unstable-gated-patterns.rs} | 7 ++----- .../usefulness/unstable-gated-patterns.stderr | 17 +++++++++++++++++ .../reachable-patterns.stderr | 4 ++-- .../stable-reachable.stderr | 16 ---------------- .../unstable-reachable.stderr | 16 ---------------- 8 files changed, 39 insertions(+), 45 deletions(-) rename src/test/ui/{rfc-2008-non-exhaustive => pattern/usefulness}/auxiliary/unstable.rs (95%) rename src/test/ui/{rfc-2008-non-exhaustive/stable-reachable.rs => pattern/usefulness/stable-gated-patterns.rs} (61%) create mode 100644 src/test/ui/pattern/usefulness/stable-gated-patterns.stderr rename src/test/ui/{rfc-2008-non-exhaustive/unstable-reachable.rs => pattern/usefulness/unstable-gated-patterns.rs} (65%) create mode 100644 src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr delete mode 100644 src/test/ui/rfc-2008-non-exhaustive/stable-reachable.stderr delete mode 100644 src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.stderr diff --git a/src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs b/src/test/ui/pattern/usefulness/auxiliary/unstable.rs similarity index 95% rename from src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs rename to src/test/ui/pattern/usefulness/auxiliary/unstable.rs index 4341b98e45273..3142489c86103 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs +++ b/src/test/ui/pattern/usefulness/auxiliary/unstable.rs @@ -2,7 +2,6 @@ #![stable(feature = "stable_test_feature", since = "1.0.0")] #[stable(feature = "stable_test_feature", since = "1.0.0")] -#[non_exhaustive] pub enum Foo { #[stable(feature = "stable_test_feature", since = "1.0.0")] Stable, diff --git a/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.rs b/src/test/ui/pattern/usefulness/stable-gated-patterns.rs similarity index 61% rename from src/test/ui/rfc-2008-non-exhaustive/stable-reachable.rs rename to src/test/ui/pattern/usefulness/stable-gated-patterns.rs index f6a73bf5cbe7e..4a87eea11795b 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.rs +++ b/src/test/ui/pattern/usefulness/stable-gated-patterns.rs @@ -5,18 +5,14 @@ extern crate unstable; use unstable::Foo; fn main() { - #[deny(non_exhaustive_omitted_patterns)] match Foo::Stable { Foo::Stable => {} - _ => {} } - //~^^ some variants are not matched explicitly + //~^^^ non-exhaustive patterns: `Stable2` not covered // Ok: all variants are explicitly matched - #[deny(non_exhaustive_omitted_patterns)] match Foo::Stable { Foo::Stable => {} Foo::Stable2 => {} - _ => {} } } diff --git a/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr b/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr new file mode 100644 index 0000000000000..b2729ebab6994 --- /dev/null +++ b/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr @@ -0,0 +1,17 @@ +error[E0004]: non-exhaustive patterns: `Stable2` not covered + --> $DIR/stable-gated-patterns.rs:8:11 + | +LL | match Foo::Stable { + | ^^^^^^^^^^^ pattern `Stable2` not covered + | + ::: $DIR/auxiliary/unstable.rs:9:5 + | +LL | Stable2, + | ------- not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.rs b/src/test/ui/pattern/usefulness/unstable-gated-patterns.rs similarity index 65% rename from src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.rs rename to src/test/ui/pattern/usefulness/unstable-gated-patterns.rs index 8ffaba479261b..b9804b0ffe77a 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.rs +++ b/src/test/ui/pattern/usefulness/unstable-gated-patterns.rs @@ -7,19 +7,16 @@ extern crate unstable; use unstable::Foo; fn main() { - #[deny(non_exhaustive_omitted_patterns)] match Foo::Stable { Foo::Stable => {} - _ => {} + Foo::Stable2 => {} } - //~^^ some variants are not matched explicitly + //~^^^^ non-exhaustive patterns: `Unstable` not covered // Ok: all variants are explicitly matched - #[deny(non_exhaustive_omitted_patterns)] match Foo::Stable { Foo::Stable => {} Foo::Stable2 => {} Foo::Unstable => {} - _ => {} } } diff --git a/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr b/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr new file mode 100644 index 0000000000000..f9c0196b76598 --- /dev/null +++ b/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr @@ -0,0 +1,17 @@ +error[E0004]: non-exhaustive patterns: `Unstable` not covered + --> $DIR/unstable-gated-patterns.rs:10:11 + | +LL | match Foo::Stable { + | ^^^^^^^^^^^ pattern `Unstable` not covered + | + ::: $DIR/auxiliary/unstable.rs:11:5 + | +LL | Unstable, + | -------- not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr index 4bce850eed0d9..9457ab2cced82 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr @@ -115,13 +115,13 @@ LL | _ => {} = note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found error: some variants are not matched explicitly - --> $DIR/reachable-patterns.rs:124:9 + --> $DIR/reachable-patterns.rs:128:9 | LL | _ => {} | ^ pattern `A(_)` not covered | note: the lint level is defined here - --> $DIR/reachable-patterns.rs:122:12 + --> $DIR/reachable-patterns.rs:126:12 | LL | #[deny(non_exhaustive_omitted_patterns)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.stderr b/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.stderr deleted file mode 100644 index fcfe793a1edec..0000000000000 --- a/src/test/ui/rfc-2008-non-exhaustive/stable-reachable.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: some variants are not matched explicitly - --> $DIR/stable-reachable.rs:11:9 - | -LL | _ => {} - | ^ pattern `Stable2` not covered - | -note: the lint level is defined here - --> $DIR/stable-reachable.rs:8:12 - | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `Foo` and the `non_exhaustive_omitted_patterns` attribute was found - -error: aborting due to previous error - diff --git a/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.stderr b/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.stderr deleted file mode 100644 index b6b69eccaefba..0000000000000 --- a/src/test/ui/rfc-2008-non-exhaustive/unstable-reachable.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: some variants are not matched explicitly - --> $DIR/unstable-reachable.rs:13:9 - | -LL | _ => {} - | ^ patterns `Stable2` and `Unstable` not covered - | -note: the lint level is defined here - --> $DIR/unstable-reachable.rs:10:12 - | -LL | #[deny(non_exhaustive_omitted_patterns)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: ensure that all variants are matched explicitly by adding the suggested match arms - = note: the matched value is of type `Foo` and the `non_exhaustive_omitted_patterns` attribute was found - -error: aborting due to previous error - From 34d907564789a0014360201dfc41be764ed617a4 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 1 Oct 2021 23:19:03 +0100 Subject: [PATCH 10/26] Add a test --- .../or-patterns/exhaustiveness-unreachable-pattern.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index 8429799cabf15..e77c5a4dcfe33 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -61,7 +61,7 @@ fn main() { } match None { // There is only one error that correctly points to the whole subpattern - Some(0) | + Some(0) | //xx Some( //~ ERROR unreachable 0 | 0) => {} _ => {} @@ -131,7 +131,7 @@ fn main() { // https://github.com/rust-lang/rust/issues/76836 match None { Some(false) => {} - None | Some(true + None | Some(true //xx | false) => {} //~ ERROR unreachable } @@ -139,14 +139,16 @@ fn main() { match (true, true) { (false, true) => {} (true, true) => {} - (false | true, false + (false | true, false //xx | true) => {} //~ ERROR unreachable } match (true, true) { (true, false) => {} (true, true) => {} - (false + (false //xx | true, //~ ERROR unreachable false | true) => {} } + + let (true | false | true, _) = (true, true); } From 0f1dcc6954173f30598332175c01b2c71a489d96 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 29 Sep 2021 22:17:56 +0100 Subject: [PATCH 11/26] Fix a comment --- compiler/rustc_mir_build/src/thir/pattern/usefulness.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index afc842a6c44e5..9e4892a33287c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -860,7 +860,6 @@ fn is_useful<'p, 'tcx>( let start_matrix = &matrix; for ctor in split_ctors { debug!("specialize({:?})", ctor); - // We cache the result of `Fields::wildcards` because it is used a lot. let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); let v = v.pop_head_constructor(cx, &ctor); let usefulness = From 033aeb84207d0423d3464867f41da3113415f96e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 2 Oct 2021 11:44:44 +0100 Subject: [PATCH 12/26] `PatternFolder` is no longer used --- .../rustc_mir_build/src/thir/pattern/mod.rs | 143 +----------------- 1 file changed, 2 insertions(+), 141 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index cb74ae4df2ef8..98069082ebbd4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -17,12 +17,10 @@ use rustc_hir::RangeEnd; use rustc_index::vec::Idx; use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue}; use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; -use rustc_middle::mir::UserTypeProjection; use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; -use rustc_middle::ty::subst::{GenericArg, SubstsRef}; -use rustc_middle::ty::{self, AdtDef, ConstKind, DefIdTree, Region, Ty, TyCtxt, UserType}; -use rustc_span::{Span, Symbol}; +use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt}; +use rustc_span::Span; use std::cmp::Ordering; @@ -584,143 +582,6 @@ impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { } } -crate trait PatternFoldable<'tcx>: Sized { - fn fold_with>(&self, folder: &mut F) -> Self { - self.super_fold_with(folder) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self; -} - -crate trait PatternFolder<'tcx>: Sized { - fn fold_pattern(&mut self, pattern: &Pat<'tcx>) -> Pat<'tcx> { - pattern.super_fold_with(self) - } - - fn fold_pattern_kind(&mut self, kind: &PatKind<'tcx>) -> PatKind<'tcx> { - kind.super_fold_with(self) - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Box { - fn super_fold_with>(&self, folder: &mut F) -> Self { - let content: T = (**self).fold_with(folder); - Box::new(content) - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Vec { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.iter().map(|t| t.fold_with(folder)).collect() - } -} - -impl<'tcx, T: PatternFoldable<'tcx>> PatternFoldable<'tcx> for Option { - fn super_fold_with>(&self, folder: &mut F) -> Self { - self.as_ref().map(|t| t.fold_with(folder)) - } -} - -macro_rules! CloneImpls { - (<$lt_tcx:tt> $($ty:ty),+) => { - $( - impl<$lt_tcx> PatternFoldable<$lt_tcx> for $ty { - fn super_fold_with>(&self, _: &mut F) -> Self { - Clone::clone(self) - } - } - )+ - } -} - -CloneImpls! { <'tcx> - Span, Field, Mutability, Symbol, hir::HirId, usize, ty::Const<'tcx>, - Region<'tcx>, Ty<'tcx>, BindingMode, &'tcx AdtDef, - SubstsRef<'tcx>, &'tcx GenericArg<'tcx>, UserType<'tcx>, - UserTypeProjection, PatTyProj<'tcx> -} - -impl<'tcx> PatternFoldable<'tcx> for FieldPat<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - FieldPat { field: self.field.fold_with(folder), pattern: self.pattern.fold_with(folder) } - } -} - -impl<'tcx> PatternFoldable<'tcx> for Pat<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_pattern(self) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self { - Pat { - ty: self.ty.fold_with(folder), - span: self.span.fold_with(folder), - kind: self.kind.fold_with(folder), - } - } -} - -impl<'tcx> PatternFoldable<'tcx> for PatKind<'tcx> { - fn fold_with>(&self, folder: &mut F) -> Self { - folder.fold_pattern_kind(self) - } - - fn super_fold_with>(&self, folder: &mut F) -> Self { - match *self { - PatKind::Wild => PatKind::Wild, - PatKind::AscribeUserType { - ref subpattern, - ascription: Ascription { variance, ref user_ty, user_ty_span }, - } => PatKind::AscribeUserType { - subpattern: subpattern.fold_with(folder), - ascription: Ascription { - user_ty: user_ty.fold_with(folder), - variance, - user_ty_span, - }, - }, - PatKind::Binding { mutability, name, mode, var, ty, ref subpattern, is_primary } => { - PatKind::Binding { - mutability: mutability.fold_with(folder), - name: name.fold_with(folder), - mode: mode.fold_with(folder), - var: var.fold_with(folder), - ty: ty.fold_with(folder), - subpattern: subpattern.fold_with(folder), - is_primary, - } - } - PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { - PatKind::Variant { - adt_def: adt_def.fold_with(folder), - substs: substs.fold_with(folder), - variant_index, - subpatterns: subpatterns.fold_with(folder), - } - } - PatKind::Leaf { ref subpatterns } => { - PatKind::Leaf { subpatterns: subpatterns.fold_with(folder) } - } - PatKind::Deref { ref subpattern } => { - PatKind::Deref { subpattern: subpattern.fold_with(folder) } - } - PatKind::Constant { value } => PatKind::Constant { value }, - PatKind::Range(range) => PatKind::Range(range), - PatKind::Slice { ref prefix, ref slice, ref suffix } => PatKind::Slice { - prefix: prefix.fold_with(folder), - slice: slice.fold_with(folder), - suffix: suffix.fold_with(folder), - }, - PatKind::Array { ref prefix, ref slice, ref suffix } => PatKind::Array { - prefix: prefix.fold_with(folder), - slice: slice.fold_with(folder), - suffix: suffix.fold_with(folder), - }, - PatKind::Or { ref pats } => PatKind::Or { pats: pats.fold_with(folder) }, - } - } -} - crate fn compare_const_vals<'tcx>( tcx: TyCtxt<'tcx>, a: &'tcx ty::Const<'tcx>, From e8c10f562d7779350b91e7531960434e1a2baf9c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 29 Sep 2021 22:08:45 +0100 Subject: [PATCH 13/26] Add a constructor for box patterns And in the process greatly clarify how they're handled. --- .../src/thir/pattern/check_match.rs | 2 +- .../src/thir/pattern/deconstruct_pat.rs | 86 +++++++------------ 2 files changed, 34 insertions(+), 54 deletions(-) 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 e28fd2c50814f..ab31c95a6992f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -360,7 +360,7 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { use Constructor::*; match pat.ctor() { Wildcard => true, - Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)), + Single | BoxPat(_) => pat.iter_fields().all(|pat| pat_is_catchall(pat)), _ => false, } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index bd4ab020a4544..9d50ecf800a23 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -628,6 +628,8 @@ pub(super) enum Constructor<'tcx> { Single, /// Enum variants. Variant(VariantIdx), + /// Box patterns. + BoxPat(Ty<'tcx>), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). @@ -695,17 +697,12 @@ impl<'tcx> Constructor<'tcx> { 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 - // patterns. If we're here we can assume this is a box pattern. - 1 - } else { - let variant = &adt.variants[self.variant_index_for_adt(adt)]; - Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count() - } + let variant = &adt.variants[self.variant_index_for_adt(adt)]; + Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count() } _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty), }, + BoxPat(_) => 1, Slice(slice) => slice.arity(), Str(..) | FloatRange(..) @@ -778,6 +775,7 @@ impl<'tcx> Constructor<'tcx> { (Missing { .. } | Wildcard, _) => false, (Single, Single) => true, + (BoxPat(_), BoxPat(_)) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), @@ -835,8 +833,9 @@ impl<'tcx> Constructor<'tcx> { // This must be kept in sync with `is_covered_by`. match self { - // If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s. - Single => !used_ctors.is_empty(), + // If `self` is `Single` or `BoxPat`, `used_ctors` contains at most one ctor and that + // ctor is the expected one. + Single | BoxPat(_) => !used_ctors.is_empty(), Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)), IntRange(range) => used_ctors .iter() @@ -998,6 +997,9 @@ impl<'tcx> SplitWildcard<'tcx> { } ty::Never => smallvec![], _ if cx.is_uninhabited(pcx.ty) => smallvec![], + // The only legal non-wildcard patterns of type `Box` are box patterns, so we emit + // that. + ty::Adt(def, substs) if def.is_box() => smallvec![BoxPat(substs.type_at(0))], ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => smallvec![Single], // This type is one for which we cannot list constructors, like `str` or `f64`. _ => smallvec![NonExhaustive], @@ -1181,20 +1183,15 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { Single | Variant(_) => match ty.kind() { ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())), ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)), - ty::Adt(adt, substs) => { - if adt.is_box() { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - Fields::wildcards_from_tys(cx, once(substs.type_at(0))) - } else { - let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; - let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant) - .map(|(_, ty)| ty); - Fields::wildcards_from_tys(cx, tys) - } + ty::Adt(adt, _) => { + let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; + let tys = + Fields::list_variant_nonhidden_fields(cx, ty, variant).map(|(_, ty)| ty); + Fields::wildcards_from_tys(cx, tys) } _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), }, + BoxPat(ty) => Fields::wildcards_from_tys(cx, once(*ty)), Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { let arity = slice.arity(); @@ -1278,7 +1275,10 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { fields = Fields::empty(); } PatKind::Deref { subpattern } => { - ctor = Single; + ctor = match pat.ty.kind() { + ty::Adt(adt, substs) if adt.is_box() => BoxPat(substs.type_at(0)), + _ => Single, + }; fields = Fields::singleton(cx, mkpat(subpattern)); } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { @@ -1296,26 +1296,14 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { fields = Fields::from_iter(cx, wilds); } ty::Adt(adt, substs) if adt.is_box() => { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, - // _)` 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. + // If we're here, the pattern is using the private `Box(_, _)` constructor. + // Since this is private, we can ignore the subpatterns here and pretend it's a + // `box _`. There'll be an error later anyways. This prevents an ICE. // 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 - // solution when we introduce generalized deref patterns. Also need to - // prevent mixing of those two options. - let pat = subpatterns.into_iter().find(|pat| pat.field.index() == 0); - let pat = if let Some(pat) = pat { - mkpat(&pat.pattern) - } else { - DeconstructedPat::wildcard(substs.type_at(0)) - }; - ctor = Single; - fields = Fields::singleton(cx, pat); + let inner_ty = substs.type_at(0); + ctor = Constructor::BoxPat(inner_ty); + fields = Fields::singleton(cx, DeconstructedPat::wildcard(inner_ty)); } ty::Adt(adt, _) => { ctor = match pat.kind.as_ref() { @@ -1429,12 +1417,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) .collect(), }, - ty::Adt(adt_def, _) if adt_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 - // the pattern is a box pattern. - PatKind::Deref { subpattern: subpatterns.next().unwrap() } - } ty::Adt(adt_def, substs) => { let variant_index = self.ctor.variant_index_for_adt(adt_def); let variant = &adt_def.variants[variant_index]; @@ -1456,6 +1438,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty), }, + BoxPat(_) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, Slice(slice) => { match slice.kind { FixedLen(_) => PatKind::Slice { @@ -1609,13 +1592,6 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { match &self.ctor { Single | Variant(_) => match self.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 - // the pattern is a box pattern. - let subpattern = self.iter_fields().next().unwrap(); - write!(f, "box {:?}", subpattern) - } ty::Adt(..) | ty::Tuple(..) => { let variant = match self.ty.kind() { ty::Adt(adt, _) => { @@ -1648,6 +1624,10 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } _ => write!(f, "_"), }, + BoxPat(_) => { + let subpattern = self.iter_fields().next().unwrap(); + write!(f, "box {:?}", subpattern) + } Slice(slice) => { let mut subpatterns = self.fields.iter_patterns(); write!(f, "[")?; From 25f07102770bad82acfde74adf2dd31eac93f452 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 29 Sep 2021 22:35:22 +0100 Subject: [PATCH 14/26] Add a constructor for ref patterns --- .../src/thir/pattern/check_match.rs | 2 +- .../src/thir/pattern/deconstruct_pat.rs | 46 +++++++++---------- 2 files changed, 23 insertions(+), 25 deletions(-) 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 ab31c95a6992f..2fc1dbb311246 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -360,7 +360,7 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { use Constructor::*; match pat.ctor() { Wildcard => true, - Single | BoxPat(_) => pat.iter_fields().all(|pat| pat_is_catchall(pat)), + Single | Ref(_) | BoxPat(_) => pat.iter_fields().all(|pat| pat_is_catchall(pat)), _ => false, } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 9d50ecf800a23..3559f3a6cdfac 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -628,6 +628,8 @@ pub(super) enum Constructor<'tcx> { Single, /// Enum variants. Variant(VariantIdx), + /// Ref patterns (`&_` and `&mut _`). + Ref(Ty<'tcx>), /// Box patterns. BoxPat(Ty<'tcx>), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). @@ -695,14 +697,13 @@ impl<'tcx> Constructor<'tcx> { match self { Single | Variant(_) => match pcx.ty.kind() { ty::Tuple(fs) => fs.len(), - ty::Ref(..) => 1, ty::Adt(adt, ..) => { let variant = &adt.variants[self.variant_index_for_adt(adt)]; Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count() } _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty), }, - BoxPat(_) => 1, + Ref(_) | BoxPat(_) => 1, Slice(slice) => slice.arity(), Str(..) | FloatRange(..) @@ -775,6 +776,7 @@ impl<'tcx> Constructor<'tcx> { (Missing { .. } | Wildcard, _) => false, (Single, Single) => true, + (Ref(_), Ref(_)) => true, (BoxPat(_), BoxPat(_)) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, @@ -833,9 +835,9 @@ impl<'tcx> Constructor<'tcx> { // This must be kept in sync with `is_covered_by`. match self { - // If `self` is `Single` or `BoxPat`, `used_ctors` contains at most one ctor and that - // ctor is the expected one. - Single | BoxPat(_) => !used_ctors.is_empty(), + // If `self` is one of these the type has exactly one constructor so the check is + // simpler. + Single | Ref(_) | BoxPat(_) => !used_ctors.is_empty(), Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)), IntRange(range) => used_ctors .iter() @@ -1000,7 +1002,8 @@ impl<'tcx> SplitWildcard<'tcx> { // The only legal non-wildcard patterns of type `Box` are box patterns, so we emit // that. ty::Adt(def, substs) if def.is_box() => smallvec![BoxPat(substs.type_at(0))], - ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => smallvec![Single], + ty::Ref(_, ty, _) => smallvec![Ref(*ty)], + ty::Adt(..) | ty::Tuple(..) => smallvec![Single], // This type is one for which we cannot list constructors, like `str` or `f64`. _ => smallvec![NonExhaustive], }; @@ -1182,7 +1185,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { let ret = match constructor { Single | Variant(_) => match ty.kind() { ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())), - ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)), ty::Adt(adt, _) => { let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; let tys = @@ -1191,7 +1193,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), }, - BoxPat(ty) => Fields::wildcards_from_tys(cx, once(*ty)), + Ref(ty) | BoxPat(ty) => Fields::wildcards_from_tys(cx, once(*ty)), Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { let arity = slice.arity(); @@ -1276,8 +1278,8 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } PatKind::Deref { subpattern } => { ctor = match pat.ty.kind() { - ty::Adt(adt, substs) if adt.is_box() => BoxPat(substs.type_at(0)), - _ => Single, + ty::Adt(adt, _) if adt.is_box() => BoxPat(subpattern.ty), + _ => Ref(subpattern.ty), }; fields = Fields::singleton(cx, mkpat(subpattern)); } @@ -1353,7 +1355,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { // Note: `t` is `str`, not `&str`. let subpattern = DeconstructedPat::new(Str(value), Fields::empty(), t, pat.span); - ctor = Single; + ctor = Ref(t); fields = Fields::singleton(cx, subpattern) } // All constants that can be structurally matched have already been expanded @@ -1431,14 +1433,13 @@ impl<'p, 'tcx> DeconstructedPat<'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 {:?} {:?}", self.ctor, self.ty), }, - BoxPat(_) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, + // Note: given the expansion of `&str` patterns done in `DeconstructedPat::from_pat`, + // 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(_) | BoxPat(_) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, Slice(slice) => { match slice.kind { FixedLen(_) => PatKind::Slice { @@ -1615,15 +1616,12 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'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 = self.iter_fields().next().unwrap(); - write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern) - } _ => write!(f, "_"), }, + Ref(_) => { + let subpattern = self.iter_fields().next().unwrap(); + write!(f, "&{:?}", subpattern) + } BoxPat(_) => { let subpattern = self.iter_fields().next().unwrap(); write!(f, "box {:?}", subpattern) From 13c418f64e3c94e4fbbd32ff5f931df2ec9ede62 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 29 Sep 2021 22:40:46 +0100 Subject: [PATCH 15/26] Add a constructor for tuple patterns --- .../src/thir/pattern/check_match.rs | 2 +- .../src/thir/pattern/deconstruct_pat.rs | 71 +++++++++---------- 2 files changed, 36 insertions(+), 37 deletions(-) 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 2fc1dbb311246..382b68a9ab345 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -360,7 +360,7 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { use Constructor::*; match pat.ctor() { Wildcard => true, - Single | Ref(_) | BoxPat(_) => pat.iter_fields().all(|pat| pat_is_catchall(pat)), + Single | Tuple(_) | Ref(_) | BoxPat(_) => pat.iter_fields().all(|pat| pat_is_catchall(pat)), _ => false, } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 3559f3a6cdfac..da81a16af0f48 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -57,6 +57,7 @@ use rustc_middle::mir::interpret::ConstValue; use rustc_middle::mir::Field; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::{self, Const, Ty, TyCtxt, VariantDef}; use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; @@ -623,11 +624,12 @@ impl SplitVarLenSlice { /// `Fields`. #[derive(Clone, Debug, PartialEq)] pub(super) enum Constructor<'tcx> { - /// The constructor for patterns that have a single constructor, like tuples, struct patterns - /// and fixed-length arrays. + /// Structs. Single, /// Enum variants. Variant(VariantIdx), + /// Tuple patterns. + Tuple(&'tcx [GenericArg<'tcx>]), /// Ref patterns (`&_` and `&mut _`). Ref(Ty<'tcx>), /// Box patterns. @@ -696,13 +698,13 @@ impl<'tcx> Constructor<'tcx> { pub(super) fn arity(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> usize { match self { Single | Variant(_) => match pcx.ty.kind() { - ty::Tuple(fs) => fs.len(), ty::Adt(adt, ..) => { let variant = &adt.variants[self.variant_index_for_adt(adt)]; Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count() } _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty), }, + Tuple(fs) => fs.len(), Ref(_) | BoxPat(_) => 1, Slice(slice) => slice.arity(), Str(..) @@ -776,6 +778,7 @@ impl<'tcx> Constructor<'tcx> { (Missing { .. } | Wildcard, _) => false, (Single, Single) => true, + (Tuple(_), Tuple(_)) => true, (Ref(_), Ref(_)) => true, (BoxPat(_), BoxPat(_)) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, @@ -837,7 +840,7 @@ impl<'tcx> Constructor<'tcx> { match self { // If `self` is one of these the type has exactly one constructor so the check is // simpler. - Single | Ref(_) | BoxPat(_) => !used_ctors.is_empty(), + Single | Tuple(_) | Ref(_) | BoxPat(_) => !used_ctors.is_empty(), Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)), IntRange(range) => used_ctors .iter() @@ -1003,7 +1006,8 @@ impl<'tcx> SplitWildcard<'tcx> { // that. ty::Adt(def, substs) if def.is_box() => smallvec![BoxPat(substs.type_at(0))], ty::Ref(_, ty, _) => smallvec![Ref(*ty)], - ty::Adt(..) | ty::Tuple(..) => smallvec![Single], + ty::Tuple(fs) => smallvec![Tuple(*fs)], + ty::Adt(..) => smallvec![Single], // This type is one for which we cannot list constructors, like `str` or `f64`. _ => smallvec![NonExhaustive], }; @@ -1184,7 +1188,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { ) -> Self { let ret = match constructor { Single | Variant(_) => match ty.kind() { - ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())), ty::Adt(adt, _) => { let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; let tys = @@ -1193,6 +1196,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), }, + Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())), Ref(ty) | BoxPat(ty) => Fields::wildcards_from_tys(cx, once(*ty)), Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { @@ -1286,7 +1290,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { match pat.ty.kind() { ty::Tuple(fs) => { - ctor = Single; + ctor = Tuple(fs); let mut wilds: SmallVec<[_; 2]> = fs .iter() .map(|ty| ty.expect_ty()) @@ -1413,12 +1417,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx)); let pat = match &self.ctor { Single | Variant(_) => match self.ty.kind() { - ty::Tuple(..) => PatKind::Leaf { - subpatterns: subpatterns - .enumerate() - .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) - .collect(), - }, ty::Adt(adt_def, substs) => { let variant_index = self.ctor.variant_index_for_adt(adt_def); let variant = &adt_def.variants[variant_index]; @@ -1435,6 +1433,12 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty), }, + Tuple(..) => PatKind::Leaf { + subpatterns: subpatterns + .enumerate() + .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + .collect(), + }, // Note: given the expansion of `&str` patterns done in `DeconstructedPat::from_pat`, // 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 @@ -1592,32 +1596,27 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { let mut start_or_comma = || start_or_continue(", "); match &self.ctor { - Single | Variant(_) => match self.ty.kind() { - ty::Adt(..) | ty::Tuple(..) => { - let variant = match self.ty.kind() { - ty::Adt(adt, _) => { - Some(&adt.variants[self.ctor.variant_index_for_adt(adt)]) - } - ty::Tuple(_) => None, - _ => unreachable!(), - }; + Single | Variant(_) | Tuple(_) => { + let variant = match self.ty.kind() { + ty::Adt(adt, _) => Some(&adt.variants[self.ctor.variant_index_for_adt(adt)]), + ty::Tuple(_) => None, + _ => unreachable!(), + }; - if let Some(variant) = variant { - write!(f, "{}", variant.ident)?; - } + if let Some(variant) = variant { + write!(f, "{}", variant.ident)?; + } - // Without `cx`, we can't know which field corresponds to which, so we can't - // get the names of the fields. Instead we just display everything as a suple - // struct, which should be good enough. - write!(f, "(")?; - for p in self.iter_fields() { - write!(f, "{}", start_or_comma())?; - write!(f, "{:?}", p)?; - } - write!(f, ")") + // Without `cx`, we can't know which field corresponds to which, so we can't + // get the names of the fields. Instead we just display everything as a suple + // struct, which should be good enough. + write!(f, "(")?; + for p in self.iter_fields() { + write!(f, "{}", start_or_comma())?; + write!(f, "{:?}", p)?; } - _ => write!(f, "_"), - }, + write!(f, ")") + } Ref(_) => { let subpattern = self.iter_fields().next().unwrap(); write!(f, "&{:?}", subpattern) From 2cb2a54c141c72fc9279cff9feed514aae1f2512 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 29 Sep 2021 22:59:43 +0100 Subject: [PATCH 16/26] Reorder the logic of `SplitWildcard::new` --- .../src/thir/pattern/deconstruct_pat.rs | 100 +++++++++--------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index da81a16af0f48..7dd14f6a44701 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -901,19 +901,34 @@ impl<'tcx> SplitWildcard<'tcx> { // Invariant: this is empty if and only if the type is uninhabited (as determined by // `cx.is_uninhabited()`). let all_ctors = match pcx.ty.kind() { + _ if cx.is_uninhabited(pcx.ty) => smallvec![], ty::Bool => smallvec![make_range(0, 1)], - ty::Array(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) as usize; - if len != 0 && cx.is_uninhabited(sub_ty) { - smallvec![] - } else { - smallvec![Slice(Slice::new(Some(len), VarLen(0, 0)))] - } + ty::Char => { + smallvec![ + // The valid Unicode Scalar Value ranges. + make_range('\u{0000}' as u128, '\u{D7FF}' as u128), + make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), + ] } - // Treat arrays of a constant but unknown length like slices. - ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { - let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - smallvec![Slice(Slice::new(None, kind))] + ty::Int(_) | ty::Uint(_) + if pcx.ty.is_ptr_sized_integral() + && !cx.tcx.features().precise_pointer_size_matching => + { + // `usize`/`isize` are not allowed to be matched exhaustively unless the + // `precise_pointer_size_matching` feature is enabled. So we treat those types like + // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. + smallvec![NonExhaustive] + } + &ty::Int(ity) => { + let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128; + let min = 1u128 << (bits - 1); + let max = min - 1; + smallvec![make_range(min, max)] + } + &ty::Uint(uty) => { + let size = Integer::from_uint_ty(&cx.tcx, uty).size(); + let max = size.truncate(u128::MAX); + smallvec![make_range(0, max)] } ty::Adt(def, substs) if def.is_enum() => { // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an @@ -967,48 +982,37 @@ impl<'tcx> SplitWildcard<'tcx> { } ctors } - ty::Char => { - smallvec![ - // The valid Unicode Scalar Value ranges. - make_range('\u{0000}' as u128, '\u{D7FF}' as u128), - make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), - ] - } - ty::Int(_) | ty::Uint(_) - if pcx.ty.is_ptr_sized_integral() - && !cx.tcx.features().precise_pointer_size_matching => - { - // `usize`/`isize` are not allowed to be matched exhaustively unless the - // `precise_pointer_size_matching` feature is enabled. So we treat those types like - // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. - smallvec![NonExhaustive] - } - &ty::Int(ity) => { - let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128; - let min = 1u128 << (bits - 1); - let max = min - 1; - smallvec![make_range(min, max)] - } - &ty::Uint(uty) => { - let size = Integer::from_uint_ty(&cx.tcx, uty).size(); - let max = size.truncate(u128::MAX); - smallvec![make_range(0, max)] - } - // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we cannot - // expose its emptiness. The exception is if the pattern is at the top level, because we - // want empty matches to be considered exhaustive. - ty::Never if !cx.tcx.features().exhaustive_patterns && !pcx.is_top_level => { - smallvec![NonExhaustive] - } - ty::Never => smallvec![], - _ if cx.is_uninhabited(pcx.ty) => smallvec![], // The only legal non-wildcard patterns of type `Box` are box patterns, so we emit // that. ty::Adt(def, substs) if def.is_box() => smallvec![BoxPat(substs.type_at(0))], + ty::Adt(..) => smallvec![Single], ty::Ref(_, ty, _) => smallvec![Ref(*ty)], ty::Tuple(fs) => smallvec![Tuple(*fs)], - ty::Adt(..) => smallvec![Single], - // This type is one for which we cannot list constructors, like `str` or `f64`. + ty::Array(sub_ty, len) => { + match len.try_eval_usize(cx.tcx, cx.param_env) { + Some(len) => { + // The uninhabited case is already handled. + smallvec![Slice(Slice::new(Some(len as usize), VarLen(0, 0)))] + } + None => { + // Treat arrays of a constant but unknown length like slices. + let kind = + if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + smallvec![Slice(Slice::new(None, kind))] + } + } + } + ty::Slice(sub_ty) => { + let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + smallvec![Slice(Slice::new(None, kind))] + } + // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we don't + // expose its emptiness and return `NonExhaustive` below. The exception is if the + // pattern is at the top level, because we want empty matches to be considered + // exhaustive. + ty::Never if pcx.is_top_level => smallvec![], + // This type is one for which we cannot list constructors, like `str` or `f64`, or is + // uninhabited but `exhaustive_patterns` is disabled. _ => smallvec![NonExhaustive], }; From 3ca5461877b22e46ef035386187e6c21085e98a3 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 29 Sep 2021 23:21:11 +0100 Subject: [PATCH 17/26] Add explicit constructors for bool values Instead of going through the complicated `IntRange` logic --- .../src/thir/pattern/deconstruct_pat.rs | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 7dd14f6a44701..c101d50255577 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -109,7 +109,7 @@ pub(super) struct IntRange { impl IntRange { #[inline] fn is_integral(ty: Ty<'_>) -> bool { - matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool) + matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) } fn is_singleton(&self) -> bool { @@ -123,7 +123,6 @@ impl IntRange { #[inline] fn integral_size_and_signed_bias(tcx: TyCtxt<'_>, 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_int_ty(&tcx, ity).size(); @@ -634,6 +633,8 @@ pub(super) enum Constructor<'tcx> { Ref(Ty<'tcx>), /// Box patterns. BoxPat(Ty<'tcx>), + /// Booleans. + Bool(bool), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). @@ -708,8 +709,9 @@ impl<'tcx> Constructor<'tcx> { Ref(_) | BoxPat(_) => 1, Slice(slice) => slice.arity(), Str(..) - | FloatRange(..) + | Bool(..) | IntRange(..) + | FloatRange(..) | NonExhaustive | Opaque | Missing { .. } @@ -782,6 +784,7 @@ impl<'tcx> Constructor<'tcx> { (Ref(_), Ref(_)) => true, (BoxPat(_), BoxPat(_)) => true, (Variant(self_id), Variant(other_id)) => self_id == other_id, + (Bool(self_b), Bool(other_b)) => self_b == other_b, (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), ( @@ -842,6 +845,9 @@ impl<'tcx> Constructor<'tcx> { // simpler. Single | Tuple(_) | Ref(_) | BoxPat(_) => !used_ctors.is_empty(), Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)), + Bool(self_b) => { + used_ctors.iter().any(|c| matches!(c, Bool(other_b) if self_b == other_b)) + } IntRange(range) => used_ctors .iter() .filter_map(|c| c.as_int_range()) @@ -902,7 +908,7 @@ impl<'tcx> SplitWildcard<'tcx> { // `cx.is_uninhabited()`). let all_ctors = match pcx.ty.kind() { _ if cx.is_uninhabited(pcx.ty) => smallvec![], - ty::Bool => smallvec![make_range(0, 1)], + ty::Bool => smallvec![Bool(false), Bool(true)], ty::Char => { smallvec![ // The valid Unicode Scalar Value ranges. @@ -1078,7 +1084,8 @@ impl<'tcx> SplitWildcard<'tcx> { // // The exception is: if we are at the top-level, for example in an empty match, we // sometimes prefer reporting the list of constructors instead of just `_`. - let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty); + let report_when_all_missing = pcx.is_top_level + && !matches!(pcx.ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_)); let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing { if pcx.is_non_exhaustive { Missing { @@ -1210,8 +1217,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, Str(..) - | FloatRange(..) + | Bool(..) | IntRange(..) + | FloatRange(..) | NonExhaustive | Opaque | Missing { .. } @@ -1349,6 +1357,14 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { fields = Fields::empty(); } else { match pat.ty.kind() { + ty::Bool => { + ctor = match value.try_eval_bool(cx.tcx, cx.param_env) { + Some(b) => Bool(b), + // FIXME(Nadrieril): Does this ever happen? + None => Opaque, + }; + fields = Fields::empty(); + } ty::Float(_) => { ctor = FloatRange(value, value, RangeEnd::Included); fields = Fields::empty(); @@ -1478,9 +1494,10 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } } - &Str(value) => PatKind::Constant { value }, - &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), + Bool(b) => PatKind::Constant { value: ty::Const::from_bool(cx.tcx, *b) }, IntRange(range) => return range.to_pat(cx.tcx, self.ty), + &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), + &Str(value) => PatKind::Constant { value }, Wildcard | NonExhaustive => PatKind::Wild, Missing { .. } => bug!( "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, @@ -1651,12 +1668,13 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } write!(f, "]") } + Bool(b) => write!(f, "{}", b), + IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render chars as ranges etc. &FloatRange(lo, hi, end) => { write!(f, "{}", lo)?; write!(f, "{}", end)?; write!(f, "{}", hi) } - IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render e.g. `false` as `0..=0` Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty), Or => { for pat in self.iter_fields() { From 4c286e2f42392f265454ce296a0bf239efd3f45b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 30 Sep 2021 12:39:10 +0100 Subject: [PATCH 18/26] Evaluate consts earlier Also rework const evaluation a bit --- .../src/thir/pattern/deconstruct_pat.rs | 264 +++++++++--------- 1 file changed, 139 insertions(+), 125 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index c101d50255577..8a659c8092a1d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -45,15 +45,15 @@ use self::Constructor::*; use self::SliceKind::*; -use super::compare_const_vals; use super::usefulness::{MatchCheckCtxt, PatCtxt}; +use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; use rustc_hir::{HirId, RangeEnd}; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue, Scalar}; use rustc_middle::mir::Field; use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::layout::IntegerExt; @@ -61,6 +61,7 @@ use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::{self, Const, Ty, TyCtxt, VariantDef}; use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::Primitive; use rustc_target::abi::{Integer, Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; @@ -87,6 +88,32 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { pats } +/// Evaluate an int constant, with a faster branch for a common case. +#[inline] +fn fast_try_eval_bits<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &Const<'tcx>, +) -> Option { + let int = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) = value.val { + // If the constant is already evaluated, we shortcut here. + int + } else { + // This is a more general but slower form of the previous case. + value.val.eval(tcx, param_env).try_to_scalar_int()? + }; + let size = match value.ty.kind() { + ty::Bool => Size::from_bytes(1), + ty::Char => Size::from_bytes(4), + ty::Int(ity) => Integer::from_int_ty(&tcx, *ity).size(), + ty::Uint(uty) => Integer::from_uint_ty(&tcx, *uty).size(), + ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx), + ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx), + _ => bug!("unexpected type: {}", value.ty), + }; + int.to_bits(size).ok() +} + /// An inclusive interval, used for precise integer exhaustiveness checking. /// `IntRange`s always store a contiguous range. This means that values are /// encoded such that `0` encodes the minimum value for the integer, @@ -94,6 +121,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { /// For example, the pattern `-128..=127i8` is encoded as `0..=255`. /// This makes comparisons and arithmetic on interval endpoints much more /// straightforward. See `signed_bias` for details. +/// Only use for chars, ints and uints. /// /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. @@ -107,11 +135,6 @@ pub(super) struct IntRange { } impl IntRange { - #[inline] - fn is_integral(ty: Ty<'_>) -> bool { - matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) - } - fn is_singleton(&self) -> bool { self.range.start() == self.range.end() } @@ -120,45 +143,26 @@ impl IntRange { (*self.range.start(), *self.range.end()) } + // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. #[inline] - fn integral_size_and_signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Size, u128)> { + fn signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> u128 { match *ty.kind() { - ty::Char => Some((Size::from_bytes(4), 0)), ty::Int(ity) => { let size = Integer::from_int_ty(&tcx, ity).size(); - Some((size, 1u128 << (size.bits() as u128 - 1))) + 1u128 << (size.bits() as u128 - 1) } - ty::Uint(uty) => Some((Integer::from_uint_ty(&tcx, uty).size(), 0)), - _ => None, + ty::Char | ty::Uint(_) => 0, + _ => bug!("invalid type for `IntRange`: {}", ty), } } #[inline] - fn from_const<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: &Const<'tcx>, - ) -> Option { - if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { - let ty = value.ty; - let val = (|| { - if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val { - // For this specific pattern we can skip a lot of effort and go - // straight to the result, after doing a bit of checking. (We - // could remove this branch and just fall through, which - // is more general but much slower.) - if let Ok(bits) = scalar.to_bits_or_ptr_internal(target_size) { - return Some(bits); - } - } - // This is a more general form of the previous case. - value.try_eval_bits(tcx, param_env, ty) - })()?; - let val = val ^ bias; - Some(IntRange { range: val..=val, bias }) - } else { - None - } + fn from_bits<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, bits: u128) -> IntRange { + let bias = IntRange::signed_bias(tcx, ty); + // Perform a shift if the underlying types are signed, + // which makes the interval arithmetic simpler. + let val = bits ^ bias; + IntRange { range: val..=val, bias } } #[inline] @@ -168,32 +172,17 @@ impl IntRange { hi: u128, ty: Ty<'tcx>, end: &RangeEnd, - ) -> Option { - if Self::is_integral(ty) { - // Perform a shift if the underlying types are signed, - // which makes the interval arithmetic simpler. - let bias = IntRange::signed_bias(tcx, ty); - let (lo, hi) = (lo ^ bias, hi ^ bias); - let offset = (*end == RangeEnd::Excluded) as u128; - if lo > hi || (lo == hi && *end == RangeEnd::Excluded) { - // This should have been caught earlier by E0030. - bug!("malformed range pattern: {}..={}", lo, (hi - offset)); - } - Some(IntRange { range: lo..=(hi - offset), bias }) - } else { - None - } - } - - // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. - fn signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> u128 { - match *ty.kind() { - ty::Int(ity) => { - let bits = Integer::from_int_ty(&tcx, ity).size().bits() as u128; - 1u128 << (bits - 1) - } - _ => 0, + ) -> IntRange { + let bias = IntRange::signed_bias(tcx, ty); + // Perform a shift if the underlying types are signed, + // which makes the interval arithmetic simpler. + let (lo, hi) = (lo ^ bias, hi ^ bias); + let offset = (*end == RangeEnd::Excluded) as u128; + if lo > hi || (lo == hi && *end == RangeEnd::Excluded) { + // This should have been caught earlier by E0030. + bug!("malformed range pattern: {}..={}", lo, (hi - offset)); } + IntRange { range: lo..=(hi - offset), bias } } fn is_subrange(&self, other: &Self) -> bool { @@ -638,9 +627,10 @@ pub(super) enum Constructor<'tcx> { /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). - FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), + F32Range(IeeeFloat, IeeeFloat, RangeEnd), + F64Range(IeeeFloat, IeeeFloat, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. - Str(&'tcx ty::Const<'tcx>), + Str(&'tcx [u8]), /// Array and slice patterns. Slice(Slice), /// Constants that must not be matched structurally. They are treated as black @@ -653,7 +643,9 @@ pub(super) enum Constructor<'tcx> { /// Stands for constructors that are not seen in the matrix, as explained in the documentation /// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns` /// lint. - Missing { nonexhaustive_enum_missing_real_variants: bool }, + Missing { + nonexhaustive_enum_missing_real_variants: bool, + }, /// Wildcard pattern. Wildcard, /// Or-pattern. @@ -711,7 +703,8 @@ impl<'tcx> Constructor<'tcx> { Str(..) | Bool(..) | IntRange(..) - | FloatRange(..) + | F32Range(..) + | F64Range(..) | NonExhaustive | Opaque | Missing { .. } @@ -787,14 +780,8 @@ impl<'tcx> Constructor<'tcx> { (Bool(self_b), Bool(other_b)) => self_b == other_b, (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), - ( - FloatRange(self_from, self_to, self_end), - FloatRange(other_from, other_to, other_end), - ) => { - match ( - compare_const_vals(pcx.cx.tcx, self_to, other_to, pcx.cx.param_env, pcx.ty), - compare_const_vals(pcx.cx.tcx, self_from, other_from, pcx.cx.param_env, pcx.ty), - ) { + (F32Range(self_from, self_to, self_end), F32Range(other_from, other_to, other_end)) => { + match (self_to.partial_cmp(other_to), self_from.partial_cmp(other_from)) { (Some(to), Some(from)) => { (from == Ordering::Greater || from == Ordering::Equal) && (to == Ordering::Less @@ -803,14 +790,17 @@ impl<'tcx> Constructor<'tcx> { _ => false, } } - (Str(self_val), Str(other_val)) => { - // FIXME: there's probably a more direct way of comparing for equality - match compare_const_vals(pcx.cx.tcx, self_val, other_val, pcx.cx.param_env, pcx.ty) - { - Some(comparison) => comparison == Ordering::Equal, - None => false, + (F64Range(self_from, self_to, self_end), F64Range(other_from, other_to, other_end)) => { + match (self_to.partial_cmp(other_to), self_from.partial_cmp(other_from)) { + (Some(to), Some(from)) => { + (from == Ordering::Greater || from == Ordering::Equal) + && (to == Ordering::Less + || (other_end == self_end && to == Ordering::Equal)) + } + _ => false, } } + (Str(self_val), Str(other_val)) => self_val == other_val, (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice), // We are trying to inspect an opaque constant. Thus we skip the row. @@ -858,7 +848,7 @@ impl<'tcx> Constructor<'tcx> { .any(|other| slice.is_covered_by(other)), // This constructor is never covered by anything else NonExhaustive => false, - Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => { + Str(..) | F32Range(..) | F64Range(..) | Opaque | Missing { .. } | Wildcard | Or => { span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self) } } @@ -893,10 +883,7 @@ impl<'tcx> SplitWildcard<'tcx> { debug!("SplitWildcard::new({:?})", pcx.ty); let cx = pcx.cx; let make_range = |start, end| { - IntRange( - // `unwrap()` is ok because we know the type is an integer. - IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(), - ) + IntRange(IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included)) }; // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, // arrays and slices we use ranges and variable-length slices when appropriate. @@ -1219,7 +1206,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { Str(..) | Bool(..) | IntRange(..) - | FloatRange(..) + | F32Range(..) + | F64Range(..) | NonExhaustive | Opaque | Missing { .. } @@ -1352,24 +1340,33 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } } PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) { - ctor = IntRange(int_range); - fields = Fields::empty(); - } else { - match pat.ty.kind() { - ty::Bool => { - ctor = match value.try_eval_bool(cx.tcx, cx.param_env) { - Some(b) => Bool(b), - // FIXME(Nadrieril): Does this ever happen? - None => Opaque, - }; - fields = Fields::empty(); - } - ty::Float(_) => { - ctor = FloatRange(value, value, RangeEnd::Included); - fields = Fields::empty(); - } - ty::Ref(_, t, _) if t.is_str() => { + match pat.ty.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { + use rustc_apfloat::Float; + ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) { + Some(bits) => match pat.ty.kind() { + ty::Bool => Bool(bits != 0), + ty::Char | ty::Int(_) | ty::Uint(_) => { + let int_range = IntRange::from_bits(cx.tcx, pat.ty, bits); + IntRange(int_range) + } + ty::Float(ty::FloatTy::F32) => { + let value = rustc_apfloat::ieee::Single::from_bits(bits); + F32Range(value, value, RangeEnd::Included) + } + ty::Float(ty::FloatTy::F64) => { + let value = rustc_apfloat::ieee::Double::from_bits(bits); + F64Range(value, value, RangeEnd::Included) + } + _ => unreachable!(), + }, + None => Opaque, + }; + fields = Fields::empty(); + } + ty::Ref(_, t, _) if t.is_str() => { + if let ty::ConstKind::Value(val @ ConstValue::Slice { .. }) = value.val { + let bytes = get_slice_bytes(&cx.tcx, val); // We want a `&str` constant to behave like a `Deref` pattern, to be compatible // with other `Deref` patterns. This could have been done in `const_to_pat`, // but that causes issues with the rest of the matching code. @@ -1378,32 +1375,44 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { // fields. // Note: `t` is `str`, not `&str`. let subpattern = - DeconstructedPat::new(Str(value), Fields::empty(), t, pat.span); + DeconstructedPat::new(Str(bytes), Fields::empty(), t, pat.span); ctor = Ref(t); fields = Fields::singleton(cx, subpattern) - } - // 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. - _ => { + } else { + // FIXME(Nadrieril): Does this ever happen? ctor = Opaque; fields = Fields::empty(); } } + // 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. + _ => { + ctor = Opaque; + fields = Fields::empty(); + } } } &PatKind::Range(PatRange { lo, hi, end }) => { + use rustc_apfloat::Float; let ty = lo.ty; - ctor = if let Some(int_range) = IntRange::from_range( - cx.tcx, - lo.eval_bits(cx.tcx, cx.param_env, lo.ty), - hi.eval_bits(cx.tcx, cx.param_env, hi.ty), - ty, - &end, - ) { - IntRange(int_range) - } else { - FloatRange(lo, hi, end) + let lo = lo.eval_bits(cx.tcx, cx.param_env, ty); + let hi = hi.eval_bits(cx.tcx, cx.param_env, ty); + ctor = match ty.kind() { + ty::Char | ty::Int(_) | ty::Uint(_) => { + IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, &end)) + } + ty::Float(ty::FloatTy::F32) => { + let lo = rustc_apfloat::ieee::Single::from_bits(lo); + let hi = rustc_apfloat::ieee::Single::from_bits(hi); + F32Range(lo, hi, RangeEnd::Included) + } + ty::Float(ty::FloatTy::F64) => { + let lo = rustc_apfloat::ieee::Double::from_bits(lo); + let hi = rustc_apfloat::ieee::Double::from_bits(hi); + F64Range(lo, hi, RangeEnd::Included) + } + _ => bug!("invalid type for range pattern: {}", ty), }; fields = Fields::empty(); } @@ -1496,14 +1505,14 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } Bool(b) => PatKind::Constant { value: ty::Const::from_bool(cx.tcx, *b) }, IntRange(range) => return range.to_pat(cx.tcx, self.ty), - &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), - &Str(value) => PatKind::Constant { value }, Wildcard | NonExhaustive => PatKind::Wild, Missing { .. } => bug!( "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, `Missing` should have been processed in `apply_constructors`" ), - Opaque | Or => { + // These will never be converted because we don't emit them as non-exhaustiveness + // witnesses. And that's good because we're missing the relevant `&Const`. + F32Range(..) | F64Range(..) | Str(_) | Opaque | Or => { bug!("can't convert to pattern: {:?}", self) } }; @@ -1670,7 +1679,12 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } Bool(b) => write!(f, "{}", b), IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render chars as ranges etc. - &FloatRange(lo, hi, end) => { + F32Range(lo, hi, end) => { + write!(f, "{}", lo)?; + write!(f, "{}", end)?; + write!(f, "{}", hi) + } + F64Range(lo, hi, end) => { write!(f, "{}", lo)?; write!(f, "{}", end)?; write!(f, "{}", hi) @@ -1682,7 +1696,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } Ok(()) } - Str(value) => write!(f, "{}", value), + Str(bytes) => write!(f, "{}", String::from_utf8(bytes.to_vec()).unwrap()), Opaque => write!(f, ""), } } From 604b31b668ab8526e194618df31514e6cebd6f41 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 1 Oct 2021 23:05:31 +0100 Subject: [PATCH 19/26] Don't lint from within the algorithm; collect instead --- .../src/thir/pattern/check_match.rs | 69 ++++++++- .../src/thir/pattern/deconstruct_pat.rs | 55 ++++---- .../src/thir/pattern/usefulness.rs | 132 ++++++++---------- 3 files changed, 154 insertions(+), 102 deletions(-) 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 382b68a9ab345..8c58b5beb6387 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -14,7 +14,8 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{HirId, Pat}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_session::lint::builtin::{ - BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS, + BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, NON_EXHAUSTIVE_OMITTED_PATTERNS, + OVERLAPPING_RANGE_ENDPOINTS, UNREACHABLE_PATTERNS, }; use rustc_session::Session; use rustc_span::{DesugaringKind, ExpnKind, Span}; @@ -197,7 +198,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { match source { hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { - report_arm_reachability(&cx, &report) + report_exhaustiveness_lints(&cx, &report); + report_arm_reachability(&cx, &report); } // Unreachable patterns in try and await expressions occur when one of // the arms are an uninhabited type. Which is OK. @@ -219,6 +221,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let pattern_ty = pattern.ty(); let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }]; let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty); + report_exhaustiveness_lints(&cx, &report); // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We // only care about exhaustiveness here. @@ -444,6 +447,7 @@ fn check_let_reachability<'p, 'tcx>( ) { let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }]; let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty()); + report_exhaustiveness_lints(&cx, &report); // Report if the pattern is unreachable, which can only occur when the type is uninhabited. // This also reports unreachable sub-patterns though, so we can't just replace it with an @@ -483,6 +487,67 @@ fn report_arm_reachability<'p, 'tcx>( } } +/// Report various things detected during exhaustiveness checking. +fn report_exhaustiveness_lints<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + report: &UsefulnessReport<'p, 'tcx>, +) { + for (ty, span, hir_id, patterns) in &report.non_exhaustive_omitted_patterns { + lint_non_exhaustive_omitted_patterns(cx, ty, *span, *hir_id, patterns.as_slice()); + } + for (span, hir_id, overlaps) in &report.overlapping_range_endpoints { + lint_overlapping_range_endpoints(cx, *span, *hir_id, overlaps.as_slice()); + } +} + +/// Report when some variants of a `non_exhaustive` enum failed to be listed explicitly. +/// +/// NB: The partner lint for structs lives in `compiler/rustc_typeck/src/check/pat.rs`. +pub(super) fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + scrut_ty: Ty<'tcx>, + span: Span, + hir_id: HirId, + witnesses: &[DeconstructedPat<'p, 'tcx>], +) { + let joined_patterns = joined_uncovered_patterns(cx, &witnesses); + cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, span, |build| { + let mut lint = build.build("some variants are not matched explicitly"); + lint.span_label(span, pattern_not_covered_label(witnesses, &joined_patterns)); + lint.help( + "ensure that all variants are matched explicitly by adding the suggested match arms", + ); + lint.note(&format!( + "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", + scrut_ty, + )); + lint.emit(); + }); +} + +/// Lint on likely incorrect range patterns (#63987) +pub(super) fn lint_overlapping_range_endpoints<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + span: Span, + hir_id: HirId, + overlaps: &[DeconstructedPat<'p, 'tcx>], +) { + if !overlaps.is_empty() { + cx.tcx.struct_span_lint_hir(OVERLAPPING_RANGE_ENDPOINTS, hir_id, span, |lint| { + let mut err = lint.build("multiple patterns overlap on their endpoints"); + for pat in overlaps { + err.span_label( + pat.span(), + &format!("this range overlaps on `{}`...", pat.to_pat(cx)), + ); + } + err.span_label(span, "... with this range"); + err.note("you likely meant to write mutually exclusive ranges"); + err.emit(); + }); + } +} + /// Report that a match is not exhaustive. fn non_exhaustive_match<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 8a659c8092a1d..c2cf802ab6ac4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -59,7 +59,6 @@ use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::{self, Const, Ty, TyCtxt, VariantDef}; -use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Primitive; use rustc_target::abi::{Integer, Size, VariantIdx}; @@ -199,7 +198,11 @@ impl IntRange { } } - fn suspicious_intersection(&self, other: &Self) -> bool { + /// Check whether two non-singleton ranges touch exactly on on of their endpoints. + fn suspicious_intersection(&self, other: &Self) -> Option { + if self.is_singleton() || other.is_singleton() { + return None; + } // `false` in the following cases: // 1 ---- // 1 ---------- // 1 ---- // 1 ---- // 2 ---------- // 2 ---- // 2 ---- // 2 ---- @@ -213,7 +216,11 @@ impl IntRange { // 2 -------- // 2 ------- let (lo, hi) = self.boundaries(); let (other_lo, other_hi) = other.boundaries(); - (lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton() + if lo == other_hi || hi == other_lo { + Some(self.intersection(other).unwrap()) + } else { + None + } } /// Only used for displaying the range properly. @@ -240,6 +247,7 @@ impl IntRange { pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>( &self, pcx: PatCtxt<'_, 'p, 'tcx>, + report: &mut Vec<(Span, HirId, Vec>)>, pats: impl Iterator>, column_count: usize, hir_id: HirId, @@ -262,33 +270,22 @@ impl IntRange { return; } - let overlaps: Vec<_> = pats - .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span()))) - .filter(|(range, _)| self.suspicious_intersection(range)) - .map(|(range, span)| (self.intersection(&range).unwrap(), span)) - .collect(); - + let mut overlaps = Vec::new(); + for pat in pats { + if let IntRange(range) = pat.ctor() { + if let Some(intersection) = self.suspicious_intersection(range) { + let intersection = DeconstructedPat::new( + IntRange(intersection), + Fields::empty(), + pcx.ty, + pat.span(), + ); + overlaps.push(intersection); + } + } + } if !overlaps.is_empty() { - pcx.cx.tcx.struct_span_lint_hir( - lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, - hir_id, - pcx.span, - |lint| { - let mut err = lint.build("multiple patterns overlap on their endpoints"); - for (int_range, span) in overlaps { - err.span_label( - span, - &format!( - "this range overlaps on `{}`...", - int_range.to_pat(pcx.cx.tcx, pcx.ty) - ), - ); - } - err.span_label(pcx.span, "... with this range"); - err.note("you likely meant to write mutually exclusive ranges"); - err.emit(); - }, - ); + report.push((pcx.span, hir_id, overlaps)); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 9e4892a33287c..97dfb67747bdf 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -283,7 +283,6 @@ use self::ArmType::*; use self::Usefulness::*; -use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label}; use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; use rustc_data_structures::captures::Captures; @@ -292,7 +291,6 @@ use rustc_arena::TypedArena; use rustc_hir::def_id::DefId; use rustc_hir::HirId; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::{sym, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; @@ -739,32 +737,6 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { } } -/// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` -/// is not exhaustive enough. -/// -/// NB: The partner lint for structs lives in `compiler/rustc_typeck/src/check/pat.rs`. -fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - scrut_ty: Ty<'tcx>, - sp: Span, - hir_id: HirId, - witnesses: Vec>, -) { - let joined_patterns = joined_uncovered_patterns(cx, &witnesses); - cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, |build| { - let mut lint = build.build("some variants are not matched explicitly"); - lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); - lint.help( - "ensure that all variants are matched explicitly by adding the suggested match arms", - ); - lint.note(&format!( - "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", - scrut_ty, - )); - lint.emit(); - }); -} - /// Algorithm from . /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: @@ -789,12 +761,13 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( /// relied on for soundness. #[instrument( level = "debug", - skip(cx, matrix, witness_preference, hir_id, is_under_guard, is_top_level) + skip(cx, matrix, report, witness_preference, hir_id, is_under_guard, is_top_level) )] fn is_useful<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'p, 'tcx>, + report: &mut UsefulnessReport<'p, 'tcx>, witness_preference: ArmType, hir_id: HirId, is_under_guard: bool, @@ -831,8 +804,16 @@ fn is_useful<'p, 'tcx>( // We try each or-pattern branch in turn. let mut matrix = matrix.clone(); for v in v.expand_or_pat() { - let usefulness = - is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); + let usefulness = is_useful( + cx, + &matrix, + &v, + report, + witness_preference, + hir_id, + is_under_guard, + false, + ); ret.extend(usefulness); // If pattern has a guard don't add it to the matrix. if !is_under_guard { @@ -847,6 +828,7 @@ fn is_useful<'p, 'tcx>( // Lint on likely incorrect range patterns (#63987) ctor_range.lint_overlapping_range_endpoints( pcx, + &mut report.overlapping_range_endpoints, matrix.heads(), matrix.column_count().unwrap_or(0), hir_id, @@ -862,8 +844,16 @@ fn is_useful<'p, 'tcx>( debug!("specialize({:?})", ctor); let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); let v = v.pop_head_constructor(cx, &ctor); - let usefulness = - is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false); + let usefulness = is_useful( + cx, + &spec_matrix, + &v, + report, + witness_preference, + hir_id, + is_under_guard, + false, + ); let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor); // When all the conditions are met we have a match with a `non_exhaustive` enum @@ -879,24 +869,20 @@ fn is_useful<'p, 'tcx>( Constructor::Missing { nonexhaustive_enum_missing_real_variants: true } ) { - let patterns = { - let mut split_wildcard = SplitWildcard::new(pcx); - split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); - // Construct for each missing constructor a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, we get the pattern `Some(_)`. - split_wildcard - .iter_missing(pcx) - // Filter out the `NonExhaustive` because we want to list only real - // variants. - .filter(|c| !c.is_non_exhaustive()) - .cloned() - .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) - .collect::>() - }; - - lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns); + let mut split_wildcard = SplitWildcard::new(pcx); + split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor)); + // Construct for each missing constructor a "wild" version of this constructor, + // that matches everything that can be built with it. For example, if `ctor` is a + // `Constructor::Variant` for `Option::Some`, we get the pattern `Some(_)`. + let patterns: Vec<_> = split_wildcard + .iter_missing(pcx) + // Filter out `NonExhaustive` because we want to list only real variants. + .filter(|c| !c.is_non_exhaustive()) + .cloned() + .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) + .collect(); + + report.non_exhaustive_omitted_patterns.push((pcx.ty, pcx.span, hir_id, patterns)); } ret.extend(usefulness); @@ -932,12 +918,18 @@ crate enum Reachability { } /// The output of checking a match for exhaustiveness and arm reachability. +#[derive(Default)] crate struct UsefulnessReport<'p, 'tcx> { /// For each arm of the input, whether that arm is reachable after the arms above it. crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. crate non_exhaustiveness_witnesses: Vec>, + /// Report when some variants of a `non_exhaustive` enum failed to be listed explicitly. + crate non_exhaustive_omitted_patterns: + Vec<(Ty<'tcx>, Span, HirId, Vec>)>, + /// Report likely incorrect range patterns (#63987) + crate overlapping_range_endpoints: Vec<(Span, HirId, Vec>)>, } /// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which @@ -951,31 +943,29 @@ crate fn compute_match_usefulness<'p, 'tcx>( scrut_hir_id: HirId, scrut_ty: Ty<'tcx>, ) -> UsefulnessReport<'p, 'tcx> { + let mut report = UsefulnessReport::default(); let mut matrix = Matrix::empty(); - let arm_usefulness: Vec<_> = arms - .iter() - .copied() - .map(|arm| { - let v = PatStack::from_pattern(arm.pat); - is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true); - if !arm.has_guard { - matrix.push(v); - } - let reachability = if arm.pat.is_reachable() { - Reachability::Reachable(arm.pat.unreachable_spans()) - } else { - Reachability::Unreachable - }; - (arm, reachability) - }) - .collect(); + for arm in arms { + let v = PatStack::from_pattern(arm.pat); + is_useful(cx, &matrix, &v, &mut report, RealArm, arm.hir_id, arm.has_guard, true); + if !arm.has_guard { + matrix.push(v); + } + let reachability = if arm.pat.is_reachable() { + Reachability::Reachable(arm.pat.unreachable_spans()) + } else { + Reachability::Unreachable + }; + report.arm_usefulness.push((*arm, reachability)); + } let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); let v = PatStack::from_pattern(wild_pattern); - let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true); - let non_exhaustiveness_witnesses = match usefulness { + let usefulness = + is_useful(cx, &matrix, &v, &mut report, FakeExtraWildcard, scrut_hir_id, false, true); + report.non_exhaustiveness_witnesses = match usefulness { WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(), NoWitnesses { .. } => bug!(), }; - UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses } + report } From 1684c640f82d62212771c70cf81bf54666b65b6c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 1 Oct 2021 23:26:42 +0100 Subject: [PATCH 20/26] Always report unreachable subpatterns --- .../src/thir/pattern/check_match.rs | 28 +++++++++---------- .../src/thir/pattern/usefulness.rs | 13 +++++---- .../exhaustiveness-unreachable-pattern.rs | 1 + .../exhaustiveness-unreachable-pattern.stderr | 8 +++++- 4 files changed, 28 insertions(+), 22 deletions(-) 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 8c58b5beb6387..3d051616fe8dd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -450,8 +450,6 @@ fn check_let_reachability<'p, 'tcx>( report_exhaustiveness_lints(&cx, &report); // Report if the pattern is unreachable, which can only occur when the type is uninhabited. - // This also reports unreachable sub-patterns though, so we can't just replace it with an - // `is_uninhabited` check. report_arm_reachability(&cx, &report); if report.non_exhaustiveness_witnesses.is_empty() { @@ -465,21 +463,13 @@ fn report_arm_reachability<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>, ) { - use Reachability::*; let mut catchall = None; - for (arm, is_useful) in report.arm_usefulness.iter() { - match is_useful { - Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall), - Reachable(unreachables) if unreachables.is_empty() => {} - // The arm is reachable, but contains unreachable subpatterns (from or-patterns). - Reachable(unreachables) => { - let mut unreachables = unreachables.clone(); - // Emit lints in the order in which they occur in the file. - unreachables.sort_unstable(); - for span in unreachables { - unreachable_pattern(cx.tcx, span, arm.hir_id, None); - } + for (arm, reachability) in report.arm_usefulness.iter() { + match reachability { + Reachability::Unreachable => { + unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall) } + Reachability::Reachable => {} } if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { catchall = Some(arm.pat.span()); @@ -492,6 +482,14 @@ fn report_exhaustiveness_lints<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>, ) { + if !report.unreachable_subpatterns.is_empty() { + let mut unreachables = report.unreachable_subpatterns.clone(); + // Emit lints in the order in which they occur in the file. + unreachables.sort_unstable(); + for (span, hir_id) in unreachables { + unreachable_pattern(cx.tcx, span, hir_id, None); + } + } for (ty, span, hir_id, patterns) in &report.non_exhaustive_omitted_patterns { lint_non_exhaustive_omitted_patterns(cx, ty, *span, *hir_id, patterns.as_slice()); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 97dfb67747bdf..dfa7e0e1510b6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -909,11 +909,7 @@ crate struct MatchArm<'p, 'tcx> { /// Indicates whether or not a given arm is reachable. #[derive(Clone, Debug)] crate enum Reachability { - /// The arm is reachable. This additionally carries a set of or-pattern branches that have been - /// found to be unreachable despite the overall arm being reachable. Used only in the presence - /// of or-patterns, otherwise it stays empty. - Reachable(Vec), - /// The arm is unreachable. + Reachable, Unreachable, } @@ -925,6 +921,8 @@ crate struct UsefulnessReport<'p, 'tcx> { /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. crate non_exhaustiveness_witnesses: Vec>, + /// Reports any unreachable sub-or-patterns. + crate unreachable_subpatterns: Vec<(Span, HirId)>, /// Report when some variants of a `non_exhaustive` enum failed to be listed explicitly. crate non_exhaustive_omitted_patterns: Vec<(Ty<'tcx>, Span, HirId, Vec>)>, @@ -952,7 +950,10 @@ crate fn compute_match_usefulness<'p, 'tcx>( matrix.push(v); } let reachability = if arm.pat.is_reachable() { - Reachability::Reachable(arm.pat.unreachable_spans()) + for span in arm.pat.unreachable_spans() { + report.unreachable_subpatterns.push((span, arm.hir_id)); + } + Reachability::Reachable } else { Reachability::Unreachable }; diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index e77c5a4dcfe33..2b9281e0ba34f 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -151,4 +151,5 @@ fn main() { } let (true | false | true, _) = (true, true); + //~^ ERROR unreachable pattern } diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index 3f7d47dcb8ceb..b7cd2151ec7ea 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -166,5 +166,11 @@ error: unreachable pattern LL | | true, | ^^^^ -error: aborting due to 26 previous errors +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:153:25 + | +LL | let (true | false | true, _) = (true, true); + | ^^^^ + +error: aborting due to 27 previous errors From 784d29a740553ae0c0de1c67f281d621e6c28805 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 2 Oct 2021 12:31:25 +0100 Subject: [PATCH 21/26] Move conversion to/from `thir::Pat` out of deconstruct_pat --- .../src/thir/pattern/check_match.rs | 12 +- .../src/thir/pattern/deconstruct_pat.rs | 362 ++---------------- .../rustc_mir_build/src/thir/pattern/mod.rs | 328 +++++++++++++++- 3 files changed, 361 insertions(+), 341 deletions(-) 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 3d051616fe8dd..384c771b7a940 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -2,7 +2,7 @@ use super::deconstruct_pat::{Constructor, DeconstructedPat}; use super::usefulness::{ compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport, }; -use super::{PatCtxt, PatternError}; +use super::{deconstructed_to_pat, pat_to_deconstructed, PatCtxt, PatternError}; use rustc_arena::TypedArena; use rustc_ast::Mutability; @@ -135,7 +135,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.typeck_results); patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); - let pattern: &_ = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern)); + let pattern: &_ = cx.pattern_arena.alloc(pat_to_deconstructed(cx, &pattern)); if !patcx.errors.is_empty() { *have_errors = true; patcx.report_inlining_errors(); @@ -536,7 +536,7 @@ pub(super) fn lint_overlapping_range_endpoints<'p, 'tcx>( for pat in overlaps { err.span_label( pat.span(), - &format!("this range overlaps on `{}`...", pat.to_pat(cx)), + &format!("this range overlaps on `{}`...", deconstructed_to_pat(cx, pat)), ); } err.span_label(span, "... with this range"); @@ -623,13 +623,13 @@ crate fn joined_uncovered_patterns<'p, 'tcx>( witnesses: &[DeconstructedPat<'p, 'tcx>], ) -> String { const LIMIT: usize = 3; - let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string(); + let pat_to_str = |pat| deconstructed_to_pat(cx, pat).to_string(); match witnesses { [] => bug!(), - [witness] => format!("`{}`", witness.to_pat(cx)), + [witness] => format!("`{}`", deconstructed_to_pat(cx, witness)), [head @ .., tail] if head.len() < LIMIT => { let head: Vec<_> = head.iter().map(pat_to_str).collect(); - format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx)) + format!("`{}` and `{}`", head.join("`, `"), deconstructed_to_pat(cx, tail)) } _ => { let (head, tail) = witnesses.split_at(LIMIT); diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index c2cf802ab6ac4..f41585723b48d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -53,15 +53,13 @@ use rustc_index::vec::Idx; use rustc_hir::{HirId, RangeEnd}; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue, Scalar}; use rustc_middle::mir::Field; -use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, Const, Ty, TyCtxt, VariantDef}; +use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::Primitive; -use rustc_target::abi::{Integer, Size, VariantIdx}; +use rustc_target::abi::Integer; +use rustc_target::abi::VariantIdx; use smallvec::{smallvec, SmallVec}; use std::cell::Cell; @@ -70,49 +68,6 @@ use std::fmt; use std::iter::{once, IntoIterator}; use std::ops::RangeInclusive; -/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. -fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { - fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) { - if let PatKind::Or { pats } = pat.kind.as_ref() { - for pat in pats { - expand(pat, vec); - } - } else { - vec.push(pat) - } - } - - let mut pats = Vec::new(); - expand(pat, &mut pats); - pats -} - -/// Evaluate an int constant, with a faster branch for a common case. -#[inline] -fn fast_try_eval_bits<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: &Const<'tcx>, -) -> Option { - let int = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) = value.val { - // If the constant is already evaluated, we shortcut here. - int - } else { - // This is a more general but slower form of the previous case. - value.val.eval(tcx, param_env).try_to_scalar_int()? - }; - let size = match value.ty.kind() { - ty::Bool => Size::from_bytes(1), - ty::Char => Size::from_bytes(4), - ty::Int(ity) => Integer::from_int_ty(&tcx, *ity).size(), - ty::Uint(uty) => Integer::from_uint_ty(&tcx, *uty).size(), - ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx), - ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx), - _ => bug!("unexpected type: {}", value.ty), - }; - int.to_bits(size).ok() -} - /// An inclusive interval, used for precise integer exhaustiveness checking. /// `IntRange`s always store a contiguous range. This means that values are /// encoded such that `0` encodes the minimum value for the integer, @@ -156,7 +111,11 @@ impl IntRange { } #[inline] - fn from_bits<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, bits: u128) -> IntRange { + pub(super) fn from_singleton_bits<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + bits: u128, + ) -> IntRange { let bias = IntRange::signed_bias(tcx, ty); // Perform a shift if the underlying types are signed, // which makes the interval arithmetic simpler. @@ -165,7 +124,7 @@ impl IntRange { } #[inline] - fn from_range<'tcx>( + pub(super) fn from_range_bits<'tcx>( tcx: TyCtxt<'tcx>, lo: u128, hi: u128, @@ -184,6 +143,13 @@ impl IntRange { IntRange { range: lo..=(hi - offset), bias } } + /// The reverse of `Self::from_range/from_bits`. + pub(super) fn to_bits(&self) -> (u128, u128) { + let (lo, hi) = self.boundaries(); + let bias = self.bias; + (lo ^ bias, hi ^ bias) + } + fn is_subrange(&self, other: &Self) -> bool { other.range.start() <= self.range.start() && self.range.end() <= other.range.end() } @@ -223,26 +189,6 @@ impl IntRange { } } - /// Only used for displaying the range properly. - fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { - let (lo, hi) = self.boundaries(); - - let bias = self.bias; - let (lo, hi) = (lo ^ bias, hi ^ bias); - - let env = ty::ParamEnv::empty().and(ty); - let lo_const = ty::Const::from_bits(tcx, lo, env); - let hi_const = ty::Const::from_bits(tcx, hi, env); - - let kind = if lo == hi { - PatKind::Constant { value: lo_const } - } else { - PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) - }; - - Pat { ty, span: DUMMY_SP, kind: Box::new(kind) } - } - /// Lint on likely incorrect range patterns (#63987) pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>( &self, @@ -302,8 +248,8 @@ impl IntRange { } } -/// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and -/// would be displayed as such. To render properly, convert to a pattern first. +/// Note: this is missing some information. It will render chars as numbers among other things. To +/// render properly, convert to a pattern first. impl fmt::Debug for IntRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (lo, hi) = self.boundaries(); @@ -411,7 +357,7 @@ impl SplitIntRange { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum SliceKind { +pub(super) enum SliceKind { /// Patterns of length `n` (`[x, y]`). FixedLen(usize), /// Patterns using the `..` notation (`[x, .., y]`). @@ -443,13 +389,13 @@ impl SliceKind { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(super) struct Slice { /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`. - array_len: Option, + pub(super) array_len: Option, /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`. - kind: SliceKind, + pub(super) kind: SliceKind, } impl Slice { - fn new(array_len: Option, kind: SliceKind) -> Self { + pub(super) fn new(array_len: Option, kind: SliceKind) -> Self { let kind = match (array_len, kind) { // If the middle `..` is empty, we effectively have a fixed-length pattern. (Some(len), VarLen(prefix, suffix)) if prefix + suffix >= len => FixedLen(len), @@ -672,7 +618,7 @@ impl<'tcx> Constructor<'tcx> { } } - fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx { + pub(super) fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx { match *self { Variant(idx) => idx, Single => { @@ -880,7 +826,7 @@ impl<'tcx> SplitWildcard<'tcx> { debug!("SplitWildcard::new({:?})", pcx.ty); let cx = pcx.cx; let make_range = |start, end| { - IntRange(IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included)) + IntRange(IntRange::from_range_bits(cx.tcx, start, end, pcx.ty, &RangeEnd::Included)) }; // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, // arrays and slices we use ranges and variable-length slices when appropriate. @@ -1120,11 +1066,14 @@ pub(super) struct Fields<'p, 'tcx> { } impl<'p, 'tcx> Fields<'p, 'tcx> { - fn empty() -> Self { + pub(super) fn empty() -> Self { Fields { fields: &[] } } - fn singleton(cx: &MatchCheckCtxt<'p, 'tcx>, field: DeconstructedPat<'p, 'tcx>) -> Self { + pub(super) fn singleton( + cx: &MatchCheckCtxt<'p, 'tcx>, + field: DeconstructedPat<'p, 'tcx>, + ) -> Self { let field: &_ = cx.pattern_arena.alloc(field); Fields { fields: std::slice::from_ref(field) } } @@ -1147,7 +1096,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide // uninhabited fields in order not to reveal the uninhabitedness of the whole variant. // This lists the fields we keep along with their types. - fn list_variant_nonhidden_fields<'a>( + pub(super) fn list_variant_nonhidden_fields<'a>( cx: &'a MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>, variant: &'a VariantDef, @@ -1266,257 +1215,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty, self.span) } - pub(crate) fn from_pat(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &Pat<'tcx>) -> Self { - let mkpat = |pat| DeconstructedPat::from_pat(cx, pat); - let ctor; - let fields; - match pat.kind.as_ref() { - PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern), - PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat), - PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { - ctor = Wildcard; - fields = Fields::empty(); - } - PatKind::Deref { subpattern } => { - ctor = match pat.ty.kind() { - ty::Adt(adt, _) if adt.is_box() => BoxPat(subpattern.ty), - _ => Ref(subpattern.ty), - }; - fields = Fields::singleton(cx, mkpat(subpattern)); - } - PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { - match pat.ty.kind() { - ty::Tuple(fs) => { - ctor = Tuple(fs); - let mut wilds: SmallVec<[_; 2]> = fs - .iter() - .map(|ty| ty.expect_ty()) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - wilds[pat.field.index()] = mkpat(&pat.pattern); - } - fields = Fields::from_iter(cx, wilds); - } - ty::Adt(adt, substs) if adt.is_box() => { - // If we're here, the pattern is using the private `Box(_, _)` constructor. - // Since this is private, we can ignore the subpatterns here and pretend it's a - // `box _`. There'll be an error later anyways. This prevents an ICE. - // See https://github.com/rust-lang/rust/issues/82772 , - // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 - let inner_ty = substs.type_at(0); - ctor = Constructor::BoxPat(inner_ty); - fields = Fields::singleton(cx, DeconstructedPat::wildcard(inner_ty)); - } - ty::Adt(adt, _) => { - ctor = match pat.kind.as_ref() { - PatKind::Leaf { .. } => Single, - PatKind::Variant { variant_index, .. } => Variant(*variant_index), - _ => bug!(), - }; - let variant = &adt.variants[ctor.variant_index_for_adt(adt)]; - // For each field in the variant, we store the relevant index into `self.fields` if any. - let mut field_id_to_id: Vec> = - (0..variant.fields.len()).map(|_| None).collect(); - let tys = Fields::list_variant_nonhidden_fields(cx, pat.ty, variant) - .enumerate() - .map(|(i, (field, ty))| { - field_id_to_id[field.index()] = Some(i); - ty - }); - let mut wilds: SmallVec<[_; 2]> = - tys.map(DeconstructedPat::wildcard).collect(); - for pat in subpatterns { - if let Some(i) = field_id_to_id[pat.field.index()] { - wilds[i] = mkpat(&pat.pattern); - } - } - fields = Fields::from_iter(cx, wilds); - } - _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty), - } - } - PatKind::Constant { value } => { - match pat.ty.kind() { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { - use rustc_apfloat::Float; - ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) { - Some(bits) => match pat.ty.kind() { - ty::Bool => Bool(bits != 0), - ty::Char | ty::Int(_) | ty::Uint(_) => { - let int_range = IntRange::from_bits(cx.tcx, pat.ty, bits); - IntRange(int_range) - } - ty::Float(ty::FloatTy::F32) => { - let value = rustc_apfloat::ieee::Single::from_bits(bits); - F32Range(value, value, RangeEnd::Included) - } - ty::Float(ty::FloatTy::F64) => { - let value = rustc_apfloat::ieee::Double::from_bits(bits); - F64Range(value, value, RangeEnd::Included) - } - _ => unreachable!(), - }, - None => Opaque, - }; - fields = Fields::empty(); - } - ty::Ref(_, t, _) if t.is_str() => { - if let ty::ConstKind::Value(val @ ConstValue::Slice { .. }) = value.val { - let bytes = get_slice_bytes(&cx.tcx, val); - // We want a `&str` constant to behave like a `Deref` pattern, to be compatible - // 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. - // Note: `t` is `str`, not `&str`. - let subpattern = - DeconstructedPat::new(Str(bytes), Fields::empty(), t, pat.span); - ctor = Ref(t); - fields = Fields::singleton(cx, subpattern) - } else { - // FIXME(Nadrieril): Does this ever happen? - ctor = Opaque; - fields = Fields::empty(); - } - } - // 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. - _ => { - ctor = Opaque; - fields = Fields::empty(); - } - } - } - &PatKind::Range(PatRange { lo, hi, end }) => { - use rustc_apfloat::Float; - let ty = lo.ty; - let lo = lo.eval_bits(cx.tcx, cx.param_env, ty); - let hi = hi.eval_bits(cx.tcx, cx.param_env, ty); - ctor = match ty.kind() { - ty::Char | ty::Int(_) | ty::Uint(_) => { - IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, &end)) - } - ty::Float(ty::FloatTy::F32) => { - let lo = rustc_apfloat::ieee::Single::from_bits(lo); - let hi = rustc_apfloat::ieee::Single::from_bits(hi); - F32Range(lo, hi, RangeEnd::Included) - } - ty::Float(ty::FloatTy::F64) => { - let lo = rustc_apfloat::ieee::Double::from_bits(lo); - let hi = rustc_apfloat::ieee::Double::from_bits(hi); - F64Range(lo, hi, RangeEnd::Included) - } - _ => bug!("invalid type for range pattern: {}", ty), - }; - fields = Fields::empty(); - } - PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { - let array_len = match pat.ty.kind() { - ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env) as usize), - ty::Slice(_) => None, - _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), - }; - let kind = if slice.is_some() { - VarLen(prefix.len(), suffix.len()) - } else { - FixedLen(prefix.len() + suffix.len()) - }; - ctor = Slice(Slice::new(array_len, kind)); - fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat)); - } - PatKind::Or { .. } => { - ctor = Or; - let pats = expand_or_pat(pat); - fields = Fields::from_iter(cx, pats.into_iter().map(mkpat)); - } - } - DeconstructedPat::new(ctor, fields, pat.ty, pat.span) - } - - pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> { - let is_wildcard = |pat: &Pat<'_>| { - matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) - }; - let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx)); - let pat = match &self.ctor { - Single | Variant(_) => match self.ty.kind() { - ty::Adt(adt_def, substs) => { - let variant_index = self.ctor.variant_index_for_adt(adt_def); - let variant = &adt_def.variants[variant_index]; - let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant) - .zip(subpatterns) - .map(|((field, _ty), pattern)| FieldPat { field, pattern }) - .collect(); - - if adt_def.is_enum() { - PatKind::Variant { adt_def, substs, variant_index, subpatterns } - } else { - PatKind::Leaf { subpatterns } - } - } - _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty), - }, - Tuple(..) => PatKind::Leaf { - subpatterns: subpatterns - .enumerate() - .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) - .collect(), - }, - // Note: given the expansion of `&str` patterns done in `DeconstructedPat::from_pat`, - // 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(_) | BoxPat(_) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - Slice(slice) => { - match slice.kind { - FixedLen(_) => PatKind::Slice { - prefix: subpatterns.collect(), - slice: None, - suffix: vec![], - }, - VarLen(prefix, _) => { - let mut subpatterns = subpatterns.peekable(); - let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect(); - if slice.array_len.is_some() { - // Improves diagnostics a bit: if the type is a known-size array, instead - // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. - // This is incorrect if the size is not known, since `[_, ..]` captures - // arrays of lengths `>= 1` whereas `[..]` captures any length. - while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) { - prefix.pop(); - } - while subpatterns.peek().is_some() - && is_wildcard(subpatterns.peek().unwrap()) - { - subpatterns.next(); - } - } - let suffix: Vec<_> = subpatterns.collect(); - let wild = Pat::wildcard_from_ty(self.ty); - PatKind::Slice { prefix, slice: Some(wild), suffix } - } - } - } - Bool(b) => PatKind::Constant { value: ty::Const::from_bool(cx.tcx, *b) }, - IntRange(range) => return range.to_pat(cx.tcx, self.ty), - Wildcard | NonExhaustive => PatKind::Wild, - Missing { .. } => bug!( - "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, - `Missing` should have been processed in `apply_constructors`" - ), - // These will never be converted because we don't emit them as non-exhaustiveness - // witnesses. And that's good because we're missing the relevant `&Const`. - F32Range(..) | F64Range(..) | Str(_) | Opaque | Or => { - bug!("can't convert to pattern: {:?}", self) - } - }; - - Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(pat) } - } - pub(super) fn is_or_pat(&self) -> bool { matches!(self.ctor, Or) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 98069082ebbd4..67eca6f535b8f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -8,6 +8,8 @@ mod usefulness; pub(crate) use self::check_match::check_match; use crate::thir::util::UserAnnotatedTyHelpers; +use deconstruct_pat::{Constructor, DeconstructedPat, Fields, IntRange, Slice, SliceKind}; +use usefulness::MatchCheckCtxt; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -15,13 +17,15 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::RangeEnd; use rustc_index::vec::Idx; -use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue}; -use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::{ + get_slice_bytes, ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar, +}; use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt}; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; +use smallvec::SmallVec; use std::cmp::Ordering; #[derive(Clone, Debug)] @@ -646,3 +650,321 @@ crate fn compare_const_vals<'tcx>( } fallback() } + +/// Return the size of the corresponding number type. +#[inline] +fn number_type_size<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> rustc_target::abi::Size { + use rustc_middle::ty::layout::IntegerExt; + use rustc_target::abi::{Integer, Primitive, Size}; + match ty.kind() { + ty::Bool => Size::from_bytes(1), + ty::Char => Size::from_bytes(4), + ty::Int(ity) => Integer::from_int_ty(&tcx, *ity).size(), + ty::Uint(uty) => Integer::from_uint_ty(&tcx, *uty).size(), + ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx), + ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx), + _ => bug!("unexpected type: {}", ty), + } +} + +/// Evaluate an int constant, with a faster branch for a common case. +#[inline] +fn fast_try_eval_bits<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &ty::Const<'tcx>, +) -> Option { + let int = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) = value.val { + // If the constant is already evaluated, we shortcut here. + int + } else { + // This is a more general but slower form of the previous case. + value.val.eval(tcx, param_env).try_to_scalar_int()? + }; + let size = number_type_size(tcx, value.ty); + int.to_bits(size).ok() +} + +fn pat_to_deconstructed<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + pat: &Pat<'tcx>, +) -> DeconstructedPat<'p, 'tcx> { + let mkpat = |pat| pat_to_deconstructed(cx, pat); + let ctor; + let fields; + match pat.kind.as_ref() { + PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern), + PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat), + PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { + ctor = Constructor::Wildcard; + fields = Fields::empty(); + } + PatKind::Deref { subpattern } => { + ctor = match pat.ty.kind() { + ty::Adt(adt, _) if adt.is_box() => Constructor::BoxPat(subpattern.ty), + _ => Constructor::Ref(subpattern.ty), + }; + fields = Fields::singleton(cx, mkpat(subpattern)); + } + PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { + match pat.ty.kind() { + ty::Tuple(fs) => { + ctor = Constructor::Tuple(fs); + let mut wilds: SmallVec<[_; 2]> = fs + .iter() + .map(|ty| ty.expect_ty()) + .map(DeconstructedPat::wildcard) + .collect(); + for pat in subpatterns { + wilds[pat.field.index()] = mkpat(&pat.pattern); + } + fields = Fields::from_iter(cx, wilds); + } + ty::Adt(adt, substs) if adt.is_box() => { + // If we're here, the pattern is using the private `Box(_, _)` constructor. + // Since this is private, we can ignore the subpatterns here and pretend it's a + // `box _`. There'll be an error later anyways. This prevents an ICE. + // See https://github.com/rust-lang/rust/issues/82772 , + // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 + let inner_ty = substs.type_at(0); + ctor = Constructor::BoxPat(inner_ty); + fields = Fields::singleton(cx, DeconstructedPat::wildcard(inner_ty)); + } + ty::Adt(adt, _) => { + ctor = match pat.kind.as_ref() { + PatKind::Leaf { .. } => Constructor::Single, + PatKind::Variant { variant_index, .. } => { + Constructor::Variant(*variant_index) + } + _ => bug!(), + }; + let variant = &adt.variants[ctor.variant_index_for_adt(adt)]; + // For each field in the variant, we store the relevant index into `self.fields` if any. + let mut field_id_to_id: Vec> = + (0..variant.fields.len()).map(|_| None).collect(); + let tys = Fields::list_variant_nonhidden_fields(cx, pat.ty, variant) + .enumerate() + .map(|(i, (field, ty))| { + field_id_to_id[field.index()] = Some(i); + ty + }); + let mut wilds: SmallVec<[_; 2]> = tys.map(DeconstructedPat::wildcard).collect(); + for pat in subpatterns { + if let Some(i) = field_id_to_id[pat.field.index()] { + wilds[i] = mkpat(&pat.pattern); + } + } + fields = Fields::from_iter(cx, wilds); + } + _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty), + } + } + PatKind::Constant { value } => { + match pat.ty.kind() { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { + use rustc_apfloat::Float; + ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) { + Some(bits) => match pat.ty.kind() { + ty::Bool => Constructor::Bool(bits != 0), + ty::Char | ty::Int(_) | ty::Uint(_) => { + let int_range = IntRange::from_singleton_bits(cx.tcx, pat.ty, bits); + Constructor::IntRange(int_range) + } + ty::Float(ty::FloatTy::F32) => { + let value = rustc_apfloat::ieee::Single::from_bits(bits); + Constructor::F32Range(value, value, RangeEnd::Included) + } + ty::Float(ty::FloatTy::F64) => { + let value = rustc_apfloat::ieee::Double::from_bits(bits); + Constructor::F64Range(value, value, RangeEnd::Included) + } + _ => unreachable!(), + }, + None => Constructor::Opaque, + }; + fields = Fields::empty(); + } + ty::Ref(_, t, _) if t.is_str() => { + if let ty::ConstKind::Value(val @ ConstValue::Slice { .. }) = value.val { + let bytes = get_slice_bytes(&cx.tcx, val); + // We want a `&str` constant to behave like a `Deref` pattern, to be compatible + // 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. + // Note: `t` is `str`, not `&str`. + let subpattern = DeconstructedPat::new( + Constructor::Str(bytes), + Fields::empty(), + t, + pat.span, + ); + ctor = Constructor::Ref(t); + fields = Fields::singleton(cx, subpattern) + } else { + // FIXME(Nadrieril): Does this ever happen? + ctor = Constructor::Opaque; + fields = Fields::empty(); + } + } + // 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. + _ => { + ctor = Constructor::Opaque; + fields = Fields::empty(); + } + } + } + &PatKind::Range(PatRange { lo, hi, end }) => { + use rustc_apfloat::Float; + let ty = lo.ty; + let lo = lo.eval_bits(cx.tcx, cx.param_env, ty); + let hi = hi.eval_bits(cx.tcx, cx.param_env, ty); + ctor = match ty.kind() { + ty::Char | ty::Int(_) | ty::Uint(_) => { + Constructor::IntRange(IntRange::from_range_bits(cx.tcx, lo, hi, ty, &end)) + } + ty::Float(ty::FloatTy::F32) => { + let lo = rustc_apfloat::ieee::Single::from_bits(lo); + let hi = rustc_apfloat::ieee::Single::from_bits(hi); + Constructor::F32Range(lo, hi, RangeEnd::Included) + } + ty::Float(ty::FloatTy::F64) => { + let lo = rustc_apfloat::ieee::Double::from_bits(lo); + let hi = rustc_apfloat::ieee::Double::from_bits(hi); + Constructor::F64Range(lo, hi, RangeEnd::Included) + } + _ => bug!("invalid type for range pattern: {}", ty), + }; + fields = Fields::empty(); + } + PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { + let array_len = match pat.ty.kind() { + ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env) as usize), + ty::Slice(_) => None, + _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), + }; + let kind = if slice.is_some() { + SliceKind::VarLen(prefix.len(), suffix.len()) + } else { + SliceKind::FixedLen(prefix.len() + suffix.len()) + }; + ctor = Constructor::Slice(Slice::new(array_len, kind)); + fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat)); + } + PatKind::Or { .. } => { + /// Recursively expand this pattern into its subpatterns. + fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) { + if let PatKind::Or { pats } = pat.kind.as_ref() { + for pat in pats { + expand(pat, vec); + } + } else { + vec.push(pat) + } + } + + let mut pats = Vec::new(); + expand(pat, &mut pats); + ctor = Constructor::Or; + fields = Fields::from_iter(cx, pats.into_iter().map(mkpat)); + } + } + DeconstructedPat::new(ctor, fields, pat.ty, pat.span) +} + +fn deconstructed_to_pat<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + pat: &DeconstructedPat<'p, 'tcx>, +) -> Pat<'tcx> { + use Constructor::*; + let is_wildcard = |pat: &Pat<'_>| { + matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) + }; + let mut subpatterns = pat.iter_fields().map(|pat| deconstructed_to_pat(cx, pat)); + let kind = match pat.ctor() { + Single | Variant(_) => match pat.ty().kind() { + ty::Adt(adt_def, substs) => { + let variant_index = pat.ctor().variant_index_for_adt(adt_def); + let variant = &adt_def.variants[variant_index]; + let subpatterns = Fields::list_variant_nonhidden_fields(cx, pat.ty(), variant) + .zip(subpatterns) + .map(|((field, _ty), pattern)| FieldPat { field, pattern }) + .collect(); + + if adt_def.is_enum() { + PatKind::Variant { adt_def, substs, variant_index, subpatterns } + } else { + PatKind::Leaf { subpatterns } + } + } + _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()), + }, + Tuple(..) => PatKind::Leaf { + subpatterns: subpatterns + .enumerate() + .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + .collect(), + }, + // Note: given the expansion of `&str` patterns done in `DeconstructedPat::from_pat`, + // 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(_) | BoxPat(_) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, + Slice(slice) => { + match slice.kind { + SliceKind::FixedLen(_) => { + PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } + } + SliceKind::VarLen(prefix, _) => { + let mut subpatterns = subpatterns.peekable(); + let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect(); + if slice.array_len.is_some() { + // Improves diagnostics a bit: if the type is a known-size array, instead + // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. + // This is incorrect if the size is not known, since `[_, ..]` captures + // arrays of lengths `>= 1` whereas `[..]` captures any length. + while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) { + prefix.pop(); + } + while subpatterns.peek().is_some() + && is_wildcard(subpatterns.peek().unwrap()) + { + subpatterns.next(); + } + } + let suffix: Vec<_> = subpatterns.collect(); + let wild = Pat::wildcard_from_ty(pat.ty()); + PatKind::Slice { prefix, slice: Some(wild), suffix } + } + } + } + Bool(b) => PatKind::Constant { value: ty::Const::from_bool(cx.tcx, *b) }, + IntRange(range) => { + let (lo, hi) = range.to_bits(); + let env = ty::ParamEnv::empty().and(pat.ty()); + let lo_const = ty::Const::from_bits(cx.tcx, lo, env); + let hi_const = ty::Const::from_bits(cx.tcx, hi, env); + + if lo == hi { + PatKind::Constant { value: lo_const } + } else { + PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) + } + } + Wildcard | NonExhaustive => PatKind::Wild, + Missing { .. } => bug!( + "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, + `Missing` should have been processed in `apply_constructors`" + ), + // These will never be converted because we don't emit them as non-exhaustiveness + // witnesses. And that's good because we're missing the relevant `&Const`. + F32Range(..) | F64Range(..) | Str(_) | Opaque | Or => { + bug!("can't convert to pattern: {:?}", pat) + } + }; + + Pat { ty: pat.ty(), span: DUMMY_SP, kind: Box::new(kind) } +} From 03eb4be842d0586e1a0a99d043b6c34d7b6529b9 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 2 Oct 2021 12:50:38 +0100 Subject: [PATCH 22/26] Move abi-related computations out of deconstruct_pat --- .../src/thir/pattern/deconstruct_pat.rs | 70 ++++++++----------- .../rustc_mir_build/src/thir/pattern/mod.rs | 26 +++---- 2 files changed, 42 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index f41585723b48d..f1aaf4f68166e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -45,6 +45,7 @@ use self::Constructor::*; use self::SliceKind::*; +use super::number_type_size; use super::usefulness::{MatchCheckCtxt, PatCtxt}; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; @@ -54,11 +55,9 @@ use rustc_index::vec::Idx; use rustc_hir::{HirId, RangeEnd}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::Field; -use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; +use rustc_middle::ty::{self, Ty, VariantDef}; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::Integer; use rustc_target::abi::VariantIdx; use smallvec::{smallvec, SmallVec}; @@ -99,39 +98,23 @@ impl IntRange { // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. #[inline] - fn signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> u128 { + fn signed_bias(ty: Ty<'_>, ty_size: rustc_target::abi::Size) -> u128 { match *ty.kind() { - ty::Int(ity) => { - let size = Integer::from_int_ty(&tcx, ity).size(); - 1u128 << (size.bits() as u128 - 1) - } + ty::Int(_) => 1u128 << (ty_size.bits() as u128 - 1), ty::Char | ty::Uint(_) => 0, _ => bug!("invalid type for `IntRange`: {}", ty), } } #[inline] - pub(super) fn from_singleton_bits<'tcx>( - tcx: TyCtxt<'tcx>, + pub(super) fn from_bits<'tcx>( ty: Ty<'tcx>, - bits: u128, - ) -> IntRange { - let bias = IntRange::signed_bias(tcx, ty); - // Perform a shift if the underlying types are signed, - // which makes the interval arithmetic simpler. - let val = bits ^ bias; - IntRange { range: val..=val, bias } - } - - #[inline] - pub(super) fn from_range_bits<'tcx>( - tcx: TyCtxt<'tcx>, + ty_size: rustc_target::abi::Size, lo: u128, hi: u128, - ty: Ty<'tcx>, end: &RangeEnd, ) -> IntRange { - let bias = IntRange::signed_bias(tcx, ty); + let bias = IntRange::signed_bias(ty, ty_size); // Perform a shift if the underlying types are signed, // which makes the interval arithmetic simpler. let (lo, hi) = (lo ^ bias, hi ^ bias); @@ -143,11 +126,11 @@ impl IntRange { IntRange { range: lo..=(hi - offset), bias } } - /// The reverse of `Self::from_range/from_bits`. - pub(super) fn to_bits(&self) -> (u128, u128) { + /// The reverse of `Self::from_bits`. + pub(super) fn to_bits(&self) -> (u128, u128, RangeEnd) { let (lo, hi) = self.boundaries(); let bias = self.bias; - (lo ^ bias, hi ^ bias) + (lo ^ bias, hi ^ bias, RangeEnd::Included) } fn is_subrange(&self, other: &Self) -> bool { @@ -825,10 +808,11 @@ impl<'tcx> SplitWildcard<'tcx> { pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self { debug!("SplitWildcard::new({:?})", pcx.ty); let cx = pcx.cx; - let make_range = |start, end| { - IntRange(IntRange::from_range_bits(cx.tcx, start, end, pcx.ty, &RangeEnd::Included)) + let ty = pcx.ty; + let make_range = |size, start, end| { + IntRange(IntRange::from_bits(ty, size, start, end, &RangeEnd::Included)) }; - // This determines the set of all possible constructors for the type `pcx.ty`. For numbers, + // This determines the set of all possible constructors for the type `ty`. For numbers, // arrays and slices we use ranges and variable-length slices when appropriate. // // If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that @@ -836,18 +820,19 @@ impl<'tcx> SplitWildcard<'tcx> { // returned list of constructors. // Invariant: this is empty if and only if the type is uninhabited (as determined by // `cx.is_uninhabited()`). - let all_ctors = match pcx.ty.kind() { - _ if cx.is_uninhabited(pcx.ty) => smallvec![], + let all_ctors = match ty.kind() { + _ if cx.is_uninhabited(ty) => smallvec![], ty::Bool => smallvec![Bool(false), Bool(true)], ty::Char => { + let size = number_type_size(cx.tcx, ty); smallvec![ // The valid Unicode Scalar Value ranges. - make_range('\u{0000}' as u128, '\u{D7FF}' as u128), - make_range('\u{E000}' as u128, '\u{10FFFF}' as u128), + make_range(size, '\u{0000}' as u128, '\u{D7FF}' as u128), + make_range(size, '\u{E000}' as u128, '\u{10FFFF}' as u128), ] } ty::Int(_) | ty::Uint(_) - if pcx.ty.is_ptr_sized_integral() + if ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching => { // `usize`/`isize` are not allowed to be matched exhaustively unless the @@ -855,16 +840,17 @@ impl<'tcx> SplitWildcard<'tcx> { // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. smallvec![NonExhaustive] } - &ty::Int(ity) => { - let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128; + &ty::Int(_) => { + let size = number_type_size(cx.tcx, ty); + let bits = size.bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; - smallvec![make_range(min, max)] + smallvec![make_range(size, min, max)] } - &ty::Uint(uty) => { - let size = Integer::from_uint_ty(&cx.tcx, uty).size(); + &ty::Uint(_) => { + let size = number_type_size(cx.tcx, ty); let max = size.truncate(u128::MAX); - smallvec![make_range(0, max)] + smallvec![make_range(size, 0, max)] } ty::Adt(def, substs) if def.is_enum() => { // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an @@ -883,7 +869,7 @@ impl<'tcx> SplitWildcard<'tcx> { // // we don't want to show every possible IO error, but instead have only `_` as the // witness. - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty); let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns; diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 67eca6f535b8f..38d6d7b954969 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -24,6 +24,7 @@ use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::Size; use smallvec::SmallVec; use std::cmp::Ordering; @@ -653,9 +654,9 @@ crate fn compare_const_vals<'tcx>( /// Return the size of the corresponding number type. #[inline] -fn number_type_size<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> rustc_target::abi::Size { +fn number_type_size<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Size { use rustc_middle::ty::layout::IntegerExt; - use rustc_target::abi::{Integer, Primitive, Size}; + use rustc_target::abi::{Integer, Primitive}; match ty.kind() { ty::Bool => Size::from_bytes(1), ty::Char => Size::from_bytes(4), @@ -672,6 +673,7 @@ fn number_type_size<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> rustc_target::abi: fn fast_try_eval_bits<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, + ty_size: Size, value: &ty::Const<'tcx>, ) -> Option { let int = if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) = value.val { @@ -681,8 +683,7 @@ fn fast_try_eval_bits<'tcx>( // This is a more general but slower form of the previous case. value.val.eval(tcx, param_env).try_to_scalar_int()? }; - let size = number_type_size(tcx, value.ty); - int.to_bits(size).ok() + int.to_bits(ty_size).ok() } fn pat_to_deconstructed<'p, 'tcx>( @@ -763,13 +764,13 @@ fn pat_to_deconstructed<'p, 'tcx>( match pat.ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { use rustc_apfloat::Float; - ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) { + let size = number_type_size(cx.tcx, value.ty); + ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, size, value) { Some(bits) => match pat.ty.kind() { ty::Bool => Constructor::Bool(bits != 0), - ty::Char | ty::Int(_) | ty::Uint(_) => { - let int_range = IntRange::from_singleton_bits(cx.tcx, pat.ty, bits); - Constructor::IntRange(int_range) - } + ty::Char | ty::Int(_) | ty::Uint(_) => Constructor::IntRange( + IntRange::from_bits(pat.ty, size, bits, bits, &RangeEnd::Included), + ), ty::Float(ty::FloatTy::F32) => { let value = rustc_apfloat::ieee::Single::from_bits(bits); Constructor::F32Range(value, value, RangeEnd::Included) @@ -820,11 +821,12 @@ fn pat_to_deconstructed<'p, 'tcx>( &PatKind::Range(PatRange { lo, hi, end }) => { use rustc_apfloat::Float; let ty = lo.ty; + let size = number_type_size(cx.tcx, ty); let lo = lo.eval_bits(cx.tcx, cx.param_env, ty); let hi = hi.eval_bits(cx.tcx, cx.param_env, ty); ctor = match ty.kind() { ty::Char | ty::Int(_) | ty::Uint(_) => { - Constructor::IntRange(IntRange::from_range_bits(cx.tcx, lo, hi, ty, &end)) + Constructor::IntRange(IntRange::from_bits(ty, size, lo, hi, &end)) } ty::Float(ty::FloatTy::F32) => { let lo = rustc_apfloat::ieee::Single::from_bits(lo); @@ -943,7 +945,7 @@ fn deconstructed_to_pat<'p, 'tcx>( } Bool(b) => PatKind::Constant { value: ty::Const::from_bool(cx.tcx, *b) }, IntRange(range) => { - let (lo, hi) = range.to_bits(); + let (lo, hi, end) = range.to_bits(); let env = ty::ParamEnv::empty().and(pat.ty()); let lo_const = ty::Const::from_bits(cx.tcx, lo, env); let hi_const = ty::Const::from_bits(cx.tcx, hi, env); @@ -951,7 +953,7 @@ fn deconstructed_to_pat<'p, 'tcx>( if lo == hi { PatKind::Constant { value: lo_const } } else { - PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included }) + PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end }) } } Wildcard | NonExhaustive => PatKind::Wild, From 474e763f35622f7928f45fa737dc48ea7a1e70e0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 2 Oct 2021 12:50:55 +0100 Subject: [PATCH 23/26] Reuse `size` since we have it --- compiler/rustc_mir_build/src/thir/pattern/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 38d6d7b954969..0743507797baa 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -822,8 +822,10 @@ fn pat_to_deconstructed<'p, 'tcx>( use rustc_apfloat::Float; let ty = lo.ty; let size = number_type_size(cx.tcx, ty); - let lo = lo.eval_bits(cx.tcx, cx.param_env, ty); - let hi = hi.eval_bits(cx.tcx, cx.param_env, ty); + let lo = fast_try_eval_bits(cx.tcx, cx.param_env, size, lo) + .unwrap_or_else(|| bug!("expected bits of {:?}, got {:?}", ty, lo)); + let hi = fast_try_eval_bits(cx.tcx, cx.param_env, size, hi) + .unwrap_or_else(|| bug!("expected bits of {:?}, got {:?}", ty, hi)); ctor = match ty.kind() { ty::Char | ty::Int(_) | ty::Uint(_) => { Constructor::IntRange(IntRange::from_bits(ty, size, lo, hi, &end)) From c4f840da16435c25402ffa27efe247bb67fee784 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 2 Oct 2021 13:51:11 +0100 Subject: [PATCH 24/26] Move `tcx`-dependent code out of deconstruct_pat --- .../src/thir/pattern/deconstruct_pat.rs | 173 +-------------- .../rustc_mir_build/src/thir/pattern/mod.rs | 210 +++++++++++++++++- .../src/thir/pattern/usefulness.rs | 20 -- 3 files changed, 207 insertions(+), 196 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index f1aaf4f68166e..cd3faf823a588 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -45,7 +45,6 @@ use self::Constructor::*; use self::SliceKind::*; -use super::number_type_size; use super::usefulness::{MatchCheckCtxt, PatCtxt}; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; @@ -53,10 +52,8 @@ use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::middle::stability::EvalResult; -use rustc_middle::mir::Field; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, Ty, VariantDef}; +use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; @@ -619,7 +616,7 @@ impl<'tcx> Constructor<'tcx> { Single | Variant(_) => match pcx.ty.kind() { ty::Adt(adt, ..) => { let variant = &adt.variants[self.variant_index_for_adt(adt)]; - Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count() + pcx.cx.list_variant_nonhidden_fields(pcx.ty, variant).count() } _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty), }, @@ -806,138 +803,7 @@ pub(super) struct SplitWildcard<'tcx> { impl<'tcx> SplitWildcard<'tcx> { pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self { - debug!("SplitWildcard::new({:?})", pcx.ty); - let cx = pcx.cx; - let ty = pcx.ty; - let make_range = |size, start, end| { - IntRange(IntRange::from_bits(ty, size, start, end, &RangeEnd::Included)) - }; - // This determines the set of all possible constructors for the type `ty`. For numbers, - // arrays and slices we use ranges and variable-length slices when appropriate. - // - // If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that - // are statically impossible. E.g., for `Option`, we do not include `Some(_)` in the - // returned list of constructors. - // Invariant: this is empty if and only if the type is uninhabited (as determined by - // `cx.is_uninhabited()`). - let all_ctors = match ty.kind() { - _ if cx.is_uninhabited(ty) => smallvec![], - ty::Bool => smallvec![Bool(false), Bool(true)], - ty::Char => { - let size = number_type_size(cx.tcx, ty); - smallvec![ - // The valid Unicode Scalar Value ranges. - make_range(size, '\u{0000}' as u128, '\u{D7FF}' as u128), - make_range(size, '\u{E000}' as u128, '\u{10FFFF}' as u128), - ] - } - ty::Int(_) | ty::Uint(_) - if ty.is_ptr_sized_integral() - && !cx.tcx.features().precise_pointer_size_matching => - { - // `usize`/`isize` are not allowed to be matched exhaustively unless the - // `precise_pointer_size_matching` feature is enabled. So we treat those types like - // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. - smallvec![NonExhaustive] - } - &ty::Int(_) => { - let size = number_type_size(cx.tcx, ty); - let bits = size.bits() as u128; - let min = 1u128 << (bits - 1); - let max = min - 1; - smallvec![make_range(size, min, max)] - } - &ty::Uint(_) => { - let size = number_type_size(cx.tcx, ty); - let max = size.truncate(u128::MAX); - smallvec![make_range(size, 0, max)] - } - ty::Adt(def, substs) if def.is_enum() => { - // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an - // additional "unknown" constructor. - // There is no point in enumerating all possible variants, because the user can't - // actually match against them all themselves. So we always 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. - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty); - - let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns; - - // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it - // as though it had an "unknown" constructor to avoid exposing its emptiness. The - // exception is if the pattern is at the top level, because we want empty matches to be - // considered exhaustive. - let is_secretly_empty = - def.variants.is_empty() && !is_exhaustive_pat_feature && !pcx.is_top_level; - - let mut ctors: SmallVec<[_; 1]> = def - .variants - .iter_enumerated() - .filter(|(_, v)| { - // Filter variants that depend on a disabled unstalbe feature. - let is_enabled = !matches!( - cx.tcx.eval_stability(v.def_id, None, DUMMY_SP, None), - EvalResult::Deny { .. } - ); - // If `exhaustive_patterns` is enabled, we exclude variants known to be - // uninhabited. - let is_uninhabited = is_exhaustive_pat_feature - && v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) - .contains(cx.tcx, cx.module); - is_enabled && !is_uninhabited - }) - .map(|(idx, _)| Variant(idx)) - .collect(); - - if is_secretly_empty || is_declared_nonexhaustive { - ctors.push(NonExhaustive); - } - ctors - } - // The only legal non-wildcard patterns of type `Box` are box patterns, so we emit - // that. - ty::Adt(def, substs) if def.is_box() => smallvec![BoxPat(substs.type_at(0))], - ty::Adt(..) => smallvec![Single], - ty::Ref(_, ty, _) => smallvec![Ref(*ty)], - ty::Tuple(fs) => smallvec![Tuple(*fs)], - ty::Array(sub_ty, len) => { - match len.try_eval_usize(cx.tcx, cx.param_env) { - Some(len) => { - // The uninhabited case is already handled. - smallvec![Slice(Slice::new(Some(len as usize), VarLen(0, 0)))] - } - None => { - // Treat arrays of a constant but unknown length like slices. - let kind = - if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - smallvec![Slice(Slice::new(None, kind))] - } - } - } - ty::Slice(sub_ty) => { - let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - smallvec![Slice(Slice::new(None, kind))] - } - // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we don't - // expose its emptiness and return `NonExhaustive` below. The exception is if the - // pattern is at the top level, because we want empty matches to be considered - // exhaustive. - ty::Never if pcx.is_top_level => smallvec![], - // This type is one for which we cannot list constructors, like `str` or `f64`, or is - // uninhabited but `exhaustive_patterns` is disabled. - _ => smallvec![NonExhaustive], - }; - + let all_ctors = pcx.cx.list_constructors_for_type(pcx.ty, pcx.is_top_level); SplitWildcard { matrix_ctors: Vec::new(), all_ctors } } @@ -1079,36 +945,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { Fields::from_iter(cx, tys.into_iter().map(DeconstructedPat::wildcard)) } - // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide - // uninhabited fields in order not to reveal the uninhabitedness of the whole variant. - // This lists the fields we keep along with their types. - pub(super) fn list_variant_nonhidden_fields<'a>( - cx: &'a MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - variant: &'a VariantDef, - ) -> impl Iterator)> + Captures<'a> + Captures<'p> { - let (adt, substs) = match ty.kind() { - ty::Adt(adt, substs) => (adt, substs), - _ => bug!(), - }; - // 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(); - - variant.fields.iter().enumerate().filter_map(move |(i, field)| { - let ty = field.ty(cx.tcx, substs); - // `field.ty()` doesn't normalize after substituting. - let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); - let is_uninhabited = cx.is_uninhabited(ty); - - if is_uninhabited && (!is_visible || is_non_exhaustive) { - None - } else { - Some((Field::new(i), ty)) - } - }) - } - /// Creates a new list of wildcard fields for a given constructor. The result must have a /// length of `constructor.arity()`. pub(super) fn wildcards( @@ -1120,8 +956,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { Single | Variant(_) => match ty.kind() { ty::Adt(adt, _) => { let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; - let tys = - Fields::list_variant_nonhidden_fields(cx, ty, variant).map(|(_, ty)| ty); + let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty); Fields::wildcards_from_tys(cx, tys) } _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 0743507797baa..5ef0f48a82a09 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -11,22 +11,24 @@ use crate::thir::util::UserAnnotatedTyHelpers; use deconstruct_pat::{Constructor, DeconstructedPat, Fields, IntRange, Slice, SliceKind}; use usefulness::MatchCheckCtxt; +use rustc_data_structures::captures::Captures; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::RangeEnd; use rustc_index::vec::Idx; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::interpret::{ get_slice_bytes, ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar, }; use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; -use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt, VariantDef}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Size; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use std::cmp::Ordering; #[derive(Clone, Debug)] @@ -743,12 +745,12 @@ fn pat_to_deconstructed<'p, 'tcx>( // For each field in the variant, we store the relevant index into `self.fields` if any. let mut field_id_to_id: Vec> = (0..variant.fields.len()).map(|_| None).collect(); - let tys = Fields::list_variant_nonhidden_fields(cx, pat.ty, variant) - .enumerate() - .map(|(i, (field, ty))| { + let tys = cx.list_variant_nonhidden_fields(pat.ty, variant).enumerate().map( + |(i, (field, ty))| { field_id_to_id[field.index()] = Some(i); ty - }); + }, + ); let mut wilds: SmallVec<[_; 2]> = tys.map(DeconstructedPat::wildcard).collect(); for pat in subpatterns { if let Some(i) = field_id_to_id[pat.field.index()] { @@ -893,7 +895,8 @@ fn deconstructed_to_pat<'p, 'tcx>( ty::Adt(adt_def, substs) => { let variant_index = pat.ctor().variant_index_for_adt(adt_def); let variant = &adt_def.variants[variant_index]; - let subpatterns = Fields::list_variant_nonhidden_fields(cx, pat.ty(), variant) + let subpatterns = cx + .list_variant_nonhidden_fields(pat.ty(), variant) .zip(subpatterns) .map(|((field, _ty), pattern)| FieldPat { field, pattern }) .collect(); @@ -972,3 +975,196 @@ fn deconstructed_to_pat<'p, 'tcx>( Pat { ty: pat.ty(), span: DUMMY_SP, kind: Box::new(kind) } } + +impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { + fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { + if self.tcx.features().exhaustive_patterns { + self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) + } else { + false + } + } + + /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. + fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Adt(def, ..) => { + def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() + } + _ => false, + } + } + + /// List the set of possible constructors for the type `ty`. For numbers, arrays and slices we use + /// ranges and variable-length slices when appropriate. + /// + /// If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that are + /// statically impossible. E.g., for `Option`, we do not include `Some(_)` in the returned list + /// of constructors. + /// Even if `exhaustive_patterns` is disabled, we also omit constructors for empty enums and the + /// never type if we are at the top-level in a match, because we want to allow empty matches to be + /// valid for those. + /// Invariant: this is only empty when the type is uninhabited, and the reverse holds if + /// `exhaustive_patterns` is enabled. + fn list_constructors_for_type( + &self, + ty: Ty<'tcx>, + is_top_level_pattern: bool, + ) -> SmallVec<[Constructor<'tcx>; 1]> { + use Constructor::*; + use SliceKind::*; + let cx = self; + debug!("list_constructors_for_type({:?})", ty); + let make_range = |size, start, end| { + IntRange(self::IntRange::from_bits(ty, size, start, end, &RangeEnd::Included)) + }; + match ty.kind() { + _ if cx.is_uninhabited(ty) => smallvec![], + ty::Bool => smallvec![Bool(false), Bool(true)], + ty::Char => { + let size = number_type_size(cx.tcx, ty); + smallvec![ + // The valid Unicode Scalar Value ranges. + make_range(size, '\u{0000}' as u128, '\u{D7FF}' as u128), + make_range(size, '\u{E000}' as u128, '\u{10FFFF}' as u128), + ] + } + ty::Int(_) | ty::Uint(_) + if ty.is_ptr_sized_integral() + && !cx.tcx.features().precise_pointer_size_matching => + { + // `usize`/`isize` are not allowed to be matched exhaustively unless the + // `precise_pointer_size_matching` feature is enabled. So we treat those types like + // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. + smallvec![NonExhaustive] + } + &ty::Int(_) => { + let size = number_type_size(cx.tcx, ty); + let bits = size.bits() as u128; + let min = 1u128 << (bits - 1); + let max = min - 1; + smallvec![make_range(size, min, max)] + } + &ty::Uint(_) => { + let size = number_type_size(cx.tcx, ty); + let max = size.truncate(u128::MAX); + smallvec![make_range(size, 0, max)] + } + ty::Adt(def, substs) if def.is_enum() => { + let exhaustive_patterns_feature_enabled = cx.tcx.features().exhaustive_patterns; + let mut ctors: SmallVec<[_; 1]> = def + .variants + .iter_enumerated() + .filter(|(_, v)| { + // Filter variants that depend on a disabled unstable feature. + let is_enabled = !matches!( + cx.tcx.eval_stability(v.def_id, None, DUMMY_SP, None), + EvalResult::Deny { .. } + ); + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + let is_uninhabited = exhaustive_patterns_feature_enabled + && v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) + .contains(cx.tcx, cx.module); + is_enabled && !is_uninhabited + }) + .map(|(idx, _)| Variant(idx)) + .collect(); + + // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an + // additional "unknown" constructor. + // There is no point in enumerating all possible variants, because the user can't + // actually match against them all themselves. So we always 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. + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty); + + // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it + // as though it had an "unknown" constructor to avoid exposing its emptiness. The + // exception is if the pattern is at the top level, because we want empty matches to be + // considered exhaustive. + let is_secretly_empty = def.variants.is_empty() + && !exhaustive_patterns_feature_enabled + && !is_top_level_pattern; + + if is_declared_nonexhaustive || is_secretly_empty { + ctors.push(NonExhaustive); + } + ctors + } + // The only legal non-wildcard patterns of type `Box` are box patterns, so we emit + // that. + ty::Adt(def, substs) if def.is_box() => smallvec![BoxPat(substs.type_at(0))], + ty::Adt(..) => smallvec![Single], + ty::Ref(_, ty, _) => smallvec![Ref(*ty)], + ty::Tuple(fs) => smallvec![Tuple(*fs)], + ty::Array(sub_ty, len) => { + match len.try_eval_usize(cx.tcx, cx.param_env) { + Some(len) => { + // The uninhabited case is already handled. + smallvec![Slice(self::Slice::new(Some(len as usize), VarLen(0, 0)))] + } + None => { + // Treat arrays of a constant but unknown length like slices. + let kind = + if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + smallvec![Slice(self::Slice::new(None, kind))] + } + } + } + ty::Slice(sub_ty) => { + let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; + smallvec![Slice(self::Slice::new(None, kind))] + } + // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we don't + // expose its emptiness and return `NonExhaustive` below. The exception is if the + // pattern is at the top level, because we want empty matches to be considered + // exhaustive. + ty::Never if is_top_level_pattern => smallvec![], + // This type is one for which we cannot list constructors, like `str` or `f64`, or is + // uninhabited but `exhaustive_patterns` is disabled. + _ => smallvec![NonExhaustive], + } + } + + /// List the fields for this variant. + /// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide + /// uninhabited fields in order not to reveal the uninhabitedness of the whole variant. + fn list_variant_nonhidden_fields<'a>( + &'a self, + ty: Ty<'tcx>, + variant: &'a VariantDef, + ) -> impl Iterator)> + Captures<'a> + Captures<'p> { + let cx = self; + let (adt, substs) = match ty.kind() { + ty::Adt(adt, substs) => (adt, substs), + _ => bug!(), + }; + // 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(); + + variant.fields.iter().enumerate().filter_map(move |(i, field)| { + let ty = field.ty(cx.tcx, substs); + // `field.ty()` doesn't normalize after substituting. + let ty = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); + let is_uninhabited = cx.is_uninhabited(ty); + + if is_uninhabited && (!is_visible || is_non_exhaustive) { + None + } else { + Some((Field::new(i), ty)) + } + }) + } +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index dfa7e0e1510b6..7cea655d876b8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -309,26 +309,6 @@ crate struct MatchCheckCtxt<'p, 'tcx> { crate pattern_arena: &'p TypedArena>, } -impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { - pub(super) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { - if self.tcx.features().exhaustive_patterns { - self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) - } else { - false - } - } - - /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. - pub(super) fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { - match ty.kind() { - ty::Adt(def, ..) => { - def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() - } - _ => false, - } - } -} - #[derive(Copy, Clone)] pub(super) struct PatCtxt<'a, 'p, 'tcx> { pub(super) cx: &'a MatchCheckCtxt<'p, 'tcx>, From f1e56c663b07d6565a35c0d12631327e4d2f6b55 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Oct 2021 17:28:45 +0100 Subject: [PATCH 25/26] Remove all matching on `Ty` from deconstruct_pat --- .../src/thir/pattern/deconstruct_pat.rs | 96 ++++++------------- .../rustc_mir_build/src/thir/pattern/mod.rs | 82 ++++++++++++---- 2 files changed, 88 insertions(+), 90 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index cd3faf823a588..23b085d42fdfd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -49,11 +49,10 @@ use super::usefulness::{MatchCheckCtxt, PatCtxt}; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; use rustc_data_structures::captures::Captures; -use rustc_index::vec::Idx; use rustc_hir::{HirId, RangeEnd}; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; @@ -93,16 +92,6 @@ impl IntRange { (*self.range.start(), *self.range.end()) } - // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. - #[inline] - fn signed_bias(ty: Ty<'_>, ty_size: rustc_target::abi::Size) -> u128 { - match *ty.kind() { - ty::Int(_) => 1u128 << (ty_size.bits() as u128 - 1), - ty::Char | ty::Uint(_) => 0, - _ => bug!("invalid type for `IntRange`: {}", ty), - } - } - #[inline] pub(super) fn from_bits<'tcx>( ty: Ty<'tcx>, @@ -111,7 +100,11 @@ impl IntRange { hi: u128, end: &RangeEnd, ) -> IntRange { - let bias = IntRange::signed_bias(ty, ty_size); + let bias = if MatchCheckCtxt::is_signed_int(ty) { + 1u128 << (ty_size.bits() as u128 - 1) + } else { + 0 + }; // Perform a shift if the underlying types are signed, // which makes the interval arithmetic simpler. let (lo, hi) = (lo ^ bias, hi ^ bias); @@ -555,7 +548,7 @@ pub(super) enum Constructor<'tcx> { /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. Str(&'tcx [u8]), /// Array and slice patterns. - Slice(Slice), + Slice(Slice, Ty<'tcx>), /// 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. @@ -593,36 +586,19 @@ impl<'tcx> Constructor<'tcx> { fn as_slice(&self) -> Option { match self { - Slice(slice) => Some(*slice), + Slice(slice, _) => Some(*slice), _ => None, } } - pub(super) fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx { - match *self { - Variant(idx) => idx, - Single => { - assert!(!adt.is_enum()); - VariantIdx::new(0) - } - _ => bug!("bad constructor {:?} for adt {:?}", self, adt), - } - } - /// The number of fields for this constructor. This must be kept in sync with /// `Fields::wildcards`. pub(super) fn arity(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> usize { match self { - Single | Variant(_) => match pcx.ty.kind() { - ty::Adt(adt, ..) => { - let variant = &adt.variants[self.variant_index_for_adt(adt)]; - pcx.cx.list_variant_nonhidden_fields(pcx.ty, variant).count() - } - _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty), - }, + Single | Variant(_) => pcx.cx.list_variant_nonhidden_fields(pcx.ty, self).count(), Tuple(fs) => fs.len(), Ref(_) | BoxPat(_) => 1, - Slice(slice) => slice.arity(), + Slice(slice, _) => slice.arity(), Str(..) | Bool(..) | IntRange(..) @@ -671,11 +647,11 @@ impl<'tcx> Constructor<'tcx> { split_range.split(int_ranges.cloned()); split_range.iter().map(IntRange).collect() } - &Slice(Slice { kind: VarLen(self_prefix, self_suffix), array_len }) => { + &Slice(Slice { kind: VarLen(self_prefix, self_suffix), array_len }, ty) => { let mut split_self = SplitVarLenSlice::new(self_prefix, self_suffix, array_len); let slices = ctors.filter_map(|c| c.as_slice()).map(|s| s.kind); split_self.split(slices); - split_self.iter().map(Slice).collect() + split_self.iter().map(|s| Slice(s, ty)).collect() } // Any other constructor can be used unchanged. _ => smallvec![self.clone()], @@ -724,7 +700,7 @@ impl<'tcx> Constructor<'tcx> { } } (Str(self_val), Str(other_val)) => self_val == other_val, - (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice), + (Slice(self_slice, _), Slice(other_slice, _)) => self_slice.is_covered_by(*other_slice), // We are trying to inspect an opaque constant. Thus we skip the row. (Opaque, _) | (_, Opaque) => false, @@ -765,7 +741,7 @@ impl<'tcx> Constructor<'tcx> { .iter() .filter_map(|c| c.as_int_range()) .any(|other| range.is_covered_by(other)), - Slice(slice) => used_ctors + Slice(slice, _) => used_ctors .iter() .filter_map(|c| c.as_slice()) .any(|other| slice.is_covered_by(other)), @@ -866,8 +842,7 @@ impl<'tcx> SplitWildcard<'tcx> { // // The exception is: if we are at the top-level, for example in an empty match, we // sometimes prefer reporting the list of constructors instead of just `_`. - let report_when_all_missing = pcx.is_top_level - && !matches!(pcx.ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_)); + let report_when_all_missing = pcx.is_top_level && !MatchCheckCtxt::is_numeric(pcx.ty); let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing { if pcx.is_non_exhaustive { Missing { @@ -953,23 +928,16 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { constructor: &Constructor<'tcx>, ) -> Self { let ret = match constructor { - Single | Variant(_) => match ty.kind() { - ty::Adt(adt, _) => { - let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; - let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty); - Fields::wildcards_from_tys(cx, tys) - } - _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), - }, + Single | Variant(_) => { + let tys = cx.list_variant_nonhidden_fields(ty, constructor).map(|(_, ty)| ty); + Fields::wildcards_from_tys(cx, tys) + } Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())), Ref(ty) | BoxPat(ty) => Fields::wildcards_from_tys(cx, once(*ty)), - Slice(slice) => match *ty.kind() { - ty::Slice(ty) | ty::Array(ty, _) => { - let arity = slice.arity(); - Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) - } - _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), - }, + Slice(slice, ty) => { + let arity = slice.arity(); + Fields::wildcards_from_tys(cx, (0..arity).map(|_| *ty)) + } Str(..) | Bool(..) | IntRange(..) @@ -1068,7 +1036,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { // We return a wildcard for each field of `other_ctor`. Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect() } - (Slice(self_slice), Slice(other_slice)) + (Slice(self_slice, inner_ty), Slice(other_slice, _)) if self_slice.arity() != other_slice.arity() => { // The only tricky case: two slices of different arity. Since `self_slice` covers @@ -1079,10 +1047,6 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { match self_slice.kind { FixedLen(_) => bug!("{:?} doesn't cover {:?}", self_slice, other_slice), VarLen(prefix, suffix) => { - let inner_ty = match *self.ty.kind() { - ty::Slice(ty) | ty::Array(ty, _) => ty, - _ => bug!("bad slice pattern {:?} {:?}", self.ctor, self.ty), - }; let prefix = &self.fields.fields[..prefix]; let suffix = &self.fields.fields[self_slice.arity() - suffix..]; let wildcard: &_ = @@ -1143,14 +1107,8 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { match &self.ctor { Single | Variant(_) | Tuple(_) => { - let variant = match self.ty.kind() { - ty::Adt(adt, _) => Some(&adt.variants[self.ctor.variant_index_for_adt(adt)]), - ty::Tuple(_) => None, - _ => unreachable!(), - }; - - if let Some(variant) = variant { - write!(f, "{}", variant.ident)?; + if let Some(ident) = MatchCheckCtxt::variant_ident(self.ty(), self.ctor()) { + write!(f, "{}", ident)?; } // Without `cx`, we can't know which field corresponds to which, so we can't @@ -1171,7 +1129,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { let subpattern = self.iter_fields().next().unwrap(); write!(f, "box {:?}", subpattern) } - Slice(slice) => { + Slice(slice, _) => { let mut subpatterns = self.fields.iter_patterns(); write!(f, "[")?; match slice.kind { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 5ef0f48a82a09..ffb79d8f5adba 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -24,12 +24,13 @@ use rustc_middle::mir::interpret::{ }; use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; -use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt, VariantDef}; +use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt}; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::Size; +use rustc_target::abi::{Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; use std::cmp::Ordering; +use std::fmt::Display; #[derive(Clone, Debug)] crate enum PatternError { @@ -734,18 +735,21 @@ fn pat_to_deconstructed<'p, 'tcx>( fields = Fields::singleton(cx, DeconstructedPat::wildcard(inner_ty)); } ty::Adt(adt, _) => { - ctor = match pat.kind.as_ref() { - PatKind::Leaf { .. } => Constructor::Single, + let variant = match *pat.kind { + PatKind::Leaf { .. } => { + ctor = Constructor::Single; + adt.non_enum_variant() + } PatKind::Variant { variant_index, .. } => { - Constructor::Variant(*variant_index) + ctor = Constructor::Variant(variant_index); + &adt.variants[variant_index] } _ => bug!(), }; - let variant = &adt.variants[ctor.variant_index_for_adt(adt)]; // For each field in the variant, we store the relevant index into `self.fields` if any. let mut field_id_to_id: Vec> = (0..variant.fields.len()).map(|_| None).collect(); - let tys = cx.list_variant_nonhidden_fields(pat.ty, variant).enumerate().map( + let tys = cx.list_variant_nonhidden_fields(pat.ty, &ctor).enumerate().map( |(i, (field, ty))| { field_id_to_id[field.index()] = Some(i); ty @@ -847,9 +851,11 @@ fn pat_to_deconstructed<'p, 'tcx>( fields = Fields::empty(); } PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { - let array_len = match pat.ty.kind() { - ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env) as usize), - ty::Slice(_) => None, + let (ty, array_len) = match pat.ty.kind() { + ty::Array(ty, length) => { + (ty, Some(length.eval_usize(cx.tcx, cx.param_env) as usize)) + } + ty::Slice(ty) => (ty, None), _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), }; let kind = if slice.is_some() { @@ -857,7 +863,7 @@ fn pat_to_deconstructed<'p, 'tcx>( } else { SliceKind::FixedLen(prefix.len() + suffix.len()) }; - ctor = Constructor::Slice(Slice::new(array_len, kind)); + ctor = Constructor::Slice(Slice::new(array_len, kind), ty); fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat)); } PatKind::Or { .. } => { @@ -893,10 +899,13 @@ fn deconstructed_to_pat<'p, 'tcx>( let kind = match pat.ctor() { Single | Variant(_) => match pat.ty().kind() { ty::Adt(adt_def, substs) => { - let variant_index = pat.ctor().variant_index_for_adt(adt_def); - let variant = &adt_def.variants[variant_index]; + let variant_index = match pat.ctor() { + Variant(idx) => *idx, + Single => VariantIdx::new(0), + _ => bug!("bad constructor {:?} for adt", pat.ctor()), + }; let subpatterns = cx - .list_variant_nonhidden_fields(pat.ty(), variant) + .list_variant_nonhidden_fields(pat.ty(), pat.ctor()) .zip(subpatterns) .map(|((field, _ty), pattern)| FieldPat { field, pattern }) .collect(); @@ -920,7 +929,7 @@ fn deconstructed_to_pat<'p, 'tcx>( // string literal pattern will never be reported as a non-exhaustiveness witness, so we // ignore this issue. Ref(_) | BoxPat(_) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - Slice(slice) => { + Slice(slice, _) => { match slice.kind { SliceKind::FixedLen(_) => { PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } @@ -977,6 +986,14 @@ fn deconstructed_to_pat<'p, 'tcx>( } impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { + fn is_numeric(ty: Ty<'tcx>) -> bool { + matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_)) + } + + fn is_signed_int(ty: Ty<'tcx>) -> bool { + matches!(ty.kind(), ty::Int(_)) + } + fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) @@ -1109,22 +1126,23 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { ty::Ref(_, ty, _) => smallvec![Ref(*ty)], ty::Tuple(fs) => smallvec![Tuple(*fs)], ty::Array(sub_ty, len) => { - match len.try_eval_usize(cx.tcx, cx.param_env) { + let slice = match len.try_eval_usize(cx.tcx, cx.param_env) { Some(len) => { // The uninhabited case is already handled. - smallvec![Slice(self::Slice::new(Some(len as usize), VarLen(0, 0)))] + self::Slice::new(Some(len as usize), VarLen(0, 0)) } None => { // Treat arrays of a constant but unknown length like slices. let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - smallvec![Slice(self::Slice::new(None, kind))] + self::Slice::new(None, kind) } - } + }; + smallvec![Slice(slice, sub_ty)] } ty::Slice(sub_ty) => { let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - smallvec![Slice(self::Slice::new(None, kind))] + smallvec![Slice(self::Slice::new(None, kind), sub_ty)] } // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we don't // expose its emptiness and return `NonExhaustive` below. The exception is if the @@ -1137,19 +1155,41 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { } } + /// Show the name of the variant corresponding to this constructor. + /// `ty` must be `ty::Adt`, and `ctor` must be a constructor for that type. + fn variant_ident(ty: Ty<'tcx>, ctor: &Constructor<'tcx>) -> Option { + match ty.kind() { + ty::Adt(adt, _) => { + let variant = match ctor { + Constructor::Variant(idx) => &adt.variants[*idx], + Constructor::Single => adt.non_enum_variant(), + _ => return None, + }; + Some(variant.ident) + } + _ => None, + } + } + /// List the fields for this variant. /// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide /// uninhabited fields in order not to reveal the uninhabitedness of the whole variant. + /// `ty` must be `ty::Adt`, and `ctor` must be a constructor for that type. fn list_variant_nonhidden_fields<'a>( &'a self, ty: Ty<'tcx>, - variant: &'a VariantDef, + ctor: &Constructor<'tcx>, ) -> impl Iterator)> + Captures<'a> + Captures<'p> { let cx = self; let (adt, substs) = match ty.kind() { ty::Adt(adt, substs) => (adt, substs), _ => bug!(), }; + let variant = match ctor { + Constructor::Variant(variant_index) => &adt.variants[*variant_index], + Constructor::Single => adt.non_enum_variant(), + _ => bug!("bad constructor {:?} for adt {:?}", ctor, ty), + }; // 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(); From ec2324bcfec1d9031804f24828eb8c78341f7c16 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Oct 2021 17:48:34 +0100 Subject: [PATCH 26/26] Thread everything rustc-dependent via a trait --- .../src/thir/pattern/check_match.rs | 84 +++--- .../src/thir/pattern/deconstruct_pat.rs | 225 +++++++------- .../rustc_mir_build/src/thir/pattern/mod.rs | 213 +++++++++---- .../src/thir/pattern/usefulness.rs | 282 ++++++++++-------- 4 files changed, 474 insertions(+), 330 deletions(-) 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 384c771b7a940..334e97f9b0a31 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,8 +1,8 @@ use super::deconstruct_pat::{Constructor, DeconstructedPat}; use super::usefulness::{ - compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport, + compute_match_usefulness, MatchArm, Reachability, UsefulnessCtxt, UsefulnessReport, }; -use super::{deconstructed_to_pat, pat_to_deconstructed, PatCtxt, PatternError}; +use super::{deconstructed_to_pat, pat_to_deconstructed, MatchCheckCtxt, PatCtxt, PatternError}; use rustc_arena::TypedArena; use rustc_ast::Mutability; @@ -51,7 +51,7 @@ struct MatchVisitor<'a, 'p, 'tcx> { tcx: TyCtxt<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, - pattern_arena: &'p TypedArena>, + pattern_arena: &'p TypedArena>>, } impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { @@ -128,10 +128,10 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { fn lower_pattern( &self, - cx: &mut MatchCheckCtxt<'p, 'tcx>, + cx: &UsefulnessCtxt<'p, MatchCheckCtxt<'tcx>>, pat: &'tcx hir::Pat<'tcx>, have_errors: &mut bool, - ) -> &'p DeconstructedPat<'p, 'tcx> { + ) -> &'p DeconstructedPat<'p, MatchCheckCtxt<'tcx>> { let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.typeck_results); patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); @@ -143,13 +143,13 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { pattern } - fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> { - MatchCheckCtxt { + fn new_cx(&self, hir_id: HirId) -> UsefulnessCtxt<'p, MatchCheckCtxt<'tcx>> { + let cx = MatchCheckCtxt { tcx: self.tcx, param_env: self.param_env, module: self.tcx.parent_module(hir_id).to_def_id(), - pattern_arena: &self.pattern_arena, - } + }; + UsefulnessCtxt { cx, pattern_arena: &self.pattern_arena } } fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) { @@ -165,15 +165,16 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { arms: &'tcx [hir::Arm<'tcx>], source: hir::MatchSource, ) { - let mut cx = self.new_cx(scrut.hir_id); + let ucx = self.new_cx(scrut.hir_id); + let cx = &ucx.cx; for arm in arms { // Check the arm for some things unrelated to exhaustiveness. self.check_patterns(&arm.pat, Refutable); if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard { self.check_patterns(pat, Refutable); - let tpat = self.lower_pattern(&mut cx, pat, &mut false); - check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span()); + let tpat = self.lower_pattern(&ucx, pat, &mut false); + check_let_reachability(&ucx, pat.hir_id, tpat, tpat.span()); } } @@ -182,7 +183,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let arms: Vec<_> = arms .iter() .map(|hir::Arm { pat, guard, .. }| MatchArm { - pat: self.lower_pattern(&mut cx, pat, &mut have_errors), + pat: self.lower_pattern(&ucx, pat, &mut have_errors), hir_id: pat.hir_id, has_guard: guard.is_some(), }) @@ -194,7 +195,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut); - let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty); + let report = compute_match_usefulness(&ucx, &arms, scrut.hir_id, scrut_ty); match source { hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { @@ -215,12 +216,13 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option) { - let mut cx = self.new_cx(pat.hir_id); + let ucx = self.new_cx(pat.hir_id); + let cx = &ucx.cx; - let pattern = self.lower_pattern(&mut cx, pat, &mut false); + let pattern = self.lower_pattern(&ucx, pat, &mut false); let pattern_ty = pattern.ty(); let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty); + let report = compute_match_usefulness(&ucx, &arms, pat.hir_id, pattern_ty); report_exhaustiveness_lints(&cx, &report); // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We @@ -359,7 +361,7 @@ fn check_for_bindings_named_same_as_variants( } /// Checks for common cases of "catchall" patterns that may not be intended as such. -fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { +fn pat_is_catchall(pat: &DeconstructedPat<'_, MatchCheckCtxt<'_>>) -> bool { use Constructor::*; match pat.ctor() { Wildcard => true, @@ -440,13 +442,14 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) { } fn check_let_reachability<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, + ucx: &UsefulnessCtxt<'p, MatchCheckCtxt<'tcx>>, pat_id: HirId, - pat: &'p DeconstructedPat<'p, 'tcx>, + pat: &'p DeconstructedPat<'p, MatchCheckCtxt<'tcx>>, span: Span, ) { + let cx = &ucx.cx; let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty()); + let report = compute_match_usefulness(&ucx, &arms, pat_id, pat.ty()); report_exhaustiveness_lints(&cx, &report); // Report if the pattern is unreachable, which can only occur when the type is uninhabited. @@ -460,8 +463,8 @@ fn check_let_reachability<'p, 'tcx>( /// Report unreachable arms, if any. fn report_arm_reachability<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - report: &UsefulnessReport<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, + report: &UsefulnessReport<'p, MatchCheckCtxt<'tcx>>, ) { let mut catchall = None; for (arm, reachability) in report.arm_usefulness.iter() { @@ -479,8 +482,8 @@ fn report_arm_reachability<'p, 'tcx>( /// Report various things detected during exhaustiveness checking. fn report_exhaustiveness_lints<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - report: &UsefulnessReport<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, + report: &UsefulnessReport<'p, MatchCheckCtxt<'tcx>>, ) { if !report.unreachable_subpatterns.is_empty() { let mut unreachables = report.unreachable_subpatterns.clone(); @@ -502,11 +505,11 @@ fn report_exhaustiveness_lints<'p, 'tcx>( /// /// NB: The partner lint for structs lives in `compiler/rustc_typeck/src/check/pat.rs`. pub(super) fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, scrut_ty: Ty<'tcx>, span: Span, hir_id: HirId, - witnesses: &[DeconstructedPat<'p, 'tcx>], + witnesses: &[DeconstructedPat<'p, MatchCheckCtxt<'tcx>>], ) { let joined_patterns = joined_uncovered_patterns(cx, &witnesses); cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, span, |build| { @@ -525,10 +528,10 @@ pub(super) fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( /// Lint on likely incorrect range patterns (#63987) pub(super) fn lint_overlapping_range_endpoints<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, span: Span, hir_id: HirId, - overlaps: &[DeconstructedPat<'p, 'tcx>], + overlaps: &[DeconstructedPat<'p, MatchCheckCtxt<'tcx>>], ) { if !overlaps.is_empty() { cx.tcx.struct_span_lint_hir(OVERLAPPING_RANGE_ENDPOINTS, hir_id, span, |lint| { @@ -548,10 +551,10 @@ pub(super) fn lint_overlapping_range_endpoints<'p, 'tcx>( /// Report that a match is not exhaustive. fn non_exhaustive_match<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, scrut_ty: Ty<'tcx>, sp: Span, - witnesses: Vec>, + witnesses: Vec>>, is_empty_match: bool, ) { let non_empty_enum = match scrut_ty.kind() { @@ -619,8 +622,8 @@ fn non_exhaustive_match<'p, 'tcx>( } crate fn joined_uncovered_patterns<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - witnesses: &[DeconstructedPat<'p, 'tcx>], + cx: &MatchCheckCtxt<'tcx>, + witnesses: &[DeconstructedPat<'p, MatchCheckCtxt<'tcx>>], ) -> String { const LIMIT: usize = 3; let pat_to_str = |pat| deconstructed_to_pat(cx, pat).to_string(); @@ -640,7 +643,7 @@ crate fn joined_uncovered_patterns<'p, 'tcx>( } crate fn pattern_not_covered_label( - witnesses: &[DeconstructedPat<'_, '_>], + witnesses: &[DeconstructedPat<'_, MatchCheckCtxt<'_>>], joined_patterns: &str, ) -> String { format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns) @@ -648,10 +651,10 @@ crate fn pattern_not_covered_label( /// Point at the definition of non-covered `enum` variants. fn adt_defined_here<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, err: &mut DiagnosticBuilder<'_>, ty: Ty<'tcx>, - witnesses: &[DeconstructedPat<'p, 'tcx>], + witnesses: &[DeconstructedPat<'p, MatchCheckCtxt<'tcx>>], ) { let ty = ty.peel_refs(); if let ty::Adt(def, _) = ty.kind() { @@ -668,10 +671,13 @@ fn adt_defined_here<'p, 'tcx>( } fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( - cx: &MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, def: &AdtDef, - patterns: impl Iterator>, -) -> Vec { + patterns: impl Iterator>>, +) -> Vec +where + MatchCheckCtxt<'tcx>: 'p, +{ use Constructor::*; let mut covered = vec![]; for pattern in patterns { diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 23b085d42fdfd..aa88d6b68a80a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -45,17 +45,10 @@ use self::Constructor::*; use self::SliceKind::*; -use super::usefulness::{MatchCheckCtxt, PatCtxt}; +use super::usefulness::{Context, PatCtxt, UsefulnessCtxt}; -use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; use rustc_data_structures::captures::Captures; -use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::Ty; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::VariantIdx; - use smallvec::{smallvec, SmallVec}; use std::cell::Cell; use std::cmp::{self, max, min, Ordering}; @@ -63,6 +56,27 @@ use std::fmt; use std::iter::{once, IntoIterator}; use std::ops::RangeInclusive; +macro_rules! span_bug { + ($span:expr, $($rest:tt)*) => { + Cx::span_bug($span, || format!($($rest)*)) + }; +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub(crate) enum RangeEnd { + Included, + Excluded, +} + +impl fmt::Display for RangeEnd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + RangeEnd::Included => "..=", + RangeEnd::Excluded => "..", + }) + } +} + /// An inclusive interval, used for precise integer exhaustiveness checking. /// `IntRange`s always store a contiguous range. This means that values are /// encoded such that `0` encodes the minimum value for the integer, @@ -75,7 +89,7 @@ use std::ops::RangeInclusive; /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. #[derive(Clone, PartialEq, Eq)] -pub(super) struct IntRange { +pub(crate) struct IntRange { range: RangeInclusive, /// Keeps the bias used for encoding the range. It depends on the type of the range and /// possibly the pointer size of the current architecture. The algorithm ensures we never @@ -93,18 +107,14 @@ impl IntRange { } #[inline] - pub(super) fn from_bits<'tcx>( - ty: Ty<'tcx>, - ty_size: rustc_target::abi::Size, + pub(super) fn from_bits( + ty_size: u64, + is_signed_int: bool, lo: u128, hi: u128, end: &RangeEnd, ) -> IntRange { - let bias = if MatchCheckCtxt::is_signed_int(ty) { - 1u128 << (ty_size.bits() as u128 - 1) - } else { - 0 - }; + let bias = if is_signed_int { 1u128 << (ty_size as u128 - 1) } else { 0 }; // Perform a shift if the underlying types are signed, // which makes the interval arithmetic simpler. let (lo, hi) = (lo ^ bias, hi ^ bias); @@ -163,13 +173,13 @@ impl IntRange { } /// Lint on likely incorrect range patterns (#63987) - pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>( + pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, Cx: Context + 'a>( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - report: &mut Vec<(Span, HirId, Vec>)>, - pats: impl Iterator>, + pcx: PatCtxt<'_, 'p, Cx>, + report: &mut Vec<(Cx::Span, Cx::HirId, Vec>)>, + pats: impl Iterator>, column_count: usize, - hir_id: HirId, + hir_id: Cx::HirId, ) { if self.is_singleton() { return; @@ -360,7 +370,7 @@ impl SliceKind { /// A constructor for array and slice patterns. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(super) struct Slice { +pub(crate) struct Slice { /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`. pub(super) array_len: Option, /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`. @@ -527,28 +537,28 @@ impl SplitVarLenSlice { /// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and /// `Fields`. #[derive(Clone, Debug, PartialEq)] -pub(super) enum Constructor<'tcx> { - /// Structs. +pub(crate) enum Constructor { + /// Structs and unions. Single, /// Enum variants. - Variant(VariantIdx), + Variant(Cx::VariantIdx), /// Tuple patterns. - Tuple(&'tcx [GenericArg<'tcx>]), + Tuple(Cx::TyList), /// Ref patterns (`&_` and `&mut _`). - Ref(Ty<'tcx>), + Ref(Cx::Ty), /// Box patterns. - BoxPat(Ty<'tcx>), + BoxPat(Cx::Ty), /// Booleans. Bool(bool), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange), /// Ranges of floating-point literal values (`2.0..=5.2`). - F32Range(IeeeFloat, IeeeFloat, RangeEnd), - F64Range(IeeeFloat, IeeeFloat, RangeEnd), + F32Range(Cx::F32Float, Cx::F32Float, RangeEnd), + F64Range(Cx::F64Float, Cx::F64Float, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. - Str(&'tcx [u8]), + Str(Cx::StringLit), /// Array and slice patterns. - Slice(Slice, Ty<'tcx>), + Slice(Slice, Cx::Ty), /// 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. @@ -568,7 +578,7 @@ pub(super) enum Constructor<'tcx> { Or, } -impl<'tcx> Constructor<'tcx> { +impl Constructor { pub(super) fn is_wildcard(&self) -> bool { matches!(self, Wildcard) } @@ -593,10 +603,10 @@ impl<'tcx> Constructor<'tcx> { /// The number of fields for this constructor. This must be kept in sync with /// `Fields::wildcards`. - pub(super) fn arity(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> usize { + pub(super) fn arity(&self, pcx: PatCtxt<'_, '_, Cx>) -> usize { match self { - Single | Variant(_) => pcx.cx.list_variant_nonhidden_fields(pcx.ty, self).count(), - Tuple(fs) => fs.len(), + Single | Variant(_) => pcx.ucx.cx.list_variant_nonhidden_fields(pcx.ty, self).len(), + Tuple(fs) => Cx::ty_list_len(fs), Ref(_) | BoxPat(_) => 1, Slice(slice, _) => slice.arity(), Str(..) @@ -627,11 +637,11 @@ impl<'tcx> Constructor<'tcx> { /// matrix, unless all of them are. pub(super) fn split<'a>( &self, - pcx: PatCtxt<'_, '_, 'tcx>, - ctors: impl Iterator> + Clone, + pcx: PatCtxt<'_, '_, Cx>, + ctors: impl Iterator> + Clone, ) -> SmallVec<[Self; 1]> where - 'tcx: 'a, + Cx: 'a, { match self { Wildcard => { @@ -663,7 +673,7 @@ impl<'tcx> Constructor<'tcx> { /// this checks for inclusion. // We inline because this has a single call site in `Matrix::specialize_constructor`. #[inline] - pub(super) fn is_covered_by<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool { + pub(super) fn is_covered_by<'p>(&self, pcx: PatCtxt<'_, 'p, Cx>, other: &Self) -> bool { // This must be kept in sync with `is_covered_by_any`. match (self, other) { // Wildcards cover anything @@ -721,8 +731,8 @@ impl<'tcx> Constructor<'tcx> { /// assumed to have been split from a wildcard. fn is_covered_by_any<'p>( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - used_ctors: &[Constructor<'tcx>], + pcx: PatCtxt<'_, 'p, Cx>, + used_ctors: &[Constructor], ) -> bool { if used_ctors.is_empty() { return false; @@ -770,16 +780,16 @@ impl<'tcx> Constructor<'tcx> { /// or not. In fact this is quite natural from the point of view of diagnostics too. This is done /// in `to_ctors`: in some cases we only return `Missing`. #[derive(Debug)] -pub(super) struct SplitWildcard<'tcx> { +pub(super) struct SplitWildcard { /// Constructors seen in the matrix. - matrix_ctors: Vec>, + matrix_ctors: Vec>, /// All the constructors for this type - all_ctors: SmallVec<[Constructor<'tcx>; 1]>, + all_ctors: SmallVec<[Constructor; 1]>, } -impl<'tcx> SplitWildcard<'tcx> { - pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self { - let all_ctors = pcx.cx.list_constructors_for_type(pcx.ty, pcx.is_top_level); +impl SplitWildcard { + pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, Cx>) -> Self { + let all_ctors = pcx.ucx.cx.list_constructors_for_type(pcx.ty, pcx.is_top_level); SplitWildcard { matrix_ctors: Vec::new(), all_ctors } } @@ -787,10 +797,10 @@ impl<'tcx> SplitWildcard<'tcx> { /// do what you want. pub(super) fn split<'a>( &mut self, - pcx: PatCtxt<'_, '_, 'tcx>, - ctors: impl Iterator> + Clone, + pcx: PatCtxt<'_, '_, Cx>, + ctors: impl Iterator> + Clone, ) where - 'tcx: 'a, + Cx: 'a, { // Since `all_ctors` never contains wildcards, this won't recurse further. self.all_ctors = @@ -799,21 +809,21 @@ impl<'tcx> SplitWildcard<'tcx> { } /// Whether there are any value constructors for this type that are not present in the matrix. - fn any_missing(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> bool { + fn any_missing(&self, pcx: PatCtxt<'_, '_, Cx>) -> bool { self.iter_missing(pcx).next().is_some() } /// Iterate over the constructors for this type that are not present in the matrix. pub(super) fn iter_missing<'a, 'p>( &'a self, - pcx: PatCtxt<'a, 'p, 'tcx>, - ) -> impl Iterator> + Captures<'p> { + pcx: PatCtxt<'a, 'p, Cx>, + ) -> impl Iterator> + Captures<'p> { self.all_ctors.iter().filter(move |ctor| !ctor.is_covered_by_any(pcx, &self.matrix_ctors)) } /// Return the set of constructors resulting from splitting the wildcard. As explained at the /// top of the file, if any constructors are missing we can ignore the present ones. - fn into_ctors(self, pcx: PatCtxt<'_, '_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { + fn into_ctors(self, pcx: PatCtxt<'_, '_, Cx>) -> SmallVec<[Constructor; 1]> { if self.any_missing(pcx) { // Some constructors are missing, thus we can specialize with the special `Missing` // constructor, which stands for those constructors that are not seen in the matrix, @@ -842,7 +852,7 @@ impl<'tcx> SplitWildcard<'tcx> { // // The exception is: if we are at the top-level, for example in an empty match, we // sometimes prefer reporting the list of constructors instead of just `_`. - let report_when_all_missing = pcx.is_top_level && !MatchCheckCtxt::is_numeric(pcx.ty); + let report_when_all_missing = pcx.is_top_level && !Cx::is_numeric(pcx.ty); let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing { if pcx.is_non_exhaustive { Missing { @@ -888,55 +898,52 @@ impl<'tcx> SplitWildcard<'tcx> { /// included in `fields`. For that reason, when you have a `mir::Field` you must use /// `index_with_declared_idx`. #[derive(Debug, Clone, Copy)] -pub(super) struct Fields<'p, 'tcx> { - fields: &'p [DeconstructedPat<'p, 'tcx>], +pub(super) struct Fields<'p, Cx: Context> { + fields: &'p [DeconstructedPat<'p, Cx>], } -impl<'p, 'tcx> Fields<'p, 'tcx> { +impl<'p, Cx: Context> Fields<'p, Cx> { pub(super) fn empty() -> Self { Fields { fields: &[] } } - pub(super) fn singleton( - cx: &MatchCheckCtxt<'p, 'tcx>, - field: DeconstructedPat<'p, 'tcx>, - ) -> Self { - let field: &_ = cx.pattern_arena.alloc(field); + pub(super) fn singleton(ucx: &UsefulnessCtxt<'p, Cx>, field: DeconstructedPat<'p, Cx>) -> Self { + let field: &_ = ucx.pattern_arena.alloc(field); Fields { fields: std::slice::from_ref(field) } } pub(super) fn from_iter( - cx: &MatchCheckCtxt<'p, 'tcx>, - fields: impl IntoIterator>, + ucx: &UsefulnessCtxt<'p, Cx>, + fields: impl IntoIterator>, ) -> Self { - let fields: &[_] = cx.pattern_arena.alloc_from_iter(fields); + let fields: &[_] = ucx.pattern_arena.alloc_from_iter(fields); Fields { fields } } fn wildcards_from_tys( - cx: &MatchCheckCtxt<'p, 'tcx>, - tys: impl IntoIterator>, + ucx: &UsefulnessCtxt<'p, Cx>, + tys: impl IntoIterator, ) -> Self { - Fields::from_iter(cx, tys.into_iter().map(DeconstructedPat::wildcard)) + Fields::from_iter(ucx, tys.into_iter().map(DeconstructedPat::wildcard)) } /// Creates a new list of wildcard fields for a given constructor. The result must have a /// length of `constructor.arity()`. pub(super) fn wildcards( - cx: &MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - constructor: &Constructor<'tcx>, + ucx: &UsefulnessCtxt<'p, Cx>, + ty: Cx::Ty, + constructor: &Constructor, ) -> Self { let ret = match constructor { Single | Variant(_) => { - let tys = cx.list_variant_nonhidden_fields(ty, constructor).map(|(_, ty)| ty); - Fields::wildcards_from_tys(cx, tys) + let tys = ucx.cx.list_variant_nonhidden_fields(ty, constructor); + Fields::wildcards_from_tys(ucx, tys) } - Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())), - Ref(ty) | BoxPat(ty) => Fields::wildcards_from_tys(cx, once(*ty)), + Tuple(fs) => Fields::wildcards_from_tys(ucx, Cx::ty_list_iter(fs)), + Ref(ty) | BoxPat(ty) => Fields::wildcards_from_tys(ucx, once(*ty)), Slice(slice, ty) => { let arity = slice.arity(); - Fields::wildcards_from_tys(cx, (0..arity).map(|_| *ty)) + Fields::wildcards_from_tys(ucx, (0..arity).map(|_| *ty)) } Str(..) | Bool(..) @@ -958,7 +965,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// Returns the list of patterns. pub(super) fn iter_patterns<'a>( &'a self, - ) -> impl Iterator> + Captures<'a> { + ) -> impl Iterator> + Captures<'a> { self.fields.iter() } } @@ -968,24 +975,24 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// This also keeps track of whether the pattern has been foundreachable during analysis. For this /// reason we should be careful not to clone patterns for which we care about that. Use /// `clone_and_forget_reachability` is you're sure. -pub(crate) struct DeconstructedPat<'p, 'tcx> { - ctor: Constructor<'tcx>, - fields: Fields<'p, 'tcx>, - ty: Ty<'tcx>, - span: Span, +pub(crate) struct DeconstructedPat<'p, Cx: Context> { + ctor: Constructor, + fields: Fields<'p, Cx>, + ty: Cx::Ty, + span: Cx::Span, reachable: Cell, } -impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { - pub(super) fn wildcard(ty: Ty<'tcx>) -> Self { - Self::new(Wildcard, Fields::empty(), ty, DUMMY_SP) +impl<'p, Cx: Context> DeconstructedPat<'p, Cx> { + pub(super) fn wildcard(ty: Cx::Ty) -> Self { + Self::new(Wildcard, Fields::empty(), ty, Cx::Span::default()) } pub(super) fn new( - ctor: Constructor<'tcx>, - fields: Fields<'p, 'tcx>, - ty: Ty<'tcx>, - span: Span, + ctor: Constructor, + fields: Fields<'p, Cx>, + ty: Cx::Ty, + span: Cx::Span, ) -> Self { DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) } } @@ -993,9 +1000,9 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// Construct a pattern that matches everything that starts with this constructor. /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. - pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self { - let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor); - DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP) + pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, Cx>, ctor: Constructor) -> Self { + let fields = Fields::wildcards(pcx.ucx, pcx.ty, &ctor); + DeconstructedPat::new(ctor, fields, pcx.ty, Cx::Span::default()) } /// Clone this value. This method emphasizes that cloning loses reachability information and @@ -1008,19 +1015,19 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { matches!(self.ctor, Or) } - pub(super) fn ctor(&self) -> &Constructor<'tcx> { + pub(super) fn ctor(&self) -> &Constructor { &self.ctor } - pub(super) fn ty(&self) -> Ty<'tcx> { + pub(super) fn ty(&self) -> Cx::Ty { self.ty } - pub(super) fn span(&self) -> Span { + pub(super) fn span(&self) -> Cx::Span { self.span } pub(super) fn iter_fields<'a>( &'a self, - ) -> impl Iterator> + Captures<'a> { + ) -> impl Iterator> + Captures<'a> { self.fields.iter_patterns() } @@ -1028,13 +1035,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// `other_ctor` can be different from `self.ctor`, but must be covered by it. pub(super) fn specialize<'a>( &'a self, - cx: &MatchCheckCtxt<'p, 'tcx>, - other_ctor: &Constructor<'tcx>, - ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> { + ucx: &UsefulnessCtxt<'p, Cx>, + other_ctor: &Constructor, + ) -> SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]> { match (&self.ctor, other_ctor) { (Wildcard, _) => { // We return a wildcard for each field of `other_ctor`. - Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect() + Fields::wildcards(ucx, self.ty, other_ctor).iter_patterns().collect() } (Slice(self_slice, inner_ty), Slice(other_slice, _)) if self_slice.arity() != other_slice.arity() => @@ -1050,7 +1057,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { let prefix = &self.fields.fields[..prefix]; let suffix = &self.fields.fields[self_slice.arity() - suffix..]; let wildcard: &_ = - cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); + ucx.pattern_arena.alloc(DeconstructedPat::wildcard(*inner_ty)); let extra_wildcards = other_slice.arity() - self_slice.arity(); let extra_wildcards = (0..extra_wildcards).map(|_| wildcard); prefix.iter().chain(extra_wildcards).chain(suffix).collect() @@ -1071,13 +1078,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { } /// Report the spans of subpatterns that were not reachable, if any. - pub(super) fn unreachable_spans(&self) -> Vec { + pub(super) fn unreachable_spans(&self) -> Vec { let mut spans = Vec::new(); self.collect_unreachable_spans(&mut spans); spans } - fn collect_unreachable_spans(&self, spans: &mut Vec) { + fn collect_unreachable_spans(&self, spans: &mut Vec) { // We don't look at subpatterns if we already reported the whole pattern as unreachable. if !self.is_reachable() { spans.push(self.span); @@ -1091,7 +1098,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a /// `Display` impl. -impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { +impl<'p, Cx: Context> fmt::Debug for DeconstructedPat<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Printing lists is a chore. let mut first = true; @@ -1107,7 +1114,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { match &self.ctor { Single | Variant(_) | Tuple(_) => { - if let Some(ident) = MatchCheckCtxt::variant_ident(self.ty(), self.ctor()) { + if let Some(ident) = Cx::variant_ident(self.ty(), self.ctor()) { write!(f, "{}", ident)?; } @@ -1170,7 +1177,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } Ok(()) } - Str(bytes) => write!(f, "{}", String::from_utf8(bytes.to_vec()).unwrap()), + Str(_) => write!(f, ""), Opaque => write!(f, ""), } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index ffb79d8f5adba..c25fcaac3e859 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -6,17 +6,18 @@ mod deconstruct_pat; mod usefulness; pub(crate) use self::check_match::check_match; - +use self::deconstruct_pat::{Constructor, DeconstructedPat, Fields, IntRange, Slice, SliceKind}; +use self::usefulness::UsefulnessCtxt; use crate::thir::util::UserAnnotatedTyHelpers; -use deconstruct_pat::{Constructor, DeconstructedPat, Fields, IntRange, Slice, SliceKind}; -use usefulness::MatchCheckCtxt; +use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; use rustc_data_structures::captures::Captures; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::RangeEnd; +use rustc_hir::{HirId, RangeEnd}; use rustc_index::vec::Idx; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::interpret::{ @@ -24,13 +25,15 @@ use rustc_middle::mir::interpret::{ }; use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, Pat, PatKind, PatRange, PatTyProj}; +use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::{self, ConstKind, DefIdTree, Ty, TyCtxt}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; use std::cmp::Ordering; -use std::fmt::Display; +use std::fmt; #[derive(Clone, Debug)] crate enum PatternError { @@ -252,7 +255,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Tuple(ref pats, ddpos) => { let tys = match ty.kind() { - ty::Tuple(ref tys) => tys, + ty::Tuple(tys) => tys, _ => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty), }; let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos); @@ -690,10 +693,12 @@ fn fast_try_eval_bits<'tcx>( } fn pat_to_deconstructed<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, + ucx: &UsefulnessCtxt<'p, MatchCheckCtxt<'tcx>>, pat: &Pat<'tcx>, -) -> DeconstructedPat<'p, 'tcx> { - let mkpat = |pat| pat_to_deconstructed(cx, pat); +) -> DeconstructedPat<'p, MatchCheckCtxt<'tcx>> { + use deconstruct_pat::RangeEnd; + let cx = &ucx.cx; + let mkpat = |pat| pat_to_deconstructed(ucx, pat); let ctor; let fields; match pat.kind.as_ref() { @@ -708,12 +713,12 @@ fn pat_to_deconstructed<'p, 'tcx>( ty::Adt(adt, _) if adt.is_box() => Constructor::BoxPat(subpattern.ty), _ => Constructor::Ref(subpattern.ty), }; - fields = Fields::singleton(cx, mkpat(subpattern)); + fields = Fields::singleton(ucx, mkpat(subpattern)); } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { match pat.ty.kind() { ty::Tuple(fs) => { - ctor = Constructor::Tuple(fs); + ctor = Constructor::Tuple(fs.as_ref()); let mut wilds: SmallVec<[_; 2]> = fs .iter() .map(|ty| ty.expect_ty()) @@ -722,7 +727,7 @@ fn pat_to_deconstructed<'p, 'tcx>( for pat in subpatterns { wilds[pat.field.index()] = mkpat(&pat.pattern); } - fields = Fields::from_iter(cx, wilds); + fields = Fields::from_iter(ucx, wilds); } ty::Adt(adt, substs) if adt.is_box() => { // If we're here, the pattern is using the private `Box(_, _)` constructor. @@ -732,7 +737,7 @@ fn pat_to_deconstructed<'p, 'tcx>( // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 let inner_ty = substs.type_at(0); ctor = Constructor::BoxPat(inner_ty); - fields = Fields::singleton(cx, DeconstructedPat::wildcard(inner_ty)); + fields = Fields::singleton(ucx, DeconstructedPat::wildcard(inner_ty)); } ty::Adt(adt, _) => { let variant = match *pat.kind { @@ -761,7 +766,7 @@ fn pat_to_deconstructed<'p, 'tcx>( wilds[i] = mkpat(&pat.pattern); } } - fields = Fields::from_iter(cx, wilds); + fields = Fields::from_iter(ucx, wilds); } _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty), } @@ -774,9 +779,15 @@ fn pat_to_deconstructed<'p, 'tcx>( ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, size, value) { Some(bits) => match pat.ty.kind() { ty::Bool => Constructor::Bool(bits != 0), - ty::Char | ty::Int(_) | ty::Uint(_) => Constructor::IntRange( - IntRange::from_bits(pat.ty, size, bits, bits, &RangeEnd::Included), - ), + ty::Char | ty::Int(_) | ty::Uint(_) => { + Constructor::IntRange(IntRange::from_bits( + size.bits(), + MatchCheckCtxt::is_signed_int(pat.ty), + bits, + bits, + &RangeEnd::Included, + )) + } ty::Float(ty::FloatTy::F32) => { let value = rustc_apfloat::ieee::Single::from_bits(bits); Constructor::F32Range(value, value, RangeEnd::Included) @@ -791,7 +802,7 @@ fn pat_to_deconstructed<'p, 'tcx>( }; fields = Fields::empty(); } - ty::Ref(_, t, _) if t.is_str() => { + &ty::Ref(_, t, _) if t.is_str() => { if let ty::ConstKind::Value(val @ ConstValue::Slice { .. }) = value.val { let bytes = get_slice_bytes(&cx.tcx, val); // We want a `&str` constant to behave like a `Deref` pattern, to be compatible @@ -808,7 +819,7 @@ fn pat_to_deconstructed<'p, 'tcx>( pat.span, ); ctor = Constructor::Ref(t); - fields = Fields::singleton(cx, subpattern) + fields = Fields::singleton(ucx, subpattern) } else { // FIXME(Nadrieril): Does this ever happen? ctor = Constructor::Opaque; @@ -827,15 +838,23 @@ fn pat_to_deconstructed<'p, 'tcx>( &PatKind::Range(PatRange { lo, hi, end }) => { use rustc_apfloat::Float; let ty = lo.ty; + let end = match end { + rustc_hir::RangeEnd::Included => RangeEnd::Included, + rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded, + }; let size = number_type_size(cx.tcx, ty); let lo = fast_try_eval_bits(cx.tcx, cx.param_env, size, lo) .unwrap_or_else(|| bug!("expected bits of {:?}, got {:?}", ty, lo)); let hi = fast_try_eval_bits(cx.tcx, cx.param_env, size, hi) .unwrap_or_else(|| bug!("expected bits of {:?}, got {:?}", ty, hi)); ctor = match ty.kind() { - ty::Char | ty::Int(_) | ty::Uint(_) => { - Constructor::IntRange(IntRange::from_bits(ty, size, lo, hi, &end)) - } + ty::Char | ty::Int(_) | ty::Uint(_) => Constructor::IntRange(IntRange::from_bits( + size.bits(), + MatchCheckCtxt::is_signed_int(ty), + lo, + hi, + &end, + )), ty::Float(ty::FloatTy::F32) => { let lo = rustc_apfloat::ieee::Single::from_bits(lo); let hi = rustc_apfloat::ieee::Single::from_bits(hi); @@ -863,8 +882,8 @@ fn pat_to_deconstructed<'p, 'tcx>( } else { SliceKind::FixedLen(prefix.len() + suffix.len()) }; - ctor = Constructor::Slice(Slice::new(array_len, kind), ty); - fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat)); + ctor = Constructor::Slice(Slice::new(array_len, kind), *ty); + fields = Fields::from_iter(ucx, prefix.iter().chain(suffix).map(mkpat)); } PatKind::Or { .. } => { /// Recursively expand this pattern into its subpatterns. @@ -881,15 +900,15 @@ fn pat_to_deconstructed<'p, 'tcx>( let mut pats = Vec::new(); expand(pat, &mut pats); ctor = Constructor::Or; - fields = Fields::from_iter(cx, pats.into_iter().map(mkpat)); + fields = Fields::from_iter(ucx, pats.into_iter().map(mkpat)); } } DeconstructedPat::new(ctor, fields, pat.ty, pat.span) } fn deconstructed_to_pat<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - pat: &DeconstructedPat<'p, 'tcx>, + cx: &MatchCheckCtxt<'tcx>, + pat: &DeconstructedPat<'p, MatchCheckCtxt<'tcx>>, ) -> Pat<'tcx> { use Constructor::*; let is_wildcard = |pat: &Pat<'_>| { @@ -967,6 +986,10 @@ fn deconstructed_to_pat<'p, 'tcx>( if lo == hi { PatKind::Constant { value: lo_const } } else { + let end = match end { + deconstruct_pat::RangeEnd::Included => rustc_hir::RangeEnd::Included, + deconstruct_pat::RangeEnd::Excluded => rustc_hir::RangeEnd::Excluded, + }; PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end }) } } @@ -985,24 +1008,39 @@ fn deconstructed_to_pat<'p, 'tcx>( Pat { ty: pat.ty(), span: DUMMY_SP, kind: Box::new(kind) } } -impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { - fn is_numeric(ty: Ty<'tcx>) -> bool { - matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_)) - } +#[derive(Copy, Clone)] +crate struct MatchCheckCtxt<'tcx> { + crate tcx: TyCtxt<'tcx>, + crate param_env: ty::ParamEnv<'tcx>, + /// The module in which the match occurs. This is necessary for + /// checking inhabited-ness of types because whether a type is (visibly) + /// inhabited can depend on whether it was defined in the current module or + /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty + /// outside its module and should not be matchable with an empty match statement. + crate module: DefId, +} - fn is_signed_int(ty: Ty<'tcx>) -> bool { - matches!(ty.kind(), ty::Int(_)) +impl<'tcx> fmt::Debug for MatchCheckCtxt<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MatchCheckCtxt").field("module", &self.module).finish() } +} - fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { - if self.tcx.features().exhaustive_patterns { - self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) - } else { - false - } +impl<'tcx> usefulness::Context for MatchCheckCtxt<'tcx> { + type Ty = Ty<'tcx>; + type TyList = &'tcx [GenericArg<'tcx>]; + type VariantIdx = VariantIdx; + type F32Float = IeeeFloat; + type F64Float = IeeeFloat; + type StringLit = &'tcx [u8]; + type HirId = HirId; + type Span = Span; + type Ident = Ident; + + fn is_numeric(ty: Ty<'tcx>) -> bool { + matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_)) } - /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.kind() { ty::Adt(def, ..) => { @@ -1012,28 +1050,36 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { } } - /// List the set of possible constructors for the type `ty`. For numbers, arrays and slices we use - /// ranges and variable-length slices when appropriate. - /// - /// If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that are - /// statically impossible. E.g., for `Option`, we do not include `Some(_)` in the returned list - /// of constructors. - /// Even if `exhaustive_patterns` is disabled, we also omit constructors for empty enums and the - /// never type if we are at the top-level in a match, because we want to allow empty matches to be - /// valid for those. - /// Invariant: this is only empty when the type is uninhabited, and the reverse holds if - /// `exhaustive_patterns` is enabled. + fn ty_list_len(ty_list: &Self::TyList) -> usize { + ty_list.len() + } + + type TyListIter = std::iter::Map< + std::iter::Copied>>, + fn(GenericArg<'tcx>) -> Ty<'tcx>, + >; + fn ty_list_iter(ty_list: &Self::TyList) -> Self::TyListIter { + ty_list.iter().copied().map(GenericArg::expect_ty) + } + fn list_constructors_for_type( &self, ty: Ty<'tcx>, is_top_level_pattern: bool, - ) -> SmallVec<[Constructor<'tcx>; 1]> { + ) -> SmallVec<[Constructor; 1]> { use Constructor::*; use SliceKind::*; let cx = self; + debug!("list_constructors_for_type({:?})", ty); - let make_range = |size, start, end| { - IntRange(self::IntRange::from_bits(ty, size, start, end, &RangeEnd::Included)) + let make_range = |size: Size, start, end| { + IntRange(self::IntRange::from_bits( + size.bits(), + Self::is_signed_int(ty), + start, + end, + &deconstruct_pat::RangeEnd::Included, + )) }; match ty.kind() { _ if cx.is_uninhabited(ty) => smallvec![], @@ -1124,7 +1170,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { ty::Adt(def, substs) if def.is_box() => smallvec![BoxPat(substs.type_at(0))], ty::Adt(..) => smallvec![Single], ty::Ref(_, ty, _) => smallvec![Ref(*ty)], - ty::Tuple(fs) => smallvec![Tuple(*fs)], + ty::Tuple(fs) => smallvec![Tuple(fs.as_ref())], ty::Array(sub_ty, len) => { let slice = match len.try_eval_usize(cx.tcx, cx.param_env) { Some(len) => { @@ -1138,11 +1184,11 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { self::Slice::new(None, kind) } }; - smallvec![Slice(slice, sub_ty)] + smallvec![Slice(slice, *sub_ty)] } ty::Slice(sub_ty) => { let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - smallvec![Slice(self::Slice::new(None, kind), sub_ty)] + smallvec![Slice(self::Slice::new(None, kind), *sub_ty)] } // If `exhaustive_patterns` is disabled and our scrutinee is the never type, we don't // expose its emptiness and return `NonExhaustive` below. The exception is if the @@ -1155,9 +1201,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { } } - /// Show the name of the variant corresponding to this constructor. - /// `ty` must be `ty::Adt`, and `ctor` must be a constructor for that type. - fn variant_ident(ty: Ty<'tcx>, ctor: &Constructor<'tcx>) -> Option { + fn variant_ident(ty: Ty<'tcx>, ctor: &Constructor) -> Option { match ty.kind() { ty::Adt(adt, _) => { let variant = match ctor { @@ -1171,6 +1215,49 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { } } + fn is_doc_hidden_variant(&self, ty: Ty<'tcx>, idx: Self::VariantIdx) -> bool { + if let ty::Adt(adt, _) = ty.kind() { + let variant = &adt.variants[idx]; + let attrs = self.tcx.get_attrs(variant.def_id); + for attr in attrs.iter() { + if attr.has_name(sym::doc) { + let items = attr.meta_item_list().unwrap_or_default(); + if items.iter().find(|item| item.has_name(sym::hidden)).is_some() { + return true; + } + } + } + } + false + } + + fn list_variant_nonhidden_fields( + &self, + ty: Ty<'tcx>, + ctor: &Constructor, + ) -> Vec> { + self.list_variant_nonhidden_fields(ty, ctor).map(|(_, ty)| ty).collect() + } + + fn span_bug(span: Self::Span, f: impl Fn() -> String) -> ! { + let error = f(); + span_bug!(span, "{}", error) + } +} + +impl<'tcx> MatchCheckCtxt<'tcx> { + fn is_signed_int(ty: Ty<'tcx>) -> bool { + matches!(ty.kind(), ty::Int(_)) + } + + fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { + if self.tcx.features().exhaustive_patterns { + self.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) + } else { + false + } + } + /// List the fields for this variant. /// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide /// uninhabited fields in order not to reveal the uninhabitedness of the whole variant. @@ -1178,8 +1265,8 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> { fn list_variant_nonhidden_fields<'a>( &'a self, ty: Ty<'tcx>, - ctor: &Constructor<'tcx>, - ) -> impl Iterator)> + Captures<'a> + Captures<'p> { + ctor: &Constructor, + ) -> impl Iterator)> + Captures<'a> { let cx = self; let (adt, substs) = match ty.kind() { ty::Adt(adt, substs) => (adt, substs), diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 7cea655d876b8..39b7713f582cc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -285,37 +285,88 @@ use self::Usefulness::*; use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; -use rustc_data_structures::captures::Captures; - use rustc_arena::TypedArena; -use rustc_hir::def_id::DefId; -use rustc_hir::HirId; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_data_structures::captures::Captures; use smallvec::{smallvec, SmallVec}; -use std::fmt; +use std::fmt::Display; +use std::fmt::{self, Debug}; use std::iter::once; -crate struct MatchCheckCtxt<'p, 'tcx> { - crate tcx: TyCtxt<'tcx>, - /// The module in which the match occurs. This is necessary for - /// checking inhabited-ness of types because whether a type is (visibly) - /// inhabited can depend on whether it was defined in the current module or - /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty - /// outside its module and should not be matchable with an empty match statement. - crate module: DefId, - crate param_env: ty::ParamEnv<'tcx>, - crate pattern_arena: &'p TypedArena>, +/// Context for running the algorithm. This decouples the algorithm from specifics of types and the +/// like. +/// The `Copy` and `Debug` bounds are not actually used, but are needed because derived impls add +/// extra useless bounds on `Cx`. +pub(crate) trait Context: Debug + Copy { + type Ty: Debug + Copy; + type TyList: Debug + Clone; + type TyListIter: Iterator; + type VariantIdx: Debug + Copy + Eq; + type F32Float: Debug + Display + Clone + PartialOrd; + type F64Float: Debug + Display + Clone + PartialOrd; + type StringLit: Debug + Clone + Ord; + type HirId: Debug + Copy; + type Span: Debug + Copy + Default; + type Ident: Display; + + fn is_numeric(ty: Self::Ty) -> bool; + + /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. + fn is_foreign_non_exhaustive_enum(&self, ty: Self::Ty) -> bool; + + fn ty_list_len(ty_list: &Self::TyList) -> usize; + fn ty_list_iter(ty_list: &Self::TyList) -> Self::TyListIter; + + /// List the set of possible constructors for the type `ty`. For numbers, arrays and slices we use + /// ranges and variable-length slices when appropriate. + /// + /// If the `exhaustive_patterns` feature is enabled, we make sure to omit constructors that are + /// statically impossible. E.g., for `Option`, we do not include `Some(_)` in the returned list + /// of constructors. + /// Even if `exhaustive_patterns` is disabled, we also omit constructors for empty enums and the + /// never type if we are at the top-level in a match, because we want to allow empty matches to be + /// valid for those. + /// Invariant: this is only empty when the type is uninhabited, and the reverse holds if + /// `exhaustive_patterns` is enabled. + fn list_constructors_for_type( + &self, + ty: Self::Ty, + is_top_level_pattern: bool, + ) -> SmallVec<[Constructor; 1]>; + + /// Show the name of the variant corresponding to this constructor. + /// `ty` must be `ty::Adt`, and `ctor` must be a constructor for that type. + fn variant_ident(ty: Self::Ty, ctor: &Constructor) -> Option; + + /// Whether the variant should be hidden from lints. + /// `ty` must be `ty::Adt` for an enum, and `idx` must be the index of a variant for that type. + fn is_doc_hidden_variant(&self, ty: Self::Ty, idx: Self::VariantIdx) -> bool; + + /// List the fields for this variant. + /// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide + /// uninhabited fields in order not to reveal the uninhabitedness of the whole variant. + /// `ty` must be `ty::Adt`, and `ctor` must be a constructor for that type. + fn list_variant_nonhidden_fields( + &self, + ty: Self::Ty, + ctor: &Constructor, + ) -> Vec; + + fn span_bug(span: Self::Span, f: impl Fn() -> String) -> !; +} + +crate struct UsefulnessCtxt<'p, Cx: Context> { + crate cx: Cx, + crate pattern_arena: &'p TypedArena>, } #[derive(Copy, Clone)] -pub(super) struct PatCtxt<'a, 'p, 'tcx> { - pub(super) cx: &'a MatchCheckCtxt<'p, 'tcx>, +pub(super) struct PatCtxt<'a, 'p, Cx: Context> { + pub(super) ucx: &'a UsefulnessCtxt<'p, Cx>, /// Type of the current column under investigation. - pub(super) ty: Ty<'tcx>, + pub(super) ty: Cx::Ty, /// Span of the current pattern under investigation. - pub(super) span: Span, + pub(super) span: Cx::Span, /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a /// subpattern. pub(super) is_top_level: bool, @@ -323,7 +374,7 @@ pub(super) struct PatCtxt<'a, 'p, 'tcx> { pub(super) is_non_exhaustive: bool, } -impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> { +impl<'a, 'p, Cx: Context> Debug for PatCtxt<'a, 'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PatCtxt").field("ty", &self.ty).finish() } @@ -332,16 +383,16 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> { /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// works well. #[derive(Clone)] -struct PatStack<'p, 'tcx> { - pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>, +struct PatStack<'p, Cx: Context> { + pats: SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]>, } -impl<'p, 'tcx> PatStack<'p, 'tcx> { - fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self { +impl<'p, Cx: Context> PatStack<'p, Cx> { + fn from_pattern(pat: &'p DeconstructedPat<'p, Cx>) -> Self { Self::from_vec(smallvec![pat]) } - fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>) -> Self { + fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]>) -> Self { PatStack { pats: vec } } @@ -353,17 +404,17 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { self.pats.len() } - fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> { + fn head(&self) -> &'p DeconstructedPat<'p, Cx> { self.pats[0] } - fn iter(&self) -> impl Iterator> { + fn iter(&self) -> impl Iterator> { self.pats.iter().copied() } // Recursively expand the first pattern into its subpatterns. Only useful if the pattern is an // or-pattern. Panics if `self` is empty. - fn expand_or_pat<'a>(&'a self) -> impl Iterator> + Captures<'a> { + fn expand_or_pat<'a>(&'a self) -> impl Iterator> + Captures<'a> { self.head().iter_fields().map(move |pat| { let mut new_patstack = PatStack::from_pattern(pat); new_patstack.pats.extend_from_slice(&self.pats[1..]); @@ -379,19 +430,19 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { /// This is roughly the inverse of `Constructor::apply`. fn pop_head_constructor( &self, - cx: &MatchCheckCtxt<'p, 'tcx>, - ctor: &Constructor<'tcx>, - ) -> PatStack<'p, 'tcx> { + ucx: &UsefulnessCtxt<'p, Cx>, + ctor: &Constructor, + ) -> PatStack<'p, Cx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. - let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor); + let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(ucx, ctor); new_fields.extend_from_slice(&self.pats[1..]); PatStack::from_vec(new_fields) } } /// Pretty-printing for matrix row. -impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> { +impl<'p, Cx: Context> Debug for PatStack<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "+")?; for pat in self.iter() { @@ -403,11 +454,11 @@ impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> { /// A 2D matrix. #[derive(Clone)] -pub(super) struct Matrix<'p, 'tcx> { - patterns: Vec>, +pub(super) struct Matrix<'p, Cx: Context> { + patterns: Vec>, } -impl<'p, 'tcx> Matrix<'p, 'tcx> { +impl<'p, Cx: Context> Matrix<'p, Cx> { fn empty() -> Self { Matrix { patterns: vec![] } } @@ -419,7 +470,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively /// expands it. - fn push(&mut self, row: PatStack<'p, 'tcx>) { + fn push(&mut self, row: PatStack<'p, Cx>) { if !row.is_empty() && row.head().is_or_pat() { for row in row.expand_or_pat() { self.patterns.push(row); @@ -432,20 +483,20 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// Iterate over the first component of each row fn heads<'a>( &'a self, - ) -> impl Iterator> + Clone + Captures<'a> { + ) -> impl Iterator> + Clone + Captures<'a> { self.patterns.iter().map(|r| r.head()) } /// This computes `S(constructor, self)`. See top of the file for explanations. fn specialize_constructor( &self, - pcx: PatCtxt<'_, 'p, 'tcx>, - ctor: &Constructor<'tcx>, - ) -> Matrix<'p, 'tcx> { + pcx: PatCtxt<'_, 'p, Cx>, + ctor: &Constructor, + ) -> Matrix<'p, Cx> { let mut matrix = Matrix::empty(); for row in &self.patterns { if ctor.is_covered_by(pcx, row.head().ctor()) { - let new_row = row.pop_head_constructor(pcx.cx, ctor); + let new_row = row.pop_head_constructor(pcx.ucx, ctor); matrix.push(new_row); } } @@ -462,7 +513,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// + false + [_] + /// + _ + [_, _, tail @ ..] + /// ``` -impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { +impl<'p, Cx: Context> Debug for Matrix<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -496,15 +547,15 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { /// witnesses of non-exhaustiveness when there are any. /// Which variant to use is dictated by `ArmType`. #[derive(Debug)] -enum Usefulness<'p, 'tcx> { +enum Usefulness<'p, Cx: Context> { /// If we don't care about witnesses, simply remember if the pattern was useful. NoWitnesses { useful: bool }, /// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole /// pattern is unreachable. - WithWitnesses(Vec>), + WithWitnesses(Vec>), } -impl<'p, 'tcx> Usefulness<'p, 'tcx> { +impl<'p, Cx: Context> Usefulness<'p, Cx> { fn new_useful(preference: ArmType) -> Self { match preference { // A single (empty) witness of reachability. @@ -545,9 +596,9 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { /// with the results of specializing with the other constructors. fn apply_constructor( self, - pcx: PatCtxt<'_, 'p, 'tcx>, - matrix: &Matrix<'p, 'tcx>, // used to compute missing ctors - ctor: &Constructor<'tcx>, + pcx: PatCtxt<'_, 'p, Cx>, + matrix: &Matrix<'p, Cx>, // used to compute missing ctors + ctor: &Constructor, ) -> Self { match self { NoWitnesses { .. } => self, @@ -566,47 +617,28 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> { // This lets us know if we skipped any variants because they are marked // `doc(hidden)`. - let mut doc_hidden_variant = false; + let mut hid_some_variants = false; // Construct for each missing constructor a "wild" version of this // constructor, that matches everything that can be built with // it. For example, if `ctor` is a `Constructor::Variant` for // `Option::Some`, we get the pattern `Some(_)`. - let mut new: Vec> = split_wildcard + let mut new: Vec> = split_wildcard .iter_missing(pcx) - .filter_map(|missing_ctor| { - // Check if this variant is marked `doc(hidden)` + .filter(|missing_ctor| { if let Constructor::Variant(idx) = missing_ctor { - if let ty::Adt(adt, _) = pcx.ty.kind() { - if pcx - .cx - .tcx - .get_attrs(adt.variants[*idx].def_id) - .iter() - .find_map(|attr| { - if attr.has_name(sym::doc) { - attr.meta_item_list() - } else { - None - } - }) - .map(|items| { - items - .iter() - .find(|item| item.has_name(sym::hidden)) - .is_some() - }) - .unwrap_or(false) - { - doc_hidden_variant = true; - return None; - } + if pcx.ucx.cx.is_doc_hidden_variant(pcx.ty, *idx) { + hid_some_variants = true; + return false; } } - Some(DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone())) + true + }) + .map(|missing_ctor| { + DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone()) }) .collect(); - if doc_hidden_variant { + if hid_some_variants { new.push(DeconstructedPat::wildcard(pcx.ty)); } @@ -680,11 +712,11 @@ enum ArmType { /// /// The final `Pair(Some(_), true)` is then the resulting witness. #[derive(Debug)] -crate struct Witness<'p, 'tcx>(Vec>); +crate struct Witness<'p, Cx: Context>(Vec>); -impl<'p, 'tcx> Witness<'p, 'tcx> { +impl<'p, Cx: Context> Witness<'p, Cx> { /// Asserts that the witness contains a single pattern, and returns it. - fn single_pattern(self) -> DeconstructedPat<'p, 'tcx> { + fn single_pattern(self) -> DeconstructedPat<'p, Cx> { assert_eq!(self.0.len(), 1); self.0.into_iter().next().unwrap() } @@ -702,13 +734,13 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { /// /// left_ty: struct X { a: (bool, &'static str), b: usize} /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } - fn apply_constructor(mut self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Self { + fn apply_constructor(mut self, pcx: PatCtxt<'_, 'p, Cx>, ctor: &Constructor) -> Self { let pat = { let len = self.0.len(); let arity = ctor.arity(pcx); let pats = self.0.drain((len - arity)..).rev(); - let fields = Fields::from_iter(pcx.cx, pats); - DeconstructedPat::new(ctor.clone(), fields, pcx.ty, DUMMY_SP) + let fields = Fields::from_iter(pcx.ucx, pats); + DeconstructedPat::new(ctor.clone(), fields, pcx.ty, Cx::Span::default()) }; self.0.push(pat); @@ -741,18 +773,18 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { /// relied on for soundness. #[instrument( level = "debug", - skip(cx, matrix, report, witness_preference, hir_id, is_under_guard, is_top_level) + skip(ucx, matrix, report, witness_preference, hir_id, is_under_guard, is_top_level) )] -fn is_useful<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - matrix: &Matrix<'p, 'tcx>, - v: &PatStack<'p, 'tcx>, - report: &mut UsefulnessReport<'p, 'tcx>, +fn is_useful<'p, Cx: Context>( + ucx: &UsefulnessCtxt<'p, Cx>, + matrix: &Matrix<'p, Cx>, + v: &PatStack<'p, Cx>, + report: &mut UsefulnessReport<'p, Cx>, witness_preference: ArmType, - hir_id: HirId, + hir_id: Cx::HirId, is_under_guard: bool, is_top_level: bool, -) -> Usefulness<'p, 'tcx> { +) -> Usefulness<'p, Cx> { debug!("matrix,v={:?}{:?}", matrix, v); let Matrix { patterns: rows, .. } = matrix; @@ -774,8 +806,8 @@ fn is_useful<'p, 'tcx>( assert!(rows.iter().all(|r| r.len() == v.len())); let ty = v.head().ty(); - let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); - let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; + let is_non_exhaustive = ucx.cx.is_foreign_non_exhaustive_enum(ty); + let pcx = PatCtxt { ucx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; // If the first pattern is an or-pattern, expand it. let mut ret = Usefulness::new_not_useful(witness_preference); @@ -785,7 +817,7 @@ fn is_useful<'p, 'tcx>( let mut matrix = matrix.clone(); for v in v.expand_or_pat() { let usefulness = is_useful( - cx, + ucx, &matrix, &v, report, @@ -823,9 +855,9 @@ fn is_useful<'p, 'tcx>( for ctor in split_ctors { debug!("specialize({:?})", ctor); let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); - let v = v.pop_head_constructor(cx, &ctor); + let v = v.pop_head_constructor(ucx, &ctor); let usefulness = is_useful( - cx, + ucx, &spec_matrix, &v, report, @@ -879,10 +911,10 @@ fn is_useful<'p, 'tcx>( /// The arm of a match expression. #[derive(Clone, Copy)] -crate struct MatchArm<'p, 'tcx> { +crate struct MatchArm<'p, Cx: Context> { /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`. - crate pat: &'p DeconstructedPat<'p, 'tcx>, - crate hir_id: HirId, + crate pat: &'p DeconstructedPat<'p, Cx>, + crate hir_id: Cx::HirId, crate has_guard: bool, } @@ -894,20 +926,32 @@ crate enum Reachability { } /// The output of checking a match for exhaustiveness and arm reachability. -#[derive(Default)] -crate struct UsefulnessReport<'p, 'tcx> { +crate struct UsefulnessReport<'p, Cx: Context> { /// For each arm of the input, whether that arm is reachable after the arms above it. - crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>, + crate arm_usefulness: Vec<(MatchArm<'p, Cx>, Reachability)>, /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. - crate non_exhaustiveness_witnesses: Vec>, + crate non_exhaustiveness_witnesses: Vec>, /// Reports any unreachable sub-or-patterns. - crate unreachable_subpatterns: Vec<(Span, HirId)>, + crate unreachable_subpatterns: Vec<(Cx::Span, Cx::HirId)>, /// Report when some variants of a `non_exhaustive` enum failed to be listed explicitly. crate non_exhaustive_omitted_patterns: - Vec<(Ty<'tcx>, Span, HirId, Vec>)>, + Vec<(Cx::Ty, Cx::Span, Cx::HirId, Vec>)>, /// Report likely incorrect range patterns (#63987) - crate overlapping_range_endpoints: Vec<(Span, HirId, Vec>)>, + crate overlapping_range_endpoints: Vec<(Cx::Span, Cx::HirId, Vec>)>, +} + +// Can't derive because it adds a bound on `Cx`. +impl<'p, Cx: Context> Default for UsefulnessReport<'p, Cx> { + fn default() -> Self { + Self { + arm_usefulness: Default::default(), + non_exhaustiveness_witnesses: Default::default(), + unreachable_subpatterns: Default::default(), + non_exhaustive_omitted_patterns: Default::default(), + overlapping_range_endpoints: Default::default(), + } + } } /// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which @@ -915,17 +959,17 @@ crate struct UsefulnessReport<'p, 'tcx> { /// /// Note: the input patterns must have been lowered through /// `check_match::MatchVisitor::lower_pattern`. -crate fn compute_match_usefulness<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - arms: &[MatchArm<'p, 'tcx>], - scrut_hir_id: HirId, - scrut_ty: Ty<'tcx>, -) -> UsefulnessReport<'p, 'tcx> { +crate fn compute_match_usefulness<'p, Cx: Context>( + ucx: &UsefulnessCtxt<'p, Cx>, + arms: &[MatchArm<'p, Cx>], + scrut_hir_id: Cx::HirId, + scrut_ty: Cx::Ty, +) -> UsefulnessReport<'p, Cx> { let mut report = UsefulnessReport::default(); let mut matrix = Matrix::empty(); for arm in arms { let v = PatStack::from_pattern(arm.pat); - is_useful(cx, &matrix, &v, &mut report, RealArm, arm.hir_id, arm.has_guard, true); + is_useful(ucx, &matrix, &v, &mut report, RealArm, arm.hir_id, arm.has_guard, true); if !arm.has_guard { matrix.push(v); } @@ -940,10 +984,10 @@ crate fn compute_match_usefulness<'p, 'tcx>( report.arm_usefulness.push((*arm, reachability)); } - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); + let wild_pattern = ucx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); let v = PatStack::from_pattern(wild_pattern); let usefulness = - is_useful(cx, &matrix, &v, &mut report, FakeExtraWildcard, scrut_hir_id, false, true); + is_useful(ucx, &matrix, &v, &mut report, FakeExtraWildcard, scrut_hir_id, false, true); report.non_exhaustiveness_witnesses = match usefulness { WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(), NoWitnesses { .. } => bug!(),