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