rustc_ast_lowering/
pat.rs

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