Skip to main content

rustc_ast_lowering/
pat.rs

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