1use std::sync::Arc;23use rustc_ast::*;4use rustc_data_structures::stack::ensure_sufficient_stack;5use rustc_hir::def::{DefKind, Res};6use rustc_hir::{self as hir, LangItem, Target};7use rustc_middle::span_bug;8use rustc_span::{DesugaringKind, Ident, Span, Spanned, respan};910use super::errors::{11 ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,12};13use super::{14 AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,15};1617impl<'hir> LoweringContext<'_, 'hir> {18 pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> {19 self.arena.alloc(self.lower_pat_mut(pattern))20 }2122 fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> {23 ensure_sufficient_stack(|| {24 // loop here to avoid recursion25 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 );9394 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);9798 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 loop148 PatKind::Paren(inner) => pattern = inner,149 PatKind::MacCall(_) => {150 panic!("{pattern:#?} shouldn't exist here")151 }152 PatKind::Err(guar) => break hir::PatKind::Err(*guar),153 }154 };155156 self.pat_with_node_id_of(pattern, node, pat_hir_id)157 })158 }159160 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;167168 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 pattern176 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 pattern182 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 }193194 // It was not a sub-tuple pattern so lower it normally.195 elems.push(self.lower_pat_mut(pat));196 }197198 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 }207208 (self.arena.alloc_from_iter(elems), hir::DotDotPos::new(rest.map(|(ddpos, _)| ddpos)))209 }210211 /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into212 /// `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;222223 // 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 };231232 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 }254255 // 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 }275276 hir::PatKind::Slice(277 self.arena.alloc_from_iter(before),278 slice,279 self.arena.alloc_from_iter(after),280 )281 }282283 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.get_partial_res(p.id).map(|d| d.expect_full_res()) {292 // `None` can occur in body-less function signatures293 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 patterns297 // will be resolved to the same `Res::Local`. Thus they just share a single298 // `HirId`.299 if id == p.id {300 self.ident_and_label_to_local_id.insert(id, hir_id.local_id);301 hir_id302 } 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_id312 }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: 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 }339340 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 }343344 /// 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 }353354 /// 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 }358359 /// 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 });362363 // We're not in a list context so `..` can be reasonably treated364 // as `_` because it should always be valid and roughly matches the365 // intent of `..` (notice that the rest of a single slot is that slot).366 hir::PatKind::Wild367 }368369 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 }376377 /// 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 => 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 = matches!(expr.kind, ExprKind::ConstBlock(_));424 let pattern_from_macro = expr.is_approximately_pattern()425 || matches!(426 expr.peel_parens().kind,427 ExprKind::Binary(Spanned { node: BinOpKind::BitOr, .. }, ..)428 );429 let guar = self.dcx().emit_err(ArbitraryExpressionInPattern {430 span,431 pattern_from_macro_note: pattern_from_macro,432 const_block_in_pattern_help: is_const_block,433 });434 err(guar)435 }436 };437 self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })438 }439440 pub(crate) fn lower_ty_pat(441 &mut self,442 pattern: &TyPat,443 base_type: Span,444 ) -> &'hir hir::TyPat<'hir> {445 self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))446 }447448 fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {449 // loop here to avoid recursion450 let pat_hir_id = self.lower_node_id(pattern.id);451 let node = match &pattern.kind {452 TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(453 e1.as_deref()454 .map(|e| self.lower_anon_const_to_const_arg_and_alloc(e))455 .unwrap_or_else(|| {456 self.lower_ty_pat_range_end(457 hir::LangItem::RangeMin,458 span.shrink_to_lo(),459 base_type,460 )461 }),462 e2.as_deref()463 .map(|e| match end {464 RangeEnd::Included(..) => self.lower_anon_const_to_const_arg_and_alloc(e),465 RangeEnd::Excluded => self.lower_excluded_range_end(e),466 })467 .unwrap_or_else(|| {468 self.lower_ty_pat_range_end(469 hir::LangItem::RangeMax,470 span.shrink_to_hi(),471 base_type,472 )473 }),474 ),475 TyPatKind::NotNull => hir::TyPatKind::NotNull,476 TyPatKind::Or(variants) => {477 hir::TyPatKind::Or(self.arena.alloc_from_iter(478 variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)),479 ))480 }481 TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),482 };483484 hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }485 }486487 /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).488 /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.489 fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {490 let span = self.lower_span(e.value.span);491 let unstable_span = self.mark_span_with_reason(492 DesugaringKind::PatTyRange,493 span,494 Some(Arc::clone(&self.allow_pattern_type)),495 );496 let anon_const = self.with_new_scopes(span, |this| {497 let def_id = this.local_def_id(e.id);498 let hir_id = this.lower_node_id(e.id);499 let body = this.lower_body(|this| {500 // Need to use a custom function as we can't just subtract `1` from a `char`.501 let kind = hir::ExprKind::Path(this.make_lang_item_qpath(502 hir::LangItem::RangeSub,503 unstable_span,504 None,505 ));506 let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });507 let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);508 (509 &[],510 hir::Expr {511 hir_id: this.next_id(),512 kind: hir::ExprKind::Call(fn_def, args),513 span,514 },515 )516 });517 hir::AnonConst { def_id, hir_id, body, span }518 });519 self.arena.alloc(hir::ConstArg {520 hir_id: self.next_id(),521 kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),522 span,523 })524 }525526 /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),527 /// we instead use a constant of the MAX/MIN of the type.528 /// This way the type system does not have to handle the lack of a start/end.529 fn lower_ty_pat_range_end(530 &mut self,531 lang_item: LangItem,532 span: Span,533 base_type: Span,534 ) -> &'hir hir::ConstArg<'hir> {535 let node_id = self.next_node_id();536537 // Add a definition for the in-band const def.538 // We're generating a range end that didn't exist in the AST,539 // so the def collector didn't create the def ahead of time. That's why we have to do540 // it here.541 let def_id = self.create_def(node_id, None, DefKind::AnonConst, span);542 let hir_id = self.lower_node_id(node_id);543544 let unstable_span = self.mark_span_with_reason(545 DesugaringKind::PatTyRange,546 self.lower_span(span),547 Some(Arc::clone(&self.allow_pattern_type)),548 );549 let span = self.lower_span(base_type);550551 let path_expr = hir::Expr {552 hir_id: self.next_id(),553 kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),554 span,555 };556557 let ct = self.with_new_scopes(span, |this| {558 self.arena.alloc(hir::AnonConst {559 def_id,560 hir_id,561 body: this.lower_body(|_this| (&[], path_expr)),562 span,563 })564 });565 let hir_id = self.next_id();566 self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id, span })567 }568}