1use core::ops::ControlFlow;
23use rustc_abi::{FieldIdx, VariantIdx};
4use rustc_apfloat::Float;
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::Diag;
7use rustc_hiras hir;
8use rustc_hir::attrs::AttributeKind;
9use rustc_hir::find_attr;
10use rustc_index::Idx;
11use rustc_infer::infer::TyCtxtInferExt;
12use rustc_infer::traits::Obligation;
13use rustc_middle::mir::interpret::ErrorHandled;
14use rustc_middle::span_bug;
15use rustc_middle::thir::{FieldPat, Pat, PatKind};
16use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor};
17use rustc_span::def_id::DefId;
18use rustc_span::{DUMMY_SP, Span};
19use rustc_trait_selection::traits::ObligationCause;
20use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
21use tracing::{debug, instrument, trace};
2223use super::PatCtxt;
24use crate::errors::{
25ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern,
26PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern,
27};
2829impl<'tcx> PatCtxt<'tcx> {
30/// Converts a constant to a pattern (if possible).
31 /// This means aggregate values (like structs and enums) are converted
32 /// to a pattern that matches the value (as if you'd compared via structural equality).
33 ///
34 /// Only type system constants are supported, as we are using valtrees
35 /// as an intermediate step. Unfortunately those don't carry a type
36 /// so we have to carry one ourselves.
37x;#[instrument(level = "debug", skip(self), ret)]38pub(super) fn const_to_pat(
39&self,
40 c: ty::Const<'tcx>,
41 ty: Ty<'tcx>,
42 id: hir::HirId,
43 span: Span,
44 ) -> Box<Pat<'tcx>> {
45let mut convert = ConstToPat::new(self, id, span, c);
4647match c.kind() {
48 ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
49 ty::ConstKind::Value(value) => convert.valtree_to_pat(value),
50_ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
51 }
52 }
53}
5455struct ConstToPat<'tcx> {
56 tcx: TyCtxt<'tcx>,
57 typing_env: ty::TypingEnv<'tcx>,
58 span: Span,
59 id: hir::HirId,
6061 c: ty::Const<'tcx>,
62}
6364impl<'tcx> ConstToPat<'tcx> {
65fn new(pat_ctxt: &PatCtxt<'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
66{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs:66",
"rustc_mir_build::thir::pattern::const_to_pat",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs"),
::tracing_core::__macro_support::Option::Some(66u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::const_to_pat"),
::tracing_core::field::FieldSet::new(&["pat_ctxt.typeck_results.hir_owner"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&pat_ctxt.typeck_results.hir_owner)
as &dyn Value))])
});
} else { ; }
};trace!(?pat_ctxt.typeck_results.hir_owner);
67ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c }
68 }
6970fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
71ty.is_structural_eq_shallow(self.tcx)
72 }
7374/// We errored. Signal that in the pattern, so that follow up errors can be silenced.
75fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
76if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
77let def_kind = self.tcx.def_kind(uv.def);
78if let hir::def::DefKind::AssocConst = def_kind79 && let Some(def_id) = uv.def.as_local()
80 {
81// Include the container item in the output.
82err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
83 }
84if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind {
85err.span_label(
86self.tcx.def_span(uv.def),
87crate::fluent_generated::mir_build_const_defined_here,
88 );
89 }
90 }
91Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None })
92 }
9394fn unevaluated_to_pat(
95&mut self,
96 uv: ty::UnevaluatedConst<'tcx>,
97 ty: Ty<'tcx>,
98 ) -> Box<Pat<'tcx>> {
99// It's not *technically* correct to be revealing opaque types here as borrowcheck has
100 // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
101 // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
102 // As a result we always use a revealed env when resolving the instance to evaluate.
103 //
104 // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
105 // instead of having this logic here
106let typing_env = self107 .tcx
108 .erase_and_anonymize_regions(self.typing_env)
109 .with_post_analysis_normalized(self.tcx);
110let uv = self.tcx.erase_and_anonymize_regions(uv);
111112// try to resolve e.g. associated constants to their definition on an impl, and then
113 // evaluate the const.
114let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) {
115Ok(Ok(c)) => c,
116Err(ErrorHandled::Reported(_, _)) => {
117// Let's tell the use where this failing const occurs.
118let mut err =
119self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
120// We've emitted an error on the original const, it would be redundant to complain
121 // on its use as well.
122if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
123 && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst =
124self.tcx.def_kind(uv.def)
125 {
126err.downgrade_to_delayed_bug();
127 }
128return self.mk_err(err, ty);
129 }
130Err(ErrorHandled::TooGeneric(_)) => {
131let mut e = self132 .tcx
133 .dcx()
134 .create_err(ConstPatternDependsOnGenericParameter { span: self.span });
135for arg in uv.args {
136if let ty::GenericArgKind::Type(ty) = arg.kind()
137 && let ty::Param(param_ty) = ty.kind()
138 {
139let def_id = self.tcx.hir_enclosing_body_owner(self.id);
140let generics = self.tcx.generics_of(def_id);
141let param = generics.type_param(*param_ty, self.tcx);
142let span = self.tcx.def_span(param.def_id);
143 e.span_label(span, "constant depends on this generic parameter");
144if let Some(ident) = self.tcx.def_ident_span(def_id)
145 && self.tcx.sess.source_map().is_multiline(ident.between(span))
146 {
147// Display the `fn` name as well in the diagnostic, as the generic isn't
148 // in the same line and it could be confusing otherwise.
149e.span_label(ident, "");
150 }
151 }
152 }
153return self.mk_err(e, ty);
154 }
155Ok(Err(bad_ty)) => {
156// The pattern cannot be turned into a valtree.
157let e = match bad_ty.kind() {
158 ty::Adt(def, ..) => {
159if !def.is_union() {
::core::panicking::panic("assertion failed: def.is_union()")
};assert!(def.is_union());
160self.tcx.dcx().create_err(UnionPattern { span: self.span })
161 }
162 ty::FnPtr(..) | ty::RawPtr(..) => {
163self.tcx.dcx().create_err(PointerPattern { span: self.span })
164 }
165_ => self.tcx.dcx().create_err(InvalidPattern {
166 span: self.span,
167 non_sm_ty: bad_ty,
168 prefix: bad_ty.prefix_string(self.tcx).to_string(),
169 }),
170 };
171return self.mk_err(e, ty);
172 }
173 };
174175// Lower the valtree to a THIR pattern.
176let mut thir_pat = self.valtree_to_pat(ty::Value { ty, valtree });
177178if !thir_pat.references_error() {
179// Always check for `PartialEq` if we had no other errors yet.
180if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl {
181let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
182extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
183return self.mk_err(err, ty);
184 }
185 }
186187// Mark the pattern to indicate that it is the result of lowering a named
188 // constant. This is used for diagnostics.
189thir_pat.extra.get_or_insert_default().expanded_const = Some(uv.def);
190thir_pat191 }
192193fn lower_field_values_to_fieldpats(
194&self,
195 values: impl Iterator<Item = ty::Value<'tcx>>,
196 ) -> Vec<FieldPat<'tcx>> {
197values198 .enumerate()
199 .map(|(index, value)| FieldPat {
200 field: FieldIdx::new(index),
201 pattern: *self.valtree_to_pat(value),
202 })
203 .collect()
204 }
205206// Recursive helper for `to_pat`; invoke that (instead of calling this directly).
207#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("valtree_to_pat",
"rustc_mir_build::thir::pattern::const_to_pat",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs"),
::tracing_core::__macro_support::Option::Some(207u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::const_to_pat"),
::tracing_core::field::FieldSet::new(&["value"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&value)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: Box<Pat<'tcx>> = loop {};
return __tracing_attr_fake_return;
}
{
let span = self.span;
let tcx = self.tcx;
let ty::Value { ty, valtree } = value;
let kind =
match ty.kind() {
ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs:218",
"rustc_mir_build::thir::pattern::const_to_pat",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs"),
::tracing_core::__macro_support::Option::Some(218u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::const_to_pat"),
::tracing_core::field::FieldSet::new(&["message", "adt_def",
"value.ty"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("ADT type in pattern is not `type_marked_structural`")
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&adt_def) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&value.ty)
as &dyn Value))])
});
} else { ; }
};
let PartialEqImplStatus {
is_derived, structural_partial_eq, non_blanket_impl, .. } =
type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
let (manual_partialeq_impl_span,
manual_partialeq_impl_note) =
match (structural_partial_eq, non_blanket_impl) {
(true, _) => (None, false),
(_, Some(def_id)) if def_id.is_local() && !is_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) =
valtree.to_branch().split_first().unwrap();
let variant_index =
VariantIdx::from_u32(variant_index.to_leaf().to_u32());
PatKind::Variant {
adt_def: *adt_def,
args,
variant_index,
subpatterns: self.lower_field_values_to_fieldpats(fields.iter().map(|ct|
ct.to_value())),
}
}
ty::Adt(def, _) => {
if !!def.is_union() {
::core::panicking::panic("assertion failed: !def.is_union()")
};
PatKind::Leaf {
subpatterns: self.lower_field_values_to_fieldpats(valtree.to_branch().iter().map(|ct|
ct.to_value())),
}
}
ty::Tuple(_) =>
PatKind::Leaf {
subpatterns: self.lower_field_values_to_fieldpats(valtree.to_branch().iter().map(|ct|
ct.to_value())),
},
ty::Slice(_) =>
PatKind::Slice {
prefix: valtree.to_branch().iter().map(|val|
*self.valtree_to_pat(val.to_value())).collect(),
slice: None,
suffix: Box::new([]),
},
ty::Array(_, _) =>
PatKind::Array {
prefix: valtree.to_branch().iter().map(|val|
*self.valtree_to_pat(val.to_value())).collect(),
slice: None,
suffix: Box::new([]),
},
ty::Str => { PatKind::Constant { value } }
ty::Ref(_, pointee_ty, ..) => {
if pointee_ty.is_str() || pointee_ty.is_slice() ||
pointee_ty.is_sized(tcx, self.typing_env) {
PatKind::Deref {
pin: hir::Pinnedness::Not,
subpattern: self.valtree_to_pat(ty::Value {
ty: *pointee_ty,
valtree,
}),
}
} else {
return self.mk_err(tcx.dcx().create_err(UnsizedPattern {
span,
non_sm_ty: *pointee_ty,
}), ty);
}
}
ty::Float(flt) => {
let v = valtree.to_leaf();
let is_nan =
match flt {
ty::FloatTy::F16 => v.to_f16().is_nan(),
ty::FloatTy::F32 => v.to_f32().is_nan(),
ty::FloatTy::F64 => v.to_f64().is_nan(),
ty::FloatTy::F128 => v.to_f128().is_nan(),
};
if is_nan {
return self.mk_err(tcx.dcx().create_err(NaNPattern {
span,
}), ty);
} else { PatKind::Constant { value } }
}
ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_)
| ty::RawPtr(..) => {
PatKind::Constant { value }
}
ty::FnPtr(..) => {
{
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Valtree construction would never succeed for FnPtr, so this is unreachable.")));
}
}
_ => {
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, extra: None })
}
}
}#[instrument(skip(self), level = "debug")]208fn valtree_to_pat(&self, value: ty::Value<'tcx>) -> Box<Pat<'tcx>> {
209let span = self.span;
210let tcx = self.tcx;
211let ty::Value { ty, valtree } = value;
212213let kind = match ty.kind() {
214// Extremely important check for all ADTs!
215 // Make sure they are eligible to be used in patterns, and if not, emit an error.
216ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
217// This ADT cannot be used as a constant in patterns.
218debug!(?adt_def, ?value.ty, "ADT type in pattern is not `type_marked_structural`");
219let PartialEqImplStatus {
220 is_derived, structural_partial_eq, non_blanket_impl, ..
221 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
222let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
223match (structural_partial_eq, non_blanket_impl) {
224 (true, _) => (None, false),
225 (_, Some(def_id)) if def_id.is_local() && !is_derived => {
226 (Some(tcx.def_span(def_id)), false)
227 }
228_ => (None, true),
229 };
230let ty_def_span = tcx.def_span(adt_def.did());
231let err = TypeNotStructural {
232 span,
233 ty,
234 ty_def_span,
235 manual_partialeq_impl_span,
236 manual_partialeq_impl_note,
237 };
238return self.mk_err(tcx.dcx().create_err(err), ty);
239 }
240 ty::Adt(adt_def, args) if adt_def.is_enum() => {
241let (&variant_index, fields) = valtree.to_branch().split_first().unwrap();
242let variant_index = VariantIdx::from_u32(variant_index.to_leaf().to_u32());
243 PatKind::Variant {
244 adt_def: *adt_def,
245 args,
246 variant_index,
247 subpatterns: self
248.lower_field_values_to_fieldpats(fields.iter().map(|ct| ct.to_value())),
249 }
250 }
251 ty::Adt(def, _) => {
252assert!(!def.is_union()); // Valtree construction would never succeed for unions.
253PatKind::Leaf {
254 subpatterns: self.lower_field_values_to_fieldpats(
255 valtree.to_branch().iter().map(|ct| ct.to_value()),
256 ),
257 }
258 }
259 ty::Tuple(_) => PatKind::Leaf {
260 subpatterns: self.lower_field_values_to_fieldpats(
261 valtree.to_branch().iter().map(|ct| ct.to_value()),
262 ),
263 },
264 ty::Slice(_) => PatKind::Slice {
265 prefix: valtree
266 .to_branch()
267 .iter()
268 .map(|val| *self.valtree_to_pat(val.to_value()))
269 .collect(),
270 slice: None,
271 suffix: Box::new([]),
272 },
273 ty::Array(_, _) => PatKind::Array {
274 prefix: valtree
275 .to_branch()
276 .iter()
277 .map(|val| *self.valtree_to_pat(val.to_value()))
278 .collect(),
279 slice: None,
280 suffix: Box::new([]),
281 },
282 ty::Str => {
283// Constant/literal patterns of type `&str` are lowered to a
284 // `PatKind::Deref` wrapping a `PatKind::Constant` of type `str`.
285 // This pattern node is the `str` constant part.
286 //
287 // Under `feature(deref_patterns)`, string literal patterns can also
288 // have type `str` directly, without the `&`, in order to allow things
289 // like `deref!("...")` to work when the scrutinee is `String`.
290PatKind::Constant { value }
291 }
292 ty::Ref(_, pointee_ty, ..) => {
293if pointee_ty.is_str()
294 || pointee_ty.is_slice()
295 || pointee_ty.is_sized(tcx, self.typing_env)
296 {
297 PatKind::Deref {
298// This node has type `ty::Ref`, so it's not a pin-deref.
299pin: hir::Pinnedness::Not,
300// Lower the valtree to a pattern as the pointee type.
301 // This works because references have the same valtree
302 // representation as their pointee.
303subpattern: self.valtree_to_pat(ty::Value { ty: *pointee_ty, valtree }),
304 }
305 } else {
306return self.mk_err(
307 tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
308 ty,
309 );
310 }
311 }
312 ty::Float(flt) => {
313let v = valtree.to_leaf();
314let is_nan = match flt {
315 ty::FloatTy::F16 => v.to_f16().is_nan(),
316 ty::FloatTy::F32 => v.to_f32().is_nan(),
317 ty::FloatTy::F64 => v.to_f64().is_nan(),
318 ty::FloatTy::F128 => v.to_f128().is_nan(),
319 };
320if is_nan {
321// NaNs are not ever equal to anything so they make no sense as patterns.
322 // Also see <https://github.com/rust-lang/rfcs/pull/3535>.
323return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
324 } else {
325 PatKind::Constant { value }
326 }
327 }
328 ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
329// The raw pointers we see here have been "vetted" by valtree construction to be
330 // just integers, so we simply allow them.
331PatKind::Constant { value }
332 }
333 ty::FnPtr(..) => {
334unreachable!(
335"Valtree construction would never succeed for FnPtr, so this is unreachable."
336)
337 }
338_ => {
339let err = InvalidPattern {
340 span,
341 non_sm_ty: ty,
342 prefix: ty.prefix_string(tcx).to_string(),
343 };
344return self.mk_err(tcx.dcx().create_err(err), ty);
345 }
346 };
347348 Box::new(Pat { span, ty, kind, extra: None })
349 }
350}
351352/// Given a type with type parameters, visit every ADT looking for types that need to
353/// `#[derive(PartialEq)]` for it to be a structural type.
354fn extend_type_not_partial_eq<'tcx>(
355 tcx: TyCtxt<'tcx>,
356 typing_env: ty::TypingEnv<'tcx>,
357 ty: Ty<'tcx>,
358 err: &mut Diag<'_>,
359) {
360/// Collect all types that need to be `StructuralPartialEq`.
361struct UsedParamsNeedInstantiationVisitor<'tcx> {
362 tcx: TyCtxt<'tcx>,
363 typing_env: ty::TypingEnv<'tcx>,
364/// The user has written `impl PartialEq for Ty` which means it's non-structural.
365adts_with_manual_partialeq: FxHashSet<Span>,
366/// The type has no `PartialEq` implementation, neither manual or derived.
367adts_without_partialeq: FxHashSet<Span>,
368/// The user has written `impl PartialEq for Ty` which means it's non-structural,
369 /// but we don't have a span to point at, so we'll just add them as a `note`.
370manual: FxHashSet<Ty<'tcx>>,
371/// The type has no `PartialEq` implementation, neither manual or derived, but
372 /// we don't have a span to point at, so we'll just add them as a `note`.
373without: FxHashSet<Ty<'tcx>>,
374 }
375376impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
377type Result = ControlFlow<()>;
378fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
379match ty.kind() {
380 ty::Dynamic(..) => return ControlFlow::Break(()),
381// Unsafe binders never implement `PartialEq`, so avoid walking into them
382 // which would require instantiating its binder with placeholders too.
383ty::UnsafeBinder(..) => return ControlFlow::Break(()),
384 ty::FnPtr(..) => return ControlFlow::Continue(()),
385 ty::Adt(def, _args) => {
386let ty_def_id = def.did();
387let ty_def_span = self.tcx.def_span(ty_def_id);
388let PartialEqImplStatus {
389 has_impl,
390 is_derived,
391 structural_partial_eq,
392 non_blanket_impl,
393 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
394match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) {
395 (_, _, true, _) => {}
396 (true, false, _, Some(def_id)) if def_id.is_local() => {
397self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
398 }
399 (true, false, _, _) if ty_def_id.is_local() => {
400self.adts_with_manual_partialeq.insert(ty_def_span);
401 }
402 (false, _, _, _) if ty_def_id.is_local() => {
403self.adts_without_partialeq.insert(ty_def_span);
404 }
405 (true, false, _, _) => {
406self.manual.insert(ty);
407 }
408 (false, _, _, _) => {
409self.without.insert(ty);
410 }
411_ => {}
412 };
413ty.super_visit_with(self)
414 }
415_ => ty.super_visit_with(self),
416 }
417 }
418 }
419let mut v = UsedParamsNeedInstantiationVisitor {
420tcx,
421typing_env,
422 adts_with_manual_partialeq: FxHashSet::default(),
423 adts_without_partialeq: FxHashSet::default(),
424 manual: FxHashSet::default(),
425 without: FxHashSet::default(),
426 };
427if v.visit_ty(ty).is_break() {
428return;
429 }
430#[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
431for span in v.adts_with_manual_partialeq {
432 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");
433 }
434#[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
435for span in v.adts_without_partialeq {
436 err.span_label(
437 span,
438"must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
439 );
440 }
441#[allow(rustc::potential_query_instability)]
442let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect();
443manual.sort();
444for ty in manual {
445 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` 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",
ty))
})format!(
446"`{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"
447));
448 }
449#[allow(rustc::potential_query_instability)]
450let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect();
451without.sort();
452for ty in without {
453 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
ty))
})format!(
454"`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
455));
456 }
457}
458459#[derive(#[automatically_derived]
impl ::core::fmt::Debug for PartialEqImplStatus {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field4_finish(f,
"PartialEqImplStatus", "has_impl", &self.has_impl, "is_derived",
&self.is_derived, "structural_partial_eq",
&self.structural_partial_eq, "non_blanket_impl",
&&self.non_blanket_impl)
}
}Debug)]
460struct PartialEqImplStatus {
461 has_impl: bool,
462 is_derived: bool,
463 structural_partial_eq: bool,
464 non_blanket_impl: Option<DefId>,
465}
466467x;#[instrument(level = "trace", skip(tcx), ret)]468fn type_has_partial_eq_impl<'tcx>(
469 tcx: TyCtxt<'tcx>,
470 typing_env: ty::TypingEnv<'tcx>,
471 ty: Ty<'tcx>,
472) -> PartialEqImplStatus {
473let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
474// double-check there even *is* a semantic `PartialEq` to dispatch to.
475 //
476 // (If there isn't, then we can safely issue a hard
477 // error, because that's never worked, due to compiler
478 // using `PartialEq::eq` in this scenario in the past.)
479let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, DUMMY_SP);
480let structural_partial_eq_trait_id =
481 tcx.require_lang_item(hir::LangItem::StructuralPeq, DUMMY_SP);
482483let partial_eq_obligation = Obligation::new(
484 tcx,
485 ObligationCause::dummy(),
486 param_env,
487 ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
488 );
489490let mut automatically_derived = false;
491let mut structural_peq = false;
492let mut impl_def_id = None;
493for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
494 automatically_derived =
495find_attr!(tcx.get_all_attrs(def_id), AttributeKind::AutomaticallyDerived(..));
496 impl_def_id = Some(def_id);
497 }
498for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) {
499 structural_peq = true;
500 }
501// This *could* accept a type that isn't actually `PartialEq`, because region bounds get
502 // ignored. However that should be pretty much impossible since consts that do not depend on
503 // generics can only mention the `'static` lifetime, and how would one have a type that's
504 // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
505 // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
506 // can ensure that the type really implements `PartialEq`.
507 // We also do *not* require `const PartialEq`, not even in `const fn`. This violates the model
508 // that patterns can only do things that the code could also do without patterns, but it is
509 // needed for backwards compatibility. The actual pattern matching compares primitive values,
510 // `PartialEq::eq` never gets invoked, so there's no risk of us running non-const code.
511PartialEqImplStatus {
512 has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
513 is_derived: automatically_derived,
514 structural_partial_eq: structural_peq,
515 non_blanket_impl: impl_def_id,
516 }
517}