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