rust_verify_test coverage (7c1d655)

Coverage Report

Created: 2026-07-20 10:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
rustc_mir_build/src/builder/matches/match_pair.rs
Line
Count
Source
1
use std::sync::Arc;
2
3
use rustc_abi::FieldIdx;
4
use rustc_middle::mir::*;
5
use rustc_middle::span_bug;
6
use rustc_middle::thir::*;
7
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
8
9
use crate::builder::Builder;
10
use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
11
use crate::builder::matches::{
12
    FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase,
13
};
14
15
impl<'a, 'tcx> Builder<'a, 'tcx> {
16
    /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
17
    /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or
18
    /// [`PatKind::Leaf`].
19
    ///
20
    /// Used internally by [`MatchPairTree::for_pattern`].
21
29.8k
    fn field_match_pairs(
22
29.8k
        &mut self,
23
29.8k
        match_pairs: &mut Vec<MatchPairTree<'tcx>>,
24
29.8k
        extra_data: &mut PatternExtraData<'tcx>,
25
29.8k
        place: PlaceBuilder<'tcx>,
26
29.8k
        subpatterns: &[FieldPat<'tcx>],
27
29.8k
    ) {
28
29.8k
        for fieldpat in subpatterns {
29
28.4k
            let place = place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
30
28.4k
            MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs, extra_data);
31
28.4k
        }
32
29.8k
    }
33
34
    /// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an
35
    /// array pattern or slice pattern, and adds those trees to `match_pairs`.
36
    ///
37
    /// Used internally by [`MatchPairTree::for_pattern`].
38
0
    fn prefix_slice_suffix(
39
0
        &mut self,
40
0
        match_pairs: &mut Vec<MatchPairTree<'tcx>>,
41
0
        extra_data: &mut PatternExtraData<'tcx>,
42
0
        place: &PlaceBuilder<'tcx>,
43
0
        array_len: Option<u64>,
44
0
        prefix: &[Pat<'tcx>],
45
0
        opt_slice: &Option<Box<Pat<'tcx>>>,
46
0
        suffix: &[Pat<'tcx>],
47
0
    ) {
48
0
        let prefix_len = u64::try_from(prefix.len()).unwrap();
49
0
        let suffix_len = u64::try_from(suffix.len()).unwrap();
50
51
        // For slice patterns with a `..` followed by 0 or more suffix subpatterns,
52
        // the actual slice index of those subpatterns isn't statically known, so
53
        // we have to index them relative to the end of the slice.
54
        //
55
        // For array patterns, all subpatterns are indexed relative to the start.
56
0
        let (min_length, is_array) = match array_len {
57
0
            Some(len) => (len, true),
58
0
            None => (prefix_len + suffix_len, false),
59
        };
60
61
0
        for (offset, subpattern) in (0u64..).zip(prefix) {
62
0
            let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
63
0
            let place = place.clone_project(elem);
64
0
            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
65
        }
66
67
0
        if let Some(subslice_pat) = opt_slice {
68
0
            let subslice = place.clone_project(PlaceElem::Subslice {
69
0
                from: prefix_len,
70
0
                to: if is_array { min_length - suffix_len } else { suffix_len },
71
0
                from_end: !is_array,
72
            });
73
0
            MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
74
0
        }
75
76
0
        for (end_offset, subpattern) in (1u64..).zip(suffix.iter().rev()) {
77
0
            let elem = ProjectionElem::ConstantIndex {
78
0
                offset: if is_array { min_length - end_offset } else { end_offset },
79
0
                min_length,
80
0
                from_end: !is_array,
81
            };
82
0
            let place = place.clone_project(elem);
83
0
            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
84
        }
85
0
    }
86
}
87
88
impl<'tcx> MatchPairTree<'tcx> {
89
    /// Recursively builds a match pair tree for the given pattern and its
90
    /// subpatterns.
91
102k
    pub(super) fn for_pattern(
92
102k
        mut place_builder: PlaceBuilder<'tcx>,
93
102k
        pattern: &Pat<'tcx>,
94
102k
        cx: &mut Builder<'_, 'tcx>,
95
102k
        match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector
96
102k
        extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
97
102k
    ) {
98
        // Force the place type to the pattern's type.
99
        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
100
102k
        if let Some(resolved) = place_builder.resolve_upvar(cx) {
101
0
            place_builder = resolved;
102
102k
        }
103
104
102k
        if !cx.tcx.next_trait_solver_globally() {
105
            // Only add the OpaqueCast projection if the given place is an opaque type and the
106
            // expected type from the pattern is not.
107
102k
            let may_need_cast = match place_builder.base() {
108
102k
                PlaceBase::Local(local) => {
109
102k
                    let ty =
110
102k
                        Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
111
102k
                            .ty;
112
102k
                    ty != pattern.ty && ty.has_opaque_types()
113
                }
114
2
                _ => true,
115
            };
116
102k
            if may_need_cast {
117
2
                place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
118
102k
            }
119
0
        }
120
121
102k
        let place = place_builder.try_to_place(cx);
122
123
        // Apply any type ascriptions to the value at `match_pair.place`.
124
102k
        if let Some(place) = place
125
102k
            && let Some(extra) = &pattern.extra
126
        {
127
5.18k
            for &Ascription { ref annotation, variance } in &extra.ascriptions {
128
5.15k
                extra_data.ascriptions.push(super::Ascription {
129
5.15k
                    source: place,
130
5.15k
                    annotation: annotation.clone(),
131
5.15k
                    variance,
132
5.15k
                });
133
5.15k
            }
134
97.0k
        }
135
136
102k
        let mut subpairs = Vec::new();
137
102k
        let testable_case = match pattern.kind {
138
59.1k
            PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
139
140
72
            PatKind::Or { ref pats } => {
141
72
                let pats: Box<[FlatPat<'tcx>]> =
142
144
                    pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
143
72
                if !pats[0].extra_data.bindings.is_empty() {
144
6
                    // Hold a place for any bindings established in (possibly-nested) or-patterns.
145
6
                    // By only holding a place when bindings are present, we skip over any
146
6
                    // or-patterns that will be simplified by `merge_trivial_subcandidates`. In
147
6
                    // other words, we can assume this expands into subcandidates.
148
6
                    // FIXME(@dianne): this needs updating/removing if we always merge or-patterns
149
6
                    extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
150
66
                }
151
72
                Some(TestableCase::Or { pats })
152
            }
153
154
18
            PatKind::Range(ref range) => {
155
18
                assert_eq!(pattern.ty, range.ty);
156
18
                if range.is_full_range(cx.tcx) == Some(true) {
157
0
                    None
158
                } else {
159
18
                    Some(TestableCase::Range(Arc::clone(range)))
160
                }
161
            }
162
163
24
            PatKind::Constant { value } => {
164
24
                assert_eq!(pattern.ty, value.ty);
165
166
                // Classify the constant-pattern into further kinds, to
167
                // reduce the number of ad-hoc type tests needed later on.
168
24
                let pat_ty = pattern.ty;
169
24
                let const_kind = if pat_ty.is_bool() {
170
12
                    PatConstKind::Bool
171
12
                } else if pat_ty.is_integral() || pat_ty.is_char() {
172
12
                    PatConstKind::IntOrChar
173
0
                } else if pat_ty.is_floating_point() {
174
0
                    PatConstKind::Float
175
0
                } else if pat_ty.is_str() {
176
0
                    PatConstKind::String
177
                } else {
178
                    // FIXME(Zalathar): This still covers several different
179
                    // categories (e.g. raw pointer, pattern-type)
180
                    // which could be split out into their own kinds.
181
0
                    PatConstKind::Other
182
                };
183
24
                Some(TestableCase::Constant { value, kind: const_kind })
184
            }
185
186
11.0k
            PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
187
                // In order to please the borrow checker, when lowering a pattern
188
                // like `x @ subpat` we must establish any bindings in `subpat`
189
                // before establishing the binding for `x`.
190
                //
191
                // For example (from #69971):
192
                //
193
                // ```ignore (illustrative)
194
                // struct NonCopyStruct {
195
                //     copy_field: u32,
196
                // }
197
                //
198
                // fn foo1(x: NonCopyStruct) {
199
                //     let y @ NonCopyStruct { copy_field: z } = x;
200
                //     // the above should turn into
201
                //     let z = x.copy_field;
202
                //     let y = x;
203
                // }
204
                // ```
205
206
                // First, recurse into the subpattern, if any.
207
11.0k
                if let Some(subpattern) = subpattern.as_ref() {
208
38
                    // this is the `x @ P` case; have to keep matching against `P` now
209
38
                    MatchPairTree::for_pattern(
210
38
                        place_builder,
211
38
                        subpattern,
212
38
                        cx,
213
38
                        &mut subpairs,
214
38
                        extra_data,
215
38
                    );
216
11.0k
                }
217
218
                // Then push this binding, after any bindings in the subpattern.
219
11.0k
                if let Some(source) = place {
220
11.0k
                    extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
221
11.0k
                        span: pattern.span,
222
11.0k
                        source,
223
11.0k
                        var_id: var,
224
11.0k
                        binding_mode: mode,
225
11.0k
                        is_shorthand,
226
11.0k
                    }));
227
11.0k
                }
228
229
11.0k
                None
230
            }
231
232
0
            PatKind::Array { ref prefix, ref slice, ref suffix } => {
233
                // Determine the statically-known length of the array type being matched.
234
                // This should always succeed for legal programs, but could fail for
235
                // erroneous programs (e.g. the type is `[u8; const { panic!() }]`),
236
                // so take care not to ICE if this fails.
237
0
                let array_len = match pattern.ty.kind() {
238
0
                    ty::Array(_, len) => len.try_to_target_usize(cx.tcx),
239
0
                    _ => None,
240
                };
241
0
                if let Some(array_len) = array_len {
242
0
                    cx.prefix_slice_suffix(
243
0
                        &mut subpairs,
244
0
                        extra_data,
245
0
                        &place_builder,
246
0
                        Some(array_len),
247
0
                        prefix,
248
0
                        slice,
249
0
                        suffix,
250
0
                    );
251
0
                } else {
252
0
                    // If the array length couldn't be determined, ignore the
253
0
                    // subpatterns and delayed-assert that compilation will fail.
254
0
                    cx.tcx.dcx().span_delayed_bug(
255
0
                        pattern.span,
256
0
                        format!(
257
0
                            "array length in pattern couldn't be determined for ty={:?}",
258
0
                            pattern.ty
259
0
                        ),
260
0
                    );
261
0
                }
262
263
0
                None
264
            }
265
0
            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
266
0
                cx.prefix_slice_suffix(
267
0
                    &mut subpairs,
268
0
                    extra_data,
269
0
                    &place_builder,
270
0
                    None,
271
0
                    prefix,
272
0
                    slice,
273
0
                    suffix,
274
                );
275
276
0
                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
277
                    // This pattern is shaped like `[..]`. It can match a slice
278
                    // of any length, so no length test is needed.
279
0
                    None
280
                } else {
281
                    // Any other shape of slice pattern requires a length test.
282
                    // Slice patterns with a `..` subpattern require a minimum
283
                    // length; those without `..` require an exact length.
284
                    Some(TestableCase::Slice {
285
0
                        len: u64::try_from(prefix.len() + suffix.len()).unwrap(),
286
0
                        op: if slice.is_some() {
287
0
                            SliceLenOp::GreaterOrEqual
288
                        } else {
289
0
                            SliceLenOp::Equal
290
                        },
291
                    })
292
                }
293
            }
294
295
22.0k
            PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => {
296
22.0k
                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
297
22.0k
                cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
298
299
                // We treat non-exhaustive enums the same independent of the crate they are
300
                // defined in, to avoid differences in the operational semantics between crates.
301
22.0k
                let refutable =
302
22.0k
                    adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive();
303
22.0k
                if refutable {
304
21.8k
                    Some(TestableCase::Variant { adt_def, variant_index })
305
                } else {
306
187
                    None
307
                }
308
            }
309
310
7.81k
            PatKind::Leaf { ref subpatterns } => {
311
7.81k
                cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns);
312
7.81k
                None
313
            }
314
315
0
            PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => {
316
0
                let pinned_ref_ty = match pattern.ty.pinned_ty() {
317
0
                    Some(p_ty) if p_ty.is_ref() => p_ty,
318
0
                    _ => span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty),
319
                };
320
0
                MatchPairTree::for_pattern(
321
                    // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`.
322
0
                    place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(),
323
0
                    subpattern,
324
0
                    cx,
325
0
                    &mut subpairs,
326
0
                    extra_data,
327
                );
328
329
0
                None
330
            }
331
332
2.09k
            PatKind::Deref { pin: Pinnedness::Not, ref subpattern }
333
7
            | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => {
334
2.09k
                MatchPairTree::for_pattern(
335
2.09k
                    place_builder.deref(),
336
2.09k
                    subpattern,
337
2.09k
                    cx,
338
2.09k
                    &mut subpairs,
339
2.09k
                    extra_data,
340
                );
341
2.09k
                None
342
            }
343
344
            PatKind::DerefPattern {
345
0
                ref subpattern,
346
0
                borrow: DerefPatBorrowMode::Borrow(mutability),
347
            } => {
348
                // Create a new temporary for each deref pattern.
349
                // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
350
0
                let temp = cx.temp(
351
0
                    Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
352
0
                    pattern.span,
353
                );
354
0
                MatchPairTree::for_pattern(
355
0
                    PlaceBuilder::from(temp).deref(),
356
0
                    subpattern,
357
0
                    cx,
358
0
                    &mut subpairs,
359
0
                    extra_data,
360
                );
361
0
                Some(TestableCase::Deref { temp, mutability })
362
            }
363
364
            PatKind::Guard { .. } => {
365
                // FIXME(guard_patterns)
366
0
                None
367
            }
368
369
0
            PatKind::Never => Some(TestableCase::Never),
370
        };
371
372
102k
        if let Some(testable_case) = testable_case {
373
            // This pattern is refutable, so push a new match-pair node.
374
            //
375
            // Note: unless test_case is TestCase::Or, place must not be None.
376
            // This means that the closure capture analysis in
377
            // rustc_hir_typeck::upvar, and in particular the pattern handling
378
            // code of ExprUseVisitor, must capture all of the places we'll use.
379
            // Make sure to keep these two parts in sync!
380
22.0k
            match_pairs.push(MatchPairTree {
381
22.0k
                place,
382
22.0k
                testable_case,
383
22.0k
                subpairs,
384
22.0k
                pattern_span: pattern.span,
385
22.0k
            })
386
80.2k
        } else {
387
80.2k
            // This pattern is irrefutable, so it doesn't need its own match-pair node.
388
80.2k
            // Just push its refutable subpatterns instead, if any.
389
80.2k
            match_pairs.extend(subpairs);
390
80.2k
        }
391
102k
    }
392
}