From 507ee7242a93590276907206786ed3aecefe0125 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 8 Jul 2019 10:58:58 +0200 Subject: [PATCH 1/6] Note that `eq_trait` denotes `trait PartialEq`, not `Eq`, so you don't have to go to trait def to double-check. --- src/librustc/middle/lang_items.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index a6e5bd275ae19..31a144d4e1e86 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -326,6 +326,7 @@ language_item_table! { UnpinTraitLangItem, "unpin", unpin_trait, Target::Trait; PinTypeLangItem, "pin", pin_type, Target::Struct; + // Don't be fooled by the naming here: this lang item denotes `PartialEq`, not `Eq`. EqTraitLangItem, "eq", eq_trait, Target::Trait; PartialOrdTraitLangItem, "partial_ord", partial_ord_trait, Target::Trait; OrdTraitLangItem, "ord", ord_trait, Target::Trait; From 36777f157b1f30f208e913c0ec04f9ac6b567a4e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 8 Jul 2019 11:11:41 +0200 Subject: [PATCH 2/6] future-compat lint for newly handled cases of `#[structural_match]`. --- src/librustc/lint/builtin.rs | 6 ++++++ src/librustc_lint/lib.rs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 45e598531b969..f7718cef34782 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -346,6 +346,12 @@ declare_lint! { "outlives requirements can be inferred" } +declare_lint! { + pub INDIRECT_STRUCTURAL_MATCH, + Warn, + "pattern with const indirectly referencing non-`#[structural_match]` type" +} + /// Some lints that are buffered from `libsyntax`. See `syntax::early_buffered_lints`. pub mod parser { declare_lint! { diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index d808a15982e37..4dc0fce1fc894 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -427,6 +427,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(MUTABLE_BORROW_RESERVATION_CONFLICT), reference: "issue #59159 ", edition: None, + }, + FutureIncompatibleInfo { + id: LintId::of(INDIRECT_STRUCTURAL_MATCH), + reference: "issue #62411 ", + edition: None, } ]); From b56080162bc45e1cfd561b0a21e50b827c9df679 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 3 Jul 2019 16:53:42 +0200 Subject: [PATCH 3/6] Rewrite with future-compat lint for indirect pattern omitting `#[structural_match]`. Outline of changes: * Recur as deeply as necessary when searching for `#[structural_match]`. * `#[structural_match]`: handle case of `const A: & &Wrap(NoDerive)` by including the fields of an ADT during traversal of input type. (We continue to not traverse the substs of an ADT, though, so that we continue to handle `PhantomData` and `*NoDerive` properly.) * Refactored code to use `match` instead of `if let`. This ends up *with less* right-ward drift by moving the handling of the main *`ty::Adt` case *outside* the match. * Using lint (rather than hard error) mmeans we need to check that type is `PartialEq` to avoid ICE'ing the compiler in scneario where MIR codegen dispatches to `PartialEq::eq`. Added said check, and fatal error in that case. --- src/librustc_mir/hair/pattern/check_match.rs | 2 + src/librustc_mir/hair/pattern/mod.rs | 189 ++++++++++++++++++- 2 files changed, 185 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index ed850379af60b..a619a5c35a7ca 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -170,6 +170,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let mut patcx = PatternContext::new(self.tcx, self.param_env.and(self.identity_substs), self.tables); + patcx.include_lint_checks(); let pattern = expand_pattern(cx, patcx.lower_pattern(&pat)); if !patcx.errors.is_empty() { patcx.report_inlining_errors(pat.span); @@ -266,6 +267,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let mut patcx = PatternContext::new(self.tcx, self.param_env.and(self.identity_substs), self.tables); + patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; let pats: Matrix<'_, '_> = vec![smallvec![ diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index cf597ce0b6319..a15452043b35d 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -10,9 +10,11 @@ use crate::const_eval::const_variant_index; use crate::hair::util::UserAnnotatedTyHelpers; use crate::hair::constant::*; +use rustc::lint; use rustc::mir::{Field, BorrowKind, Mutability}; use rustc::mir::{UserTypeProjection}; use rustc::mir::interpret::{GlobalId, ConstValue, sign_extend, AllocId, Pointer}; +use rustc::traits::{ObligationCause, PredicateObligation}; use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, UserType, DefIdTree}; use rustc::ty::{CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations}; use rustc::ty::subst::{SubstsRef, Kind}; @@ -22,6 +24,7 @@ use rustc::hir::def::{CtorOf, Res, DefKind, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc_data_structures::indexed_vec::Idx; +use rustc_data_structures::fx::FxHashSet; use std::cmp::Ordering; use std::fmt; @@ -332,6 +335,7 @@ pub struct PatternContext<'a, 'tcx> { pub tables: &'a ty::TypeckTables<'tcx>, pub substs: SubstsRef<'tcx>, pub errors: Vec, + include_lint_checks: bool, } impl<'a, 'tcx> Pattern<'tcx> { @@ -363,10 +367,16 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { param_env: param_env_and_substs.param_env, tables, substs: param_env_and_substs.value, - errors: vec![] + errors: vec![], + include_lint_checks: false, } } + pub fn include_lint_checks(&mut self) -> &mut Self { + self.include_lint_checks = true; + self + } + pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { // When implicit dereferences have been inserted in this pattern, the unadjusted lowered // pattern has the type that results *after* dereferencing. For example, in this code: @@ -942,7 +952,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted - /// to a pattern that matches the value (as if you'd compared via equality). + /// to a pattern that matches the value (as if you'd compared via structural equality). fn const_to_pat( &self, instance: ty::Instance<'tcx>, @@ -950,15 +960,86 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { id: hir::HirId, span: Span, ) -> Pattern<'tcx> { + // This method is just a warpper handling a validity check; the heavy lifting is + // performed by the recursive const_to_pat_inner method, which is not meant to be + // invoked except by this method. + // + // once indirect_structural_match is a full fledged error, this + // level of indirection can be eliminated + debug!("const_to_pat: cv={:#?} id={:?}", cv, id); - let adt_subpattern = |i, variant_opt| { + debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span); + + let mut saw_error = false; + let inlined_const_as_pat = self.const_to_pat_inner(instance, cv, id, span, &mut saw_error); + + if self.include_lint_checks && !saw_error { + // If we were able to successfully convert the const to some pat, double-check + // that the type of the const obeys `#[structural_match]` constraint. + if let Some(adt_def) = search_for_adt_without_structural_match(self.tcx, cv.ty) { + + let path = self.tcx.def_path_str(adt_def.did); + let msg = format!( + "to use a constant of type `{}` in a pattern, \ + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", + path, + path, + ); + + // before issuing lint, double-check there even *is* a + // semantic PartialEq for us to dispatch to. + // + // (If there isn't, then we can safely issue a hard + // error, because that's never worked, due to compiler + // using PartialEq::eq in this scenario in the past.) + + let ty_is_partial_eq: bool = { + let partial_eq_trait_id = self.tcx.lang_items().eq_trait().unwrap(); + let obligation: PredicateObligation<'_> = + self.tcx.predicate_for_trait_def(self.param_env, + ObligationCause::misc(span, id), + partial_eq_trait_id, + 0, + cv.ty, + &[]); + self.tcx + .infer_ctxt() + .enter(|infcx| infcx.predicate_may_hold(&obligation)) + }; + + if !ty_is_partial_eq { + // span_fatal avoids ICE from resolution of non-existent method (rare case). + self.tcx.sess.span_fatal(span, &msg); + } else { + self.tcx.lint_hir(lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, &msg); + } + } + } + + inlined_const_as_pat + } + + /// Recursive helper for `const_to_pat`; invoke that (instead of calling this directly). + fn const_to_pat_inner( + &self, + instance: ty::Instance<'tcx>, + cv: &'tcx ty::Const<'tcx>, + id: hir::HirId, + span: Span, + // This tracks if we signal some hard error for a given const + // value, so that we will not subsequently issue an irrelevant + // lint for the same const value. + saw_const_match_error: &mut bool, + ) -> Pattern<'tcx> { + + let mut adt_subpattern = |i, variant_opt| { let field = Field::new(i); let val = crate::const_eval::const_field( self.tcx, self.param_env, variant_opt, field, cv ); - self.const_to_pat(instance, val, id, span) + self.const_to_pat_inner(instance, val, id, span, saw_const_match_error) }; - let adt_subpatterns = |n, variant_opt| { + let mut adt_subpatterns = |n, variant_opt| { (0..n).map(|i| { let field = Field::new(i); FieldPattern { @@ -967,7 +1048,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } }).collect::>() }; - debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span); + + let kind = match cv.ty.sty { ty::Float(_) => { self.tcx.lint_hir( @@ -982,9 +1064,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } ty::Adt(adt_def, _) if adt_def.is_union() => { // Matching on union fields is unsafe, we can't hide it in constants + *saw_const_match_error = true; self.tcx.sess.span_err(span, "cannot use unions in constant patterns"); PatternKind::Wild } + // keep old code until future-compat upgraded to errors. ty::Adt(adt_def, _) if !self.tcx.has_attr(adt_def.did, sym::structural_match) => { let path = self.tcx.def_path_str(adt_def.did); let msg = format!( @@ -993,9 +1077,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { path, path, ); + *saw_const_match_error = true; self.tcx.sess.span_err(span, &msg); PatternKind::Wild } + // keep old code until future-compat upgraded to errors. ty::Ref(_, ty::TyS { sty: ty::Adt(adt_def, _), .. }, _) if !self.tcx.has_attr(adt_def.did, sym::structural_match) => { // HACK(estebank): Side-step ICE #53708, but anything other than erroring here @@ -1007,6 +1093,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { path, path, ); + *saw_const_match_error = true; self.tcx.sess.span_err(span, &msg); PatternKind::Wild } @@ -1058,6 +1145,96 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } +fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>) + -> Option<&'tcx AdtDef> +{ + // Import here (not mod level), because `TypeFoldable::fold_with` + // conflicts with `PatternFoldable::fold_with` + use crate::rustc::ty::fold::TypeVisitor; + use crate::rustc::ty::TypeFoldable; + + let mut search = Search { tcx, found: None, seen: FxHashSet::default() }; + ty.visit_with(&mut search); + return search.found; + + struct Search<'tcx> { + tcx: TyCtxt<'tcx>, + + // records the first ADT we find without `#[structural_match` + found: Option<&'tcx AdtDef>, + + // tracks ADT's previously encountered during search, so that + // we will not recur on them again. + seen: FxHashSet<&'tcx AdtDef>, + } + + impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + debug!("Search visiting ty: {:?}", ty); + + let (adt_def, substs) = match ty.sty { + ty::Adt(adt_def, substs) => (adt_def, substs), + ty::RawPtr(..) => { + // `#[structural_match]` ignores substructure of + // `*const _`/`*mut _`, so skip super_visit_with + + // (But still tell caller to continue search.) + return false; + } + ty::Array(_, n) if n.assert_usize(self.tcx) == Some(0) => { + // rust-lang/rust#62336: ignore type of contents + // for empty array. + return false; + } + _ => { + ty.super_visit_with(self); + return false; + } + }; + + if !self.tcx.has_attr(adt_def.did, sym::structural_match) { + self.found = Some(&adt_def); + debug!("Search found adt_def: {:?}", adt_def); + return true // Halt visiting! + } + + if self.seen.contains(adt_def) { + debug!("Search already seen adt_def: {:?}", adt_def); + // let caller continue its search + return false; + } + + self.seen.insert(adt_def); + + // `#[structural_match]` does not care about the + // instantiation of the generics in an ADT (it + // instead looks directly at its fields outside + // this match), so we skip super_visit_with. + // + // (Must not recur on substs for `PhantomData` cf + // rust-lang/rust#55028 and rust-lang/rust#55837; but also + // want to skip substs when only uses of generic are + // behind unsafe pointers `*const T`/`*mut T`.) + + // even though we skip super_visit_with, we must recur on + // fields of ADT. + let tcx = self.tcx; + for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) { + if field_ty.visit_with(self) { + // found an ADT without `#[structural_match]`; halt visiting! + assert!(self.found.is_some()); + return true; + } + } + + // Even though we do not want to recur on substs, we do + // want our caller to continue its own search. + false + } + } +} + impl UserAnnotatedTyHelpers<'tcx> for PatternContext<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx From 02714b8ba3424fdfa1513fc893bb692472834a59 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 3 Jul 2019 16:54:08 +0200 Subject: [PATCH 4/6] Regression tests and updates to existing tests. The regression tests explore: (direct | indirect | doubly-indirect | unsafe) x (embedded | param): where: embedded: `struct Wrapper(... NoDerive ...);` param: `struct Wrapper(... X ...);` direct: `const A: Wrapper<...> = Wrapper(NoDerive);` indirect: `const A: & & Wrapper<...> = Wrapper(NoDerive)` doubly-indirect: `const A: & & Wrapper<...> = & & Wrapper(& & NoDerive)` unsafe: `const A: UnsafeWrap<...> = UnsafeWrap(std::ptr::null())` --- src/test/ui/issues/issue-55511.rs | 2 + src/test/ui/issues/issue-55511.stderr | 10 +++++ ...-hide-behind-direct-unsafe-ptr-embedded.rs | 24 +++++++++++ ...low-hide-behind-direct-unsafe-ptr-param.rs | 24 +++++++++++ ...ide-behind-indirect-unsafe-ptr-embedded.rs | 24 +++++++++++ ...w-hide-behind-indirect-unsafe-ptr-param.rs | 24 +++++++++++ ...cant-hide-behind-direct-struct-embedded.rs | 26 +++++++++++ ...-hide-behind-direct-struct-embedded.stderr | 8 ++++ .../cant-hide-behind-direct-struct-param.rs | 26 +++++++++++ ...ant-hide-behind-direct-struct-param.stderr | 8 ++++ ...nt-hide-behind-doubly-indirect-embedded.rs | 29 +++++++++++++ ...ide-behind-doubly-indirect-embedded.stderr | 10 +++++ .../cant-hide-behind-doubly-indirect-param.rs | 29 +++++++++++++ ...t-hide-behind-doubly-indirect-param.stderr | 10 +++++ ...nt-hide-behind-indirect-struct-embedded.rs | 29 +++++++++++++ ...ide-behind-indirect-struct-embedded.stderr | 10 +++++ .../cant-hide-behind-indirect-struct-param.rs | 29 +++++++++++++ ...t-hide-behind-indirect-struct-param.stderr | 10 +++++ ...-61118-match-slice-forbidden-without-eq.rs | 19 ++++++++ ...18-match-slice-forbidden-without-eq.stderr | 8 ++++ ...2307-match-ref-ref-forbidden-without-eq.rs | 43 +++++++++++++++++++ ...-match-ref-ref-forbidden-without-eq.stderr | 19 ++++++++ ...tch-nonempty-array-forbidden-without-eq.rs | 19 ++++++++ ...nonempty-array-forbidden-without-eq.stderr | 8 ++++ 24 files changed, 448 insertions(+) create mode 100644 src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-embedded.rs create mode 100644 src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-param.rs create mode 100644 src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-embedded.rs create mode 100644 src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-param.rs create mode 100644 src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs create mode 100644 src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.stderr create mode 100644 src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs create mode 100644 src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.stderr create mode 100644 src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs create mode 100644 src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr create mode 100644 src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs create mode 100644 src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr create mode 100644 src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs create mode 100644 src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr create mode 100644 src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs create mode 100644 src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr create mode 100644 src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs create mode 100644 src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.stderr create mode 100644 src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs create mode 100644 src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr create mode 100644 src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs create mode 100644 src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.stderr diff --git a/src/test/ui/issues/issue-55511.rs b/src/test/ui/issues/issue-55511.rs index 4b9475ba62718..42c6f24b36a0c 100644 --- a/src/test/ui/issues/issue-55511.rs +++ b/src/test/ui/issues/issue-55511.rs @@ -14,6 +14,8 @@ fn main() { //~^ ERROR `a` does not live long enough [E0597] match b { <() as Foo<'static>>::C => { } + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN will become a hard error in a future release _ => { } } } diff --git a/src/test/ui/issues/issue-55511.stderr b/src/test/ui/issues/issue-55511.stderr index bf3e58e8cdb19..c0f702e4fab23 100644 --- a/src/test/ui/issues/issue-55511.stderr +++ b/src/test/ui/issues/issue-55511.stderr @@ -1,3 +1,13 @@ +warning: to use a constant of type `std::cell::Cell` in a pattern, `std::cell::Cell` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/issue-55511.rs:16:9 + | +LL | <() as Foo<'static>>::C => { } + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(indirect_structural_match)] on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + error[E0597]: `a` does not live long enough --> $DIR/issue-55511.rs:13:28 | diff --git a/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-embedded.rs b/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-embedded.rs new file mode 100644 index 0000000000000..b90a750cc16c4 --- /dev/null +++ b/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-embedded.rs @@ -0,0 +1,24 @@ +// Test explores how `#[structral_match]` behaves in tandem with +// `*const` and `*mut` pointers. + +// run-pass + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive +// (which doesn't matter here because `<*const T>::eq` won't recur on `T`). +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapEmbedded(*const NoDerive); + +const WRAP_UNSAFE_EMBEDDED: WrapEmbedded = WrapEmbedded(std::ptr::null()); + +fn main() { + match WRAP_UNSAFE_EMBEDDED { + WRAP_UNSAFE_EMBEDDED => { println!("WRAP_UNSAFE_EMBEDDED correctly matched itself"); } + _ => { panic!("WRAP_UNSAFE_EMBEDDED did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-param.rs b/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-param.rs new file mode 100644 index 0000000000000..1076b9f25d89a --- /dev/null +++ b/src/test/ui/rfc1445/allow-hide-behind-direct-unsafe-ptr-param.rs @@ -0,0 +1,24 @@ +// Test explores how `#[structral_match]` behaves in tandem with +// `*const` and `*mut` pointers. + +// run-pass + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive +// (which doesn't matter here because `<*const T>::eq` won't recur on `T`). +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapParam(*const X); + +const WRAP_UNSAFE_PARAM: WrapParam = WrapParam(std::ptr::null()); + +fn main() { + match WRAP_UNSAFE_PARAM { + WRAP_UNSAFE_PARAM => { println!("WRAP_UNSAFE_PARAM correctly matched itself"); } + _ => { panic!("WRAP_UNSAFE_PARAM did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-embedded.rs b/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-embedded.rs new file mode 100644 index 0000000000000..a4b832d377d6f --- /dev/null +++ b/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-embedded.rs @@ -0,0 +1,24 @@ +// Test explores how `#[structral_match]` behaves in tandem with +// `*const` and `*mut` pointers. + +// run-pass + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive +// (which doesn't matter here because `<*const T>::eq` won't recur on `T`). +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapEmbedded(*const NoDerive); + +const WRAP_UNSAFE_EMBEDDED: & &WrapEmbedded = & &WrapEmbedded(std::ptr::null()); + +fn main() { + match WRAP_UNSAFE_EMBEDDED { + WRAP_UNSAFE_EMBEDDED => { println!("WRAP_UNSAFE_EMBEDDED correctly matched itself"); } + _ => { panic!("WRAP_UNSAFE_EMBEDDED did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-param.rs b/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-param.rs new file mode 100644 index 0000000000000..47b70e2e9cc56 --- /dev/null +++ b/src/test/ui/rfc1445/allow-hide-behind-indirect-unsafe-ptr-param.rs @@ -0,0 +1,24 @@ +// Test explores how `#[structral_match]` behaves in tandem with +// `*const` and `*mut` pointers. + +// run-pass + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive +// (which doesn't matter here because `<*const T>::eq` won't recur on `T`). +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapParam(*const X); + +const WRAP_UNSAFE_PARAM: & &WrapParam = & &WrapParam(std::ptr::null()); + +fn main() { + match WRAP_UNSAFE_PARAM { + WRAP_UNSAFE_PARAM => { println!("WRAP_UNSAFE_PARAM correctly matched itself"); } + _ => { panic!("WRAP_UNSAFE_PARAM did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs new file mode 100644 index 0000000000000..b8949ae8b500f --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.rs @@ -0,0 +1,26 @@ +// This is part of a set of tests exploring the different ways a +// `#[structural_match]` ADT might try to hold a +// non-`#[structural_match]` in hidden manner that lets matches +// through that we had intended to reject. +// +// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapInline(NoDerive); + +const WRAP_DIRECT_INLINE: WrapInline = WrapInline(NoDerive(0)); + +fn main() { + match WRAP_DIRECT_INLINE { + WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); } + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => { println!("WRAP_DIRECT_INLINE did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.stderr b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.stderr new file mode 100644 index 0000000000000..c73a6cf1326b3 --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-embedded.stderr @@ -0,0 +1,8 @@ +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cant-hide-behind-direct-struct-embedded.rs:22:9 + | +LL | WRAP_DIRECT_INLINE => { panic!("WRAP_DIRECT_INLINE matched itself"); } + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs new file mode 100644 index 0000000000000..a3a615ea748cb --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.rs @@ -0,0 +1,26 @@ +// This is part of a set of tests exploring the different ways a +// `#[structural_match]` ADT might try to hold a +// non-`#[structural_match]` in hidden manner that lets matches +// through that we had intended to reject. +// +// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapParam(T); + +const WRAP_DIRECT_PARAM: WrapParam = WrapParam(NoDerive(0)); + +fn main() { + match WRAP_DIRECT_PARAM { + WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); } + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => { println!("WRAP_DIRECT_PARAM did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.stderr b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.stderr new file mode 100644 index 0000000000000..6fdf9db89b8dc --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-direct-struct-param.stderr @@ -0,0 +1,8 @@ +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cant-hide-behind-direct-struct-param.rs:22:9 + | +LL | WRAP_DIRECT_PARAM => { panic!("WRAP_DIRECT_PARAM matched itself"); } + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs new file mode 100644 index 0000000000000..b6d9c52b298b6 --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.rs @@ -0,0 +1,29 @@ +// This is part of a set of tests exploring the different ways a +// `#[structural_match]` ADT might try to hold a +// non-`#[structural_match]` in hidden manner that lets matches +// through that we had intended to reject. +// +// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 + +// run-pass + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapInline<'a>(&'a &'a NoDerive); + +const WRAP_DOUBLY_INDIRECT_INLINE: & &WrapInline = & &WrapInline(& & NoDerive(0)); + +fn main() { + match WRAP_DOUBLY_INDIRECT_INLINE { + WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); } + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN will become a hard error in a future release + _ => { println!("WRAP_DOUBLY_INDIRECT_INLINE correctly did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr new file mode 100644 index 0000000000000..3de63f43ecbb6 --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-embedded.stderr @@ -0,0 +1,10 @@ +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cant-hide-behind-doubly-indirect-embedded.rs:24:9 + | +LL | WRAP_DOUBLY_INDIRECT_INLINE => { panic!("WRAP_DOUBLY_INDIRECT_INLINE matched itself"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(indirect_structural_match)] on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs new file mode 100644 index 0000000000000..804d336b3ea61 --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.rs @@ -0,0 +1,29 @@ +// This is part of a set of tests exploring the different ways a +// `#[structural_match]` ADT might try to hold a +// non-`#[structural_match]` in hidden manner that lets matches +// through that we had intended to reject. +// +// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 + +// run-pass + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapParam<'a, T>(&'a &'a T); + +const WRAP_DOUBLY_INDIRECT_PARAM: & &WrapParam = & &WrapParam(& & NoDerive(0)); + +fn main() { + match WRAP_DOUBLY_INDIRECT_PARAM { + WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); } + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN will become a hard error in a future release + _ => { println!("WRAP_DOUBLY_INDIRECT_PARAM correctly did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr new file mode 100644 index 0000000000000..ee4652d153283 --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-doubly-indirect-param.stderr @@ -0,0 +1,10 @@ +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cant-hide-behind-doubly-indirect-param.rs:24:9 + | +LL | WRAP_DOUBLY_INDIRECT_PARAM => { panic!("WRAP_DOUBLY_INDIRECT_PARAM matched itself"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(indirect_structural_match)] on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs new file mode 100644 index 0000000000000..85d2e65a9a080 --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.rs @@ -0,0 +1,29 @@ +// This is part of a set of tests exploring the different ways a +// `#[structural_match]` ADT might try to hold a +// non-`#[structural_match]` in hidden manner that lets matches +// through that we had intended to reject. +// +// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 + +// run-pass + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapInline(NoDerive); + +const WRAP_INDIRECT_INLINE: & &WrapInline = & &WrapInline(NoDerive(0)); + +fn main() { + match WRAP_INDIRECT_INLINE { + WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); } + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN will become a hard error in a future release + _ => { println!("WRAP_INDIRECT_INLINE did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr new file mode 100644 index 0000000000000..eb7468499316f --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-embedded.stderr @@ -0,0 +1,10 @@ +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cant-hide-behind-indirect-struct-embedded.rs:24:9 + | +LL | WRAP_INDIRECT_INLINE => { panic!("WRAP_INDIRECT_INLINE matched itself"); } + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(indirect_structural_match)] on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs new file mode 100644 index 0000000000000..849aa14776697 --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.rs @@ -0,0 +1,29 @@ +// This is part of a set of tests exploring the different ways a +// `#[structural_match]` ADT might try to hold a +// non-`#[structural_match]` in hidden manner that lets matches +// through that we had intended to reject. +// +// See discussion on rust-lang/rust#62307 and rust-lang/rust#62339 + +// run-pass + +struct NoDerive(i32); + +// This impl makes NoDerive irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +#[derive(PartialEq, Eq)] +struct WrapParam(T); + +const WRAP_INDIRECT_PARAM: & &WrapParam = & &WrapParam(NoDerive(0)); + +fn main() { + match WRAP_INDIRECT_PARAM { + WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); } + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN will become a hard error in a future release + _ => { println!("WRAP_INDIRECT_PARAM correctly did not match itself"); } + } +} diff --git a/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr new file mode 100644 index 0000000000000..8a16556844454 --- /dev/null +++ b/src/test/ui/rfc1445/cant-hide-behind-indirect-struct-param.stderr @@ -0,0 +1,10 @@ +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cant-hide-behind-indirect-struct-param.rs:24:9 + | +LL | WRAP_INDIRECT_PARAM => { panic!("WRAP_INDIRECT_PARAM matched itself"); } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(indirect_structural_match)] on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + diff --git a/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs b/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs new file mode 100644 index 0000000000000..9a96628cac690 --- /dev/null +++ b/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.rs @@ -0,0 +1,19 @@ +// Issue 61118 pointed out a case where we hit an ICE during code gen: +// the compiler assumed that `PartialEq` was always implemented on any +// use of a `const` item in a pattern context, but the pre-existing +// checking for the presence of `#[structural_match]` was too shallow +// (see rust-lang/rust#62307), and so we hit cases where we were +// trying to dispatch to `PartialEq` on types that did not implement +// that trait. + +struct B(i32); + +const A: &[B] = &[]; + +pub fn main() { + match &[][..] { + A => (), + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => (), + } +} diff --git a/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.stderr b/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.stderr new file mode 100644 index 0000000000000..e8141f6108c5e --- /dev/null +++ b/src/test/ui/rfc1445/issue-61118-match-slice-forbidden-without-eq.stderr @@ -0,0 +1,8 @@ +error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/issue-61118-match-slice-forbidden-without-eq.rs:15:9 + | +LL | A => (), + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs new file mode 100644 index 0000000000000..7646f7558a396 --- /dev/null +++ b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.rs @@ -0,0 +1,43 @@ +// RFC 1445 introduced `#[structural_match]`; this attribute must +// appear on the `struct`/`enum` definition for any `const` used in a +// pattern. +// +// This is our (forever-unstable) way to mark a datatype as having a +// `PartialEq` implementation that is equivalent to recursion over its +// substructure. This avoids (at least in the short term) any need to +// resolve the question of what semantics is used for such matching. +// (See RFC 1445 for more details and discussion.) + +// Issue 62307 pointed out a case where the checking for +// `#[structural_match]` was too shallow. + +// run-pass + +#[derive(Debug)] +struct B(i32); + +// Overriding `PartialEq` to use this strange notion of "equality" exposes +// whether `match` is using structural-equality or method-dispatch +// under the hood, which is the antithesis of rust-lang/rfcs#1445 +impl PartialEq for B { + fn eq(&self, other: &B) -> bool { std::cmp::min(self.0, other.0) == 0 } +} + +fn main() { + const RR_B0: & & B = & & B(0); + const RR_B1: & & B = & & B(1); + + match RR_B0 { + RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); } + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN will become a hard error in a future release + _ => { } + } + + match RR_B1 { + RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); } + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN will become a hard error in a future release + _ => { } + } +} diff --git a/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr new file mode 100644 index 0000000000000..ba0275f5f9f93 --- /dev/null +++ b/src/test/ui/rfc1445/issue-62307-match-ref-ref-forbidden-without-eq.stderr @@ -0,0 +1,19 @@ +warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:31:9 + | +LL | RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0); } + | ^^^^^ + | + = note: #[warn(indirect_structural_match)] on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:38:9 + | +LL | RR_B1 => { println!("CLAIM RR1: {:?} matches {:?}", RR_B1, RR_B1); } + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + diff --git a/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs b/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs new file mode 100644 index 0000000000000..3d56fb05dc460 --- /dev/null +++ b/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.rs @@ -0,0 +1,19 @@ +// Issue 62307 pointed out a case where the checking for +// `#[structural_match]` was too shallow. +// +// Here we check similar behavior for non-empty arrays of types that +// do not derive `Eq`. +// +// (Current behavior for empty arrays differs and thus is not tested +// here; see rust-lang/rust#62336.) + +#[derive(PartialEq, Debug)] +struct B(i32); + +fn main() { + const FOO: [B; 1] = [B(0)]; + match [B(1)] { + FOO => { } + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + } +} diff --git a/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.stderr b/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.stderr new file mode 100644 index 0000000000000..371f8a0aa1d77 --- /dev/null +++ b/src/test/ui/rfc1445/match-nonempty-array-forbidden-without-eq.stderr @@ -0,0 +1,8 @@ +error: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/match-nonempty-array-forbidden-without-eq.rs:16:9 + | +LL | FOO => { } + | ^^^ + +error: aborting due to previous error + From 02af3ca5c42f35eeceb0761180193a6abb97c0ad Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 8 Jul 2019 13:40:08 +0200 Subject: [PATCH 5/6] Added test explicitly testing that this PR continues to handle empty arrays in same manner as before. --- ...mpty-array-allowed-without-eq-issue-62336.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/ui/rfc1445/match-empty-array-allowed-without-eq-issue-62336.rs diff --git a/src/test/ui/rfc1445/match-empty-array-allowed-without-eq-issue-62336.rs b/src/test/ui/rfc1445/match-empty-array-allowed-without-eq-issue-62336.rs new file mode 100644 index 0000000000000..7ba0f3a9e8dd9 --- /dev/null +++ b/src/test/ui/rfc1445/match-empty-array-allowed-without-eq-issue-62336.rs @@ -0,0 +1,17 @@ +// Pre-existing behavior has been to reject patterns with consts +// denoting non-empty arrays of non-`Eq` types, but *accept* empty +// arrays of such types. +// +// See rust-lang/rust#62336. + +// run-pass + +#[derive(PartialEq, Debug)] +struct B(i32); + +fn main() { + const FOO: [B; 0] = []; + match [] { + FOO => { } + } +} From b0b64ddcd171be6e460b2d7c1ada4f98a3cdb026 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 9 Jul 2019 16:54:19 +0200 Subject: [PATCH 6/6] Added comment explaining purpose of `search_for_adt_without_structural_match`. --- src/librustc_mir/hair/pattern/mod.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index a15452043b35d..aea5016ab34f7 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -1145,6 +1145,30 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } } +/// This method traverses the structure of `ty`, trying to find an +/// instance of an ADT (i.e. struct or enum) that was declared without +/// the `#[structural_match]` attribute. +/// +/// The "structure of a type" includes all components that would be +/// considered when doing a pattern match on a constant of that +/// type. +/// +/// * This means this method descends into fields of structs/enums, +/// and also descends into the inner type `T` of `&T` and `&mut T` +/// +/// * The traversal doesn't dereference unsafe pointers (`*const T`, +/// `*mut T`), and it does not visit the type arguments of an +/// instantiated generic like `PhantomData`. +/// +/// The reason we do this search is Rust currently require all ADT's +/// reachable from a constant's type to be annotated with +/// `#[structural_match]`, an attribute which essentially says that +/// the implementation of `PartialEq::eq` behaves *equivalently* to a +/// comparison against the unfolded structure. +/// +/// For more background on why Rust has this requirement, and issues +/// that arose when the requirement was not enforced completely, see +/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. fn search_for_adt_without_structural_match<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx AdtDef>