1use core::ops::ControlFlow;
2
3use rustc_abi::{FieldIdx, VariantIdx};
4use rustc_apfloat::Float;
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::{Diag, msg};
7use rustc_hir as hir;
8use rustc_hir::find_attr;
9use rustc_index::Idx;
10use rustc_infer::infer::TyCtxtInferExt;
11use rustc_infer::traits::Obligation;
12use rustc_middle::mir::interpret::ErrorHandled;
13use rustc_middle::span_bug;
14use rustc_middle::thir::{FieldPat, Pat, PatKind};
15use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor};
16use rustc_span::def_id::DefId;
17use rustc_span::{DUMMY_SP, Span};
18use rustc_trait_selection::traits::ObligationCause;
19use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
20use tracing::{debug, instrument, trace};
21
22use super::PatCtxt;
23use crate::errors::{
24 ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern,
25 PointerPattern, SuggestEq, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern,
26};
27
28impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> {
29 x;#[instrument(level = "debug", skip(self), ret)]
37 pub(super) fn const_to_pat(
38 &self,
39 c: ty::Const<'tcx>,
40 ty: Ty<'tcx>,
41 id: hir::HirId,
42 span: Span,
43 ) -> Box<Pat<'tcx>> {
44 let mut convert = ConstToPat::new(self, id, span, c);
45
46 match c.kind() {
47 ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
48 ty::ConstKind::Value(value) => convert.valtree_to_pat(value),
49 _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
50 }
51 }
52}
53
54struct ConstToPat<'tcx> {
55 tcx: TyCtxt<'tcx>,
56 typing_env: ty::TypingEnv<'tcx>,
57 span: Span,
58 id: hir::HirId,
59
60 c: ty::Const<'tcx>,
61}
62
63impl<'tcx> ConstToPat<'tcx> {
64 fn new(pat_ctxt: &PatCtxt<'tcx, '_>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
65 {
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:65",
"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(65u32),
::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);
66 ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c }
67 }
68
69 fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
70 ty.is_structural_eq_shallow(self.tcx)
71 }
72
73 fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
75 if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
76 if let ty::UnevaluatedConstKind::Projection { def_id }
77 | ty::UnevaluatedConstKind::Inherent { def_id } = uv.kind
78 && let Some(def_id) = def_id.as_local()
79 {
80 err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
82 }
83 if let ty::UnevaluatedConstKind::Projection { def_id }
84 | ty::UnevaluatedConstKind::Inherent { def_id }
85 | ty::UnevaluatedConstKind::Free { def_id } = uv.kind
86 {
87 err.span_label(self.tcx.def_span(def_id), rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("constant defined here"))msg!("constant defined here"));
88 }
89 }
90 Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None })
91 }
92
93 fn unevaluated_to_pat(
94 &mut self,
95 uv: ty::UnevaluatedConst<'tcx>,
96 ty: Ty<'tcx>,
97 ) -> Box<Pat<'tcx>> {
98 let typing_env =
106 self.tcx.erase_and_anonymize_regions(self.typing_env).with_codegen_normalized(self.tcx);
107 let uv = self.tcx.erase_and_anonymize_regions(uv);
108
109 let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) {
112 Ok(Ok(c)) => c,
113 Err(ErrorHandled::Reported(_, _)) => {
114 let mut err =
116 self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
117 if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
120 && let ty::UnevaluatedConstKind::Projection { .. }
121 | ty::UnevaluatedConstKind::Inherent { .. }
122 | ty::UnevaluatedConstKind::Free { .. } = uv.kind
123 {
124 err.downgrade_to_delayed_bug();
125 }
126 return self.mk_err(err, ty);
127 }
128 Err(ErrorHandled::TooGeneric(_)) => {
129 let mut e = self
130 .tcx
131 .dcx()
132 .create_err(ConstPatternDependsOnGenericParameter { span: self.span });
133 for arg in uv.args {
134 if let ty::GenericArgKind::Type(ty) = arg.kind()
135 && let ty::Param(param_ty) = ty.kind()
136 {
137 let def_id = self.tcx.hir_enclosing_body_owner(self.id);
138 let generics = self.tcx.generics_of(def_id);
139 let param = generics.type_param(*param_ty, self.tcx);
140 let span = self.tcx.def_span(param.def_id);
141 e.span_label(span, "constant depends on this generic parameter");
142 if let Some(ident) = self.tcx.def_ident_span(def_id)
143 && self.tcx.sess.source_map().is_multiline(ident.between(span))
144 {
145 e.span_label(ident, "");
148 }
149 }
150 }
151 return self.mk_err(e, ty);
152 }
153 Ok(Err(bad_ty)) => {
154 let e = match bad_ty.kind() {
156 ty::Adt(def, ..) => {
157 if !def.is_union() {
::core::panicking::panic("assertion failed: def.is_union()")
};assert!(def.is_union());
158 self.tcx.dcx().create_err(UnionPattern { span: self.span })
159 }
160 ty::FnPtr(..) | ty::RawPtr(..) => {
161 self.tcx.dcx().create_err(PointerPattern { span: self.span })
162 }
163 _ => self.tcx.dcx().create_err(InvalidPattern {
164 span: self.span,
165 non_sm_ty: bad_ty,
166 prefix: bad_ty.prefix_string(self.tcx).to_string(),
167 }),
168 };
169 return self.mk_err(e, ty);
170 }
171 };
172
173 let mut thir_pat = self.valtree_to_pat(ty::Value { ty, valtree });
175
176 if !thir_pat.references_error() {
177 if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl {
179 let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
180 extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
181 return self.mk_err(err, ty);
182 }
183 }
184
185 thir_pat.extra.get_or_insert_default().expanded_const = Some(uv.kind.def_id());
188 thir_pat
189 }
190
191 fn lower_field_values_to_fieldpats(
192 &self,
193 values: impl Iterator<Item = ty::Value<'tcx>>,
194 ) -> Vec<FieldPat<'tcx>> {
195 values
196 .enumerate()
197 .map(|(index, value)| FieldPat {
198 field: FieldIdx::new(index),
199 pattern: *self.valtree_to_pat(value),
200 })
201 .collect()
202 }
203
204 #[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(205u32),
::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:216",
"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(216u32),
::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 manual_partialeq_impl =
manual_partialeq_impl_note ||
manual_partialeq_impl_span.is_some();
let is_local = adt_def.did().is_local();
let ty_def_span = tcx.def_span(adt_def.did());
let suggestion =
if let Ok(name) =
tcx.sess.source_map().span_to_snippet(self.span) &&
(is_local || manual_partialeq_impl) {
let mut hir_id = self.id;
while let hir::Node::Pat(pat) = tcx.parent_hir_node(hir_id)
{
hir_id = pat.hir_id;
}
match tcx.parent_hir_node(hir_id) {
hir::Node::Arm(hir::Arm { pat, guard: None, .. }) => {
Some(SuggestEq::AddIf {
if_span: pat.span.shrink_to_hi(),
pat_span: self.span,
name,
ty,
manual_partialeq_impl,
})
}
hir::Node::Arm(hir::Arm { guard: Some(guard), .. }) => {
Some(SuggestEq::AddToIf {
span: guard.span.shrink_to_hi(),
pat_span: self.span,
name,
ty,
manual_partialeq_impl,
})
}
hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Let(let_expr), span, .. }) => {
if let_expr.pat.span == self.span {
Some(SuggestEq::ReplaceWithEq {
removal: span.until(self.span),
eq: self.span.between(let_expr.init.span),
ty,
manual_partialeq_impl,
})
} else if tcx.sess.edition().at_least_rust_2024() {
Some(SuggestEq::AddToLetChain {
span: span.shrink_to_hi(),
pat_span: self.span,
name,
ty,
manual_partialeq_impl,
})
} else { None }
}
hir::Node::LetStmt(let_stmt) if
let Some(init) = let_stmt.init &&
let Some(els) = let_stmt.els && init.span.ctxt().is_root()
&& els.span.ctxt().is_root() => {
Some(SuggestEq::ReplaceLetElseWithIf {
if_span: let_stmt.span.until(let_stmt.pat.span),
eq: let_stmt.pat.span.between(init.span),
else_span: init.span.between(els.span),
ty,
manual_partialeq_impl,
})
}
_ => None,
}
} else { None };
let err =
TypeNotStructural {
span,
ty,
ty_def_span,
manual_partialeq_impl_span,
manual_partialeq_impl_note,
is_local,
suggestion,
};
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")]
206 fn valtree_to_pat(&self, value: ty::Value<'tcx>) -> Box<Pat<'tcx>> {
207 let span = self.span;
208 let tcx = self.tcx;
209 let ty::Value { ty, valtree } = value;
210
211 let kind = match ty.kind() {
212 ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
215 debug!(?adt_def, ?value.ty, "ADT type in pattern is not `type_marked_structural`");
217 let PartialEqImplStatus {
218 is_derived, structural_partial_eq, non_blanket_impl, ..
219 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
220 let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
221 match (structural_partial_eq, non_blanket_impl) {
222 (true, _) => (None, false),
223 (_, Some(def_id)) if def_id.is_local() && !is_derived => {
224 (Some(tcx.def_span(def_id)), false)
225 }
226 _ => (None, true),
227 };
228 let manual_partialeq_impl =
229 manual_partialeq_impl_note || manual_partialeq_impl_span.is_some();
230 let is_local = adt_def.did().is_local();
231 let ty_def_span = tcx.def_span(adt_def.did());
232 let suggestion = if let Ok(name) = tcx.sess.source_map().span_to_snippet(self.span)
233 && (is_local || manual_partialeq_impl)
234 {
235 let mut hir_id = self.id;
236 while let hir::Node::Pat(pat) = tcx.parent_hir_node(hir_id) {
237 hir_id = pat.hir_id;
238 }
239 match tcx.parent_hir_node(hir_id) {
240 hir::Node::Arm(hir::Arm { pat, guard: None, .. }) => {
241 Some(SuggestEq::AddIf {
243 if_span: pat.span.shrink_to_hi(),
244 pat_span: self.span,
245 name,
246 ty,
247 manual_partialeq_impl,
248 })
249 }
250 hir::Node::Arm(hir::Arm { guard: Some(guard), .. }) => {
251 Some(SuggestEq::AddToIf {
253 span: guard.span.shrink_to_hi(),
254 pat_span: self.span,
255 name,
256 ty,
257 manual_partialeq_impl,
258 })
259 }
260 hir::Node::Expr(hir::Expr {
261 kind: hir::ExprKind::Let(let_expr),
262 span,
263 ..
264 }) => {
265 if let_expr.pat.span == self.span {
266 Some(SuggestEq::ReplaceWithEq {
268 removal: span.until(self.span),
269 eq: self.span.between(let_expr.init.span),
270 ty,
271 manual_partialeq_impl,
272 })
273 } else if tcx.sess.edition().at_least_rust_2024() {
274 Some(SuggestEq::AddToLetChain {
277 span: span.shrink_to_hi(),
278 pat_span: self.span,
279 name,
280 ty,
281 manual_partialeq_impl,
282 })
283 } else {
284 None
285 }
286 }
287 hir::Node::LetStmt(let_stmt)
288 if let Some(init) = let_stmt.init
289 && let Some(els) = let_stmt.els
290 && init.span.ctxt().is_root()
291 && els.span.ctxt().is_root() =>
292 {
293 Some(SuggestEq::ReplaceLetElseWithIf {
295 if_span: let_stmt.span.until(let_stmt.pat.span),
296 eq: let_stmt.pat.span.between(init.span),
297 else_span: init.span.between(els.span),
298 ty,
299 manual_partialeq_impl,
300 })
301 }
302 _ => None,
303 }
304 } else {
305 None
306 };
307 let err = TypeNotStructural {
308 span,
309 ty,
310 ty_def_span,
311 manual_partialeq_impl_span,
312 manual_partialeq_impl_note,
313 is_local,
314 suggestion,
315 };
316 return self.mk_err(tcx.dcx().create_err(err), ty);
317 }
318 ty::Adt(adt_def, args) if adt_def.is_enum() => {
319 let (&variant_index, fields) = valtree.to_branch().split_first().unwrap();
320 let variant_index = VariantIdx::from_u32(variant_index.to_leaf().to_u32());
321 PatKind::Variant {
322 adt_def: *adt_def,
323 args,
324 variant_index,
325 subpatterns: self
326 .lower_field_values_to_fieldpats(fields.iter().map(|ct| ct.to_value())),
327 }
328 }
329 ty::Adt(def, _) => {
330 assert!(!def.is_union()); PatKind::Leaf {
332 subpatterns: self.lower_field_values_to_fieldpats(
333 valtree.to_branch().iter().map(|ct| ct.to_value()),
334 ),
335 }
336 }
337 ty::Tuple(_) => PatKind::Leaf {
338 subpatterns: self.lower_field_values_to_fieldpats(
339 valtree.to_branch().iter().map(|ct| ct.to_value()),
340 ),
341 },
342 ty::Slice(_) => PatKind::Slice {
343 prefix: valtree
344 .to_branch()
345 .iter()
346 .map(|val| *self.valtree_to_pat(val.to_value()))
347 .collect(),
348 slice: None,
349 suffix: Box::new([]),
350 },
351 ty::Array(_, _) => PatKind::Array {
352 prefix: valtree
353 .to_branch()
354 .iter()
355 .map(|val| *self.valtree_to_pat(val.to_value()))
356 .collect(),
357 slice: None,
358 suffix: Box::new([]),
359 },
360 ty::Str => {
361 PatKind::Constant { value }
369 }
370 ty::Ref(_, pointee_ty, ..) => {
371 if pointee_ty.is_str()
372 || pointee_ty.is_slice()
373 || pointee_ty.is_sized(tcx, self.typing_env)
374 {
375 PatKind::Deref {
376 pin: hir::Pinnedness::Not,
378 subpattern: self.valtree_to_pat(ty::Value { ty: *pointee_ty, valtree }),
382 }
383 } else {
384 return self.mk_err(
385 tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
386 ty,
387 );
388 }
389 }
390 ty::Float(flt) => {
391 let v = valtree.to_leaf();
392 let is_nan = match flt {
393 ty::FloatTy::F16 => v.to_f16().is_nan(),
394 ty::FloatTy::F32 => v.to_f32().is_nan(),
395 ty::FloatTy::F64 => v.to_f64().is_nan(),
396 ty::FloatTy::F128 => v.to_f128().is_nan(),
397 };
398 if is_nan {
399 return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
402 } else {
403 PatKind::Constant { value }
404 }
405 }
406 ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
407 PatKind::Constant { value }
410 }
411 ty::FnPtr(..) => {
412 unreachable!(
413 "Valtree construction would never succeed for FnPtr, so this is unreachable."
414 )
415 }
416 _ => {
417 let err = InvalidPattern {
418 span,
419 non_sm_ty: ty,
420 prefix: ty.prefix_string(tcx).to_string(),
421 };
422 return self.mk_err(tcx.dcx().create_err(err), ty);
423 }
424 };
425
426 Box::new(Pat { span, ty, kind, extra: None })
427 }
428}
429
430fn extend_type_not_partial_eq<'tcx>(
433 tcx: TyCtxt<'tcx>,
434 typing_env: ty::TypingEnv<'tcx>,
435 ty: Ty<'tcx>,
436 err: &mut Diag<'_>,
437) {
438 struct UsedParamsNeedInstantiationVisitor<'tcx> {
440 tcx: TyCtxt<'tcx>,
441 typing_env: ty::TypingEnv<'tcx>,
442 adts_with_manual_partialeq: FxHashSet<Span>,
444 adts_without_partialeq: FxHashSet<Span>,
446 manual: FxHashSet<Ty<'tcx>>,
449 without: FxHashSet<Ty<'tcx>>,
452 }
453
454 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
455 type Result = ControlFlow<()>;
456 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
457 match ty.kind() {
458 ty::Dynamic(..) => return ControlFlow::Break(()),
459 ty::UnsafeBinder(..) => return ControlFlow::Break(()),
462 ty::FnPtr(..) => return ControlFlow::Continue(()),
463 ty::Adt(def, _args) => {
464 let ty_def_id = def.did();
465 let ty_def_span = self.tcx.def_span(ty_def_id);
466 let PartialEqImplStatus {
467 has_impl,
468 is_derived,
469 structural_partial_eq,
470 non_blanket_impl,
471 } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
472 match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) {
473 (_, _, true, _) => {}
474 (true, false, _, Some(def_id)) if def_id.is_local() => {
475 self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
476 }
477 (true, false, _, _) if ty_def_id.is_local() => {
478 self.adts_with_manual_partialeq.insert(ty_def_span);
479 }
480 (false, _, _, _) if ty_def_id.is_local() => {
481 self.adts_without_partialeq.insert(ty_def_span);
482 }
483 (true, false, _, _) => {
484 self.manual.insert(ty);
485 }
486 (false, _, _, _) => {
487 self.without.insert(ty);
488 }
489 _ => {}
490 };
491 ty.super_visit_with(self)
492 }
493 _ => ty.super_visit_with(self),
494 }
495 }
496 }
497 let mut v = UsedParamsNeedInstantiationVisitor {
498 tcx,
499 typing_env,
500 adts_with_manual_partialeq: FxHashSet::default(),
501 adts_without_partialeq: FxHashSet::default(),
502 manual: FxHashSet::default(),
503 without: FxHashSet::default(),
504 };
505 if v.visit_ty(ty).is_break() {
506 return;
507 }
508 #[allow(rustc::potential_query_instability)] for span in v.adts_with_manual_partialeq {
510 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");
511 }
512 #[allow(rustc::potential_query_instability)] for span in v.adts_without_partialeq {
514 err.span_label(
515 span,
516 "must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
517 );
518 }
519 #[allow(rustc::potential_query_instability)]
520 let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect();
521 manual.sort();
522 for ty in manual {
523 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!(
524 "`{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"
525 ));
526 }
527 #[allow(rustc::potential_query_instability)]
528 let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect();
529 without.sort();
530 for ty in without {
531 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!(
532 "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
533 ));
534 }
535}
536
537#[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)]
538struct PartialEqImplStatus {
539 has_impl: bool,
540 is_derived: bool,
541 structural_partial_eq: bool,
542 non_blanket_impl: Option<DefId>,
543}
544
545x;#[instrument(level = "trace", skip(tcx), ret)]
546fn type_has_partial_eq_impl<'tcx>(
547 tcx: TyCtxt<'tcx>,
548 typing_env: ty::TypingEnv<'tcx>,
549 ty: Ty<'tcx>,
550) -> PartialEqImplStatus {
551 let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
552 let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, DUMMY_SP);
558 let structural_partial_eq_trait_id =
559 tcx.require_lang_item(hir::LangItem::StructuralPeq, DUMMY_SP);
560
561 let partial_eq_obligation = Obligation::new(
562 tcx,
563 ObligationCause::dummy(),
564 param_env,
565 ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
566 );
567
568 let mut automatically_derived = false;
569 let mut structural_peq = false;
570 let mut impl_def_id = None;
571 for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
572 automatically_derived = find_attr!(tcx, def_id, AutomaticallyDerived);
573 impl_def_id = Some(def_id);
574 }
575 for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) {
576 structural_peq = true;
577 }
578 PartialEqImplStatus {
589 has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
590 is_derived: automatically_derived,
591 structural_partial_eq: structural_peq,
592 non_blanket_impl: impl_def_id,
593 }
594}