]>
Commit | Line | Data |
---|---|---|
1b1a35ee XL |
1 | use rustc_hir as hir; |
2 | use rustc_index::vec::Idx; | |
3 | use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; | |
353b0b11 FG |
4 | use rustc_infer::traits::Obligation; |
5 | use rustc_middle::mir; | |
17df50a5 | 6 | use rustc_middle::thir::{FieldPat, Pat, PatKind}; |
9c376795 | 7 | use rustc_middle::ty::{self, Ty, TyCtxt}; |
1b1a35ee XL |
8 | use rustc_session::lint; |
9 | use rustc_span::Span; | |
353b0b11 | 10 | use rustc_target::abi::FieldIdx; |
1b1a35ee | 11 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; |
353b0b11 | 12 | use rustc_trait_selection::traits::{self, ObligationCause}; |
1b1a35ee XL |
13 | |
14 | use std::cell::Cell; | |
15 | ||
17df50a5 | 16 | use super::PatCtxt; |
9c376795 FG |
17 | use crate::errors::{ |
18 | FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch, | |
19 | PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, | |
20 | }; | |
1b1a35ee XL |
21 | |
22 | impl<'a, 'tcx> PatCtxt<'a, 'tcx> { | |
23 | /// Converts an evaluated constant to a pattern (if possible). | |
24 | /// This means aggregate values (like structs and enums) are converted | |
25 | /// to a pattern that matches the value (as if you'd compared via structural equality). | |
f2b60f7d | 26 | #[instrument(level = "debug", skip(self), ret)] |
1b1a35ee XL |
27 | pub(super) fn const_to_pat( |
28 | &self, | |
923072b8 | 29 | cv: mir::ConstantKind<'tcx>, |
1b1a35ee XL |
30 | id: hir::HirId, |
31 | span: Span, | |
32 | mir_structural_match_violation: bool, | |
f2b60f7d | 33 | ) -> Box<Pat<'tcx>> { |
2b03887a FG |
34 | let infcx = self.tcx.infer_ctxt().build(); |
35 | let mut convert = ConstToPat::new(self, id, span, infcx); | |
36 | convert.to_pat(cv, mir_structural_match_violation) | |
1b1a35ee XL |
37 | } |
38 | } | |
39 | ||
2b03887a | 40 | struct ConstToPat<'tcx> { |
1b1a35ee XL |
41 | id: hir::HirId, |
42 | span: Span, | |
43 | param_env: ty::ParamEnv<'tcx>, | |
44 | ||
45 | // This tracks if we emitted some hard error for a given const value, so that | |
46 | // we will not subsequently issue an irrelevant lint for the same const | |
47 | // value. | |
48 | saw_const_match_error: Cell<bool>, | |
49 | ||
50 | // This tracks if we emitted some diagnostic for a given const value, so that | |
51 | // we will not subsequently issue an irrelevant lint for the same const | |
52 | // value. | |
53 | saw_const_match_lint: Cell<bool>, | |
54 | ||
55 | // For backcompat we need to keep allowing non-structurally-eq types behind references. | |
56 | // See also all the `cant-hide-behind` tests. | |
57 | behind_reference: Cell<bool>, | |
58 | ||
59 | // inference context used for checking `T: Structural` bounds. | |
2b03887a | 60 | infcx: InferCtxt<'tcx>, |
1b1a35ee | 61 | |
29967ef6 | 62 | treat_byte_string_as_slice: bool, |
1b1a35ee XL |
63 | } |
64 | ||
65 | mod fallback_to_const_ref { | |
66 | #[derive(Debug)] | |
67 | /// This error type signals that we encountered a non-struct-eq situation behind a reference. | |
68 | /// We bubble this up in order to get back to the reference destructuring and make that emit | |
69 | /// a const pattern instead of a deref pattern. This allows us to simply call `PartialEq::eq` | |
70 | /// on such patterns (since that function takes a reference) and not have to jump through any | |
71 | /// hoops to get a reference to the value. | |
72 | pub(super) struct FallbackToConstRef(()); | |
73 | ||
9c376795 | 74 | pub(super) fn fallback_to_const_ref(c2p: &super::ConstToPat<'_>) -> FallbackToConstRef { |
1b1a35ee XL |
75 | assert!(c2p.behind_reference.get()); |
76 | FallbackToConstRef(()) | |
77 | } | |
78 | } | |
79 | use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef}; | |
80 | ||
2b03887a | 81 | impl<'tcx> ConstToPat<'tcx> { |
1b1a35ee XL |
82 | fn new( |
83 | pat_ctxt: &PatCtxt<'_, 'tcx>, | |
84 | id: hir::HirId, | |
85 | span: Span, | |
2b03887a | 86 | infcx: InferCtxt<'tcx>, |
1b1a35ee | 87 | ) -> Self { |
29967ef6 | 88 | trace!(?pat_ctxt.typeck_results.hir_owner); |
1b1a35ee XL |
89 | ConstToPat { |
90 | id, | |
91 | span, | |
92 | infcx, | |
93 | param_env: pat_ctxt.param_env, | |
1b1a35ee XL |
94 | saw_const_match_error: Cell::new(false), |
95 | saw_const_match_lint: Cell::new(false), | |
96 | behind_reference: Cell::new(false), | |
29967ef6 XL |
97 | treat_byte_string_as_slice: pat_ctxt |
98 | .typeck_results | |
99 | .treat_byte_string_as_slice | |
100 | .contains(&id.local_id), | |
1b1a35ee XL |
101 | } |
102 | } | |
103 | ||
104 | fn tcx(&self) -> TyCtxt<'tcx> { | |
105 | self.infcx.tcx | |
106 | } | |
107 | ||
1b1a35ee XL |
108 | fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { |
109 | ty.is_structural_eq_shallow(self.infcx.tcx) | |
110 | } | |
111 | ||
923072b8 FG |
112 | fn to_pat( |
113 | &mut self, | |
114 | cv: mir::ConstantKind<'tcx>, | |
115 | mir_structural_match_violation: bool, | |
f2b60f7d | 116 | ) -> Box<Pat<'tcx>> { |
29967ef6 | 117 | trace!(self.treat_byte_string_as_slice); |
1b1a35ee XL |
118 | // This method is just a wrapper handling a validity check; the heavy lifting is |
119 | // performed by the recursive `recur` method, which is not meant to be | |
120 | // invoked except by this method. | |
121 | // | |
122 | // once indirect_structural_match is a full fledged error, this | |
123 | // level of indirection can be eliminated | |
124 | ||
f2b60f7d FG |
125 | let inlined_const_as_pat = |
126 | self.recur(cv, mir_structural_match_violation).unwrap_or_else(|_| { | |
127 | Box::new(Pat { | |
128 | span: self.span, | |
129 | ty: cv.ty(), | |
130 | kind: PatKind::Constant { value: cv }, | |
131 | }) | |
132 | }); | |
1b1a35ee | 133 | |
353b0b11 | 134 | if !self.saw_const_match_error.get() { |
1b1a35ee XL |
135 | // If we were able to successfully convert the const to some pat, |
136 | // double-check that all types in the const implement `Structural`. | |
137 | ||
9c376795 FG |
138 | let structural = |
139 | traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty()); | |
1b1a35ee XL |
140 | debug!( |
141 | "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", | |
5099ac24 FG |
142 | cv.ty(), |
143 | structural | |
1b1a35ee XL |
144 | ); |
145 | ||
146 | // This can occur because const qualification treats all associated constants as | |
147 | // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them | |
148 | // before it runs. | |
149 | // | |
150 | // FIXME(#73448): Find a way to bring const qualification into parity with | |
151 | // `search_for_structural_match_violation`. | |
152 | if structural.is_none() && mir_structural_match_violation { | |
153 | warn!("MIR const-checker found novel structural match violation. See #73448."); | |
154 | return inlined_const_as_pat; | |
155 | } | |
156 | ||
9c376795 | 157 | if let Some(non_sm_ty) = structural { |
5099ac24 | 158 | if !self.type_may_have_partial_eq_impl(cv.ty()) { |
9c376795 FG |
159 | // fatal avoids ICE from resolution of non-existent method (rare case). |
160 | self.tcx() | |
161 | .sess | |
162 | .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty }); | |
1b1a35ee | 163 | } else if mir_structural_match_violation && !self.saw_const_match_lint.get() { |
9c376795 | 164 | self.tcx().emit_spanned_lint( |
1b1a35ee XL |
165 | lint::builtin::INDIRECT_STRUCTURAL_MATCH, |
166 | self.id, | |
167 | self.span, | |
9c376795 | 168 | IndirectStructuralMatch { non_sm_ty }, |
1b1a35ee XL |
169 | ); |
170 | } else { | |
171 | debug!( | |
172 | "`search_for_structural_match_violation` found one, but `CustomEq` was \ | |
173 | not in the qualifs for that `const`" | |
174 | ); | |
175 | } | |
176 | } | |
177 | } | |
178 | ||
179 | inlined_const_as_pat | |
180 | } | |
181 | ||
182 | fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool { | |
183 | // double-check there even *is* a semantic `PartialEq` to dispatch to. | |
184 | // | |
185 | // (If there isn't, then we can safely issue a hard | |
186 | // error, because that's never worked, due to compiler | |
187 | // using `PartialEq::eq` in this scenario in the past.) | |
188 | let partial_eq_trait_id = | |
189 | self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span)); | |
353b0b11 | 190 | let partial_eq_obligation = Obligation::new( |
1b1a35ee | 191 | self.tcx(), |
353b0b11 | 192 | ObligationCause::dummy(), |
1b1a35ee | 193 | self.param_env, |
353b0b11 | 194 | self.tcx().mk_trait_ref(partial_eq_trait_id, [ty, ty]), |
1b1a35ee | 195 | ); |
1b1a35ee | 196 | |
353b0b11 FG |
197 | // FIXME: should this call a `predicate_must_hold` variant instead? |
198 | let has_impl = self.infcx.predicate_may_hold(&partial_eq_obligation); | |
1b1a35ee XL |
199 | |
200 | // Note: To fix rust-lang/rust#65466, we could just remove this type | |
201 | // walk hack for function pointers, and unconditionally error | |
202 | // if `PartialEq` is not implemented. However, that breaks stable | |
203 | // code at the moment, because types like `for <'a> fn(&'a ())` do | |
204 | // not *yet* implement `PartialEq`. So for now we leave this here. | |
205 | has_impl | |
5099ac24 | 206 | || ty.walk().any(|t| match t.unpack() { |
1b1a35ee XL |
207 | ty::subst::GenericArgKind::Lifetime(_) => false, |
208 | ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(), | |
209 | ty::subst::GenericArgKind::Const(_) => false, | |
210 | }) | |
211 | } | |
212 | ||
cdc7bbd5 XL |
213 | fn field_pats( |
214 | &self, | |
923072b8 | 215 | vals: impl Iterator<Item = mir::ConstantKind<'tcx>>, |
cdc7bbd5 XL |
216 | ) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> { |
217 | vals.enumerate() | |
218 | .map(|(idx, val)| { | |
353b0b11 | 219 | let field = FieldIdx::new(idx); |
cdc7bbd5 XL |
220 | Ok(FieldPat { field, pattern: self.recur(val, false)? }) |
221 | }) | |
222 | .collect() | |
223 | } | |
224 | ||
1b1a35ee | 225 | // Recursive helper for `to_pat`; invoke that (instead of calling this directly). |
923072b8 | 226 | #[instrument(skip(self), level = "debug")] |
1b1a35ee XL |
227 | fn recur( |
228 | &self, | |
923072b8 | 229 | cv: mir::ConstantKind<'tcx>, |
1b1a35ee | 230 | mir_structural_match_violation: bool, |
f2b60f7d | 231 | ) -> Result<Box<Pat<'tcx>>, FallbackToConstRef> { |
1b1a35ee XL |
232 | let id = self.id; |
233 | let span = self.span; | |
234 | let tcx = self.tcx(); | |
235 | let param_env = self.param_env; | |
236 | ||
5099ac24 | 237 | let kind = match cv.ty().kind() { |
1b1a35ee | 238 | ty::Float(_) => { |
9c376795 | 239 | tcx.emit_spanned_lint( |
136023e0 XL |
240 | lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, |
241 | id, | |
242 | span, | |
9c376795 | 243 | FloatPattern, |
136023e0 | 244 | ); |
1b1a35ee XL |
245 | PatKind::Constant { value: cv } |
246 | } | |
247 | ty::Adt(adt_def, _) if adt_def.is_union() => { | |
248 | // Matching on union fields is unsafe, we can't hide it in constants | |
249 | self.saw_const_match_error.set(true); | |
9c376795 | 250 | let err = UnionPattern { span }; |
353b0b11 | 251 | tcx.sess.emit_err(err); |
1b1a35ee XL |
252 | PatKind::Wild |
253 | } | |
254 | ty::Adt(..) | |
5099ac24 | 255 | if !self.type_may_have_partial_eq_impl(cv.ty()) |
1b1a35ee XL |
256 | // FIXME(#73448): Find a way to bring const qualification into parity with |
257 | // `search_for_structural_match_violation` and then remove this condition. | |
9c376795 FG |
258 | |
259 | // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we | |
260 | // could get `Option<NonStructEq>`, even though `Option` is annotated with derive. | |
261 | && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) => | |
1b1a35ee | 262 | { |
1b1a35ee | 263 | self.saw_const_match_error.set(true); |
9c376795 | 264 | let err = TypeNotStructural { span, non_sm_ty }; |
353b0b11 | 265 | tcx.sess.emit_err(err); |
1b1a35ee XL |
266 | PatKind::Wild |
267 | } | |
268 | // If the type is not structurally comparable, just emit the constant directly, | |
269 | // causing the pattern match code to treat it opaquely. | |
270 | // FIXME: This code doesn't emit errors itself, the caller emits the errors. | |
271 | // So instead of specific errors, you just get blanket errors about the whole | |
272 | // const type. See | |
273 | // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for | |
274 | // details. | |
275 | // Backwards compatibility hack because we can't cause hard errors on these | |
276 | // types, so we compare them via `PartialEq::eq` at runtime. | |
5099ac24 | 277 | ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => { |
353b0b11 | 278 | if !self.saw_const_match_error.get() |
1b1a35ee XL |
279 | && !self.saw_const_match_lint.get() |
280 | { | |
281 | self.saw_const_match_lint.set(true); | |
9c376795 | 282 | tcx.emit_spanned_lint( |
1b1a35ee XL |
283 | lint::builtin::INDIRECT_STRUCTURAL_MATCH, |
284 | id, | |
285 | span, | |
9c376795 | 286 | IndirectStructuralMatch { non_sm_ty: cv.ty() }, |
1b1a35ee XL |
287 | ); |
288 | } | |
289 | // Since we are behind a reference, we can just bubble the error up so we get a | |
290 | // constant at reference type, making it easy to let the fallback call | |
291 | // `PartialEq::eq` on it. | |
292 | return Err(fallback_to_const_ref(self)); | |
293 | } | |
5099ac24 FG |
294 | ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => { |
295 | debug!( | |
296 | "adt_def {:?} has !type_marked_structural for cv.ty: {:?}", | |
297 | adt_def, | |
298 | cv.ty() | |
299 | ); | |
1b1a35ee | 300 | self.saw_const_match_error.set(true); |
9c376795 | 301 | let err = TypeNotStructural { span, non_sm_ty: cv.ty() }; |
353b0b11 | 302 | tcx.sess.emit_err(err); |
1b1a35ee XL |
303 | PatKind::Wild |
304 | } | |
305 | ty::Adt(adt_def, substs) if adt_def.is_enum() => { | |
923072b8 FG |
306 | let destructured = tcx.destructure_mir_constant(param_env, cv); |
307 | ||
1b1a35ee | 308 | PatKind::Variant { |
5e7ed085 | 309 | adt_def: *adt_def, |
1b1a35ee XL |
310 | substs, |
311 | variant_index: destructured | |
312 | .variant | |
313 | .expect("destructed const of adt without variant id"), | |
cdc7bbd5 | 314 | subpatterns: self.field_pats(destructured.fields.iter().copied())?, |
1b1a35ee XL |
315 | } |
316 | } | |
317 | ty::Tuple(_) | ty::Adt(_, _) => { | |
923072b8 | 318 | let destructured = tcx.destructure_mir_constant(param_env, cv); |
cdc7bbd5 | 319 | PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? } |
1b1a35ee XL |
320 | } |
321 | ty::Array(..) => PatKind::Array { | |
322 | prefix: tcx | |
923072b8 | 323 | .destructure_mir_constant(param_env, cv) |
1b1a35ee XL |
324 | .fields |
325 | .iter() | |
5099ac24 | 326 | .map(|val| self.recur(*val, false)) |
1b1a35ee XL |
327 | .collect::<Result<_, _>>()?, |
328 | slice: None, | |
f2b60f7d | 329 | suffix: Box::new([]), |
1b1a35ee XL |
330 | }, |
331 | ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() { | |
332 | // These are not allowed and will error elsewhere anyway. | |
333 | ty::Dynamic(..) => { | |
334 | self.saw_const_match_error.set(true); | |
9c376795 | 335 | let err = InvalidPattern { span, non_sm_ty: cv.ty() }; |
353b0b11 | 336 | tcx.sess.emit_err(err); |
1b1a35ee XL |
337 | PatKind::Wild |
338 | } | |
29967ef6 | 339 | // `&str` is represented as `ConstValue::Slice`, let's keep using this |
1b1a35ee XL |
340 | // optimization for now. |
341 | ty::Str => PatKind::Constant { value: cv }, | |
1b1a35ee XL |
342 | // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when |
343 | // matching against references, you can only use byte string literals. | |
29967ef6 XL |
344 | // The typechecker has a special case for byte string literals, by treating them |
345 | // as slices. This means we turn `&[T; N]` constants into slice patterns, which | |
346 | // has no negative effects on pattern matching, even if we're actually matching on | |
347 | // arrays. | |
348 | ty::Array(..) if !self.treat_byte_string_as_slice => { | |
349 | let old = self.behind_reference.replace(true); | |
923072b8 | 350 | let array = tcx.deref_mir_constant(self.param_env.and(cv)); |
29967ef6 | 351 | let val = PatKind::Deref { |
f2b60f7d FG |
352 | subpattern: Box::new(Pat { |
353 | kind: PatKind::Array { | |
29967ef6 | 354 | prefix: tcx |
923072b8 | 355 | .destructure_mir_constant(param_env, array) |
29967ef6 XL |
356 | .fields |
357 | .iter() | |
5099ac24 | 358 | .map(|val| self.recur(*val, false)) |
29967ef6 XL |
359 | .collect::<Result<_, _>>()?, |
360 | slice: None, | |
f2b60f7d FG |
361 | suffix: Box::new([]), |
362 | }, | |
29967ef6 | 363 | span, |
5099ac24 | 364 | ty: *pointee_ty, |
f2b60f7d | 365 | }), |
29967ef6 XL |
366 | }; |
367 | self.behind_reference.set(old); | |
368 | val | |
369 | } | |
370 | ty::Array(elem_ty, _) | | |
1b1a35ee | 371 | // Cannot merge this with the catch all branch below, because the `const_deref` |
29967ef6 XL |
372 | // changes the type from slice to array, we need to keep the original type in the |
373 | // pattern. | |
374 | ty::Slice(elem_ty) => { | |
1b1a35ee | 375 | let old = self.behind_reference.replace(true); |
923072b8 | 376 | let array = tcx.deref_mir_constant(self.param_env.and(cv)); |
1b1a35ee | 377 | let val = PatKind::Deref { |
f2b60f7d FG |
378 | subpattern: Box::new(Pat { |
379 | kind: PatKind::Slice { | |
1b1a35ee | 380 | prefix: tcx |
923072b8 | 381 | .destructure_mir_constant(param_env, array) |
1b1a35ee XL |
382 | .fields |
383 | .iter() | |
5099ac24 | 384 | .map(|val| self.recur(*val, false)) |
1b1a35ee XL |
385 | .collect::<Result<_, _>>()?, |
386 | slice: None, | |
f2b60f7d FG |
387 | suffix: Box::new([]), |
388 | }, | |
1b1a35ee | 389 | span, |
29967ef6 | 390 | ty: tcx.mk_slice(elem_ty), |
f2b60f7d | 391 | }), |
1b1a35ee XL |
392 | }; |
393 | self.behind_reference.set(old); | |
394 | val | |
395 | } | |
396 | // Backwards compatibility hack: support references to non-structural types. | |
397 | // We'll lower | |
398 | // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a | |
399 | // reference. This makes the rest of the matching logic simpler as it doesn't have | |
400 | // to figure out how to get a reference again. | |
9c376795 | 401 | ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => { |
1b1a35ee | 402 | if self.behind_reference.get() { |
353b0b11 | 403 | if !self.saw_const_match_error.get() |
1b1a35ee XL |
404 | && !self.saw_const_match_lint.get() |
405 | { | |
9c376795 FG |
406 | self.saw_const_match_lint.set(true); |
407 | tcx.emit_spanned_lint( | |
1b1a35ee XL |
408 | lint::builtin::INDIRECT_STRUCTURAL_MATCH, |
409 | self.id, | |
9c376795 FG |
410 | span, |
411 | IndirectStructuralMatch { non_sm_ty: *pointee_ty }, | |
1b1a35ee XL |
412 | ); |
413 | } | |
414 | PatKind::Constant { value: cv } | |
415 | } else { | |
416 | if !self.saw_const_match_error.get() { | |
417 | self.saw_const_match_error.set(true); | |
9c376795 | 418 | let err = TypeNotStructural { span, non_sm_ty: *pointee_ty }; |
353b0b11 | 419 | tcx.sess.emit_err(err); |
1b1a35ee XL |
420 | } |
421 | PatKind::Wild | |
422 | } | |
423 | } | |
424 | // All other references are converted into deref patterns and then recursively | |
425 | // convert the dereferenced constant to a pattern that is the sub-pattern of the | |
426 | // deref pattern. | |
427 | _ => { | |
2b03887a | 428 | if !pointee_ty.is_sized(tcx, param_env) { |
923072b8 | 429 | // `tcx.deref_mir_constant()` below will ICE with an unsized type |
136023e0 | 430 | // (except slices, which are handled in a separate arm above). |
9c376795 FG |
431 | |
432 | let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; | |
353b0b11 | 433 | tcx.sess.emit_err(err); |
9c376795 | 434 | |
136023e0 XL |
435 | PatKind::Wild |
436 | } else { | |
437 | let old = self.behind_reference.replace(true); | |
438 | // In case there are structural-match violations somewhere in this subpattern, | |
439 | // we fall back to a const pattern. If we do not do this, we may end up with | |
440 | // a !structural-match constant that is not of reference type, which makes it | |
441 | // very hard to invoke `PartialEq::eq` on it as a fallback. | |
923072b8 | 442 | let val = match self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false) { |
136023e0 XL |
443 | Ok(subpattern) => PatKind::Deref { subpattern }, |
444 | Err(_) => PatKind::Constant { value: cv }, | |
445 | }; | |
446 | self.behind_reference.set(old); | |
447 | val | |
448 | } | |
1b1a35ee XL |
449 | } |
450 | }, | |
451 | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => { | |
452 | PatKind::Constant { value: cv } | |
453 | } | |
2b03887a | 454 | ty::RawPtr(pointee) if pointee.ty.is_sized(tcx, param_env) => { |
1b1a35ee XL |
455 | PatKind::Constant { value: cv } |
456 | } | |
5e7ed085 | 457 | // FIXME: these can have very surprising behaviour where optimization levels or other |
1b1a35ee XL |
458 | // compilation choices change the runtime behaviour of the match. |
459 | // See https://github.com/rust-lang/rust/issues/70861 for examples. | |
460 | ty::FnPtr(..) | ty::RawPtr(..) => { | |
353b0b11 | 461 | if !self.saw_const_match_error.get() |
1b1a35ee XL |
462 | && !self.saw_const_match_lint.get() |
463 | { | |
464 | self.saw_const_match_lint.set(true); | |
9c376795 | 465 | tcx.emit_spanned_lint( |
1b1a35ee XL |
466 | lint::builtin::POINTER_STRUCTURAL_MATCH, |
467 | id, | |
468 | span, | |
9c376795 | 469 | PointerPattern |
1b1a35ee XL |
470 | ); |
471 | } | |
472 | PatKind::Constant { value: cv } | |
473 | } | |
474 | _ => { | |
475 | self.saw_const_match_error.set(true); | |
9c376795 | 476 | let err = InvalidPattern { span, non_sm_ty: cv.ty() }; |
353b0b11 | 477 | tcx.sess.emit_err(err); |
1b1a35ee XL |
478 | PatKind::Wild |
479 | } | |
480 | }; | |
481 | ||
353b0b11 | 482 | if !self.saw_const_match_error.get() |
1b1a35ee XL |
483 | && !self.saw_const_match_lint.get() |
484 | && mir_structural_match_violation | |
485 | // FIXME(#73448): Find a way to bring const qualification into parity with | |
486 | // `search_for_structural_match_violation` and then remove this condition. | |
9c376795 | 487 | |
1b1a35ee XL |
488 | // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we |
489 | // could get `Option<NonStructEq>`, even though `Option` is annotated with derive. | |
9c376795 FG |
490 | && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) |
491 | { | |
492 | self.saw_const_match_lint.set(true); | |
493 | tcx.emit_spanned_lint( | |
1b1a35ee XL |
494 | lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH, |
495 | id, | |
496 | span, | |
9c376795 | 497 | NontrivialStructuralMatch {non_sm_ty} |
1b1a35ee XL |
498 | ); |
499 | } | |
500 | ||
f2b60f7d | 501 | Ok(Box::new(Pat { span, ty: cv.ty(), kind })) |
1b1a35ee XL |
502 | } |
503 | } |