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