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