Skip to main content

rustc_ast_lowering/
pat.rs

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