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