From 0850c3bbb89c08523136a57e89cd3d84f407d2bf Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 16 Mar 2020 10:44:46 -0700 Subject: [PATCH 01/10] Pass `Substs` to `in_adt_inherently` --- .../transform/check_consts/qualifs.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index f82f06599b74a..04bd632553eee 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -3,7 +3,7 @@ //! See the `Qualif` trait for more info. use rustc_middle::mir::*; -use rustc_middle::ty::{self, AdtDef, Ty}; +use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; use rustc_span::DUMMY_SP; use super::ConstCx; @@ -53,7 +53,11 @@ pub trait Qualif { /// with a custom `Drop` impl is inherently `NeedsDrop`. /// /// Returning `true` for `in_adt_inherently` but `false` for `in_any_value_of_ty` is unsound. - fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool; + fn in_adt_inherently( + cx: &ConstCx<'_, 'tcx>, + adt: &'tcx AdtDef, + substs: SubstsRef<'tcx>, + ) -> bool; } /// Constant containing interior mutability (`UnsafeCell`). @@ -74,7 +78,7 @@ impl Qualif for HasMutInterior { !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) } - fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool { + fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { // Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently. // It arises structurally for all other types. Some(adt.did) == cx.tcx.lang_items().unsafe_cell_type() @@ -99,7 +103,7 @@ impl Qualif for NeedsDrop { ty.needs_drop(cx.tcx, cx.param_env) } - fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &AdtDef) -> bool { + fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool { adt.has_dtor(cx.tcx) } } @@ -147,8 +151,8 @@ where Rvalue::Aggregate(kind, operands) => { // Return early if we know that the struct or enum being constructed is always // qualified. - if let AggregateKind::Adt(def, ..) = **kind { - if Q::in_adt_inherently(cx, def) { + if let AggregateKind::Adt(def, _, substs, ..) = **kind { + if Q::in_adt_inherently(cx, def, substs) { return true; } } From 6b54829b784bb5e16354fd0e63c1de4e6238800c Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 16 Mar 2020 10:45:39 -0700 Subject: [PATCH 02/10] Add `CustomEq` qualif --- src/librustc_middle/mir/query.rs | 1 + .../transform/check_consts/qualifs.rs | 36 +++++++++++++++++++ .../transform/check_consts/validation.rs | 28 ++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs index 8c81f5227d260..95a28df99aad0 100644 --- a/src/librustc_middle/mir/query.rs +++ b/src/librustc_middle/mir/query.rs @@ -80,6 +80,7 @@ pub struct BorrowCheckResult<'tcx> { pub struct ConstQualifs { pub has_mut_interior: bool, pub needs_drop: bool, + pub custom_eq: bool, } /// After we borrow check a closure, we are left with various diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index 04bd632553eee..fc6860b40e8d2 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -2,9 +2,11 @@ //! //! See the `Qualif` trait for more info. +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty}; use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits; use super::ConstCx; @@ -12,6 +14,7 @@ pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs ConstQualifs { has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), + custom_eq: CustomEq::in_any_value_of_ty(cx, ty), } } @@ -108,6 +111,39 @@ impl Qualif for NeedsDrop { } } +/// 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(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::::Some`), in which case some values of this type may be + // structural-match (`Option::None`). + let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id.as_local().unwrap()); + traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).is_some() + } + + fn in_adt_inherently( + cx: &ConstCx<'_, 'tcx>, + adt: &'tcx AdtDef, + substs: SubstsRef<'tcx>, + ) -> bool { + let ty = cx.tcx.mk_ty(ty::Adt(adt, substs)); + let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id.as_local().unwrap()); + cx.tcx + .infer_ctxt() + .enter(|infcx| !traits::type_marked_structural(id, cx.body.span, &infcx, ty)) + } +} + // FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return. /// Returns `true` if this `Rvalue` contains qualif `Q`. diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index b87429199ec3a..c5938426f61a9 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -17,7 +17,7 @@ use std::borrow::Cow; use std::ops::Deref; use super::ops::{self, NonConstOp}; -use super::qualifs::{self, HasMutInterior, NeedsDrop}; +use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{is_lang_panic_fn, ConstCx, ConstKind, Qualif}; use crate::const_eval::{is_const_fn, is_unstable_const_fn}; @@ -142,9 +142,35 @@ impl Qualifs<'mir, 'tcx> { 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. + ConstKind::ConstFn => true, + + // If we know that all values of the return type are structurally matchable, there's no + // need to run dataflow. + ConstKind::Const | ConstKind::Static | ConstKind::StaticMut + if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) => + { + false + } + + ConstKind::Const | ConstKind::Static | ConstKind::StaticMut => { + let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx) + .into_engine(ccx.tcx, &ccx.body, ccx.def_id) + .iterate_to_fixpoint() + .into_results_cursor(&ccx.body); + + cursor.seek_after(return_loc); + cursor.contains(RETURN_PLACE) + } + }; + ConstQualifs { needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc), has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc), + custom_eq, } } } From 796c0ca5548de6d4a5220dbae500e00db616243b Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 16 Mar 2020 20:50:20 -0700 Subject: [PATCH 03/10] Incorporate MIR const-checker into `Pat` lowering --- .../hair/pattern/const_to_pat.rs | 21 ++- src/librustc_mir_build/hair/pattern/mod.rs | 131 ++++++++++-------- 2 files changed, 89 insertions(+), 63 deletions(-) diff --git a/src/librustc_mir_build/hair/pattern/const_to_pat.rs b/src/librustc_mir_build/hair/pattern/const_to_pat.rs index 50034fb02ace9..ad87afc3e0301 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -22,13 +22,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { cv: &'tcx ty::Const<'tcx>, id: hir::HirId, span: Span, + mir_structural_match_violation: bool, ) -> Pat<'tcx> { debug!("const_to_pat: cv={:#?} id={:?}", cv, id); debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span); self.tcx.infer_ctxt().enter(|infcx| { let mut convert = ConstToPat::new(self, id, span, infcx); - convert.to_pat(cv) + convert.to_pat(cv, mir_structural_match_violation) }) } } @@ -81,7 +82,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { traits::type_marked_structural(self.id, self.span, &self.infcx, ty) } - fn to_pat(&mut self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> { + fn to_pat( + &mut self, + cv: &'tcx ty::Const<'tcx>, + mir_structural_match_violation: bool, + ) -> Pat<'tcx> { // 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 // invoked except by this method. @@ -100,6 +105,11 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", cv.ty, structural ); + + if structural.is_none() && mir_structural_match_violation { + bug!("MIR const-checker found novel structural match violation"); + } + if let Some(non_sm_ty) = structural { let adt_def = match non_sm_ty { traits::NonStructuralMatchTy::Adt(adt_def) => adt_def, @@ -146,13 +156,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { if !ty_is_partial_eq { // span_fatal avoids ICE from resolution of non-existent method (rare case). self.tcx().sess.span_fatal(self.span, &make_msg()); - } else { + } else if mir_structural_match_violation { self.tcx().struct_span_lint_hir( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, |lint| lint.build(&make_msg()).emit(), ); + } else { + debug!( + "`search_for_structural_match_violation` found one, but `CustomEq` was \ + not in the qualifs for that `const`" + ); } } } diff --git a/src/librustc_mir_build/hair/pattern/mod.rs b/src/librustc_mir_build/hair/pattern/mod.rs index 20ee98b0a9c0f..bd75f11fb8f94 100644 --- a/src/librustc_mir_build/hair/pattern/mod.rs +++ b/src/librustc_mir_build/hair/pattern/mod.rs @@ -15,7 +15,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::RangeEnd; use rustc_index::vec::Idx; -use rustc_middle::mir::interpret::{get_slice_bytes, sign_extend, ConstValue, ErrorHandled}; +use rustc_middle::mir::interpret::{get_slice_bytes, sign_extend, ConstValue}; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::mir::UserTypeProjection; use rustc_middle::mir::{BorrowKind, Field, Mutability}; @@ -762,69 +762,80 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Pat<'tcx> { let ty = self.tables.node_type(id); let res = self.tables.qpath_res(qpath, id); - let is_associated_const = match res { - Res::Def(DefKind::AssocConst, _) => true, - _ => false, + + let pat_from_kind = |kind| Pat { span, ty, kind: Box::new(kind) }; + + let (def_id, is_associated_const) = match res { + Res::Def(DefKind::Const, def_id) => (def_id, false), + Res::Def(DefKind::AssocConst, def_id) => (def_id, true), + + _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])), }; - let kind = match res { - Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { - let substs = self.tables.node_substs(id); - // Use `Reveal::All` here because patterns are always monomorphic even if their function isn't. - match self.tcx.const_eval_resolve( - self.param_env.with_reveal_all(), - def_id, - substs, - None, - Some(span), - ) { - Ok(value) => { - let const_ = - ty::Const::from_value(self.tcx, value, self.tables.node_type(id)); - - let pattern = self.const_to_pat(&const_, id, span); - if !is_associated_const { - return pattern; - } - let user_provided_types = self.tables().user_provided_types(); - return if let Some(u_ty) = user_provided_types.get(id) { - let user_ty = PatTyProj::from_user_type(*u_ty); - Pat { - span, - kind: Box::new(PatKind::AscribeUserType { - subpattern: pattern, - ascription: Ascription { - /// Note that use `Contravariant` here. See the - /// `variance` field documentation for details. - variance: ty::Variance::Contravariant, - user_ty, - user_ty_span: span, - }, - }), - ty: const_.ty, - } - } else { - pattern - }; - } - Err(ErrorHandled::TooGeneric) => { - self.errors.push(if is_associated_const { - PatternError::AssocConstInPattern(span) - } else { - PatternError::StaticInPattern(span) - }); - PatKind::Wild - } - Err(_) => { - self.tcx.sess.span_err(span, "could not evaluate constant pattern"); - PatKind::Wild - } - } + // Use `Reveal::All` here because patterns are always monomorphic even if their function + // isn't. + let param_env_reveal_all = self.param_env.with_reveal_all(); + let substs = self.tables.node_substs(id); + let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, substs) { + Ok(Some(i)) => i, + Ok(None) => { + self.errors.push(if is_associated_const { + PatternError::AssocConstInPattern(span) + } else { + PatternError::StaticInPattern(span) + }); + + return pat_from_kind(PatKind::Wild); + } + + Err(_) => { + self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + return pat_from_kind(PatKind::Wild); } - _ => self.lower_variant_or_leaf(res, id, span, ty, vec![]), }; - Pat { span, ty, kind: Box::new(kind) } + // `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. + let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq; + debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation); + + match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) { + Ok(value) => { + let const_ = ty::Const::from_value(self.tcx, value, self.tables.node_type(id)); + + let pattern = self.const_to_pat(&const_, id, span, mir_structural_match_violation); + + if !is_associated_const { + return pattern; + } + + let user_provided_types = self.tables().user_provided_types(); + if let Some(u_ty) = user_provided_types.get(id) { + let user_ty = PatTyProj::from_user_type(*u_ty); + Pat { + span, + kind: Box::new(PatKind::AscribeUserType { + subpattern: pattern, + ascription: Ascription { + /// Note that use `Contravariant` here. See the + /// `variance` field documentation for details. + variance: ty::Variance::Contravariant, + user_ty, + user_ty_span: span, + }, + }), + ty: const_.ty, + } + } else { + pattern + } + } + Err(_) => { + self.tcx.sess.span_err(span, "could not evaluate constant pattern"); + pat_from_kind(PatKind::Wild) + } + } } /// Converts literals, paths and negation of literals to patterns. @@ -849,7 +860,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let lit_input = LitToConstInput { lit: &lit.node, ty: self.tables.expr_ty(expr), neg }; match self.tcx.at(expr.span).lit_to_const(lit_input) { - Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span).kind, + Ok(val) => *self.const_to_pat(val, expr.hir_id, lit.span, false).kind, Err(LitToConstError::UnparseableFloat) => { self.errors.push(PatternError::FloatBug); PatKind::Wild From 66f2d44c737134d36778956f1b1c928a3b36cb87 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 16 Mar 2020 21:01:05 -0700 Subject: [PATCH 04/10] Add tests from #67088 and the issues mentioned in its description --- .../const_in_pattern/accept_structural.rs | 66 +++++++++ .../ui/consts/const_in_pattern/issue-62614.rs | 24 ++++ .../ui/consts/const_in_pattern/issue-65466.rs | 19 +++ .../const_in_pattern/issue-65466.stderr | 15 ++ .../const_in_pattern/reject_non_partial_eq.rs | 32 +++++ .../reject_non_partial_eq.stderr | 8 ++ .../const_in_pattern/reject_non_structural.rs | 93 ++++++++++++ .../reject_non_structural.stderr | 136 ++++++++++++++++++ .../const_in_pattern/warn_corner_cases.rs | 41 ++++++ .../const_in_pattern/warn_corner_cases.stderr | 34 +++++ src/test/ui/issues/issue-55511.rs | 2 - src/test/ui/issues/issue-55511.stderr | 14 -- 12 files changed, 468 insertions(+), 16 deletions(-) create mode 100644 src/test/ui/consts/const_in_pattern/accept_structural.rs create mode 100644 src/test/ui/consts/const_in_pattern/issue-62614.rs create mode 100644 src/test/ui/consts/const_in_pattern/issue-65466.rs create mode 100644 src/test/ui/consts/const_in_pattern/issue-65466.stderr create mode 100644 src/test/ui/consts/const_in_pattern/reject_non_partial_eq.rs create mode 100644 src/test/ui/consts/const_in_pattern/reject_non_partial_eq.stderr create mode 100644 src/test/ui/consts/const_in_pattern/reject_non_structural.rs create mode 100644 src/test/ui/consts/const_in_pattern/reject_non_structural.stderr create mode 100644 src/test/ui/consts/const_in_pattern/warn_corner_cases.rs create mode 100644 src/test/ui/consts/const_in_pattern/warn_corner_cases.stderr diff --git a/src/test/ui/consts/const_in_pattern/accept_structural.rs b/src/test/ui/consts/const_in_pattern/accept_structural.rs new file mode 100644 index 0000000000000..70ec95faf6ac5 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/accept_structural.rs @@ -0,0 +1,66 @@ +// run-pass + +#![warn(indirect_structural_match)] + +// This test is checking our logic for structural match checking by enumerating +// the different kinds of const expressions. This test is collecting cases where +// we have accepted the const expression as a pattern in the past and wish to +// continue doing so. +// +// Even if a non-structural-match type is part of an expression in a const's +// definition, that does not necessarily disqualify the const from being a match +// pattern: in principle, we just need the types involved in the final value to +// be structurally matchable. + +// See also RFC 1445 + +#![feature(type_ascription)] + +#[derive(Copy, Clone, Debug)] +struct NoPartialEq(u32); + +#[derive(Copy, Clone, Debug)] +struct NoDerive(u32); + +// This impl makes `NoDerive` irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } +impl Eq for NoDerive { } + +type OND = Option; + +fn main() { + const FIELD1: u32 = NoPartialEq(1).0; + match 1 { FIELD1 => dbg!(FIELD1), _ => panic!("whoops"), }; + const FIELD2: u32 = NoDerive(1).0; + match 1 { FIELD2 => dbg!(FIELD2), _ => panic!("whoops"), }; + + enum CLike { One = 1, #[allow(dead_code)] Two = 2, } + const ONE_CAST: u32 = CLike::One as u32; + match 1 { ONE_CAST => dbg!(ONE_CAST), _ => panic!("whoops"), }; + + const NO_DERIVE_NONE: OND = None; + const INDIRECT: OND = NO_DERIVE_NONE; + match None { INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + + const TUPLE: (OND, OND) = (None, None); + match (None, None) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + + const TYPE: OND = None: OND; + match None { TYPE => dbg!(TYPE), _ => panic!("whoops"), }; + + const ARRAY: [OND; 2] = [None, None]; + match [None; 2] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + + const REPEAT: [OND; 2] = [None; 2]; + match [None, None] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + + trait Trait: Sized { const ASSOC: Option; } + impl Trait for NoDerive { const ASSOC: Option = None; } + match None { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + + const BLOCK: OND = { NoDerive(10); None }; + match None { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + + const ADDR_OF: &OND = &None; + match &None { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; +} diff --git a/src/test/ui/consts/const_in_pattern/issue-62614.rs b/src/test/ui/consts/const_in_pattern/issue-62614.rs new file mode 100644 index 0000000000000..4ea9a283618ea --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-62614.rs @@ -0,0 +1,24 @@ +// run-pass + +struct Sum(u32, u32); + +impl PartialEq for Sum { + fn eq(&self, other: &Self) -> bool { self.0 + self.1 == other.0 + other.1 } +} + +impl Eq for Sum { } + +#[derive(PartialEq, Eq)] +enum Eek { + TheConst, + UnusedByTheConst(Sum) +} + +const THE_CONST: Eek = Eek::TheConst; + +pub fn main() { + match Eek::UnusedByTheConst(Sum(1,2)) { + THE_CONST => { panic!(); } + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/issue-65466.rs b/src/test/ui/consts/const_in_pattern/issue-65466.rs new file mode 100644 index 0000000000000..e974fd9397d7b --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-65466.rs @@ -0,0 +1,19 @@ +#![deny(indirect_structural_match)] + +#[derive(PartialEq, Eq)] +enum O { + Some(*const T), // Can also use PhantomData + None, +} + +struct B; + +const C: &[O] = &[O::None]; + +fn main() { + let x = O::None; + match &[x][..] { + C => (), + _ => (), + } +} diff --git a/src/test/ui/consts/const_in_pattern/issue-65466.stderr b/src/test/ui/consts/const_in_pattern/issue-65466.stderr new file mode 100644 index 0000000000000..9fe3049d1d85f --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-65466.stderr @@ -0,0 +1,15 @@ +error[E0601]: `main` function not found in crate `issue_65466` + --> $DIR/issue-65466.rs:1:1 + | +LL | / #![deny(indirect_structural_match)] +LL | | +LL | | #[derive(PartialEq, Eq)] +LL | | enum O { +... | +LL | | } +LL | | } + | |_^ consider adding a `main` function to `$DIR/issue-65466.rs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.rs b/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.rs new file mode 100644 index 0000000000000..a8216901c027f --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.rs @@ -0,0 +1,32 @@ +// This test is illustrating the difference between how failing to derive +// `PartialEq` is handled compared to failing to implement it at all. + +// See also RFC 1445 + +#[derive(PartialEq, Eq)] +struct Structural(u32); + +struct NoPartialEq(u32); + +struct NoDerive(u32); + +// This impl makes NoDerive irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +const NO_DERIVE_NONE: Option = None; +const NO_PARTIAL_EQ_NONE: Option = None; + +fn main() { + match None { + NO_DERIVE_NONE => println!("NO_DERIVE_NONE"), + _ => panic!("whoops"), + } + + match None { + NO_PARTIAL_EQ_NONE => println!("NO_PARTIAL_EQ_NONE"), + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => panic!("whoops"), + } +} diff --git a/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.stderr b/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.stderr new file mode 100644 index 0000000000000..95cfa4a9ebe95 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_partial_eq.stderr @@ -0,0 +1,8 @@ +error: to use a constant of type `NoPartialEq` in a pattern, `NoPartialEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_partial_eq.rs:28:9 + | +LL | NO_PARTIAL_EQ_NONE => println!("NO_PARTIAL_EQ_NONE"), + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/consts/const_in_pattern/reject_non_structural.rs b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs new file mode 100644 index 0000000000000..c39163ee27824 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs @@ -0,0 +1,93 @@ +// This test of structural match checking enumerates the different kinds of +// const definitions, collecting cases where the const pattern is rejected. +// +// Note: Even if a non-structural-match type is part of an expression in a +// const's definition, that does not necessarily disqualify the const from being +// a match pattern: in principle, we just need the types involved in the final +// value to be structurally matchable. + +// See also RFC 1445 + +#![feature(type_ascription)] +#![warn(indirect_structural_match)] +//~^ NOTE lint level is defined here + +#[derive(Copy, Clone, Debug)] +struct NoPartialEq; + +#[derive(Copy, Clone, Debug)] +struct NoDerive; + +// This impl makes `NoDerive` irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } + +impl Eq for NoDerive { } + +type OND = Option; + +struct TrivialEq(OND); + +// This impl makes `TrivialEq` trivial. +impl PartialEq for TrivialEq { fn eq(&self, _: &Self) -> bool { true } } + +impl Eq for TrivialEq { } + +fn main() { + #[derive(PartialEq, Eq, Debug)] + enum Derive { Some(X), None, } + + const ENUM: Derive = Derive::Some(NoDerive); + match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const FIELD: OND = TrivialEq(Some(NoDerive)).0; + match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const NO_DERIVE_SOME: OND = Some(NoDerive); + const INDIRECT: OND = NO_DERIVE_SOME; + match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const TUPLE: (OND, OND) = (None, Some(NoDerive)); + match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const TYPE: OND = Some(NoDerive): OND; + match Some(NoDerive) { TYPE => dbg!(TYPE), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const ARRAY: [OND; 2] = [None, Some(NoDerive)]; + match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const REPEAT: [OND; 2] = [Some(NoDerive); 2]; + match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + trait Trait: Sized { const ASSOC: Option; } + impl Trait for NoDerive { const ASSOC: Option = Some(NoDerive); } + match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const BLOCK: OND = { NoDerive; Some(NoDerive) }; + match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + + const ADDR_OF: &OND = &Some(NoDerive); + match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN previously accepted by the compiler but is being phased out + //~| NOTE for more information, see issue #62411 +} diff --git a/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr b/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr new file mode 100644 index 0000000000000..8ea28e259abd9 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr @@ -0,0 +1,136 @@ +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:40:36 + | +LL | match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops"), }; + | ^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:45:28 + | +LL | match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:51:27 + | +LL | match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + | ^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:56:36 + | +LL | match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:61:28 + | +LL | match Some(NoDerive) { TYPE => dbg!(TYPE), _ => panic!("whoops"), }; + | ^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:66:36 + | +LL | match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:71:33 + | +LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | ^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:71:33 + | +LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | ^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:79:28 + | +LL | match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + | ^^^^^^^^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:84:28 + | +LL | match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + | ^^^^^ + +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:89:29 + | +LL | match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; + | ^^^^^^^ + | +note: the lint level is defined here + --> $DIR/reject_non_structural.rs:12:9 + | +LL | #![warn(indirect_structural_match)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:40:36 + | +LL | match Derive::Some(NoDerive) { ENUM => dbg!(ENUM), _ => panic!("whoops"), }; + | ^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:45:28 + | +LL | match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:51:27 + | +LL | match Some(NoDerive) {INDIRECT => dbg!(INDIRECT), _ => panic!("whoops"), }; + | ^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:56:36 + | +LL | match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:61:28 + | +LL | match Some(NoDerive) { TYPE => dbg!(TYPE), _ => panic!("whoops"), }; + | ^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:66:36 + | +LL | match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + | ^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:71:33 + | +LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | ^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:71:33 + | +LL | match [Some(NoDerive); 2] { REPEAT => dbg!(REPEAT), _ => panic!("whoops"), }; + | ^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:79:28 + | +LL | match Some(NoDerive) { NoDerive::ASSOC => dbg!(NoDerive::ASSOC), _ => panic!("whoops"), }; + | ^^^^^^^^^^^^^^^ + +error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/reject_non_structural.rs:84:28 + | +LL | match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + | ^^^^^ + +error: aborting due to 20 previous errors; 1 warning emitted + diff --git a/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs b/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs new file mode 100644 index 0000000000000..c6b794de19526 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs @@ -0,0 +1,41 @@ +// run-pass + +// This test is checking our logic for structural match checking by enumerating +// 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 +// to begin warning the user that a future version of Rust may start rejecting +// such const expressions. + +// The specific corner cases we are exploring here are instances where the +// const-evaluator computes a value that *does* meet the conditions for +// structural-match, but the const expression itself has abstractions (like +// calls to const functions) that may fit better with a type-based analysis +// rather than a committment to a specific value. + +#![warn(indirect_structural_match)] + +#[derive(Copy, Clone, Debug)] +struct NoDerive(u32); + +// This impl makes `NoDerive` irreflexive. +impl PartialEq for NoDerive { fn eq(&self, _: &Self) -> bool { false } } +impl Eq for NoDerive { } + +fn main() { + const INDEX: Option = [None, Some(NoDerive(10))][0]; + match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), }; + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN this was previously accepted + + const fn build() -> Option { None } + const CALL: Option = build(); + match None { Some(_) => panic!("whoops"), CALL => dbg!(CALL), }; + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN this was previously accepted + + impl NoDerive { const fn none() -> Option { None } } + const METHOD_CALL: Option = NoDerive::none(); + match None { Some(_) => panic!("whoops"), METHOD_CALL => dbg!(METHOD_CALL), }; + //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` + //~| WARN this was previously accepted +} diff --git a/src/test/ui/consts/const_in_pattern/warn_corner_cases.stderr b/src/test/ui/consts/const_in_pattern/warn_corner_cases.stderr new file mode 100644 index 0000000000000..3e7ed573c74d7 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/warn_corner_cases.stderr @@ -0,0 +1,34 @@ +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/warn_corner_cases.rs:26:47 + | +LL | match None { Some(_) => panic!("whoops"), INDEX => dbg!(INDEX), }; + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/warn_corner_cases.rs:15:9 + | +LL | #![warn(indirect_structural_match)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $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 #62411 + +warning: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` + --> $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 #62411 + +warning: 3 warnings emitted + diff --git a/src/test/ui/issues/issue-55511.rs b/src/test/ui/issues/issue-55511.rs index 055886bf3676c..7dfa9c7bcdffe 100644 --- a/src/test/ui/issues/issue-55511.rs +++ b/src/test/ui/issues/issue-55511.rs @@ -14,8 +14,6 @@ fn main() { //~^ ERROR `a` does not live long enough [E0597] match b { <() as Foo<'static>>::C => { } - //~^ WARN must be annotated with `#[derive(PartialEq, Eq)]` - //~| WARN will become a hard error in a future release _ => { } } } diff --git a/src/test/ui/issues/issue-55511.stderr b/src/test/ui/issues/issue-55511.stderr index d526ddb78e98e..b29ff3f7420fa 100644 --- a/src/test/ui/issues/issue-55511.stderr +++ b/src/test/ui/issues/issue-55511.stderr @@ -1,17 +1,3 @@ -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: the lint level is defined here - --> $DIR/issue-55511.rs:1:9 - | -LL | #![warn(indirect_structural_match)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #62411 - error[E0597]: `a` does not live long enough --> $DIR/issue-55511.rs:13:28 | From 135cfcb5cd563e8beec78d86d8afc40321c9c7da Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 6 Apr 2020 20:10:34 -0700 Subject: [PATCH 05/10] FIXME: ignore test that ICEs --- src/test/ui/consts/const_in_pattern/issue-65466.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/ui/consts/const_in_pattern/issue-65466.rs b/src/test/ui/consts/const_in_pattern/issue-65466.rs index e974fd9397d7b..0e3e0f6dd8834 100644 --- a/src/test/ui/consts/const_in_pattern/issue-65466.rs +++ b/src/test/ui/consts/const_in_pattern/issue-65466.rs @@ -1,3 +1,7 @@ +// FIXME: This still ICEs. +// +// ignore-test + #![deny(indirect_structural_match)] #[derive(PartialEq, Eq)] From b58da533bc0fc6afd61eafaf3860b9934c8120b8 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 7 Apr 2020 12:11:29 -0700 Subject: [PATCH 06/10] Add branchy `const` in pattern tests --- .../const_in_pattern/custom-eq-branch-pass.rs | 33 ++++++++++++++++ .../const_in_pattern/custom-eq-branch-warn.rs | 39 +++++++++++++++++++ .../custom-eq-branch-warn.stderr | 16 ++++++++ .../const_in_pattern/no-eq-branch-fail.rs | 27 +++++++++++++ .../const_in_pattern/no-eq-branch-fail.stderr | 14 +++++++ 5 files changed, 129 insertions(+) create mode 100644 src/test/ui/consts/const_in_pattern/custom-eq-branch-pass.rs create mode 100644 src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.rs create mode 100644 src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.stderr create mode 100644 src/test/ui/consts/const_in_pattern/no-eq-branch-fail.rs create mode 100644 src/test/ui/consts/const_in_pattern/no-eq-branch-fail.stderr diff --git a/src/test/ui/consts/const_in_pattern/custom-eq-branch-pass.rs b/src/test/ui/consts/const_in_pattern/custom-eq-branch-pass.rs new file mode 100644 index 0000000000000..81a2024a81b7b --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/custom-eq-branch-pass.rs @@ -0,0 +1,33 @@ +// run-pass + +#![feature(const_if_match)] +#![warn(indirect_structural_match)] + +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), +} + +const BAR_BAZ: Foo = if 42 == 42 { + Foo::Bar +} else { + Foo::Baz +}; + +fn main() { + match Foo::Qux(CustomEq) { + BAR_BAZ => panic!(), + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.rs b/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.rs new file mode 100644 index 0000000000000..21c4de6fbb1f1 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.rs @@ -0,0 +1,39 @@ +// check-pass + +#![feature(const_if_match)] +#![warn(indirect_structural_match)] +//~^ NOTE lint level is defined here + +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, Eq)]` + //~| WARN this was previously accepted + //~| NOTE see issue #62411 + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.stderr b/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.stderr new file mode 100644 index 0000000000000..06ec2a7fdd35c --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/custom-eq-branch-warn.stderr @@ -0,0 +1,16 @@ +warning: to use a constant of type `CustomEq` in a pattern, `CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/custom-eq-branch-warn.rs:33:9 + | +LL | BAR_BAZ => panic!(), + | ^^^^^^^ + | +note: the lint level is defined here + --> $DIR/custom-eq-branch-warn.rs:4:9 + | +LL | #![warn(indirect_structural_match)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +warning: 1 warning emitted + diff --git a/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.rs b/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.rs new file mode 100644 index 0000000000000..28b3fbb952563 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.rs @@ -0,0 +1,27 @@ +#![feature(const_if_match)] +#![warn(indirect_structural_match)] + +struct NoEq; + +enum Foo { + Bar, + Baz, + Qux(NoEq), +} + +// Even though any of these values can be compared structurally, we still disallow it in a pattern +// because `Foo` does not impl `PartialEq`. +const BAR_BAZ: Foo = if 42 == 42 { + Foo::Baz +} else { + Foo::Bar +}; + +fn main() { + match Foo::Qux(NoEq) { + BAR_BAZ => panic!(), + //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` + //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.stderr b/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.stderr new file mode 100644 index 0000000000000..cb870ec7dbcf2 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/no-eq-branch-fail.stderr @@ -0,0 +1,14 @@ +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/no-eq-branch-fail.rs:22:9 + | +LL | BAR_BAZ => panic!(), + | ^^^^^^^ + +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/no-eq-branch-fail.rs:22:9 + | +LL | BAR_BAZ => panic!(), + | ^^^^^^^ + +error: aborting due to 2 previous errors + From e68a5c68000810cab9755754959318c4aaa93e5d Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 7 Apr 2020 13:01:41 -0700 Subject: [PATCH 07/10] Add cross-crate const in pattern tests --- .../consts/const_in_pattern/auxiliary/consts.rs | 11 +++++++++++ .../consts/const_in_pattern/cross-crate-fail.rs | 16 ++++++++++++++++ .../const_in_pattern/cross-crate-fail.stderr | 14 ++++++++++++++ .../consts/const_in_pattern/cross-crate-pass.rs | 14 ++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 src/test/ui/consts/const_in_pattern/auxiliary/consts.rs create mode 100644 src/test/ui/consts/const_in_pattern/cross-crate-fail.rs create mode 100644 src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr create mode 100644 src/test/ui/consts/const_in_pattern/cross-crate-pass.rs diff --git a/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs b/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs new file mode 100644 index 0000000000000..303c2f12bbce9 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/auxiliary/consts.rs @@ -0,0 +1,11 @@ +pub struct CustomEq; + +impl Eq for CustomEq {} +impl PartialEq for CustomEq { + fn eq(&self, _: &Self) -> bool { + false + } +} + +pub const NONE: Option = None; +pub const SOME: Option = Some(CustomEq); diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs new file mode 100644 index 0000000000000..a32450ab92fcd --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs @@ -0,0 +1,16 @@ +// aux-build:consts.rs + +#![warn(indirect_structural_match)] + +extern crate consts; +use consts::*; + +fn main() { + match None { + SOME => panic!(), + //~^ must be annotated with `#[derive(PartialEq, Eq)]` + //~| must be annotated with `#[derive(PartialEq, Eq)]` + + _ => {} + } +} diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr new file mode 100644 index 0000000000000..d5bb0dd0f2502 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr @@ -0,0 +1,14 @@ +error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cross-crate-fail.rs:10:9 + | +LL | SOME => panic!(), + | ^^^^ + +error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/cross-crate-fail.rs:10:9 + | +LL | SOME => panic!(), + | ^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs b/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs new file mode 100644 index 0000000000000..23f73a27d66f1 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs @@ -0,0 +1,14 @@ +// run-pass +// aux-build:consts.rs + +#![warn(indirect_structural_match)] + +extern crate consts; +use consts::*; + +fn main() { + match Some(CustomEq) { + NONE => panic!(), + _ => {} + } +} From 532ba46402855517c431ab98b395f788f668deed Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 28 Apr 2020 14:30:44 -0700 Subject: [PATCH 08/10] Use path to refer to constants in cross-crate pattern tests --- .../ui/consts/const_in_pattern/cross-crate-fail.rs | 3 +-- .../consts/const_in_pattern/cross-crate-fail.stderr | 12 ++++++------ .../ui/consts/const_in_pattern/cross-crate-pass.rs | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs index a32450ab92fcd..c9e6050fdc508 100644 --- a/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs @@ -3,11 +3,10 @@ #![warn(indirect_structural_match)] extern crate consts; -use consts::*; fn main() { match None { - SOME => panic!(), + consts::SOME => panic!(), //~^ must be annotated with `#[derive(PartialEq, Eq)]` //~| must be annotated with `#[derive(PartialEq, Eq)]` diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr index d5bb0dd0f2502..c97298f66e677 100644 --- a/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.stderr @@ -1,14 +1,14 @@ error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/cross-crate-fail.rs:10:9 + --> $DIR/cross-crate-fail.rs:9:9 | -LL | SOME => panic!(), - | ^^^^ +LL | consts::SOME => panic!(), + | ^^^^^^^^^^^^ error: to use a constant of type `consts::CustomEq` in a pattern, `consts::CustomEq` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/cross-crate-fail.rs:10:9 + --> $DIR/cross-crate-fail.rs:9:9 | -LL | SOME => panic!(), - | ^^^^ +LL | consts::SOME => panic!(), + | ^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs b/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs index 23f73a27d66f1..ccf655c11cf8a 100644 --- a/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs +++ b/src/test/ui/consts/const_in_pattern/cross-crate-pass.rs @@ -4,11 +4,11 @@ #![warn(indirect_structural_match)] extern crate consts; -use consts::*; +use consts::CustomEq; fn main() { match Some(CustomEq) { - NONE => panic!(), + consts::NONE => panic!(), _ => {} } } From 900cf82d4d7779d1393529e38ff3e3e28b351a37 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 28 Apr 2020 14:49:35 -0700 Subject: [PATCH 09/10] TYPE -> TYPE_ASCRIPTIONG --- src/test/ui/consts/const_in_pattern/accept_structural.rs | 4 ++-- .../ui/consts/const_in_pattern/reject_non_structural.rs | 4 ++-- .../consts/const_in_pattern/reject_non_structural.stderr | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/ui/consts/const_in_pattern/accept_structural.rs b/src/test/ui/consts/const_in_pattern/accept_structural.rs index 70ec95faf6ac5..5093fe5391547 100644 --- a/src/test/ui/consts/const_in_pattern/accept_structural.rs +++ b/src/test/ui/consts/const_in_pattern/accept_structural.rs @@ -45,8 +45,8 @@ fn main() { const TUPLE: (OND, OND) = (None, None); match (None, None) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; - const TYPE: OND = None: OND; - match None { TYPE => dbg!(TYPE), _ => panic!("whoops"), }; + const TYPE_ASCRIPTION: OND = None: OND; + match None { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; const ARRAY: [OND; 2] = [None, None]; match [None; 2] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; diff --git a/src/test/ui/consts/const_in_pattern/reject_non_structural.rs b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs index c39163ee27824..bbeaeea1f87d8 100644 --- a/src/test/ui/consts/const_in_pattern/reject_non_structural.rs +++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs @@ -57,8 +57,8 @@ fn main() { //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` - const TYPE: OND = Some(NoDerive): OND; - match Some(NoDerive) { TYPE => dbg!(TYPE), _ => panic!("whoops"), }; + const TYPE_ASCRIPTION: OND = Some(NoDerive): OND; + match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` //~| ERROR must be annotated with `#[derive(PartialEq, Eq)]` diff --git a/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr b/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr index 8ea28e259abd9..b1310cf101eaa 100644 --- a/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr +++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr @@ -25,8 +25,8 @@ LL | match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoop error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/reject_non_structural.rs:61:28 | -LL | match Some(NoDerive) { TYPE => dbg!(TYPE), _ => panic!("whoops"), }; - | ^^^^ +LL | match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; + | ^^^^^^^^^^^^^^^ error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/reject_non_structural.rs:66:36 @@ -99,8 +99,8 @@ LL | match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoop error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/reject_non_structural.rs:61:28 | -LL | match Some(NoDerive) { TYPE => dbg!(TYPE), _ => panic!("whoops"), }; - | ^^^^ +LL | match Some(NoDerive) { TYPE_ASCRIPTION => dbg!(TYPE_ASCRIPTION), _ => panic!("whoops"), }; + | ^^^^^^^^^^^^^^^ error: to use a constant of type `NoDerive` in a pattern, `NoDerive` must be annotated with `#[derive(PartialEq, Eq)]` --> $DIR/reject_non_structural.rs:66:36 From e4c650c00dfdf2581c54688a6ff98ebe006c80b1 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 28 Apr 2020 15:27:28 -0700 Subject: [PATCH 10/10] Bless test that no longer warns --- src/test/ui/issues/issue-55511.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/issues/issue-55511.stderr b/src/test/ui/issues/issue-55511.stderr index b29ff3f7420fa..bf3e58e8cdb19 100644 --- a/src/test/ui/issues/issue-55511.stderr +++ b/src/test/ui/issues/issue-55511.stderr @@ -10,6 +10,6 @@ LL | <() as Foo<'static>>::C => { } LL | } | - `a` dropped here while still borrowed -error: aborting due to previous error; 1 warning emitted +error: aborting due to previous error For more information about this error, try `rustc --explain E0597`.