rust_verify_test coverage (fbbbbcf)

Coverage Report

Created: 2026-08-23 08:37

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