1
Fork 0

get rid of nontrivial_structural_match lint and custom_eq const qualif

This commit is contained in:
Ralf Jung 2024-01-27 13:47:29 +01:00
parent ea37e8091f
commit 9f58cf43c7
15 changed files with 53 additions and 297 deletions

View file

@ -20,7 +20,7 @@ use std::mem;
use std::ops::{ControlFlow, Deref}; use std::ops::{ControlFlow, Deref};
use super::ops::{self, NonConstOp, Status}; use super::ops::{self, NonConstOp, Status};
use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
use super::resolver::FlowSensitiveAnalysis; use super::resolver::FlowSensitiveAnalysis;
use super::{ConstCx, Qualif}; use super::{ConstCx, Qualif};
use crate::const_eval::is_unstable_const_fn; use crate::const_eval::is_unstable_const_fn;
@ -149,37 +149,10 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
let return_loc = ccx.body.terminator_loc(return_block); let return_loc = ccx.body.terminator_loc(return_block);
let custom_eq = match ccx.const_kind() {
// We don't care whether a `const fn` returns a value that is not structurally
// matchable. Functions calls are opaque and always use type-based qualification, so
// this value should never be used.
hir::ConstContext::ConstFn => true,
// If we know that all values of the return type are structurally matchable, there's no
// need to run dataflow.
// Opaque types do not participate in const generics or pattern matching, so we can safely count them out.
_ if ccx.body.return_ty().has_opaque_types()
|| !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) =>
{
false
}
hir::ConstContext::Const { .. } | hir::ConstContext::Static(_) => {
let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx)
.into_engine(ccx.tcx, ccx.body)
.iterate_to_fixpoint()
.into_results_cursor(ccx.body);
cursor.seek_after_primary_effect(return_loc);
cursor.get().contains(RETURN_PLACE)
}
};
ConstQualifs { ConstQualifs {
needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc), needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc),
needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc), needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc),
has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc), has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
custom_eq,
tainted_by_errors, tainted_by_errors,
} }
} }

View file

@ -10,7 +10,7 @@ use rustc_middle::mir::*;
use rustc_middle::traits::BuiltinImplSource; use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty};
use rustc_trait_selection::traits::{ use rustc_trait_selection::traits::{
self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext,
}; };
use super::ConstCx; use super::ConstCx;
@ -24,7 +24,6 @@ pub fn in_any_value_of_ty<'tcx>(
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty), needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
tainted_by_errors, tainted_by_errors,
} }
} }
@ -213,35 +212,6 @@ impl Qualif for NeedsNonConstDrop {
} }
} }
/// A constant that cannot be used as part of a pattern in a `match` expression.
pub struct CustomEq;
impl Qualif for CustomEq {
const ANALYSIS_NAME: &'static str = "flow_custom_eq";
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
qualifs.custom_eq
}
fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
// If *any* component of a composite data type does not implement `Structural{Partial,}Eq`,
// we know that at least some values of that type are not structural-match. I say "some"
// because that component may be part of an enum variant (e.g.,
// `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
// structural-match (`Option::None`).
traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some()
}
fn in_adt_inherently<'tcx>(
cx: &ConstCx<'_, 'tcx>,
def: AdtDef<'tcx>,
args: GenericArgsRef<'tcx>,
) -> bool {
let ty = Ty::new_adt(cx.tcx, def, args);
!ty.is_structural_eq_shallow(cx.tcx)
}
}
// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return. // FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return.
/// Returns `true` if this `Rvalue` contains qualif `Q`. /// Returns `true` if this `Rvalue` contains qualif `Q`.

View file

@ -520,6 +520,11 @@ fn register_builtins(store: &mut LintStore) {
"illegal_floating_point_literal_pattern", "illegal_floating_point_literal_pattern",
"no longer a warning, float patterns behave the same as `==`", "no longer a warning, float patterns behave the same as `==`",
); );
store.register_removed(
"nontrivial_structural_match",
"no longer needed, see RFC #3535 \
<https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
);
} }
fn register_internals(store: &mut LintStore) { fn register_internals(store: &mut LintStore) {

View file

@ -3,6 +3,9 @@
//! These are the built-in lints that are emitted direct in the main //! These are the built-in lints that are emitted direct in the main
//! compiler code, rather than using their own custom pass. Those //! compiler code, rather than using their own custom pass. Those
//! lints are all available in `rustc_lint::builtin`. //! lints are all available in `rustc_lint::builtin`.
//!
//! When removing a lint, make sure to also add a call to `register_removed` in
//! compiler/rustc_lint/src/lib.rs.
use crate::{declare_lint, declare_lint_pass, FutureIncompatibilityReason}; use crate::{declare_lint, declare_lint_pass, FutureIncompatibilityReason};
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
@ -66,7 +69,6 @@ declare_lint_pass! {
MUST_NOT_SUSPEND, MUST_NOT_SUSPEND,
NAMED_ARGUMENTS_USED_POSITIONALLY, NAMED_ARGUMENTS_USED_POSITIONALLY,
NON_EXHAUSTIVE_OMITTED_PATTERNS, NON_EXHAUSTIVE_OMITTED_PATTERNS,
NONTRIVIAL_STRUCTURAL_MATCH,
ORDER_DEPENDENT_TRAIT_OBJECTS, ORDER_DEPENDENT_TRAIT_OBJECTS,
OVERLAPPING_RANGE_ENDPOINTS, OVERLAPPING_RANGE_ENDPOINTS,
PATTERNS_IN_FNS_WITHOUT_BODY, PATTERNS_IN_FNS_WITHOUT_BODY,
@ -2341,45 +2343,6 @@ declare_lint! {
}; };
} }
declare_lint! {
/// The `nontrivial_structural_match` lint detects constants that are used in patterns,
/// whose type is not structural-match and whose initializer body actually uses values
/// that are not structural-match. So `Option<NotStructuralMatch>` is ok if the constant
/// is just `None`.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(nontrivial_structural_match)]
///
/// #[derive(Copy, Clone, Debug)]
/// struct NoDerive(u32);
/// impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } }
/// impl Eq for NoDerive { }
/// fn main() {
/// const INDEX: Option<NoDerive> = [None, Some(NoDerive(10))][0];
/// match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), };
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Previous versions of Rust accepted constants in patterns, even if those constants' types
/// did not have `PartialEq` derived. Thus the compiler falls back to runtime execution of
/// `PartialEq`, which can report that two constants are not equal even if they are
/// bit-equivalent.
pub NONTRIVIAL_STRUCTURAL_MATCH,
Warn,
"constant used in pattern of non-structural-match type and the constant's initializer \
expression contains values of non-structural-match types",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
reference: "issue #73448 <https://github.com/rust-lang/rust/issues/73448>",
};
}
declare_lint! { declare_lint! {
/// The `const_patterns_without_partial_eq` lint detects constants that are used in patterns, /// The `const_patterns_without_partial_eq` lint detects constants that are used in patterns,
/// whose type does not implement `PartialEq`. /// whose type does not implement `PartialEq`.

View file

@ -189,7 +189,7 @@ pub struct BorrowCheckResult<'tcx> {
/// The result of the `mir_const_qualif` query. /// The result of the `mir_const_qualif` query.
/// ///
/// Each field (except `error_occurred`) corresponds to an implementer of the `Qualif` trait in /// Each field (except `tainted_by_errors`) corresponds to an implementer of the `Qualif` trait in
/// `rustc_const_eval/src/transform/check_consts/qualifs.rs`. See that file for more information on each /// `rustc_const_eval/src/transform/check_consts/qualifs.rs`. See that file for more information on each
/// `Qualif`. /// `Qualif`.
#[derive(Clone, Copy, Debug, Default, TyEncodable, TyDecodable, HashStable)] #[derive(Clone, Copy, Debug, Default, TyEncodable, TyDecodable, HashStable)]
@ -197,7 +197,6 @@ pub struct ConstQualifs {
pub has_mut_interior: bool, pub has_mut_interior: bool,
pub needs_drop: bool, pub needs_drop: bool,
pub needs_non_const_drop: bool, pub needs_non_const_drop: bool,
pub custom_eq: bool,
pub tainted_by_errors: Option<ErrorGuaranteed>, pub tainted_by_errors: Option<ErrorGuaranteed>,
} }

View file

@ -1,6 +1,5 @@
use rustc_apfloat::Float; use rustc_apfloat::Float;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_index::Idx; use rustc_index::Idx;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::Obligation; use rustc_infer::traits::Obligation;
@ -17,8 +16,8 @@ use std::cell::Cell;
use super::PatCtxt; use super::PatCtxt;
use crate::errors::{ use crate::errors::{
IndirectStructuralMatch, InvalidPattern, NaNPattern, NonPartialEqMatch, IndirectStructuralMatch, InvalidPattern, NaNPattern, NonPartialEqMatch, PointerPattern,
NontrivialStructuralMatch, PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
}; };
impl<'a, 'tcx> PatCtxt<'a, 'tcx> { impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
@ -33,11 +32,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
cv: mir::Const<'tcx>, cv: mir::Const<'tcx>,
id: hir::HirId, id: hir::HirId,
span: Span, span: Span,
check_body_for_struct_match_violation: Option<DefId>,
) -> Box<Pat<'tcx>> { ) -> Box<Pat<'tcx>> {
let infcx = self.tcx.infer_ctxt().build(); let infcx = self.tcx.infer_ctxt().build();
let mut convert = ConstToPat::new(self, id, span, infcx); let mut convert = ConstToPat::new(self, id, span, infcx);
convert.to_pat(cv, check_body_for_struct_match_violation) convert.to_pat(cv)
} }
} }
@ -103,11 +101,7 @@ impl<'tcx> ConstToPat<'tcx> {
ty.is_structural_eq_shallow(self.infcx.tcx) ty.is_structural_eq_shallow(self.infcx.tcx)
} }
fn to_pat( fn to_pat(&mut self, cv: mir::Const<'tcx>) -> Box<Pat<'tcx>> {
&mut self,
cv: mir::Const<'tcx>,
check_body_for_struct_match_violation: Option<DefId>,
) -> Box<Pat<'tcx>> {
trace!(self.treat_byte_string_as_slice); trace!(self.treat_byte_string_as_slice);
// This method is just a wrapper handling a validity check; the heavy lifting is // This method is just a wrapper handling a validity check; the heavy lifting is
// performed by the recursive `recur` method, which is not meant to be // performed by the recursive `recur` method, which is not meant to be
@ -116,14 +110,6 @@ impl<'tcx> ConstToPat<'tcx> {
// once indirect_structural_match is a full fledged error, this // once indirect_structural_match is a full fledged error, this
// level of indirection can be eliminated // level of indirection can be eliminated
let mir_structural_match_violation = check_body_for_struct_match_violation.map(|def_id| {
// `mir_const_qualif` must be called with the `DefId` of the item where the const is
// defined, not where it is declared. The difference is significant for associated
// constants.
self.tcx().mir_const_qualif(def_id).custom_eq
});
debug!(?check_body_for_struct_match_violation, ?mir_structural_match_violation);
let have_valtree = let have_valtree =
matches!(cv, mir::Const::Ty(c) if matches!(c.kind(), ty::ConstKind::Value(_))); matches!(cv, mir::Const::Ty(c) if matches!(c.kind(), ty::ConstKind::Value(_)));
let inlined_const_as_pat = match cv { let inlined_const_as_pat = match cv {
@ -137,15 +123,15 @@ impl<'tcx> ConstToPat<'tcx> {
| ty::ConstKind::Expr(_) => { | ty::ConstKind::Expr(_) => {
span_bug!(self.span, "unexpected const in `to_pat`: {:?}", c.kind()) span_bug!(self.span, "unexpected const in `to_pat`: {:?}", c.kind())
} }
ty::ConstKind::Value(valtree) => self ty::ConstKind::Value(valtree) => {
.recur(valtree, cv.ty(), mir_structural_match_violation.unwrap_or(false)) self.recur(valtree, cv.ty()).unwrap_or_else(|_: FallbackToOpaqueConst| {
.unwrap_or_else(|_: FallbackToOpaqueConst| {
Box::new(Pat { Box::new(Pat {
span: self.span, span: self.span,
ty: cv.ty(), ty: cv.ty(),
kind: PatKind::Constant { value: cv }, kind: PatKind::Constant { value: cv },
}) })
}), })
}
}, },
mir::Const::Unevaluated(_, _) => { mir::Const::Unevaluated(_, _) => {
span_bug!(self.span, "unevaluated const in `to_pat`: {cv:?}") span_bug!(self.span, "unevaluated const in `to_pat`: {cv:?}")
@ -160,7 +146,12 @@ impl<'tcx> ConstToPat<'tcx> {
if self.saw_const_match_error.get().is_none() { if self.saw_const_match_error.get().is_none() {
// If we were able to successfully convert the const to some pat (possibly with some // If we were able to successfully convert the const to some pat (possibly with some
// lints, but no errors), double-check that all types in the const implement // lints, but no errors), double-check that all types in the const implement
// `Structural` and `PartialEq`. // `PartialEq`. Even if we have a valtree, we may have found something
// in there with non-structural-equality, meaning we match using `PartialEq`
// and we hence have to check that that impl exists.
// This is all messy but not worth cleaning up: at some point we'll emit
// a hard error when we don't have a valtree or when we find something in
// the valtree that is not structural; then this can all be made a lot simpler.
let structural = let structural =
traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty()); traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
@ -170,19 +161,12 @@ impl<'tcx> ConstToPat<'tcx> {
structural structural
); );
// This can occur because const qualification treats all associated constants as
// opaque, whereas `search_for_structural_match_violation` tries to monomorphize them
// before it runs.
//
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation`.
if structural.is_none() && mir_structural_match_violation.unwrap_or(false) {
warn!("MIR const-checker found novel structural match violation. See #73448.");
return inlined_const_as_pat;
}
if let Some(non_sm_ty) = structural { if let Some(non_sm_ty) = structural {
if !self.type_has_partial_eq_impl(cv.ty()) { if !self.type_has_partial_eq_impl(cv.ty()) {
// This is reachable and important even if we have a valtree: there might be
// non-structural things in a valtree, in which case we fall back to `PartialEq`
// comparison, in which case we better make sure the trait is implemented for
// each inner type (and not just for the surrounding type).
let e = if let ty::Adt(def, ..) = non_sm_ty.kind() { let e = if let ty::Adt(def, ..) = non_sm_ty.kind() {
if def.is_union() { if def.is_union() {
let err = UnionPattern { span: self.span }; let err = UnionPattern { span: self.span };
@ -201,35 +185,18 @@ impl<'tcx> ConstToPat<'tcx> {
// We errored. Signal that in the pattern, so that follow up errors can be silenced. // We errored. Signal that in the pattern, so that follow up errors can be silenced.
let kind = PatKind::Error(e); let kind = PatKind::Error(e);
return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
} else if let ty::Adt(..) = cv.ty().kind() } else if !have_valtree {
&& matches!(cv, mir::Const::Val(..)) // Not being structural prevented us from constructing a valtree,
{ // so this is definitely a case we want to reject.
// This branch is only entered when the current `cv` is `mir::Const::Val`.
// This is because `mir::Const::ty` has already been handled by `Self::recur`
// and the invalid types may be ignored.
let err = TypeNotStructural { span: self.span, non_sm_ty }; let err = TypeNotStructural { span: self.span, non_sm_ty };
let e = self.tcx().dcx().emit_err(err); let e = self.tcx().dcx().emit_err(err);
let kind = PatKind::Error(e); let kind = PatKind::Error(e);
return Box::new(Pat { span: self.span, ty: cv.ty(), kind }); return Box::new(Pat { span: self.span, ty: cv.ty(), kind });
} else if !self.saw_const_match_lint.get() { } else {
if let Some(mir_structural_match_violation) = mir_structural_match_violation { // This could be a violation in an inactive enum variant.
match non_sm_ty.kind() { // Since we have a valtree, we trust that we have traversed the full valtree and
ty::Adt(..) if mir_structural_match_violation => { // complained about structural match violations there, so we don't
self.tcx().emit_node_span_lint( // have to check anything any more.
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
IndirectStructuralMatch { non_sm_ty },
);
}
_ => {
debug!(
"`search_for_structural_match_violation` found one, but `CustomEq` was \
not in the qualifs for that `const`"
);
}
}
}
} }
} else if !have_valtree && !self.saw_const_match_lint.get() { } else if !have_valtree && !self.saw_const_match_lint.get() {
// The only way valtree construction can fail without the structural match // The only way valtree construction can fail without the structural match
@ -299,7 +266,7 @@ impl<'tcx> ConstToPat<'tcx> {
let field = FieldIdx::new(idx); let field = FieldIdx::new(idx);
// Patterns can only use monomorphic types. // Patterns can only use monomorphic types.
let ty = self.tcx().normalize_erasing_regions(self.param_env, ty); let ty = self.tcx().normalize_erasing_regions(self.param_env, ty);
Ok(FieldPat { field, pattern: self.recur(val, ty, false)? }) Ok(FieldPat { field, pattern: self.recur(val, ty)? })
}) })
.collect() .collect()
} }
@ -310,7 +277,6 @@ impl<'tcx> ConstToPat<'tcx> {
&self, &self,
cv: ValTree<'tcx>, cv: ValTree<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
mir_structural_match_violation: bool,
) -> Result<Box<Pat<'tcx>>, FallbackToOpaqueConst> { ) -> Result<Box<Pat<'tcx>>, FallbackToOpaqueConst> {
let id = self.id; let id = self.id;
let span = self.span; let span = self.span;
@ -395,7 +361,7 @@ impl<'tcx> ConstToPat<'tcx> {
prefix: cv prefix: cv
.unwrap_branch() .unwrap_branch()
.iter() .iter()
.map(|val| self.recur(*val, *elem_ty, false)) .map(|val| self.recur(*val, *elem_ty))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
slice: None, slice: None,
suffix: Box::new([]), suffix: Box::new([]),
@ -404,7 +370,7 @@ impl<'tcx> ConstToPat<'tcx> {
prefix: cv prefix: cv
.unwrap_branch() .unwrap_branch()
.iter() .iter()
.map(|val| self.recur(*val, *elem_ty, false)) .map(|val| self.recur(*val, *elem_ty))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
slice: None, slice: None,
suffix: Box::new([]), suffix: Box::new([]),
@ -471,7 +437,7 @@ impl<'tcx> ConstToPat<'tcx> {
_ => *pointee_ty, _ => *pointee_ty,
}; };
// References have the same valtree representation as their pointee. // References have the same valtree representation as their pointee.
let subpattern = self.recur(cv, pointee_ty, false)?; let subpattern = self.recur(cv, pointee_ty)?;
self.behind_reference.set(old); self.behind_reference.set(old);
PatKind::Deref { subpattern } PatKind::Deref { subpattern }
} }
@ -512,25 +478,6 @@ impl<'tcx> ConstToPat<'tcx> {
} }
}; };
if self.saw_const_match_error.get().is_none()
&& !self.saw_const_match_lint.get()
&& mir_structural_match_violation
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation` and then remove this condition.
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
&& let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, ty)
{
self.saw_const_match_lint.set(true);
tcx.emit_node_span_lint(
lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
id,
span,
NontrivialStructuralMatch { non_sm_ty },
);
}
Ok(Box::new(Pat { span, ty, kind })) Ok(Box::new(Pat { span, ty, kind }))
} }
} }

View file

@ -542,7 +542,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
match const_value { match const_value {
Ok(const_) => { Ok(const_) => {
let pattern = self.const_to_pat(const_, id, span, Some(instance.def_id())); let pattern = self.const_to_pat(const_, id, span);
if !is_associated_const { if !is_associated_const {
return pattern; return pattern;
@ -612,7 +612,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}; };
if let Some(lit_input) = lit_input { if let Some(lit_input) = lit_input {
match tcx.at(expr.span).lit_to_const(lit_input) { match tcx.at(expr.span).lit_to_const(lit_input) {
Ok(c) => return self.const_to_pat(Const::Ty(c), id, span, None).kind, Ok(c) => return self.const_to_pat(Const::Ty(c), id, span).kind,
// If an error occurred, ignore that it's a literal // If an error occurred, ignore that it's a literal
// and leave reporting the error up to const eval of // and leave reporting the error up to const eval of
// the unevaluated constant below. // the unevaluated constant below.
@ -635,17 +635,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
if let Ok(Some(valtree)) = if let Ok(Some(valtree)) =
self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span)) self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span))
{ {
let subpattern = self.const_to_pat( let subpattern =
Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), self.const_to_pat(Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), id, span);
id,
span,
None,
);
PatKind::InlineConstant { subpattern, def: def_id } PatKind::InlineConstant { subpattern, def: def_id }
} else { } else {
// If that fails, convert it to an opaque constant pattern. // If that fails, convert it to an opaque constant pattern.
match tcx.const_eval_resolve(self.param_env, uneval, Some(span)) { match tcx.const_eval_resolve(self.param_env, uneval, Some(span)) {
Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span, None).kind, Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span).kind,
Err(ErrorHandled::TooGeneric(_)) => { Err(ErrorHandled::TooGeneric(_)) => {
// If we land here it means the const can't be evaluated because it's `TooGeneric`. // If we land here it means the const can't be evaluated because it's `TooGeneric`.
let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span }); let e = self.tcx.dcx().emit_err(ConstPatternDependsOnGenericParameter { span });
@ -681,9 +677,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
let lit_input = let lit_input =
LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg }; LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg };
match self.tcx.at(expr.span).lit_to_const(lit_input) { match self.tcx.at(expr.span).lit_to_const(lit_input) {
Ok(constant) => { Ok(constant) => self.const_to_pat(Const::Ty(constant), expr.hir_id, lit.span).kind,
self.const_to_pat(Const::Ty(constant), expr.hir_id, lit.span, None).kind
}
Err(LitToConstError::Reported(e)) => PatKind::Error(e), Err(LitToConstError::Reported(e)) => PatKind::Error(e),
Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"),
} }

View file

@ -2,9 +2,8 @@
// This test is checking our logic for structural match checking by enumerating // This test is checking our logic for structural match checking by enumerating
// the different kinds of const expressions. This test is collecting cases where // the different kinds of const expressions. This test is collecting cases where
// we have accepted the const expression as a pattern in the past but we want // we have accepted the const expression as a pattern in the past and wish to
// to begin warning the user that a future version of Rust may start rejecting // continue doing so.
// such const expressions.
// The specific corner cases we are exploring here are instances where the // The specific corner cases we are exploring here are instances where the
// const-evaluator computes a value that *does* meet the conditions for // const-evaluator computes a value that *does* meet the conditions for
@ -24,18 +23,12 @@ impl Eq for NoDerive { }
fn main() { fn main() {
const INDEX: Option<NoDerive> = [None, Some(NoDerive(10))][0]; const INDEX: Option<NoDerive> = [None, Some(NoDerive(10))][0];
match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), }; match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), };
//~^ WARN must be annotated with `#[derive(PartialEq)]`
//~| WARN this was previously accepted
const fn build() -> Option<NoDerive> { None } const fn build() -> Option<NoDerive> { None }
const CALL: Option<NoDerive> = build(); const CALL: Option<NoDerive> = build();
match None { Some(_) => panic!("whoops"), CALL => dbg!(CALL), }; match None { Some(_) => panic!("whoops"), CALL => dbg!(CALL), };
//~^ WARN must be annotated with `#[derive(PartialEq)]`
//~| WARN this was previously accepted
impl NoDerive { const fn none() -> Option<NoDerive> { None } } impl NoDerive { const fn none() -> Option<NoDerive> { None } }
const METHOD_CALL: Option<NoDerive> = NoDerive::none(); const METHOD_CALL: Option<NoDerive> = NoDerive::none();
match None { Some(_) => panic!("whoops"), METHOD_CALL => dbg!(METHOD_CALL), }; match None { Some(_) => panic!("whoops"), METHOD_CALL => dbg!(METHOD_CALL), };
//~^ WARN must be annotated with `#[derive(PartialEq)]`
//~| WARN this was previously accepted
} }

View file

@ -12,6 +12,7 @@ impl PartialEq for CustomEq {
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
#[allow(unused)]
enum Foo { enum Foo {
Bar, Bar,
Baz, Baz,
@ -21,7 +22,7 @@ enum Foo {
const BAR_BAZ: Foo = if 42 == 42 { const BAR_BAZ: Foo = if 42 == 42 {
Foo::Bar Foo::Bar
} else { } else {
Foo::Baz Foo::Qux(CustomEq) // dead arm
}; };
fn main() { fn main() {

View file

@ -1,38 +0,0 @@
// check-pass
struct CustomEq;
impl Eq for CustomEq {}
impl PartialEq for CustomEq {
fn eq(&self, _: &Self) -> bool {
false
}
}
#[derive(PartialEq, Eq)]
enum Foo {
Bar,
Baz,
Qux(CustomEq),
}
// We know that `BAR_BAZ` will always be `Foo::Bar` and thus eligible for structural matching, but
// dataflow will be more conservative.
const BAR_BAZ: Foo = if 42 == 42 {
Foo::Bar
} else {
Foo::Qux(CustomEq)
};
fn main() {
match Foo::Qux(CustomEq) {
BAR_BAZ => panic!(),
//~^ WARN must be annotated with `#[derive(PartialEq)]`
//~| NOTE the traits must be derived
//~| NOTE StructuralPartialEq.html for details
//~| WARN this was previously accepted
//~| NOTE see issue #73448
//~| NOTE `#[warn(nontrivial_structural_match)]` on by default
_ => {}
}
}

View file

@ -1,14 +0,0 @@
warning: to use a constant of type `CustomEq` in a pattern, the constant's initializer must be trivial or `CustomEq` must be annotated with `#[derive(PartialEq)]`
--> $DIR/custom-eq-branch-warn.rs:29:9
|
LL | BAR_BAZ => panic!(),
| ^^^^^^^
|
= 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 #73448 <https://github.com/rust-lang/rust/issues/73448>
= note: the traits must be derived, manual `impl`s are not sufficient
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
= note: `#[warn(nontrivial_structural_match)]` on by default
warning: 1 warning emitted

View file

@ -1 +0,0 @@
WARN rustc_mir_build::thir::pattern::const_to_pat MIR const-checker found novel structural match violation. See #73448.

View file

@ -1,36 +0,0 @@
warning: to use a constant of type `NoDerive` in a pattern, the constant's initializer must be trivial or `NoDerive` must be annotated with `#[derive(PartialEq)]`
--> $DIR/warn_corner_cases.rs:26:47
|
LL | match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), };
| ^^^^^
|
= 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 #73448 <https://github.com/rust-lang/rust/issues/73448>
= note: the traits must be derived, manual `impl`s are not sufficient
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
= note: `#[warn(nontrivial_structural_match)]` on by default
warning: to use a constant of type `NoDerive` in a pattern, the constant's initializer must be trivial or `NoDerive` must be annotated with `#[derive(PartialEq)]`
--> $DIR/warn_corner_cases.rs:32:47
|
LL | match None { Some(_) => panic!("whoops"), CALL => dbg!(CALL), };
| ^^^^
|
= 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 #73448 <https://github.com/rust-lang/rust/issues/73448>
= note: the traits must be derived, manual `impl`s are not sufficient
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
warning: to use a constant of type `NoDerive` in a pattern, the constant's initializer must be trivial or `NoDerive` must be annotated with `#[derive(PartialEq)]`
--> $DIR/warn_corner_cases.rs:38:47
|
LL | match None { Some(_) => panic!("whoops"), METHOD_CALL => dbg!(METHOD_CALL), };
| ^^^^^^^^^^^
|
= 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 #73448 <https://github.com/rust-lang/rust/issues/73448>
= note: the traits must be derived, manual `impl`s are not sufficient
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
warning: 3 warnings emitted

View file

@ -10,7 +10,7 @@
// Issue 62307 pointed out a case where the structural-match checking // Issue 62307 pointed out a case where the structural-match checking
// was too shallow. // was too shallow.
#![warn(indirect_structural_match, nontrivial_structural_match)] #![warn(indirect_structural_match)]
// run-pass // run-pass
#[derive(Debug)] #[derive(Debug)]

View file

@ -11,7 +11,7 @@ LL | RR_B1 => { println!("CLAIM RR0: {:?} matches {:?}", RR_B1, RR_B0);
note: the lint level is defined here note: the lint level is defined here
--> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:13:9 --> $DIR/issue-62307-match-ref-ref-forbidden-without-eq.rs:13:9
| |
LL | #![warn(indirect_structural_match, nontrivial_structural_match)] LL | #![warn(indirect_structural_match)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]` warning: to use a constant of type `B` in a pattern, `B` must be annotated with `#[derive(PartialEq)]`