]>
Commit | Line | Data |
---|---|---|
f2b60f7d FG |
1 | use super::errors::{ |
2 | ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding, | |
3 | }; | |
923072b8 | 4 | use super::ResolverAstLoweringExt; |
dfeec247 | 5 | use super::{ImplTraitContext, LoweringContext, ParamMode}; |
923072b8 | 6 | use crate::ImplTraitPosition; |
dfeec247 | 7 | |
74b04a01 | 8 | use rustc_ast::ptr::P; |
3dfed10e | 9 | use rustc_ast::*; |
f9f354fc | 10 | use rustc_data_structures::stack::ensure_sufficient_stack; |
dfeec247 XL |
11 | use rustc_hir as hir; |
12 | use rustc_hir::def::Res; | |
f9f354fc | 13 | use rustc_span::symbol::Ident; |
dfeec247 | 14 | use rustc_span::{source_map::Spanned, Span}; |
dfeec247 XL |
15 | |
16 | impl<'a, 'hir> LoweringContext<'a, 'hir> { | |
923072b8 | 17 | pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> { |
136023e0 XL |
18 | self.arena.alloc(self.lower_pat_mut(pattern)) |
19 | } | |
20 | ||
923072b8 | 21 | pub(crate) fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> { |
f9f354fc | 22 | ensure_sufficient_stack(|| { |
29967ef6 XL |
23 | // loop here to avoid recursion |
24 | let node = loop { | |
25 | match pattern.kind { | |
26 | PatKind::Wild => break hir::PatKind::Wild, | |
f2b60f7d | 27 | PatKind::Ident(binding_mode, ident, ref sub) => { |
29967ef6 XL |
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 | } | |
a2a8927a XL |
31 | PatKind::Lit(ref e) => { |
32 | break hir::PatKind::Lit(self.lower_expr_within_pat(e, false)); | |
33 | } | |
17df50a5 | 34 | PatKind::TupleStruct(ref qself, ref path, ref pats) => { |
29967ef6 XL |
35 | let qpath = self.lower_qpath( |
36 | pattern.id, | |
17df50a5 | 37 | qself, |
29967ef6 XL |
38 | path, |
39 | ParamMode::Optional, | |
f2b60f7d | 40 | &mut ImplTraitContext::Disallowed(ImplTraitPosition::Path), |
29967ef6 XL |
41 | ); |
42 | let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct"); | |
43 | break hir::PatKind::TupleStruct(qpath, pats, ddpos); | |
44 | } | |
45 | PatKind::Or(ref pats) => { | |
46 | break hir::PatKind::Or( | |
136023e0 | 47 | self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat_mut(x))), |
29967ef6 XL |
48 | ); |
49 | } | |
50 | PatKind::Path(ref qself, ref path) => { | |
51 | let qpath = self.lower_qpath( | |
52 | pattern.id, | |
53 | qself, | |
54 | path, | |
55 | ParamMode::Optional, | |
f2b60f7d | 56 | &mut ImplTraitContext::Disallowed(ImplTraitPosition::Path), |
29967ef6 XL |
57 | ); |
58 | break hir::PatKind::Path(qpath); | |
59 | } | |
17df50a5 | 60 | PatKind::Struct(ref qself, ref path, ref fields, etc) => { |
29967ef6 XL |
61 | let qpath = self.lower_qpath( |
62 | pattern.id, | |
17df50a5 | 63 | qself, |
29967ef6 XL |
64 | path, |
65 | ParamMode::Optional, | |
f2b60f7d | 66 | &mut ImplTraitContext::Disallowed(ImplTraitPosition::Path), |
29967ef6 | 67 | ); |
dfeec247 | 68 | |
f2b60f7d FG |
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 | } | |
29967ef6 XL |
80 | })); |
81 | break hir::PatKind::Struct(qpath, fs, etc); | |
82 | } | |
83 | PatKind::Tuple(ref pats) => { | |
84 | let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple"); | |
85 | break hir::PatKind::Tuple(pats, ddpos); | |
86 | } | |
87 | PatKind::Box(ref inner) => { | |
88 | break hir::PatKind::Box(self.lower_pat(inner)); | |
89 | } | |
90 | PatKind::Ref(ref inner, mutbl) => { | |
91 | break hir::PatKind::Ref(self.lower_pat(inner), mutbl); | |
92 | } | |
93 | PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => { | |
94 | break hir::PatKind::Range( | |
a2a8927a XL |
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)), | |
29967ef6 XL |
97 | self.lower_range_end(end, e2.is_some()), |
98 | ); | |
99 | } | |
100 | PatKind::Slice(ref 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(ref inner) => pattern = inner, | |
107 | PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span), | |
f9f354fc | 108 | } |
f9f354fc | 109 | }; |
dfeec247 | 110 | |
29967ef6 | 111 | self.pat_with_node_id_of(pattern, node) |
f9f354fc | 112 | }) |
dfeec247 XL |
113 | } |
114 | ||
115 | fn lower_pat_tuple( | |
116 | &mut self, | |
117 | pats: &[P<Pat>], | |
118 | ctx: &str, | |
f2b60f7d | 119 | ) -> (&'hir [hir::Pat<'hir>], hir::DotDotPos) { |
dfeec247 XL |
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. | |
f9f354fc XL |
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(ref _bm, ident, Some(ref sub)) if sub.is_rest() => { | |
f9f354fc | 138 | let sp = pat.span; |
f2b60f7d FG |
139 | self.tcx.sess.emit_err(SubTupleBinding { |
140 | span: sp, | |
141 | ident_name: ident.name, | |
142 | ident, | |
143 | ctx, | |
144 | }); | |
f9f354fc XL |
145 | } |
146 | _ => {} | |
dfeec247 | 147 | } |
f9f354fc | 148 | |
dfeec247 | 149 | // It was not a sub-tuple pattern so lower it normally. |
136023e0 | 150 | elems.push(self.lower_pat_mut(pat)); |
dfeec247 XL |
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 { | |
136023e0 | 159 | elems.push(self.lower_pat_mut(pat)); |
dfeec247 XL |
160 | } |
161 | } | |
162 | ||
f2b60f7d | 163 | (self.arena.alloc_from_iter(elems), hir::DotDotPos::new(rest.map(|(ddpos, _)| ddpos))) |
dfeec247 XL |
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 @ _`. | |
f2b60f7d | 179 | let lower_rest_sub = |this: &mut Self, pat, ann, ident, sub| { |
dfeec247 | 180 | let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub)); |
f2b60f7d | 181 | let node = this.lower_pat_ident(pat, ann, ident, lower_sub); |
dfeec247 XL |
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. | |
f2b60f7d | 197 | PatKind::Ident(ann, ident, Some(ref sub)) if sub.is_rest() => { |
dfeec247 | 198 | prev_rest_span = Some(sub.span); |
f2b60f7d | 199 | slice = Some(self.arena.alloc(lower_rest_sub(self, pat, ann, ident, sub))); |
dfeec247 XL |
200 | break; |
201 | } | |
202 | // It was not a subslice pattern so lower it normally. | |
136023e0 | 203 | _ => before.push(self.lower_pat_mut(pat)), |
dfeec247 XL |
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), | |
f2b60f7d | 212 | PatKind::Ident(ann, ident, Some(ref sub)) if sub.is_rest() => { |
dfeec247 | 213 | // #69103: Lower into `binding @ _` as above to avoid ICEs. |
f2b60f7d | 214 | after.push(lower_rest_sub(self, pat, ann, ident, sub)); |
dfeec247 XL |
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. | |
136023e0 | 224 | after.push(self.lower_pat_mut(pat)); |
dfeec247 XL |
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, | |
f2b60f7d | 238 | annotation: BindingAnnotation, |
dfeec247 XL |
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.base_res()) { | |
243 | // `None` can occur in body-less function signatures | |
ba9703b0 | 244 | res @ (None | Some(Res::Local(_))) => { |
dfeec247 XL |
245 | let canonical_id = match res { |
246 | Some(Res::Local(id)) => id, | |
247 | _ => p.id, | |
248 | }; | |
249 | ||
250 | hir::PatKind::Binding( | |
f2b60f7d | 251 | annotation, |
dfeec247 | 252 | self.lower_node_id(canonical_id), |
94222f64 | 253 | self.lower_ident(ident), |
dfeec247 XL |
254 | lower_sub(self), |
255 | ) | |
256 | } | |
f2b60f7d FG |
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 | } | |
dfeec247 XL |
269 | } |
270 | } | |
271 | ||
272 | fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> { | |
136023e0 | 273 | self.arena.alloc(self.pat_with_node_id_of(p, hir::PatKind::Wild)) |
dfeec247 XL |
274 | } |
275 | ||
276 | /// Construct a `Pat` with the `HirId` of `p.id` lowered. | |
136023e0 XL |
277 | fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> hir::Pat<'hir> { |
278 | hir::Pat { | |
29967ef6 XL |
279 | hir_id: self.lower_node_id(p.id), |
280 | kind, | |
94222f64 | 281 | span: self.lower_span(p.span), |
29967ef6 | 282 | default_binding_modes: true, |
136023e0 | 283 | } |
dfeec247 XL |
284 | } |
285 | ||
286 | /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern. | |
923072b8 | 287 | pub(crate) fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) { |
f2b60f7d | 288 | self.tcx.sess.emit_err(ExtraDoubleDot { span: sp, prev_span: prev_sp, ctx }); |
dfeec247 XL |
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> { | |
f2b60f7d | 293 | self.tcx.sess.emit_err(MisplacedDoubleDot { span: sp }); |
dfeec247 XL |
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 | } | |
a2a8927a XL |
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(..) | ExprKind::ConstBlock(..) | ExprKind::Err => {} | |
327 | ExprKind::Path(..) if allow_paths => {} | |
328 | ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} | |
329 | _ => { | |
f2b60f7d | 330 | self.tcx.sess.emit_err(ArbitraryExpressionInPattern { span: expr.span }); |
a2a8927a XL |
331 | return self.arena.alloc(self.expr_err(expr.span)); |
332 | } | |
333 | } | |
334 | self.lower_expr(expr) | |
335 | } | |
dfeec247 | 336 | } |