From 9215b46bf20322b0a5b2c6e8c95fa314394ce160 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 29 Oct 2019 16:27:10 +0100 Subject: [PATCH 1/5] HIR-based (rather than type-based) structural-match checking of consts occurring in patterns. --- src/librustc/ty/mod.rs | 4 +- src/librustc/ty/structural_match.rs | 313 ++++++++++++++++-- src/librustc_mir/hair/pattern/const_to_pat.rs | 132 ++++---- src/librustc_mir/hair/pattern/mod.rs | 7 +- src/librustc_typeck/collect.rs | 2 +- src/test/ui/issues/issue-55511.rs | 2 - src/test/ui/issues/issue-55511.stderr | 14 - 7 files changed, 364 insertions(+), 110 deletions(-) diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 60028f2488a33..46ed06eed60db 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -84,8 +84,10 @@ pub use self::context::{ pub use self::instance::{Instance, InstanceDef}; -pub use self::structural_match::search_for_structural_match_violation; +pub use self::structural_match::search_const_rhs_for_structural_match_violation; +pub use self::structural_match::search_type_for_structural_match_violation; pub use self::structural_match::type_marked_structural; +pub use self::structural_match::report_structural_match_violation; pub use self::structural_match::NonStructuralMatchTy; pub use self::trait_def::TraitDef; diff --git a/src/librustc/ty/structural_match.rs b/src/librustc/ty/structural_match.rs index cdf5734f5a506..e381ee24e066c 100644 --- a/src/librustc/ty/structural_match.rs +++ b/src/librustc/ty/structural_match.rs @@ -2,12 +2,16 @@ use crate::hir; use rustc::infer::InferCtxt; use rustc::traits::{self, ConstPatternStructural, TraitEngine}; use rustc::traits::ObligationCause; +use rustc::hir::def::{DefKind, Res}; +use rustc::hir::def_id::DefId; +use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; +use rustc::lint; use rustc_data_structures::fx::{FxHashSet}; use syntax_pos::Span; -use crate::ty::{self, AdtDef, Ty, TyCtxt}; +use crate::ty::{self, AdtDef, ToPredicate, Ty, TyCtxt}; use crate::ty::fold::{TypeFoldable, TypeVisitor}; #[derive(Debug)] @@ -16,6 +20,30 @@ pub enum NonStructuralMatchTy<'tcx> { Param, } +pub fn report_structural_match_violation(tcx: TyCtxt<'tcx>, + non_sm_ty: NonStructuralMatchTy<'tcx>, + id: hir::HirId, + span: Span, + warn_instead_of_hard_error: bool) { + let adt_def = match non_sm_ty { + ty::NonStructuralMatchTy::Adt(adt_def) => adt_def, + ty::NonStructuralMatchTy::Param => + bug!("use of constant whose type is a parameter inside a pattern"), + }; + let path = tcx.def_path_str(adt_def.did); + let msg = format!("to use a constant of type `{}` in a pattern, \ + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", + path, path); + + + if warn_instead_of_hard_error { + tcx.lint_hir(lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, &msg); + } else { + // span_fatal avoids ICE from resolution of non-existent method (rare case). + tcx.sess.span_fatal(span, &msg); + } +} + /// This method traverses the structure of `ty`, trying to find an /// instance of an ADT (i.e. struct or enum) that was declared without /// the `#[structural_match]` attribute, or a generic type parameter @@ -41,20 +69,95 @@ pub enum NonStructuralMatchTy<'tcx> { /// For more background on why Rust has this requirement, and issues /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. -pub fn search_for_structural_match_violation<'tcx>( +pub fn search_type_for_structural_match_violation<'tcx>( id: hir::HirId, span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option> { - // FIXME: we should instead pass in an `infcx` from the outside. + // FIXME: consider passing in an `infcx` from the outside. tcx.infer_ctxt().enter(|infcx| { - let mut search = Search { id, span, infcx, found: None, seen: FxHashSet::default() }; + let mut search = SearchTy { id, span, infcx, found: None, seen: FxHashSet::default() }; ty.visit_with(&mut search); search.found }) } +pub fn search_const_rhs_for_structural_match_violation<'tcx>( + infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + const_def_id: DefId, + pattern_id: hir::HirId, + pattern_span: Span, +) { + let def_id = const_def_id; + let id = pattern_id; + let span = pattern_span; + + // Traverses right-hand side of const definition, looking for: + // + // 1. literals constructing ADTs that do not implement `Structural` + // (rust-lang/rust#62614), and + // + // 2. non-scalar types that do not implement `PartialEq` (which would + // cause codegen to ICE). + + debug!("walk_const_value_looking_for_nonstructural_adt \ + def_id: {:?} id: {:?} span: {:?}", def_id, id, span); + + assert!(def_id.is_local()); + + let const_hir_id: hir::HirId = infcx.tcx.hir().local_def_id_to_hir_id(def_id.to_local()); + debug!("walk_const_value_looking_for_nonstructural_adt const_hir_id: {:?}", const_hir_id); + let body_id = infcx.tcx.hir().body_owned_by(const_hir_id); + debug!("walk_const_value_looking_for_nonstructural_adt body_id: {:?}", body_id); + let body_tables = infcx.tcx.body_tables(body_id); + let body = infcx.tcx.hir().body(body_id); + let mut v = SearchHirExpr { + infcx, param_env, body_tables, id, span, + structural_fulfillment_cx: traits::FulfillmentContext::new(), + partialeq_fulfillment_cx: traits::FulfillmentContext::new(), + }; + v.visit_body(body); + if let Err(errs) = v.structural_fulfillment_cx.select_all_or_error(&v.infcx) { + v.infcx.report_fulfillment_errors(&errs, None, false); + } + if let Err(errs) = v.partialeq_fulfillment_cx.select_all_or_error(&v.infcx) { + for err in errs { + let traits::FulfillmentError { obligation, code: _, points_at_arg_span: _ } = err; + if let ty::Predicate::Trait(pred) = obligation.predicate { + let ty = pred.skip_binder().self_ty(); + infcx.tcx.sess.span_fatal( + span, + &format!("to use a constant of type `{}` in a pattern, \ + `{}` must be annotated with `#[derive(PartialEq, Eq)]`", + ty, ty)); + } else { + bug!("only should have trait predicates"); + } + } + } +} + +fn register_structural_bound(fulfillment_cx: &mut traits::FulfillmentContext<'tcx>, + id: hir::HirId, + span: Span, + infcx: &InferCtxt<'_, 'tcx>, + adt_ty: Ty<'tcx>) +{ + let cause = ObligationCause::new(span, id, ConstPatternStructural); + // require `#[derive(PartialEq)]` + let structural_peq_def_id = infcx.tcx.lang_items().structural_peq_trait().unwrap(); + fulfillment_cx.register_bound( + infcx, ty::ParamEnv::empty(), adt_ty, structural_peq_def_id, cause); + // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around + // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) + let cause = ObligationCause::new(span, id, ConstPatternStructural); + let structural_teq_def_id = infcx.tcx.lang_items().structural_teq_trait().unwrap(); + fulfillment_cx.register_bound( + infcx, ty::ParamEnv::empty(), adt_ty, structural_teq_def_id, cause); +} + /// This method returns true if and only if `adt_ty` itself has been marked as /// eligible for structural-match: namely, if it implements both /// `StructuralPartialEq` and `StructuralEq` (which are respectively injected by @@ -69,17 +172,8 @@ pub fn type_marked_structural(id: hir::HirId, -> bool { let mut fulfillment_cx = traits::FulfillmentContext::new(); - let cause = ObligationCause::new(span, id, ConstPatternStructural); - // require `#[derive(PartialEq)]` - let structural_peq_def_id = infcx.tcx.lang_items().structural_peq_trait().unwrap(); - fulfillment_cx.register_bound( - infcx, ty::ParamEnv::empty(), adt_ty, structural_peq_def_id, cause); - // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around - // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) - let cause = ObligationCause::new(span, id, ConstPatternStructural); - let structural_teq_def_id = infcx.tcx.lang_items().structural_teq_trait().unwrap(); - fulfillment_cx.register_bound( - infcx, ty::ParamEnv::empty(), adt_ty, structural_teq_def_id, cause); + + register_structural_bound(&mut fulfillment_cx, id, span, infcx, adt_ty); // We deliberately skip *reporting* fulfillment errors (via // `report_fulfillment_errors`), for two reasons: @@ -96,7 +190,7 @@ pub fn type_marked_structural(id: hir::HirId, /// This implements the traversal over the structure of a given type to try to /// find instances of ADTs (specifically structs or enums) that do not implement /// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). -struct Search<'a, 'tcx> { +struct SearchTy<'a, 'tcx> { id: hir::HirId, span: Span, @@ -110,7 +204,7 @@ struct Search<'a, 'tcx> { seen: FxHashSet, } -impl Search<'a, 'tcx> { +impl SearchTy<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -120,9 +214,9 @@ impl Search<'a, 'tcx> { } } -impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { +impl<'a, 'tcx> TypeVisitor<'tcx> for SearchTy<'a, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - debug!("Search visiting ty: {:?}", ty); + debug!("SearchTy visiting ty: {:?}", ty); let (adt_def, substs) = match ty.kind { ty::Adt(adt_def, substs) => (adt_def, substs), @@ -170,13 +264,13 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { }; if !self.seen.insert(adt_def.did) { - debug!("Search already seen adt_def: {:?}", adt_def); + debug!("SearchTy already seen adt_def: {:?}", adt_def); // let caller continue its search return false; } if !self.type_marked_structural(ty) { - debug!("Search found ty: {:?}", ty); + debug!("SearchTy found ty: {:?}", ty); self.found = Some(NonStructuralMatchTy::Adt(&adt_def)); return true; // Halt visiting! } @@ -207,3 +301,180 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { false } } + +struct SearchHirExpr<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_tables: &'a ty::TypeckTables<'tcx>, + structural_fulfillment_cx: traits::FulfillmentContext<'tcx>, + partialeq_fulfillment_cx: traits::FulfillmentContext<'tcx>, + id: hir::HirId, + span: Span, +} + +impl<'a, 'tcx> SearchHirExpr<'a, 'tcx> { + fn recur_into_const_rhs(&self, const_def_id: DefId) { + assert!(const_def_id.is_local()); + search_const_rhs_for_structural_match_violation( + &self.infcx, self.param_env, const_def_id, self.id, self.span); + } + + fn search_for_structural_match_violation(&self, adt_ty: Ty<'tcx>) { + if let Some(witness) = + search_type_for_structural_match_violation(self.id, self.span, self.infcx.tcx, adt_ty) + { + // the logic about when to shift from value-based to type-based + // reasoning is new and open to redesign, so only warn about + // violations there for now. + let warn_instead_of_hard_error = true; + report_structural_match_violation( + self.infcx.tcx, witness, self.id, self.span, warn_instead_of_hard_error); + } + } + + fn register_structural_bound(&mut self, adt_ty: Ty<'tcx>) { + register_structural_bound( + &mut self.structural_fulfillment_cx, self.id, self.span, &self.infcx, adt_ty); + } + + fn register_partial_eq_bound(&mut self, ty: Ty<'tcx>) { + let cause = ObligationCause::new(self.span, self.id, ConstPatternStructural); + let partial_eq_def_id = self.infcx.tcx.lang_items().eq_trait().unwrap(); + + // Note: Cannot use register_bound here, because it requires (but does + // not check) that the given trait has no type parameters apart from + // `Self`, but `PartialEq` has a type parameter that defaults to `Self`. + let trait_ref = ty::TraitRef { + def_id: partial_eq_def_id, + substs: self.infcx.tcx.mk_substs_trait(ty, &[ty.into()]), + }; + let obligation = traits::Obligation { + cause, + recursion_depth: 0, + param_env: self.param_env, + predicate: trait_ref.to_predicate(), + }; + self.partialeq_fulfillment_cx.register_predicate_obligation(&self.infcx, obligation); + } +} + +impl<'a, 'v, 'tcx> Visitor<'v> for SearchHirExpr<'a, 'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> + { + NestedVisitorMap::None + } + fn visit_expr(&mut self, ex: &'v hir::Expr) { + // When we inspect the expression on a RHS of a const, we may be able to + // prove that it can participate in a match pattern, without worrying + // about its actual type being `PartialEq`. + // + // In an ideal world we would not bother with side-stepping the + // `PartialEq` and just unconditionally check that the types of a + // constant in a match pattern are all `PartialEq`. However, one big + // exception is types with `for <'a> fn(...)` in them; today these are + // not `PartialEq` due to technical limitations. + // + // So we cut the gordian knot here: the expression analysis can allow + // the omission of the `PartialEq` check. + let mut impose_partialeq_bound = true; + + let ty = self.body_tables.expr_ty(ex); + match &ex.kind { + hir::ExprKind::Struct(..) => { + // register structural requirement ... + self.register_structural_bound(ty); + // ... and continue expression-based traversal + intravisit::walk_expr(self, ex) + } + + hir::ExprKind::Path(qpath) => { + let res = self.body_tables.qpath_res(qpath, ex.hir_id); + match res { + Res::Def(DefKind::Const, def_id) | + Res::Def(DefKind::AssocConst, def_id) => { + let substs = self.body_tables.node_substs(ex.hir_id); + if let Some(instance) = ty::Instance::resolve( + self.infcx.tcx, self.param_env, def_id, substs) + { + let const_def_id = instance.def_id(); + if const_def_id.is_local() { + self.recur_into_const_rhs(const_def_id); + } else { + // abstraction barrer for non-local definition; + // traverse `typeof(expr)` instead. + debug!("SearchHirExpr switch to type analysis \ + for expr: {:?} ty: {:?}", ex, ty); + self.search_for_structural_match_violation(ty); + impose_partialeq_bound = false; + } + } else { + self.infcx.tcx.sess.delay_span_bug(self.span, &format!( + "SearchHirExpr didn't resolve def_id: {:?}", def_id)); + } + } + Res::Def(DefKind::Ctor(..), _def_id) => { + self.register_structural_bound(ty); + } + _ => { + debug!("SearchHirExpr ExprKind::Path res: {:?} \ + traverse type instead", res); + } + } + } + + hir::ExprKind::Box(..) | + hir::ExprKind::Array(..) | + hir::ExprKind::Repeat(..) | + hir::ExprKind::Tup(..) | + hir::ExprKind::Type(..) | + hir::ExprKind::DropTemps(..) | + hir::ExprKind::AddrOf(..) => { + // continue expression-based traversal + intravisit::walk_expr(self, ex) + } + + hir::ExprKind::Block(block, _opt_label) => { + // skip the statements, focus solely on the return expression + if let Some(ex) = &block.expr { + intravisit::walk_expr(self, ex) + } + } + hir::ExprKind::Match(_input, arms, _match_source) => { + // skip the input, focus solely on the arm bodies + for a in arms.iter() { + intravisit::walk_expr(self, &a.body) + } + } + + hir::ExprKind::Loop(..) | + hir::ExprKind::Call(..) | + hir::ExprKind::MethodCall(..) | + hir::ExprKind::Binary(..) | + hir::ExprKind::Unary(..) | + hir::ExprKind::Cast(..) | + hir::ExprKind::Closure(..) | + hir::ExprKind::Assign(..) | + hir::ExprKind::AssignOp(..) | + hir::ExprKind::Field(..) | + hir::ExprKind::Index(..) | + hir::ExprKind::Break(..) | + hir::ExprKind::Continue(..) | + hir::ExprKind::Ret(..) | + hir::ExprKind::Yield(..) | + hir::ExprKind::InlineAsm(..) | + hir::ExprKind::Lit(..) | + hir::ExprKind::Err => { + // abstraction barrier for non-trivial expression; traverse + // `typeof(expr)` instead of expression itself. + debug!("SearchHirExpr switch to type analysis for expr: {:?} ty: {:?}", ex, ty); + self.search_for_structural_match_violation(ty); + impose_partialeq_bound = false; + } + } + + if impose_partialeq_bound && !ty.is_scalar() { + debug!("SearchHirExpr registering PartialEq bound for non-scalar ty: {:?}", ty); + self.register_partial_eq_bound(ty); + } + } +} diff --git a/src/librustc_mir/hair/pattern/const_to_pat.rs b/src/librustc_mir/hair/pattern/const_to_pat.rs index bfc539639db1e..dce8a1db62112 100644 --- a/src/librustc_mir/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir/hair/pattern/const_to_pat.rs @@ -1,7 +1,7 @@ use crate::const_eval::const_variant_index; use rustc::hir; -use rustc::lint; +use rustc::hir::def_id::DefId; use rustc::mir::Field; use rustc::infer::InferCtxt; use rustc::traits::{ObligationCause, PredicateObligation}; @@ -20,18 +20,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). + /// + /// For literals, pass `None` as the `opt_const_def_id`; for a const + /// identifier, pass its `DefId`. pub(super) fn const_to_pat( &self, cv: &'tcx ty::Const<'tcx>, + opt_const_def_id: Option, id: hir::HirId, span: Span, ) -> Pat<'tcx> { - debug!("const_to_pat: cv={:#?} id={:?}", cv, id); - debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span); + debug!("const_def_to_pat: cv={:#?} const_def_id: {:?} id={:?}", cv, opt_const_def_id, id); + debug!("const_def_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, opt_const_def_id) }) } } @@ -67,85 +71,77 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } - fn search_for_structural_match_violation(&self, - ty: Ty<'tcx>) - -> Option> + fn search_const_def_for_structural_match_violation(&self, const_def_id: DefId) { - ty::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty) + assert!(const_def_id.is_local()); + self.tcx().infer_ctxt().enter(|infcx| { + ty::search_const_rhs_for_structural_match_violation( + &infcx, self.param_env, const_def_id, self.id, self.span); + }); + } + + fn search_ty_for_structural_match_violation(&self, ty: Ty<'tcx>) + { + let structural = ty::search_type_for_structural_match_violation( + self.id, self.span, self.tcx(), ty); + debug!("search_ty_for_structural_match_violation ty: {:?} returned: {:?}", ty, structural); + if let Some(non_sm_ty) = structural { + + // double-check there even *is* a semantic `PartialEq` to dispatch to. + // + // (If there isn't, then we can safely issue a hard + // error, because that's never worked, due to compiler + // using `PartialEq::eq` in this scenario in the past.) + // + // Note: To fix rust-lang/rust#65466, one could lift this check + // *before* any structural-match checking, and unconditionally error + // if `PartialEq` is not implemented. However, that breaks stable + // code at the moment, because types like `for <'a> fn(&'a ())` do + // not *yet* implement `PartialEq`. So for now we leave this here. + let warn_instead_of_hard_error: bool = { + let partial_eq_trait_id = self.tcx().lang_items().eq_trait().unwrap(); + let obligation: PredicateObligation<'_> = + self.tcx().predicate_for_trait_def( + self.param_env, + ObligationCause::misc(self.span, self.id), + partial_eq_trait_id, + 0, + ty, + &[]); + // FIXME: should this call a `predicate_must_hold` variant instead? + self.infcx.predicate_may_hold(&obligation) + }; + + debug!("call report_structural_match_violation non_sm_ty: {:?} id: {:?} warn: {:?}", + non_sm_ty, self.id, warn_instead_of_hard_error); + ty::report_structural_match_violation( + self.tcx(), non_sm_ty, self.id, self.span, warn_instead_of_hard_error); + } } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { ty::type_marked_structural(self.id, self.span, &self.infcx, ty) } - fn to_pat(&mut self, cv: &'tcx ty::Const<'tcx>) -> 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. - // - // once indirect_structural_match is a full fledged error, this - // level of indirection can be eliminated - + fn to_pat(&mut self, + cv: &'tcx ty::Const<'tcx>, + opt_const_def_id: Option) + -> Pat<'tcx> + { let inlined_const_as_pat = self.recur(cv); if self.include_lint_checks && !self.saw_const_match_error.get() { // If we were able to successfully convert the const to some pat, // double-check that all types in the const implement `Structural`. - - let structural = self.search_for_structural_match_violation(cv.ty); - debug!("search_for_structural_match_violation cv.ty: {:?} returned: {:?}", - cv.ty, structural); - if let Some(non_sm_ty) = structural { - let adt_def = match non_sm_ty { - ty::NonStructuralMatchTy::Adt(adt_def) => adt_def, - ty::NonStructuralMatchTy::Param => - bug!("use of constant whose type is a parameter inside a pattern"), - }; - let path = self.tcx().def_path_str(adt_def.did); - let msg = format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, - path, - ); - - // double-check there even *is* a semantic `PartialEq` to dispatch to. - // - // (If there isn't, then we can safely issue a hard - // error, because that's never worked, due to compiler - // using `PartialEq::eq` in this scenario in the past.) - // - // Note: To fix rust-lang/rust#65466, one could lift this check - // *before* any structural-match checking, and unconditionally error - // if `PartialEq` is not implemented. However, that breaks stable - // code at the moment, because types like `for <'a> fn(&'a ())` do - // not *yet* implement `PartialEq`. So for now we leave this here. - let ty_is_partial_eq: bool = { - let partial_eq_trait_id = self.tcx().lang_items().eq_trait().unwrap(); - let obligation: PredicateObligation<'_> = - self.tcx().predicate_for_trait_def( - self.param_env, - ObligationCause::misc(self.span, self.id), - partial_eq_trait_id, - 0, - cv.ty, - &[]); - // FIXME: should this call a `predicate_must_hold` variant instead? - self.infcx.predicate_may_hold(&obligation) - }; - - if !ty_is_partial_eq { - // span_fatal avoids ICE from resolution of non-existent method (rare case). - self.tcx().sess.span_fatal(self.span, &msg); - } else { - self.tcx().lint_hir(lint::builtin::INDIRECT_STRUCTURAL_MATCH, - self.id, - self.span, - &msg); + match opt_const_def_id { + Some(const_def_id) if const_def_id.is_local() => { + self.search_const_def_for_structural_match_violation(const_def_id); + } + _ => { + self.search_ty_for_structural_match_violation(cv.ty); } } } - inlined_const_as_pat } diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 1ecc78ba227ce..0a85e8f0aa787 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -863,7 +863,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; match self.tcx.at(span).const_eval(self.param_env.and(cid)) { Ok(value) => { - let pattern = self.const_to_pat(value, id, span); + let const_def_id = Some(instance.def_id()); + let pattern = self.const_to_pat(value, const_def_id, id, span); if !is_associated_const { return pattern; } @@ -930,7 +931,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ty = self.tables.expr_ty(expr); match lit_to_const(&lit.node, self.tcx, ty, false) { Ok(val) => { - *self.const_to_pat(val, expr.hir_id, lit.span).kind + *self.const_to_pat(val, None, expr.hir_id, lit.span).kind }, Err(LitToConstError::UnparseableFloat) => { self.errors.push(PatternError::FloatBug); @@ -948,7 +949,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; match lit_to_const(&lit.node, self.tcx, ty, true) { Ok(val) => { - *self.const_to_pat(val, expr.hir_id, lit.span).kind + *self.const_to_pat(val, None, expr.hir_id, lit.span).kind }, Err(LitToConstError::UnparseableFloat) => { self.errors.push(PatternError::FloatBug); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 001d98aece2a0..afaf59b2fa7ee 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1532,7 +1532,7 @@ pub fn checked_type_of(tcx: TyCtxt<'_>, def_id: DefId, fail: bool) -> Option>::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 e094256f5c827..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: lint level 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 298cc7c0ddc4f6e9037d30b3e0d79a4af78a412c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 5 Nov 2019 14:18:33 +0100 Subject: [PATCH 2/5] Handle `Index` a little more precisely in the HIR-based search. --- src/librustc/ty/structural_match.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustc/ty/structural_match.rs b/src/librustc/ty/structural_match.rs index e381ee24e066c..1c20dba736461 100644 --- a/src/librustc/ty/structural_match.rs +++ b/src/librustc/ty/structural_match.rs @@ -446,6 +446,13 @@ impl<'a, 'v, 'tcx> Visitor<'v> for SearchHirExpr<'a, 'tcx> { } } + hir::ExprKind::Index(base, _index) => { + // skip the index, focus solely on the base content. + // (alternative would be to do type-based analysis, which would + // be even more conservative). + intravisit::walk_expr(self, base); + } + hir::ExprKind::Loop(..) | hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) | @@ -456,7 +463,6 @@ impl<'a, 'v, 'tcx> Visitor<'v> for SearchHirExpr<'a, 'tcx> { hir::ExprKind::Assign(..) | hir::ExprKind::AssignOp(..) | hir::ExprKind::Field(..) | - hir::ExprKind::Index(..) | hir::ExprKind::Break(..) | hir::ExprKind::Continue(..) | hir::ExprKind::Ret(..) | From 337c0b7f8d56500a4a4b4dfa19caac331c62ad27 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 4 Nov 2019 15:14:36 +0100 Subject: [PATCH 3/5] This PR is fixing issue 62614, and we can mark that here by turning on the lint explicitly. --- src/test/ui/rfc1445/allow-use-behind-cousin-variant.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/ui/rfc1445/allow-use-behind-cousin-variant.rs b/src/test/ui/rfc1445/allow-use-behind-cousin-variant.rs index dca8aaef1500d..736c4244ce3ce 100644 --- a/src/test/ui/rfc1445/allow-use-behind-cousin-variant.rs +++ b/src/test/ui/rfc1445/allow-use-behind-cousin-variant.rs @@ -2,9 +2,8 @@ // have non-structural-match variants, *if* the constant itself does not use // any such variant. -// NOTE: for now, deliberately leaving the lint `indirect_structural_match` set -// to its default, so that we will not issue a diangostic even if -// rust-lang/rust#62614 remains an open issue. +// Even when we turn this on, there is no warning diangostic. +#![warn(indirect_structural_match)] // run-pass From 3eb56cf29ad144ba2855a6ffda489709b49e75ea Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 4 Nov 2019 15:36:44 +0100 Subject: [PATCH 4/5] Turned on INDIRECT_STRUCTURAL_MATCH lint to warn by default again. --- src/librustc/lint/builtin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 5c871bb6b6988..1d107c1655044 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -447,7 +447,7 @@ declare_lint! { declare_lint! { pub INDIRECT_STRUCTURAL_MATCH, // defaulting to allow until rust-lang/rust#62614 is fixed. - Allow, + Warn, "pattern with const indirectly referencing non-`#[structural_match]` type", @future_incompatible = FutureIncompatibleInfo { reference: "issue #62411 ", From 462b4b2633ff9eaa6d95d10ad0732f6bed5abd75 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 5 Nov 2019 14:18:46 +0100 Subject: [PATCH 5/5] Test suite. --- .../const_in_pattern/accept_structural.rs | 66 +++++++++++++++++ .../const_in_pattern/reject_non_partial_eq.rs | 32 ++++++++ .../reject_non_partial_eq.stderr | 8 ++ .../const_in_pattern/reject_non_structural.rs | 74 +++++++++++++++++++ .../reject_non_structural.stderr | 70 ++++++++++++++++++ .../const_in_pattern/warn_corner_cases.rs | 38 ++++++++++ .../const_in_pattern/warn_corner_cases.stderr | 32 ++++++++ 7 files changed, 320 insertions(+) create mode 100644 src/test/ui/consts/const_in_pattern/accept_structural.rs 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/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..35bf960e5faca --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.rs @@ -0,0 +1,74 @@ +#![warn(indirect_structural_match)] +//~^ NOTE lint level defined here + +// 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)] + +#[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)]` + + const FIELD: OND = TrivialEq(Some(NoDerive)).0; + match Some(NoDerive) { FIELD => dbg!(FIELD), _ => panic!("whoops"), }; + //~^ 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)]` + const TUPLE: (OND, OND) = (None, Some(NoDerive)); + match (None, Some(NoDerive)) { TUPLE => dbg!(TUPLE), _ => panic!("whoops"), }; + //~^ 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)]` + const ARRAY: [OND; 2] = [None, Some(NoDerive)]; + match [None, Some(NoDerive)] { ARRAY => dbg!(ARRAY), _ => panic!("whoops"), }; + //~^ 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)]` + 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)]` + const BLOCK: OND = { NoDerive; Some(NoDerive) }; + match Some(NoDerive) { BLOCK => dbg!(BLOCK), _ => panic!("whoops"), }; + //~^ 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..cdac502f87ef4 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/reject_non_structural.stderr @@ -0,0 +1,70 @@ +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:44: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:48: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:51: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:54: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:57: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:60: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:64: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:67: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:70:29 + | +LL | match &Some(NoDerive) { ADDR_OF => dbg!(ADDR_OF), _ => panic!("whoops"), }; + | ^^^^^^^ + | +note: lint level defined here + --> $DIR/reject_non_structural.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: aborting due to 9 previous errors + 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..0782d7bfd8c01 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/warn_corner_cases.rs @@ -0,0 +1,38 @@ +// 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)]` + + 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)]` + + 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)]` +} 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..372fda3522475 --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/warn_corner_cases.stderr @@ -0,0 +1,32 @@ +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: lint level 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:31: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:36: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 +