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:
commit
c6a9e766f9
|
@ -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! {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
]);
|
||||
|
||||
|
|
|
@ -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![
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
||||
|
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
@ -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"); }
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
@ -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)]`
|
||||
_ => (),
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
_ => { }
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
@ -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 => { }
|
||||
}
|
||||
}
|
|
@ -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)]`
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue