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 f82f06599b74a..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, AdtDef, Ty}; +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), } } @@ -53,7 +56,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 +81,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,11 +106,44 @@ 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) } } +/// 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`. @@ -147,8 +187,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; } } 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, } } } 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 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..5093fe5391547 --- /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_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"), }; + + 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/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..c9e6050fdc508 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/cross-crate-fail.rs @@ -0,0 +1,15 @@ +// aux-build:consts.rs + +#![warn(indirect_structural_match)] + +extern crate consts; + +fn main() { + match None { + 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 new file mode 100644 index 0000000000000..c97298f66e677 --- /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:9:9 + | +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:9:9 + | +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 new file mode 100644 index 0000000000000..ccf655c11cf8a --- /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::CustomEq; + +fn main() { + match Some(CustomEq) { + consts::NONE => panic!(), + _ => {} + } +} 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/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..0e3e0f6dd8834 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-65466.rs @@ -0,0 +1,23 @@ +// FIXME: This still ICEs. +// +// ignore-test + +#![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/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 + 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..bbeaeea1f87d8 --- /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_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)]` + + 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..b1310cf101eaa --- /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_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 + | +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_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 + | +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..bf3e58e8cdb19 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 | @@ -24,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`.