Auto merge of #62339 - pnkfelix:issue-61188-use-visitor-for-structural-match-check, r=nikomatsakis

use visitor for #[structural_match] check

This changes the code so that we recur down the structure of a type of a const (rather than just inspecting at a shallow one or two levels) when we are looking to see if it has an ADT that did not derive `PartialEq` and `Eq`.

Fix #61188

Fix #62307

Cc #62336
This commit is contained in:
bors 2019-07-10 12:26:44 +00:00
commit c6a9e766f9
30 changed files with 686 additions and 6 deletions

View File

@ -348,6 +348,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! {

View File

@ -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;

View File

@ -429,6 +429,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
id: LintId::of(MUTABLE_BORROW_RESERVATION_CONFLICT),
reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>",
edition: None,
},
FutureIncompatibleInfo {
id: LintId::of(INDIRECT_STRUCTURAL_MATCH),
reference: "issue #62411 <https://github.com/rust-lang/rust/issues/62411>",
edition: None,
}
]);

View File

@ -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![

View File

@ -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};
@ -23,6 +25,7 @@ use rustc::hir::pat_util::EnumerateAndAdjustIterator;
use rustc::hir::ptr::P;
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<PatternError>,
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::<Vec<_>>()
};
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,120 @@ 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<T>`.
///
/// 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>
{
// 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<T>` 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

View File

@ -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
_ => { }
}
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/62411>
error[E0597]: `a` does not live long enough
--> $DIR/issue-55511.rs:13:28
|

View File

@ -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"); }
}
}

View File

@ -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<X>(*const X);
const WRAP_UNSAFE_PARAM: WrapParam<NoDerive> = 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"); }
}
}

View File

@ -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"); }
}
}

View File

@ -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<X>(*const X);
const WRAP_UNSAFE_PARAM: & &WrapParam<NoDerive> = & &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"); }
}
}

View File

@ -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"); }
}
}

View File

@ -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

View File

@ -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>(T);
const WRAP_DIRECT_PARAM: WrapParam<NoDerive> = 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"); }
}
}

View File

@ -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

View File

@ -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"); }
}
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/62411>

View File

@ -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<NoDerive> = & &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"); }
}
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/62411>

View File

@ -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"); }
}
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/62411>

View File

@ -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>(T);
const WRAP_INDIRECT_PARAM: & &WrapParam<NoDerive> = & &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"); }
}
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/62411>

View File

@ -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)]`
_ => (),
}
}

View File

@ -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

View File

@ -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
_ => { }
}
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/62411>

View File

@ -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 => { }
}
}

View File

@ -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)]`
}
}

View File

@ -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