diff options
| author | León Orell Valerian Liehr <me@fmease.dev> | 2024-12-05 07:29:54 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-05 07:29:54 +0100 |
| commit | 626db0640952ed4cce66ca3f03bfcb8897df9eda (patch) | |
| tree | cd46b6561e50cde851aaa408296067a0d31416f3 /compiler | |
| parent | bc13c82e6e3ddeec1ca3d72940452ffa865837cd (diff) | |
| parent | 4e6a401b224342287469a1a294bba7ad10e8641c (diff) | |
| download | rust-626db0640952ed4cce66ca3f03bfcb8897df9eda.tar.gz rust-626db0640952ed4cce66ca3f03bfcb8897df9eda.zip | |
Rollup merge of #133233 - estebank:const-errors, r=Nadrieril
Add context to "const in pattern" errors *Each commit addresses specific diagnostics.* - Add primary span labels - Point at `const` item, and `const` generic param definition - Reword messages and notes - Point at generic param through which an associated `const` is being referenced - Silence const in pattern with evaluation errors when they come from `const` items that already emit a diagnostic - On non-structural type in const used as pattern, point at the type that should derive `PartialEq`
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_mir_build/messages.ftl | 37 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/errors.rs | 30 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs | 309 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/mod.rs | 14 |
4 files changed, 307 insertions, 83 deletions
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index f28cf84fa69..f647486f62a 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -84,12 +84,17 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable -mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns +mir_build_const_defined_here = constant defined here -mir_build_const_pattern_depends_on_generic_parameter = - constant pattern depends on a generic parameter +mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns + .label = can't be used in patterns +mir_build_const_param_in_pattern_def = constant defined here + +mir_build_const_pattern_depends_on_generic_parameter = constant pattern cannot depend on generic parameters + .label = `const` depends on a generic parameter mir_build_could_not_eval_const_pattern = could not evaluate constant pattern + .label = could not evaluate constant mir_build_deref_raw_pointer_requires_unsafe = dereference of raw pointer is unsafe and requires unsafe block @@ -147,7 +152,8 @@ mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_interpreted_as_const = introduce a variable instead -mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns +mir_build_invalid_pattern = {$prefix} `{$non_sm_ty}` cannot be used in patterns + .label = {$prefix} can't be used in patterns mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count -> [one] pattern @@ -244,10 +250,12 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa .label = mutation of layout constrained field mir_build_nan_pattern = cannot use NaN in patterns + .label = evaluates to `NaN`, which is not allowed in patterns .note = NaNs compare inequal to everything, even themselves, so this pattern would never match .help = try using the `is_nan` method instead mir_build_non_const_path = runtime values cannot be referenced in patterns + .label = references a runtime value mir_build_non_empty_never_pattern = mismatched types @@ -265,13 +273,15 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type .suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown .help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern -mir_build_non_partial_eq_match = - to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq` +mir_build_non_partial_eq_match = constant of non-structural type `{$ty}` in a pattern + .label = constant of non-structural type mir_build_pattern_not_covered = refutable pattern in {$origin} .pattern_ty = the matched value is of type `{$pattern_ty}` -mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. +mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon + .label = can't be used in patterns + .note = see https://github.com/rust-lang/rust/issues/70861 for details mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future @@ -283,6 +293,8 @@ mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly .missing_box = `#[rustc_box]` requires the `owned_box` lang item mir_build_static_in_pattern = statics cannot be referenced in patterns + .label = can't be used in patterns +mir_build_static_in_pattern_def = `static` defined here mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits @@ -310,12 +322,12 @@ mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count -> *[other] them } into the body -mir_build_type_not_structural = - to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]` - +mir_build_type_not_structural = constant of non-structural type `{$ty}` in a pattern + .label = constant of non-structural type +mir_build_type_not_structural_def = `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details - -mir_build_type_not_structural_tip = the traits must be derived, manual `impl`s are not sufficient +mir_build_type_not_structural_tip = + the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details mir_build_unconditional_recursion = function cannot return without recursing .label = cannot return without recursing @@ -334,6 +346,7 @@ mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .label = access to union field mir_build_union_pattern = cannot use unions in constant patterns + .label = can't use a `union` here mir_build_unreachable_making_this_unreachable = collectively making this unreachable diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 58487a48184..3632da943e1 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -631,20 +631,27 @@ pub(crate) struct NonExhaustiveMatchAllArmsGuarded; #[diag(mir_build_static_in_pattern, code = E0158)] pub(crate) struct StaticInPattern { #[primary_span] + #[label] pub(crate) span: Span, + #[label(mir_build_static_in_pattern_def)] + pub(crate) static_span: Span, } #[derive(Diagnostic)] #[diag(mir_build_const_param_in_pattern, code = E0158)] pub(crate) struct ConstParamInPattern { #[primary_span] + #[label] pub(crate) span: Span, + #[label(mir_build_const_param_in_pattern_def)] + pub(crate) const_span: Span, } #[derive(Diagnostic)] #[diag(mir_build_non_const_path, code = E0080)] pub(crate) struct NonConstPath { #[primary_span] + #[label] pub(crate) span: Span, } @@ -695,6 +702,7 @@ pub(crate) struct WantedConstant { #[diag(mir_build_const_pattern_depends_on_generic_parameter, code = E0158)] pub(crate) struct ConstPatternDependsOnGenericParameter { #[primary_span] + #[label] pub(crate) span: Span, } @@ -702,6 +710,7 @@ pub(crate) struct ConstPatternDependsOnGenericParameter { #[diag(mir_build_could_not_eval_const_pattern)] pub(crate) struct CouldNotEvalConstPattern { #[primary_span] + #[label] pub(crate) span: Span, } @@ -867,33 +876,43 @@ pub(crate) enum Conflict { #[diag(mir_build_union_pattern)] pub(crate) struct UnionPattern { #[primary_span] + #[label] pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_type_not_structural)] -#[note(mir_build_type_not_structural_tip)] -#[note(mir_build_type_not_structural_more_info)] pub(crate) struct TypeNotStructural<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, - pub(crate) non_sm_ty: Ty<'tcx>, + #[label(mir_build_type_not_structural_def)] + pub(crate) ty_def_span: Span, + pub(crate) ty: Ty<'tcx>, + #[note(mir_build_type_not_structural_tip)] + pub(crate) manual_partialeq_impl_span: Option<Span>, + #[note(mir_build_type_not_structural_more_info)] + pub(crate) manual_partialeq_impl_note: bool, } #[derive(Diagnostic)] #[diag(mir_build_non_partial_eq_match)] +#[note(mir_build_type_not_structural_more_info)] pub(crate) struct TypeNotPartialEq<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, - pub(crate) non_peq_ty: Ty<'tcx>, + pub(crate) ty: Ty<'tcx>, } #[derive(Diagnostic)] #[diag(mir_build_invalid_pattern)] pub(crate) struct InvalidPattern<'tcx> { #[primary_span] + #[label] pub(crate) span: Span, pub(crate) non_sm_ty: Ty<'tcx>, + pub(crate) prefix: String, } #[derive(Diagnostic)] @@ -910,13 +929,16 @@ pub(crate) struct UnsizedPattern<'tcx> { #[help] pub(crate) struct NaNPattern { #[primary_span] + #[label] pub(crate) span: Span, } #[derive(Diagnostic)] #[diag(mir_build_pointer_pattern)] +#[note] pub(crate) struct PointerPattern { #[primary_span] + #[label] pub(crate) span: Span, } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 5db08f01fdb..aed00aecefc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,14 +1,17 @@ use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Diag; use rustc_hir as hir; use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeVisitor, ValTree}; use rustc_middle::{mir, span_bug}; -use rustc_span::Span; +use rustc_span::def_id::DefId; +use rustc_span::{Span, sym}; use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use tracing::{debug, instrument, trace}; @@ -35,7 +38,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { id: hir::HirId, span: Span, ) -> Box<Pat<'tcx>> { - let mut convert = ConstToPat::new(self, id, span); + let mut convert = ConstToPat::new(self, id, span, c); match c.kind() { ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty), @@ -49,21 +52,26 @@ struct ConstToPat<'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, span: Span, + id: hir::HirId, treat_byte_string_as_slice: bool, + + c: ty::Const<'tcx>, } impl<'tcx> ConstToPat<'tcx> { - fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span) -> Self { + fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, + id, treat_byte_string_as_slice: pat_ctxt .typeck_results .treat_byte_string_as_slice .contains(&id.local_id), + c, } } @@ -71,13 +79,32 @@ impl<'tcx> ConstToPat<'tcx> { ty.is_structural_eq_shallow(self.tcx) } + /// We errored. Signal that in the pattern, so that follow up errors can be silenced. + fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> { + if let ty::ConstKind::Unevaluated(uv) = self.c.kind() { + let def_kind = self.tcx.def_kind(uv.def); + if let hir::def::DefKind::AssocConst = def_kind + && let Some(def_id) = uv.def.as_local() + { + // Include the container item in the output. + err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); + } + if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind { + err.span_label( + self.tcx.def_span(uv.def), + crate::fluent_generated::mir_build_const_defined_here, + ); + } + } + Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) }) + } + fn unevaluated_to_pat( &mut self, uv: ty::UnevaluatedConst<'tcx>, ty: Ty<'tcx>, ) -> Box<Pat<'tcx>> { trace!(self.treat_byte_string_as_slice); - let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind }); // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even @@ -96,32 +123,60 @@ impl<'tcx> ConstToPat<'tcx> { Ok(Ok(c)) => c, Err(ErrorHandled::Reported(_, _)) => { // Let's tell the use where this failing const occurs. - let e = self.tcx.dcx().emit_err(CouldNotEvalConstPattern { span: self.span }); - return pat_from_kind(PatKind::Error(e)); + let mut err = + self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); + // We've emitted an error on the original const, it would be redundant to complain + // on its use as well. + if let ty::ConstKind::Unevaluated(uv) = self.c.kind() + && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = + self.tcx.def_kind(uv.def) + { + err.downgrade_to_delayed_bug(); + } + return self.mk_err(err, ty); } Err(ErrorHandled::TooGeneric(_)) => { - let e = self + let mut e = self .tcx .dcx() - .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }); - return pat_from_kind(PatKind::Error(e)); + .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); + for arg in uv.args { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Param(param_ty) = ty.kind() + { + let def_id = self.tcx.hir().enclosing_body_owner(self.id); + let generics = self.tcx.generics_of(def_id); + let param = generics.type_param(*param_ty, self.tcx); + let span = self.tcx.def_span(param.def_id); + e.span_label(span, "constant depends on this generic parameter"); + if let Some(ident) = self.tcx.def_ident_span(def_id) + && self.tcx.sess.source_map().is_multiline(ident.between(span)) + { + // Display the `fn` name as well in the diagnostic, as the generic isn't + // in the same line and it could be confusing otherwise. + e.span_label(ident, ""); + } + } + } + return self.mk_err(e, ty); } Ok(Err(bad_ty)) => { // The pattern cannot be turned into a valtree. let e = match bad_ty.kind() { ty::Adt(def, ..) => { assert!(def.is_union()); - self.tcx.dcx().emit_err(UnionPattern { span: self.span }) + self.tcx.dcx().create_err(UnionPattern { span: self.span }) } ty::FnPtr(..) | ty::RawPtr(..) => { - self.tcx.dcx().emit_err(PointerPattern { span: self.span }) + self.tcx.dcx().create_err(PointerPattern { span: self.span }) } - _ => self - .tcx - .dcx() - .emit_err(InvalidPattern { span: self.span, non_sm_ty: bad_ty }), + _ => self.tcx.dcx().create_err(InvalidPattern { + span: self.span, + non_sm_ty: bad_ty, + prefix: bad_ty.prefix_string(self.tcx).to_string(), + }), }; - return pat_from_kind(PatKind::Error(e)); + return self.mk_err(e, ty); } }; @@ -130,42 +185,16 @@ impl<'tcx> ConstToPat<'tcx> { if !inlined_const_as_pat.references_error() { // Always check for `PartialEq` if we had no other errors yet. - if !self.type_has_partial_eq_impl(ty) { - let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; - let e = self.tcx.dcx().emit_err(err); - return pat_from_kind(PatKind::Error(e)); + if !type_has_partial_eq_impl(self.tcx, typing_env, ty).0 { + let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty }); + extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err); + return self.mk_err(err, ty); } } inlined_const_as_pat } - #[instrument(level = "trace", skip(self), ret)] - fn type_has_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool { - let (infcx, param_env) = self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); - // 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.) - let partial_eq_trait_id = - self.tcx.require_lang_item(hir::LangItem::PartialEq, Some(self.span)); - let partial_eq_obligation = Obligation::new( - self.tcx, - ObligationCause::dummy(), - param_env, - ty::TraitRef::new(self.tcx, partial_eq_trait_id, [ty, ty]), - ); - - // This *could* accept a type that isn't actually `PartialEq`, because region bounds get - // ignored. However that should be pretty much impossible since consts that do not depend on - // generics can only mention the `'static` lifetime, and how would one have a type that's - // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem - // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck - // can ensure that the type really implements `PartialEq`. - infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation) - } - fn field_pats( &self, vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>, @@ -190,10 +219,25 @@ impl<'tcx> ConstToPat<'tcx> { // Extremely important check for all ADTs! Make sure they opted-in to be used in // patterns. debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty); - let err = TypeNotStructural { span, non_sm_ty: ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + let (_impls_partial_eq, derived, structural, impl_def_id) = + type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + let (manual_partialeq_impl_span, manual_partialeq_impl_note) = + match (structural, impl_def_id) { + (true, _) => (None, false), + (_, Some(def_id)) if def_id.is_local() && !derived => { + (Some(tcx.def_span(def_id)), false) + } + _ => (None, true), + }; + let ty_def_span = tcx.def_span(adt_def.did()); + let err = TypeNotStructural { + span, + ty, + ty_def_span, + manual_partialeq_impl_span, + manual_partialeq_impl_note, + }; + return self.mk_err(tcx.dcx().create_err(err), ty); } ty::Adt(adt_def, args) if adt_def.is_enum() => { let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap(); @@ -207,7 +251,7 @@ impl<'tcx> ConstToPat<'tcx> { adt_def.variants()[variant_index] .fields .iter() - .map(|field| field.ty(self.tcx, args)), + .map(|field| field.ty(tcx, args)), ), ), } @@ -216,7 +260,7 @@ impl<'tcx> ConstToPat<'tcx> { assert!(!def.is_union()); // Valtree construction would never succeed for unions. PatKind::Leaf { subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip( - def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx, args)), + def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)), )), } } @@ -252,10 +296,10 @@ impl<'tcx> ConstToPat<'tcx> { // deref pattern. _ => { if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() { - let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + return self.mk_err( + tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }), + ty, + ); } else { // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. @@ -286,8 +330,7 @@ impl<'tcx> ConstToPat<'tcx> { if is_nan { // NaNs are not ever equal to anything so they make no sense as patterns. // Also see <https://github.com/rust-lang/rfcs/pull/3535>. - let e = tcx.dcx().emit_err(NaNPattern { span }); - PatKind::Error(e) + return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty); } else { PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)), @@ -305,13 +348,153 @@ impl<'tcx> ConstToPat<'tcx> { ) } _ => { - let err = InvalidPattern { span, non_sm_ty: ty }; - let e = tcx.dcx().emit_err(err); - // We errored. Signal that in the pattern, so that follow up errors can be silenced. - PatKind::Error(e) + let err = InvalidPattern { + span, + non_sm_ty: ty, + prefix: ty.prefix_string(tcx).to_string(), + }; + return self.mk_err(tcx.dcx().create_err(err), ty); } }; Box::new(Pat { span, ty, kind }) } } + +/// Given a type with type parameters, visit every ADT looking for types that need to +/// `#[derive(PartialEq)]` for it to be a structural type. +fn extend_type_not_partial_eq<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + err: &mut Diag<'_>, +) { + /// Collect all types that need to be `StructuralPartialEq`. + struct UsedParamsNeedInstantiationVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + /// The user has written `impl PartialEq for Ty` which means it's non-structual. + adts_with_manual_partialeq: FxHashSet<Span>, + /// The type has no `PartialEq` implementation, neither manual or derived. + adts_without_partialeq: FxHashSet<Span>, + /// The user has written `impl PartialEq for Ty` which means it's non-structual, + /// but we don't have a span to point at, so we'll just add them as a `note`. + manual: Vec<Ty<'tcx>>, + /// The type has no `PartialEq` implementation, neither manual or derived, but + /// we don't have a span to point at, so we'll just add them as a `note`. + without: Vec<Ty<'tcx>>, + } + + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::Adt(def, _args) = ty.kind() { + let ty_def_id = def.did(); + let ty_def_span = self.tcx.def_span(ty_def_id); + let (impls_partial_eq, derived, structural, impl_def_id) = + type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + match (impls_partial_eq, derived, structural, impl_def_id) { + (_, _, true, _) => {} + (true, false, _, Some(def_id)) if def_id.is_local() => { + self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id)); + } + (true, false, _, _) if ty_def_id.is_local() => { + self.adts_with_manual_partialeq.insert(ty_def_span); + } + (false, _, _, _) if ty_def_id.is_local() => { + self.adts_without_partialeq.insert(ty_def_span); + } + (true, false, _, _) => { + self.manual.push(ty); + } + (false, _, _, _) => { + self.without.push(ty); + } + _ => {} + }; + } + use rustc_middle::ty::TypeSuperVisitable; + ty.super_visit_with(self) + } + } + let mut v = UsedParamsNeedInstantiationVisitor { + tcx, + typing_env, + adts_with_manual_partialeq: FxHashSet::default(), + adts_without_partialeq: FxHashSet::default(), + manual: vec![], + without: vec![], + }; + v.visit_ty(ty); + #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering + for span in v.adts_with_manual_partialeq { + err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"); + } + #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering + for span in v.adts_without_partialeq { + err.span_label( + span, + "must be annotated with `#[derive(PartialEq)]` to be usable in patterns", + ); + } + for ty in v.manual { + err.note(format!( + "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" + )); + } + for ty in v.without { + err.note(format!( + "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns" + )); + } +} + +#[instrument(level = "trace", skip(tcx), ret)] +fn type_has_partial_eq_impl<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, +) -> ( + /* has impl */ bool, + /* is derived */ bool, + /* structural partial eq */ bool, + /* non-blanket impl */ Option<DefId>, +) { + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + // 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.) + let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, None); + let structural_partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::StructuralPeq, None); + + let partial_eq_obligation = Obligation::new( + tcx, + ObligationCause::dummy(), + param_env, + ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]), + ); + + let mut automatically_derived = false; + let mut structural_peq = false; + let mut impl_def_id = None; + for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) { + automatically_derived = tcx.has_attr(def_id, sym::automatically_derived); + impl_def_id = Some(def_id); + } + for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) { + structural_peq = true; + } + // This *could* accept a type that isn't actually `PartialEq`, because region bounds get + // ignored. However that should be pretty much impossible since consts that do not depend on + // generics can only mention the `'static` lifetime, and how would one have a type that's + // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem + // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck + // can ensure that the type really implements `PartialEq`. + ( + infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation), + automatically_derived, + structural_peq, + impl_def_id, + ) +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 08c6b4abd3b..3ac53fa6272 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -528,11 +528,17 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { | Res::SelfCtor(..) => PatKind::Leaf { subpatterns }, _ => { let e = match res { - Res::Def(DefKind::ConstParam, _) => { - self.tcx.dcx().emit_err(ConstParamInPattern { span }) + Res::Def(DefKind::ConstParam, def_id) => { + self.tcx.dcx().emit_err(ConstParamInPattern { + span, + const_span: self.tcx().def_span(def_id), + }) } - Res::Def(DefKind::Static { .. }, _) => { - self.tcx.dcx().emit_err(StaticInPattern { span }) + Res::Def(DefKind::Static { .. }, def_id) => { + self.tcx.dcx().emit_err(StaticInPattern { + span, + static_span: self.tcx().def_span(def_id), + }) } _ => self.tcx.dcx().emit_err(NonConstPath { span }), }; |
