"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! {
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;
}
}
- /// Returns this pointer's offset from the allocation base, or from NULL (for
- /// integer pointers).
- #[inline]
- pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size {
- match self {
- Scalar::Raw { data, size } => {
- assert_eq!(size as u64, cx.pointer_size().bytes());
- Size::from_bytes(data as u64)
- }
- Scalar::Ptr(ptr) => ptr.offset,
- }
- }
-
#[inline]
pub fn from_bool(b: bool) -> Self {
Scalar::Raw { data: b as u128, size: 1 }
Scalar::Raw { data: f.to_bits(), size: 8 }
}
+ /// This is very rarely the method you want! You should dispatch on the type
+ /// and use `force_bits`/`assert_bits`/`force_ptr`/`assert_ptr`.
+ /// This method only exists for the benefit of low-level memory operations
+ /// as well as the implementation of the `force_*` methods.
#[inline]
pub fn to_bits_or_ptr(
self,
}
}
+ /// Do not call this method! Use either `assert_bits` or `force_bits`.
#[inline]
pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
match self {
}
}
+ #[inline(always)]
+ pub fn assert_bits(self, target_size: Size) -> u128 {
+ self.to_bits(target_size).expect("Expected Raw bits but got a Pointer")
+ }
+
+ /// Do not call this method! Use either `assert_ptr` or `force_ptr`.
#[inline]
pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
match self {
}
}
+ #[inline(always)]
+ pub fn assert_ptr(self) -> Pointer<Tag> {
+ self.to_ptr().expect("Expected a Pointer but got Raw bits")
+ }
+
+ /// Do not call this method! Dispatch based on the type instead.
#[inline]
pub fn is_bits(self) -> bool {
match self {
}
}
+ /// Do not call this method! Dispatch based on the type instead.
#[inline]
pub fn is_ptr(self) -> bool {
match self {
}
}
+ /// Do not call this method! Use either `assert_ptr` or `force_ptr`.
#[inline(always)]
pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
self.not_undef()?.to_ptr()
}
+ /// Do not call this method! Use either `assert_bits` or `force_bits`.
#[inline(always)]
pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
self.not_undef()?.to_bits(target_size)
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,
}
]);
// `Immediate` is when we are called from `const_field`, and that `Immediate`
// comes from a constant so it can happen have `Undef`, because the indirect
// memory that was read had undefined bytes.
- let mplace = op.to_mem_place();
+ let mplace = op.assert_mem_place();
let ptr = mplace.ptr.to_ptr().unwrap();
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
ConstValue::ByRef { offset: ptr.offset, align: mplace.align, alloc }
|body| eval_body_using_ecx(&mut ecx, cid, body, key.param_env)
).and_then(|place| {
Ok(RawConst {
- alloc_id: place.to_ptr().expect("we allocated this ptr!").alloc_id,
+ alloc_id: place.ptr.assert_ptr().alloc_id,
ty: place.layout.ty
})
}).map_err(|error| {
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);
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![
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};
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;
pub tables: &'a ty::TypeckTables<'tcx>,
pub substs: SubstsRef<'tcx>,
pub errors: Vec<PatternError>,
+ include_lint_checks: bool,
}
impl<'a, 'tcx> Pattern<'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:
/// 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>,
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 {
}
}).collect::<Vec<_>>()
};
- debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
+
+
let kind = match cv.ty.sty {
ty::Float(_) => {
self.tcx.lint_hir(
}
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!(
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
path,
path,
);
+ *saw_const_match_error = true;
self.tcx.sess.span_err(span, &msg);
PatternKind::Wild
}
}
}
+/// 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
None => Size::from_bytes(self.get(ptr.alloc_id)?.bytes.len() as u64),
};
self.copy(
- ptr.into(),
- Align::from_bytes(1).unwrap(), // old_align anyway gets checked below by `deallocate`
- new_ptr.into(),
- new_align,
+ ptr,
+ new_ptr,
old_size.min(new_size),
/*nonoverlapping*/ true,
)?;
/// `Pointer` they need. And even if you already have a `Pointer`, call this method
/// to make sure it is sufficiently aligned and not dangling. Not doing that may
/// cause ICEs.
+ ///
+ /// Most of the time you should use `check_mplace_access`, but when you just have a pointer,
+ /// this method is still appropriate.
pub fn check_ptr_access(
&self,
sptr: Scalar<M::PointerTag>,
self.get(ptr.alloc_id)?.read_c_str(self, ptr)
}
- /// Performs appropriate bounds checks.
+ /// Expects the caller to have checked bounds and alignment.
pub fn copy(
&mut self,
- src: Scalar<M::PointerTag>,
- src_align: Align,
- dest: Scalar<M::PointerTag>,
- dest_align: Align,
+ src: Pointer<M::PointerTag>,
+ dest: Pointer<M::PointerTag>,
size: Size,
nonoverlapping: bool,
) -> InterpResult<'tcx> {
- self.copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
+ self.copy_repeatedly(src, dest, size, 1, nonoverlapping)
}
- /// Performs appropriate bounds checks.
+ /// Expects the caller to have checked bounds and alignment.
pub fn copy_repeatedly(
&mut self,
- src: Scalar<M::PointerTag>,
- src_align: Align,
- dest: Scalar<M::PointerTag>,
- dest_align: Align,
+ src: Pointer<M::PointerTag>,
+ dest: Pointer<M::PointerTag>,
size: Size,
length: u64,
nonoverlapping: bool,
) -> InterpResult<'tcx> {
- // We need to check *both* before early-aborting due to the size being 0.
- let (src, dest) = match (self.check_ptr_access(src, size, src_align)?,
- self.check_ptr_access(dest, size * length, dest_align)?)
- {
- (Some(src), Some(dest)) => (src, dest),
- // One of the two sizes is 0.
- _ => return Ok(()),
- };
-
// first copy the relocations to a temporary buffer, because
// `get_bytes_mut` will clear the relocations, which is correct,
// since we don't want to keep any relocations at the target.
impl<Tag> Operand<Tag> {
#[inline]
- pub fn to_mem_place(self) -> MemPlace<Tag>
+ pub fn assert_mem_place(self) -> MemPlace<Tag>
where Tag: ::std::fmt::Debug
{
match self {
Operand::Indirect(mplace) => mplace,
- _ => bug!("to_mem_place: expected Operand::Indirect, got {:?}", self),
+ _ => bug!("assert_mem_place: expected Operand::Indirect, got {:?}", self),
}
}
#[inline]
- pub fn to_immediate(self) -> Immediate<Tag>
+ pub fn assert_immediate(self) -> Immediate<Tag>
where Tag: ::std::fmt::Debug
{
match self {
Operand::Immediate(imm) => imm,
- _ => bug!("to_immediate: expected Operand::Immediate, got {:?}", self),
+ _ => bug!("assert_immediate: expected Operand::Immediate, got {:?}", self),
}
}
}
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
+ /// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST.
+ /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
+ #[inline]
+ pub fn force_op_ptr(
+ &self,
+ op: OpTy<'tcx, M::PointerTag>,
+ ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+ match op.try_as_mplace() {
+ Ok(mplace) => Ok(self.force_mplace_ptr(mplace)?.into()),
+ Err(imm) => Ok(imm.into()), // Nothing to cast/force
+ }
+ }
+
/// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
/// Returns `None` if the layout does not permit loading this as a value.
fn try_read_immediate_from_mplace(
// Don't touch unsized
return Ok(None);
}
- let (ptr, ptr_align) = mplace.to_scalar_ptr_align();
- let ptr = match self.memory.check_ptr_access(ptr, mplace.layout.size, ptr_align)? {
+ let ptr = match self.check_mplace_access(mplace, None)? {
Some(ptr) => ptr,
None => return Ok(Some(ImmTy { // zero-sized type
imm: Immediate::Scalar(Scalar::zst().into()),
} else {
// The rest should only occur as mplace, we do not use Immediates for types
// allowing such operations. This matches place_projection forcing an allocation.
- let mplace = base.to_mem_place();
+ let mplace = base.assert_mem_place();
self.mplace_projection(mplace, proj_elem)?.into()
}
})
Self::from_scalar_ptr(ptr.into(), align)
}
- #[inline(always)]
- pub fn to_scalar_ptr_align(self) -> (Scalar<Tag>, Align) {
- assert!(self.meta.is_none());
- (self.ptr, self.align)
- }
-
- /// metact the ptr part of the mplace
- #[inline(always)]
- pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
- // At this point, we forget about the alignment information --
- // the place has been turned into a reference, and no matter where it came from,
- // it now must be aligned.
- self.to_scalar_ptr_align().0.to_ptr()
- }
-
/// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
/// This is the inverse of `ref_to_mplace`.
#[inline(always)]
}
}
+// These are defined here because they produce a place.
impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> {
#[inline(always)]
pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
}
#[inline(always)]
- pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
+ pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
self.try_as_mplace().unwrap()
}
}
-impl<'tcx, Tag: ::std::fmt::Debug> Place<Tag> {
+impl<Tag: ::std::fmt::Debug> Place<Tag> {
/// Produces a Place that will error if attempted to be read from or written to
#[inline(always)]
pub fn null(cx: &impl HasDataLayout) -> Self {
}
#[inline]
- pub fn to_mem_place(self) -> MemPlace<Tag> {
+ pub fn assert_mem_place(self) -> MemPlace<Tag> {
match self {
Place::Ptr(mplace) => mplace,
- _ => bug!("to_mem_place: expected Place::Ptr, got {:?}", self),
+ _ => bug!("assert_mem_place: expected Place::Ptr, got {:?}", self),
}
}
-
- #[inline]
- pub fn to_scalar_ptr_align(self) -> (Scalar<Tag>, Align) {
- self.to_mem_place().to_scalar_ptr_align()
- }
-
- #[inline]
- pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
- self.to_mem_place().to_ptr()
- }
}
impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> {
#[inline]
- pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> {
- MPlaceTy { mplace: self.place.to_mem_place(), layout: self.layout }
+ pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
+ MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout }
}
}
{
/// Take a value, which represents a (thin or fat) reference, and make it a place.
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
- /// This does NOT call the "deref" machine hook, so it does NOT count as a
- /// deref as far as Stacked Borrows is concerned. Use `deref_operand` for that!
pub fn ref_to_mplace(
&self,
val: ImmTy<'tcx, M::PointerTag>,
Ok(MPlaceTy { mplace, layout })
}
- // Take an operand, representing a pointer, and dereference it to a place -- that
- // will always be a MemPlace. Lives in `place.rs` because it creates a place.
+ /// Take an operand, representing a pointer, and dereference it to a place -- that
+ /// will always be a MemPlace. Lives in `place.rs` because it creates a place.
pub fn deref_operand(
&self,
src: OpTy<'tcx, M::PointerTag>,
self.ref_to_mplace(val)
}
+ /// Check if the given place is good for memory access with the given
+ /// size, falling back to the layout's size if `None` (in the latter case,
+ /// this must be a statically sized type).
+ ///
+ /// On success, returns `None` for zero-sized accesses (where nothing else is
+ /// left to do) and a `Pointer` to use for the actual access otherwise.
+ #[inline]
+ pub fn check_mplace_access(
+ &self,
+ place: MPlaceTy<'tcx, M::PointerTag>,
+ size: Option<Size>,
+ ) -> InterpResult<'tcx, Option<Pointer<M::PointerTag>>> {
+ let size = size.unwrap_or_else(|| {
+ assert!(!place.layout.is_unsized());
+ assert!(place.meta.is_none());
+ place.layout.size
+ });
+ self.memory.check_ptr_access(place.ptr, size, place.align)
+ }
+
+ /// Force `place.ptr` to a `Pointer`.
+ /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
+ pub fn force_mplace_ptr(
+ &self,
+ mut place: MPlaceTy<'tcx, M::PointerTag>,
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ place.mplace.ptr = self.force_ptr(place.mplace.ptr)?.into();
+ Ok(place)
+ }
+
/// Offset a pointer to project to a field. Unlike `place_field`, this is always
/// possible without allocating, so it can take `&self`. Also return the field's layout.
/// This supports both struct and array fields.
value: Immediate<M::PointerTag>,
dest: MPlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> {
- let (ptr, ptr_align) = dest.to_scalar_ptr_align();
// Note that it is really important that the type here is the right one, and matches the
// type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here
// to handle padding properly, which is only correct if we never look at this data with the
// wrong type.
- assert!(!dest.layout.is_unsized());
- let ptr = match self.memory.check_ptr_access(ptr, dest.layout.size, ptr_align)? {
+ let ptr = match self.check_mplace_access(dest, None)? {
Some(ptr) => ptr,
None => return Ok(()), // zero-sized access
};
dest.layout.size
});
assert_eq!(src.meta, dest.meta, "Can only copy between equally-sized instances");
+
+ let src = self.check_mplace_access(src, Some(size))?;
+ let dest = self.check_mplace_access(dest, Some(size))?;
+ let (src_ptr, dest_ptr) = match (src, dest) {
+ (Some(src_ptr), Some(dest_ptr)) => (src_ptr, dest_ptr),
+ (None, None) => return Ok(()), // zero-sized copy
+ _ => bug!("The pointers should both be Some or both None"),
+ };
+
self.memory.copy(
- src.ptr, src.align,
- dest.ptr, dest.align,
+ src_ptr,
+ dest_ptr,
size,
/*nonoverlapping*/ true,
- )?;
-
- Ok(())
+ )
}
/// Copies the data from an operand to a place. The layouts may disagree, but they must
let dest = self.force_allocation(dest)?;
let length = dest.len(self)?;
- if length > 0 {
- // write the first
+ if let Some(first_ptr) = self.check_mplace_access(dest, None)? {
+ // Write the first.
let first = self.mplace_field(dest, 0)?;
self.copy_op(op, first.into())?;
if length > 1 {
- // copy the rest
- let (dest, dest_align) = first.to_scalar_ptr_align();
- let rest = dest.ptr_offset(first.layout.size, self)?;
+ let elem_size = first.layout.size;
+ // Copy the rest. This is performance-sensitive code
+ // for big static/const arrays!
+ let rest_ptr = first_ptr.offset(elem_size, self)?;
self.memory.copy_repeatedly(
- dest, dest_align, rest, dest_align, first.layout.size, length - 1, true
+ first_ptr, rest_ptr, elem_size, length - 1, /*nonoverlapping:*/true
)?;
}
}
}
None => {
// Unsized self.
- args[0].to_mem_place()
+ args[0].assert_mem_place()
}
};
// Find and consult vtable
}
}
}
- // Check if we have encountered this pointer+layout combination
- // before. Proceed recursively even for ZST, no
- // reason to skip them! E.g., `!` is a ZST and we want to validate it.
+ // Proceed recursively even for ZST, no reason to skip them!
+ // `!` is a ZST and we want to validate it.
+ // Normalize before handing `place` to tracking because that will
+ // check for duplicates.
+ let place = if size.bytes() > 0 {
+ self.ecx.force_mplace_ptr(place)
+ .expect("we already bounds-checked")
+ } else {
+ place
+ };
let path = &self.path;
ref_tracking.track(place, || {
// We need to clone the path anyway, make sure it gets created
) -> InterpResult<'tcx> {
match op.layout.ty.sty {
ty::Str => {
- let mplace = op.to_mem_place(); // strings are never immediate
+ let mplace = op.assert_mem_place(); // strings are never immediate
try_validation!(self.ecx.read_str(mplace),
"uninitialized or non-UTF-8 data in str", self.path);
}
return Ok(());
}
// non-ZST array cannot be immediate, slices are never immediate
- let mplace = op.to_mem_place();
+ let mplace = op.assert_mem_place();
// This is the length of the array/slice.
let len = mplace.len(self.ecx)?;
// zero length slices have nothing to be checked
let ty_size = self.ecx.layout_of(tys)?.size;
// This is the size in bytes of the whole array.
let size = ty_size * len;
-
+ // Size is not 0, get a pointer.
let ptr = self.ecx.force_ptr(mplace.ptr)?;
// NOTE: Keep this in sync with the handling of integer and float
/// `ref_tracking_for_consts` can be `None` to avoid recursive checking below references.
/// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
/// validation (e.g., pointer values are fine in integers at runtime) and various other const
- /// specific validation checks
+ /// specific validation checks.
pub fn validate_operand(
&self,
op: OpTy<'tcx, M::PointerTag>,
ecx: self,
};
+ // Try to cast to ptr *once* instead of all the time.
+ let op = self.force_op_ptr(op).unwrap_or(op);
+
// Run it
visitor.visit_value(op)
}
match v.layout().ty.sty {
ty::Dynamic(..) => {
// immediate trait objects are not a thing
- let dest = v.to_op(self.ecx())?.to_mem_place();
+ let dest = v.to_op(self.ecx())?.assert_mem_place();
let inner = self.ecx().unpack_dyn_trait(dest)?.1;
trace!("walk_value: dyn object layout: {:#?}", inner.layout);
// recurse with the inner type
MPlaceTy::dangling(v.layout(), self.ecx())
} else {
// non-ZST array/slice/str cannot be immediate
- v.to_op(self.ecx())?.to_mem_place()
+ v.to_op(self.ecx())?.assert_mem_place()
};
// Now we can go over all the fields.
let iter = self.ecx().mplace_array_fields(mplace)?
//~^ 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
_ => { }
}
}
+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
|
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+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>
+
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+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>
+
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+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>
+
--- /dev/null
+// 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"); }
+ }
+}
--- /dev/null
+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>
+
--- /dev/null
+// 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)]`
+ _ => (),
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+// 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
+ _ => { }
+ }
+}
--- /dev/null
+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>
+
--- /dev/null
+// 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 => { }
+ }
+}
--- /dev/null
+// 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)]`
+ }
+}
--- /dev/null
+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
+