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