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