]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ast_lowering/src/pat.rs
Rollup merge of #107339 - aliemjay:covariant, r=lcnr
[rust.git] / compiler / rustc_ast_lowering / src / pat.rs
1 use super::errors::{
2     ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
3 };
4 use super::ResolverAstLoweringExt;
5 use super::{ImplTraitContext, LoweringContext, ParamMode};
6 use crate::ImplTraitPosition;
7
8 use rustc_ast::ptr::P;
9 use rustc_ast::*;
10 use rustc_data_structures::stack::ensure_sufficient_stack;
11 use rustc_hir as hir;
12 use rustc_hir::def::Res;
13 use rustc_span::symbol::Ident;
14 use rustc_span::{source_map::Spanned, Span};
15
16 impl<'a, 'hir> LoweringContext<'a, '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     pub(crate) fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {
22         ensure_sufficient_stack(|| {
23             // loop here to avoid recursion
24             let node = loop {
25                 match &pattern.kind {
26                     PatKind::Wild => break hir::PatKind::Wild,
27                     PatKind::Ident(binding_mode, ident, sub) => {
28                         let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(s));
29                         break self.lower_pat_ident(pattern, *binding_mode, *ident, lower_sub);
30                     }
31                     PatKind::Lit(e) => {
32                         break hir::PatKind::Lit(self.lower_expr_within_pat(e, false));
33                     }
34                     PatKind::TupleStruct(qself, path, pats) => {
35                         let qpath = self.lower_qpath(
36                             pattern.id,
37                             qself,
38                             path,
39                             ParamMode::Optional,
40                             &ImplTraitContext::Disallowed(ImplTraitPosition::Path),
41                         );
42                         let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
43                         break hir::PatKind::TupleStruct(qpath, pats, ddpos);
44                     }
45                     PatKind::Or(pats) => {
46                         break hir::PatKind::Or(
47                             self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat_mut(x))),
48                         );
49                     }
50                     PatKind::Path(qself, path) => {
51                         let qpath = self.lower_qpath(
52                             pattern.id,
53                             qself,
54                             path,
55                             ParamMode::Optional,
56                             &ImplTraitContext::Disallowed(ImplTraitPosition::Path),
57                         );
58                         break hir::PatKind::Path(qpath);
59                     }
60                     PatKind::Struct(qself, path, fields, etc) => {
61                         let qpath = self.lower_qpath(
62                             pattern.id,
63                             qself,
64                             path,
65                             ParamMode::Optional,
66                             &ImplTraitContext::Disallowed(ImplTraitPosition::Path),
67                         );
68
69                         let fs = self.arena.alloc_from_iter(fields.iter().map(|f| {
70                             let hir_id = self.lower_node_id(f.id);
71                             self.lower_attrs(hir_id, &f.attrs);
72
73                             hir::PatField {
74                                 hir_id,
75                                 ident: self.lower_ident(f.ident),
76                                 pat: self.lower_pat(&f.pat),
77                                 is_shorthand: f.is_shorthand,
78                                 span: self.lower_span(f.span),
79                             }
80                         }));
81                         break hir::PatKind::Struct(qpath, fs, *etc);
82                     }
83                     PatKind::Tuple(pats) => {
84                         let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
85                         break hir::PatKind::Tuple(pats, ddpos);
86                     }
87                     PatKind::Box(inner) => {
88                         break hir::PatKind::Box(self.lower_pat(inner));
89                     }
90                     PatKind::Ref(inner, mutbl) => {
91                         break hir::PatKind::Ref(self.lower_pat(inner), *mutbl);
92                     }
93                     PatKind::Range(e1, e2, Spanned { node: end, .. }) => {
94                         break hir::PatKind::Range(
95                             e1.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
96                             e2.as_deref().map(|e| self.lower_expr_within_pat(e, true)),
97                             self.lower_range_end(end, e2.is_some()),
98                         );
99                     }
100                     PatKind::Slice(pats) => break self.lower_pat_slice(pats),
101                     PatKind::Rest => {
102                         // If we reach here the `..` pattern is not semantically allowed.
103                         break self.ban_illegal_rest_pat(pattern.span);
104                     }
105                     // return inner to be processed in next loop
106                     PatKind::Paren(inner) => pattern = inner,
107                     PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
108                 }
109             };
110
111             self.pat_with_node_id_of(pattern, node)
112         })
113     }
114
115     fn lower_pat_tuple(
116         &mut self,
117         pats: &[P<Pat>],
118         ctx: &str,
119     ) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) {
120         let mut elems = Vec::with_capacity(pats.len());
121         let mut rest = None;
122
123         let mut iter = pats.iter().enumerate();
124         for (idx, pat) in iter.by_ref() {
125             // Interpret the first `..` pattern as a sub-tuple pattern.
126             // Note that unlike for slice patterns,
127             // where `xs @ ..` is a legal sub-slice pattern,
128             // it is not a legal sub-tuple pattern.
129             match &pat.kind {
130                 // Found a sub-tuple rest pattern
131                 PatKind::Rest => {
132                     rest = Some((idx, pat.span));
133                     break;
134                 }
135                 // Found a sub-tuple pattern `$binding_mode $ident @ ..`.
136                 // This is not allowed as a sub-tuple pattern
137                 PatKind::Ident(_, ident, Some(sub)) if sub.is_rest() => {
138                     let sp = pat.span;
139                     self.tcx.sess.emit_err(SubTupleBinding {
140                         span: sp,
141                         ident_name: ident.name,
142                         ident: *ident,
143                         ctx,
144                     });
145                 }
146                 _ => {}
147             }
148
149             // It was not a sub-tuple pattern so lower it normally.
150             elems.push(self.lower_pat_mut(pat));
151         }
152
153         for (_, pat) in iter {
154             // There was a previous sub-tuple pattern; make sure we don't allow more...
155             if pat.is_rest() {
156                 // ...but there was one again, so error.
157                 self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
158             } else {
159                 elems.push(self.lower_pat_mut(pat));
160             }
161         }
162
163         (self.arena.alloc_from_iter(elems), hir::DotDotPos::new(rest.map(|(ddpos, _)| ddpos)))
164     }
165
166     /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
167     /// `hir::PatKind::Slice(before, slice, after)`.
168     ///
169     /// When encountering `($binding_mode $ident @)? ..` (`slice`),
170     /// this is interpreted as a sub-slice pattern semantically.
171     /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
172     fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
173         let mut before = Vec::new();
174         let mut after = Vec::new();
175         let mut slice = None;
176         let mut prev_rest_span = None;
177
178         // Lowers `$bm $ident @ ..` to `$bm $ident @ _`.
179         let lower_rest_sub = |this: &mut Self, pat, &ann, &ident, sub| {
180             let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
181             let node = this.lower_pat_ident(pat, ann, ident, lower_sub);
182             this.pat_with_node_id_of(pat, node)
183         };
184
185         let mut iter = pats.iter();
186         // Lower all the patterns until the first occurrence of a sub-slice pattern.
187         for pat in iter.by_ref() {
188             match &pat.kind {
189                 // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
190                 PatKind::Rest => {
191                     prev_rest_span = Some(pat.span);
192                     slice = Some(self.pat_wild_with_node_id_of(pat));
193                     break;
194                 }
195                 // Found a sub-slice pattern `$binding_mode $ident @ ..`.
196                 // Record, lower it to `$binding_mode $ident @ _`, and stop here.
197                 PatKind::Ident(ann, ident, Some(sub)) if sub.is_rest() => {
198                     prev_rest_span = Some(sub.span);
199                     slice = Some(self.arena.alloc(lower_rest_sub(self, pat, ann, ident, sub)));
200                     break;
201                 }
202                 // It was not a subslice pattern so lower it normally.
203                 _ => before.push(self.lower_pat_mut(pat)),
204             }
205         }
206
207         // Lower all the patterns after the first sub-slice pattern.
208         for pat in iter {
209             // There was a previous subslice pattern; make sure we don't allow more.
210             let rest_span = match &pat.kind {
211                 PatKind::Rest => Some(pat.span),
212                 PatKind::Ident(ann, ident, Some(sub)) if sub.is_rest() => {
213                     // #69103: Lower into `binding @ _` as above to avoid ICEs.
214                     after.push(lower_rest_sub(self, pat, ann, ident, sub));
215                     Some(sub.span)
216                 }
217                 _ => None,
218             };
219             if let Some(rest_span) = rest_span {
220                 // We have e.g., `[a, .., b, ..]`. That's no good, error!
221                 self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
222             } else {
223                 // Lower the pattern normally.
224                 after.push(self.lower_pat_mut(pat));
225             }
226         }
227
228         hir::PatKind::Slice(
229             self.arena.alloc_from_iter(before),
230             slice,
231             self.arena.alloc_from_iter(after),
232         )
233     }
234
235     fn lower_pat_ident(
236         &mut self,
237         p: &Pat,
238         annotation: BindingAnnotation,
239         ident: Ident,
240         lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
241     ) -> hir::PatKind<'hir> {
242         match self.resolver.get_partial_res(p.id).map(|d| d.expect_full_res()) {
243             // `None` can occur in body-less function signatures
244             res @ (None | Some(Res::Local(_))) => {
245                 let canonical_id = match res {
246                     Some(Res::Local(id)) => id,
247                     _ => p.id,
248                 };
249
250                 hir::PatKind::Binding(
251                     annotation,
252                     self.lower_node_id(canonical_id),
253                     self.lower_ident(ident),
254                     lower_sub(self),
255                 )
256             }
257             Some(res) => {
258                 let hir_id = self.next_id();
259                 let res = self.lower_res(res);
260                 hir::PatKind::Path(hir::QPath::Resolved(
261                     None,
262                     self.arena.alloc(hir::Path {
263                         span: self.lower_span(ident.span),
264                         res,
265                         segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)],
266                     }),
267             ))
268             }
269         }
270     }
271
272     fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
273         self.arena.alloc(self.pat_with_node_id_of(p, hir::PatKind::Wild))
274     }
275
276     /// Construct a `Pat` with the `HirId` of `p.id` lowered.
277     fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> hir::Pat<'hir> {
278         hir::Pat {
279             hir_id: self.lower_node_id(p.id),
280             kind,
281             span: self.lower_span(p.span),
282             default_binding_modes: true,
283         }
284     }
285
286     /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
287     pub(crate) fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
288         self.tcx.sess.emit_err(ExtraDoubleDot { span: sp, prev_span: prev_sp, ctx });
289     }
290
291     /// Used to ban the `..` pattern in places it shouldn't be semantically.
292     fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
293         self.tcx.sess.emit_err(MisplacedDoubleDot { span: sp });
294
295         // We're not in a list context so `..` can be reasonably treated
296         // as `_` because it should always be valid and roughly matches the
297         // intent of `..` (notice that the rest of a single slot is that slot).
298         hir::PatKind::Wild
299     }
300
301     fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
302         match *e {
303             RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
304             // No end; so `X..` behaves like `RangeFrom`.
305             RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
306         }
307     }
308
309     /// Matches `'-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus)`,
310     /// or paths for ranges.
311     //
312     // FIXME: do we want to allow `expr -> pattern` conversion to create path expressions?
313     // That means making this work:
314     //
315     // ```rust,ignore (FIXME)
316     // struct S;
317     // macro_rules! m {
318     //     ($a:expr) => {
319     //         let $a = S;
320     //     }
321     // }
322     // m!(S);
323     // ```
324     fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> {
325         match &expr.kind {
326             ExprKind::Lit(..)
327             | ExprKind::ConstBlock(..)
328             | ExprKind::IncludedBytes(..)
329             | ExprKind::Err => {}
330             ExprKind::Path(..) if allow_paths => {}
331             ExprKind::Unary(UnOp::Neg, inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
332             _ => {
333                 self.tcx.sess.emit_err(ArbitraryExpressionInPattern { span: expr.span });
334                 return self.arena.alloc(self.expr_err(expr.span));
335             }
336         }
337         self.lower_expr(expr)
338     }
339 }