Skip to main content

rustc_ast_lowering/
pat.rs

1use std::sync::Arc;
2
3use rustc_ast::*;
4use rustc_hir::attrs::lang_items::LangItem;
5use rustc_hir::def::{DefKind, Res};
6use rustc_hir::{self as hir, Target};
7use rustc_middle::span_bug;
8use rustc_span::{DesugaringKind, Ident, Span, Spanned, respan};
9
10use crate::diagnostics::{
11    ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
12};
13use crate::{
14    AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
15};
16
17impl<'hir> LoweringContext<'_, 'hir> {
18    pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> {
19        self.arena.alloc(self.lower_pat_mut(pattern))
20    }
21
22    fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
23        // loop here to avoid recursion
24        let pat_hir_id = self.lower_node_id(pattern.id);
25        let node = loop {
26            match &pattern.kind {
27                PatKind::Missing => break hir::PatKind::Missing,
28                PatKind::Wild => break hir::PatKind::Wild,
29                PatKind::Never => break hir::PatKind::Never,
30                PatKind::Ident(binding_mode, ident, sub) => {
31                    let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(s));
32                    break self.lower_pat_ident(
33                        pattern,
34                        *binding_mode,
35                        *ident,
36                        pat_hir_id,
37                        lower_sub,
38                    );
39                }
40                PatKind::Expr(e) => {
41                    break hir::PatKind::Expr(self.lower_expr_within_pat(e, false));
42                }
43                PatKind::TupleStruct(qself, path, pats) => {
44                    let qpath = self.lower_qpath(
45                        pattern.id,
46                        qself,
47                        path,
48                        ParamMode::Optional,
49                        AllowReturnTypeNotation::No,
50                        ImplTraitContext::Disallowed(ImplTraitPosition::Path),
51                        None,
52                    );
53                    let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
54                    break hir::PatKind::TupleStruct(qpath, pats, ddpos);
55                }
56                PatKind::Or(pats) => {
57                    break hir::PatKind::Or(
58                        self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat_mut(x))),
59                    );
60                }
61                PatKind::Path(qself, path) => {
62                    let qpath = self.lower_qpath(
63                        pattern.id,
64                        qself,
65                        path,
66                        ParamMode::Optional,
67                        AllowReturnTypeNotation::No,
68                        ImplTraitContext::Disallowed(ImplTraitPosition::Path),
69                        None,
70                    );
71                    let kind = hir::PatExprKind::Path(qpath);
72                    let span = self.lower_span(pattern.span);
73                    let expr = hir::PatExpr { hir_id: pat_hir_id, span, kind };
74                    let expr = self.arena.alloc(expr);
75                    return hir::Pat {
76                        hir_id: self.next_id(),
77                        kind: hir::PatKind::Expr(expr),
78                        span,
79                        default_binding_modes: true,
80                    };
81                }
82                PatKind::Struct(qself, path, fields, etc) => {
83                    let qpath = self.lower_qpath(
84                        pattern.id,
85                        qself,
86                        path,
87                        ParamMode::Optional,
88                        AllowReturnTypeNotation::No,
89                        ImplTraitContext::Disallowed(ImplTraitPosition::Path),
90                        None,
91                    );
92
93                    let fs = self.arena.alloc_from_iter(fields.iter().map(|f| {
94                        let hir_id = self.lower_node_id(f.id);
95                        self.lower_attrs(hir_id, &f.attrs, f.span, Target::PatField);
96
97                        hir::PatField {
98                            hir_id,
99                            ident: self.lower_ident(f.ident),
100                            pat: self.lower_pat(&f.pat),
101                            is_shorthand: f.is_shorthand,
102                            span: self.lower_span(f.span),
103                        }
104                    }));
105                    break hir::PatKind::Struct(
106                        qpath,
107                        fs,
108                        match etc {
109                            ast::PatFieldsRest::Rest(sp) => Some(self.lower_span(*sp)),
110                            ast::PatFieldsRest::Recovered(_) => Some(Span::default()),
111                            _ => None,
112                        },
113                    );
114                }
115                PatKind::Tuple(pats) => {
116                    let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
117                    break hir::PatKind::Tuple(pats, ddpos);
118                }
119                PatKind::Deref(inner) => {
120                    break hir::PatKind::Deref(self.lower_pat(inner));
121                }
122                PatKind::Ref(inner, pinned, mutbl) => {
123                    break hir::PatKind::Ref(self.lower_pat(inner), *pinned, *mutbl);
124                }
125                PatKind::Range(e1, e2, Spanned { node: end, .. }) => {
126                    break hir::PatKind::Range(
127                        e1.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
128                        e2.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
129                        self.lower_range_end(end, e2.is_some()),
130                    );
131                }
132                PatKind::Guard(inner, guard) => {
133                    break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(&guard.cond));
134                }
135                PatKind::Slice(pats) => break self.lower_pat_slice(pats),
136                PatKind::Rest => {
137                    // If we reach here the `..` pattern is not semantically allowed.
138                    break self.ban_illegal_rest_pat(pattern.span);
139                }
140                // return inner to be processed in next loop
141                PatKind::Paren(inner) => pattern = inner,
142                PatKind::MacCall(_) => {
143                    {
    ::core::panicking::panic_fmt(format_args!("{0:#?} shouldn\'t exist here",
            pattern));
}panic!("{pattern:#?} shouldn't exist here")
144                }
145                PatKind::Err(guar) => break hir::PatKind::Err(*guar),
146            }
147        };
148
149        self.pat_with_node_id_of(pattern, node, pat_hir_id)
150    }
151
152    fn lower_pat_tuple(
153        &mut self,
154        pats: &[Pat],
155        ctx: &str,
156    ) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) {
157        let mut elems = Vec::with_capacity(pats.len());
158        let mut rest = None;
159
160        let mut iter = pats.iter().enumerate();
161        for (idx, pat) in iter.by_ref() {
162            // Interpret the first `..` pattern as a sub-tuple pattern.
163            // Note that unlike for slice patterns,
164            // where `xs @ ..` is a legal sub-slice pattern,
165            // it is not a legal sub-tuple pattern.
166            match &pat.kind {
167                // Found a sub-tuple rest pattern
168                PatKind::Rest => {
169                    rest = Some((idx, pat.span));
170                    break;
171                }
172                // Found a sub-tuple pattern `$binding_mode $ident @ ..`.
173                // This is not allowed as a sub-tuple pattern
174                PatKind::Ident(_, ident, Some(sub)) if sub.is_rest() => {
175                    let sp = pat.span;
176                    self.dcx().emit_err(SubTupleBinding {
177                        span: sp,
178                        ident_name: ident.name,
179                        ident: *ident,
180                        ctx,
181                    });
182                }
183                _ => {}
184            }
185
186            // It was not a sub-tuple pattern so lower it normally.
187            elems.push(self.lower_pat_mut(pat));
188        }
189
190        for (_, pat) in iter {
191            // There was a previous sub-tuple pattern; make sure we don't allow more...
192            if pat.is_rest() {
193                // ...but there was one again, so error.
194                self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
195            } else {
196                elems.push(self.lower_pat_mut(pat));
197            }
198        }
199
200        (self.arena.alloc_from_iter(elems), hir::DotDotPos::new(rest.map(|(ddpos, _)| ddpos)))
201    }
202
203    /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
204    /// `hir::PatKind::Slice(before, slice, after)`.
205    ///
206    /// When encountering `($binding_mode $ident @)? ..` (`slice`),
207    /// this is interpreted as a sub-slice pattern semantically.
208    /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
209    fn lower_pat_slice(&mut self, pats: &[Pat]) -> hir::PatKind<'hir> {
210        let mut before = Vec::new();
211        let mut after = Vec::new();
212        let mut slice = None;
213        let mut prev_rest_span = None;
214
215        // Lowers `$bm $ident @ ..` to `$bm $ident @ _`.
216        let lower_rest_sub = |this: &mut Self, pat: &Pat, &ann, &ident, sub: &Pat| {
217            let sub_hir_id = this.lower_node_id(sub.id);
218            let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub, sub_hir_id));
219            let pat_hir_id = this.lower_node_id(pat.id);
220            let node = this.lower_pat_ident(pat, ann, ident, pat_hir_id, lower_sub);
221            this.pat_with_node_id_of(pat, node, pat_hir_id)
222        };
223
224        let mut iter = pats.iter();
225        // Lower all the patterns until the first occurrence of a sub-slice pattern.
226        for pat in iter.by_ref() {
227            match &pat.kind {
228                // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
229                PatKind::Rest => {
230                    prev_rest_span = Some(pat.span);
231                    let hir_id = self.lower_node_id(pat.id);
232                    slice = Some(self.pat_wild_with_node_id_of(pat, hir_id));
233                    break;
234                }
235                // Found a sub-slice pattern `$binding_mode $ident @ ..`.
236                // Record, lower it to `$binding_mode $ident @ _`, and stop here.
237                PatKind::Ident(ann, ident, Some(sub)) if sub.is_rest() => {
238                    prev_rest_span = Some(sub.span);
239                    slice = Some(self.arena.alloc(lower_rest_sub(self, pat, ann, ident, sub)));
240                    break;
241                }
242                // It was not a subslice pattern so lower it normally.
243                _ => before.push(self.lower_pat_mut(pat)),
244            }
245        }
246
247        // Lower all the patterns after the first sub-slice pattern.
248        for pat in iter {
249            // There was a previous subslice pattern; make sure we don't allow more.
250            let rest_span = match &pat.kind {
251                PatKind::Rest => Some(pat.span),
252                PatKind::Ident(ann, ident, Some(sub)) if sub.is_rest() => {
253                    // #69103: Lower into `binding @ _` as above to avoid ICEs.
254                    after.push(lower_rest_sub(self, pat, ann, ident, sub));
255                    Some(sub.span)
256                }
257                _ => None,
258            };
259            if let Some(rest_span) = rest_span {
260                // We have e.g., `[a, .., b, ..]`. That's no good, error!
261                self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
262            } else {
263                // Lower the pattern normally.
264                after.push(self.lower_pat_mut(pat));
265            }
266        }
267
268        hir::PatKind::Slice(
269            self.arena.alloc_from_iter(before),
270            slice,
271            self.arena.alloc_from_iter(after),
272        )
273    }
274
275    fn lower_pat_ident(
276        &mut self,
277        p: &Pat,
278        annotation: BindingMode,
279        ident: Ident,
280        hir_id: hir::HirId,
281        lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
282    ) -> hir::PatKind<'hir> {
283        match self.get_partial_res(p.id).map(|d| d.expect_full_res()) {
284            // `None` can occur in body-less function signatures
285            res @ (None | Some(Res::Local(_))) => {
286                let binding_id = match res {
287                    Some(Res::Local(id)) => {
288                        // In `Or` patterns like `VariantA(s) | VariantB(s, _)`, multiple identifier patterns
289                        // will be resolved to the same `Res::Local`. Thus they just share a single
290                        // `HirId`.
291                        if id == p.id {
292                            self.curr_owner.ident_and_label_to_local_id.insert(id, hir_id.local_id);
293                            hir_id
294                        } else {
295                            hir::HirId {
296                                owner: self.curr_owner.owner_id,
297                                local_id: self.curr_owner.ident_and_label_to_local_id[&id],
298                            }
299                        }
300                    }
301                    _ => {
302                        self.curr_owner.ident_and_label_to_local_id.insert(p.id, hir_id.local_id);
303                        hir_id
304                    }
305                };
306                hir::PatKind::Binding(
307                    annotation,
308                    binding_id,
309                    self.lower_ident(ident),
310                    lower_sub(self),
311                )
312            }
313            Some(res) => {
314                let res = self.lower_res(res);
315                let span = self.lower_span(ident.span);
316                hir::PatKind::Expr(self.arena.alloc(hir::PatExpr {
317                    kind: hir::PatExprKind::Path(hir::QPath::Resolved(
318                        None,
319                        self.arena.alloc(hir::Path {
320                            span,
321                            res,
322                            segments: self.arena.alloc_from_iter([hir::PathSegment::new(self.lower_ident(ident),
                self.next_id(), res)])arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), self.next_id(), res)],
323                        }),
324                    )),
325                    hir_id: self.next_id(),
326                    span,
327                }))
328            }
329        }
330    }
331
332    fn pat_wild_with_node_id_of(&mut self, p: &Pat, hir_id: hir::HirId) -> &'hir hir::Pat<'hir> {
333        self.arena.alloc(self.pat_with_node_id_of(p, hir::PatKind::Wild, hir_id))
334    }
335
336    /// Construct a `Pat` with the `HirId` of `p.id` already lowered.
337    fn pat_with_node_id_of(
338        &mut self,
339        p: &Pat,
340        kind: hir::PatKind<'hir>,
341        hir_id: hir::HirId,
342    ) -> hir::Pat<'hir> {
343        hir::Pat { hir_id, kind, span: self.lower_span(p.span), default_binding_modes: true }
344    }
345
346    /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
347    pub(crate) fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
348        self.dcx().emit_err(ExtraDoubleDot { span: sp, prev_span: prev_sp, ctx });
349    }
350
351    /// Used to ban the `..` pattern in places it shouldn't be semantically.
352    fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
353        self.dcx().emit_err(MisplacedDoubleDot { span: sp });
354
355        // We're not in a list context so `..` can be reasonably treated
356        // as `_` because it should always be valid and roughly matches the
357        // intent of `..` (notice that the rest of a single slot is that slot).
358        hir::PatKind::Wild
359    }
360
361    fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
362        match *e {
363            RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
364            // No end; so `X..` behaves like `RangeFrom`.
365            RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
366        }
367    }
368
369    /// Matches `'-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus)`,
370    /// or paths for ranges.
371    //
372    // FIXME: do we want to allow `expr -> pattern` conversion to create path expressions?
373    // That means making this work:
374    //
375    // ```rust,ignore (FIXME)
376    // struct S;
377    // macro_rules! m {
378    //     ($a:expr) => {
379    //         let $a = S;
380    //     }
381    // }
382    // m!(S);
383    // ```
384    fn lower_expr_within_pat(
385        &mut self,
386        expr: &Expr,
387        allow_paths: bool,
388    ) -> &'hir hir::PatExpr<'hir> {
389        let span = self.lower_span(expr.span);
390        let err =
391            |guar| hir::PatExprKind::Lit { lit: respan(span, LitKind::Err(guar)), negated: false };
392        let kind = match &expr.kind {
393            ExprKind::Lit(lit) => {
394                hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false }
395            }
396            ExprKind::IncludedBytes(byte_sym) => hir::PatExprKind::Lit {
397                lit: respan(span, LitKind::ByteStr(*byte_sym, StrStyle::Cooked)),
398                negated: false,
399            },
400            ExprKind::Err(guar) => err(*guar),
401            ExprKind::Dummy => ::rustc_middle::util::bug::span_bug_fmt(span,
    format_args!("lowered ExprKind::Dummy"))span_bug!(span, "lowered ExprKind::Dummy"),
402            ExprKind::Path(qself, path) if allow_paths => hir::PatExprKind::Path(self.lower_qpath(
403                expr.id,
404                qself,
405                path,
406                ParamMode::Optional,
407                AllowReturnTypeNotation::No,
408                ImplTraitContext::Disallowed(ImplTraitPosition::Path),
409                None,
410            )),
411            ExprKind::Unary(UnOp::Neg, inner) if let ExprKind::Lit(lit) = &inner.kind => {
412                hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: true }
413            }
414            _ => {
415                let is_const_block = #[allow(non_exhaustive_omitted_patterns)] match expr.kind {
    ExprKind::ConstBlock(_) => true,
    _ => false,
}matches!(expr.kind, ExprKind::ConstBlock(_));
416                let pattern_from_macro = expr.is_approximately_pattern()
417                    || #[allow(non_exhaustive_omitted_patterns)] match expr.peel_parens().kind {
    ExprKind::Binary(Spanned { node: BinOpKind::BitOr, .. }, ..) => true,
    _ => false,
}matches!(
418                        expr.peel_parens().kind,
419                        ExprKind::Binary(Spanned { node: BinOpKind::BitOr, .. }, ..)
420                    );
421                let guar = self.dcx().emit_err(ArbitraryExpressionInPattern {
422                    span,
423                    pattern_from_macro_note: pattern_from_macro,
424                    const_block_in_pattern_help: is_const_block,
425                });
426                err(guar)
427            }
428        };
429        self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })
430    }
431
432    pub(crate) fn lower_ty_pat(
433        &mut self,
434        pattern: &TyPat,
435        base_type: Span,
436    ) -> &'hir hir::TyPat<'hir> {
437        self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))
438    }
439
440    fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {
441        // loop here to avoid recursion
442        let pat_hir_id = self.lower_node_id(pattern.id);
443        let node = match &pattern.kind {
444            TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(
445                e1.as_deref()
446                    .map(|e| self.lower_anon_const_to_const_arg_and_alloc(e))
447                    .unwrap_or_else(|| {
448                        self.lower_ty_pat_range_end(
449                            LangItem::RangeMin,
450                            span.shrink_to_lo(),
451                            base_type,
452                        )
453                    }),
454                e2.as_deref()
455                    .map(|e| match end {
456                        RangeEnd::Included(..) => self.lower_anon_const_to_const_arg_and_alloc(e),
457                        RangeEnd::Excluded => self.lower_excluded_range_end(e),
458                    })
459                    .unwrap_or_else(|| {
460                        self.lower_ty_pat_range_end(
461                            LangItem::RangeMax,
462                            span.shrink_to_hi(),
463                            base_type,
464                        )
465                    }),
466            ),
467            TyPatKind::NotNull => hir::TyPatKind::NotNull,
468            TyPatKind::Or(variants) => {
469                hir::TyPatKind::Or(self.arena.alloc_from_iter(
470                    variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),
471                ))
472            }
473            TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),
474        };
475
476        hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }
477    }
478
479    /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).
480    /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.
481    fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {
482        let span = self.lower_span(e.value.span);
483        let unstable_span = self.mark_span_with_reason(
484            DesugaringKind::PatTyRange,
485            span,
486            Some(Arc::clone(&self.allow_pattern_type)),
487        );
488        let anon_const = self.with_new_scopes(span, |this| {
489            let def_id = this.local_def_id(e.id);
490            let hir_id = this.lower_node_id(e.id);
491            let body = this.lower_body(|this| {
492                // Need to use a custom function as we can't just subtract `1` from a `char`.
493                let kind = hir::ExprKind::Path(this.make_lang_item_qpath(
494                    LangItem::RangeSub,
495                    unstable_span,
496                    None,
497                ));
498                let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });
499                let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);
500                (
501                    &[],
502                    hir::Expr {
503                        hir_id: this.next_id(),
504                        kind: hir::ExprKind::Call(fn_def, args),
505                        span,
506                    },
507                )
508            });
509            hir::AnonConst { def_id, hir_id, body, span }
510        });
511        self.arena.alloc(hir::ConstArg {
512            hir_id: self.next_id(),
513            kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),
514            span,
515        })
516    }
517
518    /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),
519    /// we instead use a constant of the MAX/MIN of the type.
520    /// This way the type system does not have to handle the lack of a start/end.
521    fn lower_ty_pat_range_end(
522        &mut self,
523        lang_item: LangItem,
524        span: Span,
525        base_type: Span,
526    ) -> &'hir hir::ConstArg<'hir> {
527        let node_id = self.next_node_id();
528
529        // Add a definition for the in-band const def.
530        // We're generating a range end that didn't exist in the AST,
531        // so the def collector didn't create the def ahead of time. That's why we have to do
532        // it here.
533        let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);
534        let hir_id = self.lower_node_id(node_id);
535
536        let unstable_span = self.mark_span_with_reason(
537            DesugaringKind::PatTyRange,
538            self.lower_span(span),
539            Some(Arc::clone(&self.allow_pattern_type)),
540        );
541        let span = self.lower_span(base_type);
542
543        let path_expr = hir::Expr {
544            hir_id: self.next_id(),
545            kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),
546            span,
547        };
548
549        let ct = self.with_new_scopes(span, |this| {
550            self.arena.alloc(hir::AnonConst {
551                def_id,
552                hir_id,
553                body: this.lower_body(|_this| (&[], path_expr)),
554                span,
555            })
556        });
557        let hir_id = self.next_id();
558        self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id, span })
559    }
560}