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