diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ea0bb5feb1220..412296ac9d656 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -34,7 +34,7 @@ use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths}; use crate::ty::visit::TypeVisitableExt; use crate::ty::{ - self, AdtDef, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypingEnv, + self, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypingEnv, UserTypeAnnotationIndex, }; @@ -1483,53 +1483,10 @@ pub struct UserTypeProjections { pub contents: Vec, } -impl<'tcx> UserTypeProjections { - pub fn none() -> Self { - UserTypeProjections { contents: vec![] } - } - - pub fn is_empty(&self) -> bool { - self.contents.is_empty() - } - +impl UserTypeProjections { pub fn projections(&self) -> impl Iterator + ExactSizeIterator { self.contents.iter() } - - pub fn push_user_type(mut self, base_user_type: UserTypeAnnotationIndex) -> Self { - self.contents.push(UserTypeProjection { base: base_user_type, projs: vec![] }); - self - } - - fn map_projections(mut self, f: impl FnMut(UserTypeProjection) -> UserTypeProjection) -> Self { - self.contents = self.contents.into_iter().map(f).collect(); - self - } - - pub fn index(self) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.index()) - } - - pub fn subslice(self, from: u64, to: u64) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to)) - } - - pub fn deref(self) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.deref()) - } - - pub fn leaf(self, field: FieldIdx) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field)) - } - - pub fn variant( - self, - adt_def: AdtDef<'tcx>, - variant_index: VariantIdx, - field_index: FieldIdx, - ) -> Self { - self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field_index)) - } } /// Encodes the effect of a user-supplied type annotation on the @@ -1554,42 +1511,6 @@ pub struct UserTypeProjection { pub projs: Vec, } -impl UserTypeProjection { - pub(crate) fn index(mut self) -> Self { - self.projs.push(ProjectionElem::Index(())); - self - } - - pub(crate) fn subslice(mut self, from: u64, to: u64) -> Self { - self.projs.push(ProjectionElem::Subslice { from, to, from_end: true }); - self - } - - pub(crate) fn deref(mut self) -> Self { - self.projs.push(ProjectionElem::Deref); - self - } - - pub(crate) fn leaf(mut self, field: FieldIdx) -> Self { - self.projs.push(ProjectionElem::Field(field, ())); - self - } - - pub(crate) fn variant( - mut self, - adt_def: AdtDef<'_>, - variant_index: VariantIdx, - field_index: FieldIdx, - ) -> Self { - self.projs.push(ProjectionElem::Downcast( - Some(adt_def.variant(variant_index).name), - variant_index, - )); - self.projs.push(ProjectionElem::Field(field_index, ())); - self - } -} - rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index e5592de81cd78..485fd734344d9 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -9,9 +9,9 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html use std::cmp::Ordering; -use std::fmt; use std::ops::Index; use std::sync::Arc; +use std::{fmt, mem}; use rustc_abi::{FieldIdx, Integer, Size, VariantIdx}; use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece}; @@ -28,6 +28,7 @@ use tracing::instrument; use crate::middle::region; use crate::mir::interpret::AllocId; use crate::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp}; +use crate::thir::visit::for_each_immediate_subpat; use crate::ty::adjustment::PointerCoercion; use crate::ty::layout::IntegerExt; use crate::ty::{ @@ -83,11 +84,12 @@ macro_rules! thir_with_elements { } thir_with_elements! { - arms: ArmId => Arm<'tcx> => "a{}", + arms: ArmId => Arm => "a{}", blocks: BlockId => Block => "b{}", exprs: ExprId => Expr<'tcx> => "e{}", - stmts: StmtId => Stmt<'tcx> => "s{}", + stmts: StmtId => Stmt => "s{}", params: ParamId => Param<'tcx> => "p{}", + pats: PatId => Pat<'tcx> => "pat{}", } #[derive(Debug, HashStable)] @@ -101,7 +103,7 @@ pub enum BodyTy<'tcx> { #[derive(Debug, HashStable)] pub struct Param<'tcx> { /// The pattern that appears in the parameter list, or None for implicit parameters. - pub pat: Option>>, + pub pat: Option, /// The possibly inferred type. pub ty: Ty<'tcx>, /// Span of the explicitly provided type, or None if inferred for closures. @@ -196,12 +198,12 @@ pub enum BlockSafety { } #[derive(Debug, HashStable)] -pub struct Stmt<'tcx> { - pub kind: StmtKind<'tcx>, +pub struct Stmt { + pub kind: StmtKind, } #[derive(Debug, HashStable)] -pub enum StmtKind<'tcx> { +pub enum StmtKind { /// An expression with a trailing semicolon. Expr { /// The scope for this statement; may be used as lifetime of temporaries. @@ -224,7 +226,7 @@ pub enum StmtKind<'tcx> { /// `let = ...` /// /// If a type annotation is included, it is added as an ascription pattern. - pattern: Box>, + pattern: PatId, /// `let pat: ty = ` initializer: Option, @@ -372,7 +374,7 @@ pub enum ExprKind<'tcx> { /// (Not to be confused with [`StmtKind::Let`], which is a normal `let` statement.) Let { expr: ExprId, - pat: Box>, + pat: PatId, }, /// A `match` expression. Match { @@ -562,8 +564,8 @@ pub struct FruInfo<'tcx> { /// A `match` arm. #[derive(Debug, HashStable)] -pub struct Arm<'tcx> { - pub pattern: Box>, +pub struct Arm { + pub pattern: PatId, pub guard: Option, pub body: ExprId, pub lint_level: LintLevel, @@ -617,9 +619,9 @@ pub enum InlineAsmOperand<'tcx> { } #[derive(Debug, HashStable, TypeVisitable)] -pub struct FieldPat<'tcx> { +pub struct FieldPat { pub field: FieldIdx, - pub pattern: Pat<'tcx>, + pub pattern: PatId, } #[derive(Debug, HashStable, TypeVisitable)] @@ -638,11 +640,17 @@ impl<'tcx> Pat<'tcx> { _ => None, } } +} +impl<'tcx> Thir<'tcx> { /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` - pub fn each_binding(&self, mut f: impl FnMut(Symbol, ByRef, Ty<'tcx>, Span)) { - self.walk_always(|p| { + pub fn for_each_binding_in_pat( + &self, + pat: &Pat<'tcx>, + mut f: impl FnMut(Symbol, ByRef, Ty<'tcx>, Span), + ) { + self.walk_pat_always(pat, |p| { if let PatKind::Binding { name, mode, ty, .. } = p.kind { f(name, mode.0, ty, p.span); } @@ -652,42 +660,22 @@ impl<'tcx> Pat<'tcx> { /// Walk the pattern in left-to-right order. /// /// If `it(pat)` returns `false`, the children are not visited. - pub fn walk(&self, mut it: impl FnMut(&Pat<'tcx>) -> bool) { - self.walk_(&mut it) + pub fn walk_pat(&self, pat: &Pat<'tcx>, mut it: impl FnMut(&Pat<'tcx>) -> bool) { + self.walk_pat_inner(pat, &mut it) } - fn walk_(&self, it: &mut impl FnMut(&Pat<'tcx>) -> bool) { - if !it(self) { + fn walk_pat_inner(&self, pat: &Pat<'tcx>, it: &mut impl FnMut(&Pat<'tcx>) -> bool) { + if !it(pat) { return; } - use PatKind::*; - match &self.kind { - Wild - | Never - | Range(..) - | Binding { subpattern: None, .. } - | Constant { .. } - | Error(_) => {} - AscribeUserType { subpattern, .. } - | Binding { subpattern: Some(subpattern), .. } - | Deref { subpattern } - | DerefPattern { subpattern, .. } - | ExpandedConstant { subpattern, .. } => subpattern.walk_(it), - Leaf { subpatterns } | Variant { subpatterns, .. } => { - subpatterns.iter().for_each(|field| field.pattern.walk_(it)) - } - Or { pats } => pats.iter().for_each(|p| p.walk_(it)), - Array { box prefix, slice, box suffix } | Slice { box prefix, slice, box suffix } => { - prefix.iter().chain(slice.as_deref()).chain(suffix.iter()).for_each(|p| p.walk_(it)) - } - } + for_each_immediate_subpat(self, pat, |p| self.walk_pat_inner(p, it)); } /// Whether the pattern has a `PatKind::Error` nested within. - pub fn pat_error_reported(&self) -> Result<(), ErrorGuaranteed> { + pub fn pat_error_reported(&self, pat: &Pat<'tcx>) -> Result<(), ErrorGuaranteed> { let mut error = None; - self.walk(|pat| { + self.walk_pat(pat, |pat| { if let PatKind::Error(e) = pat.kind && error.is_none() { @@ -701,32 +689,53 @@ impl<'tcx> Pat<'tcx> { } } + pub fn pat_references_error(&self, pat: &Pat<'tcx>) -> bool { + use rustc_type_ir::visit::TypeVisitableExt; + + let mut references_error = TypeVisitableExt::references_error(pat); + if !references_error { + for_each_immediate_subpat(self, pat, |p| { + references_error = references_error || self.pat_references_error(p); + }); + } + + references_error + } + /// Walk the pattern in left-to-right order. /// /// If you always want to recurse, prefer this method over `walk`. - pub fn walk_always(&self, mut it: impl FnMut(&Pat<'tcx>)) { - self.walk(|p| { + pub fn walk_pat_always(&self, pat: &Pat<'tcx>, mut it: impl FnMut(&Pat<'tcx>)) { + self.walk_pat(pat, |p| { it(p); true }) } /// Whether this a never pattern. - pub fn is_never_pattern(&self) -> bool { + pub fn is_never_pattern(&self, pat: &Pat<'tcx>) -> bool { let mut is_never_pattern = false; - self.walk(|pat| match &pat.kind { + self.walk_pat(pat, |pat| match &pat.kind { PatKind::Never => { is_never_pattern = true; false } PatKind::Or { pats } => { - is_never_pattern = pats.iter().all(|p| p.is_never_pattern()); + is_never_pattern = pats.iter().all(|&p| self.is_never_pattern(&self[p])); false } _ => true, }); is_never_pattern } + + /// FIXME(Zalathar): This method is a concession to existing code that was + /// extracting `PatKind` from owned patterns nodes, before `PatId` was + /// introduced. Ideally all callers should be modified to not need this. + pub fn take_pat_kind(&mut self, pat_id: PatId) -> PatKind<'tcx> { + // Replace the existing kind with an arbitrary dummy kind. + mem::replace(&mut self.pats[pat_id].kind, PatKind::Wild) + } } #[derive(Debug, HashStable, TypeVisitable)] @@ -760,7 +769,7 @@ pub enum PatKind<'tcx> { AscribeUserType { ascription: Ascription<'tcx>, - subpattern: Box>, + subpattern: PatId, }, /// `x`, `ref x`, `x @ P`, etc. @@ -771,9 +780,13 @@ pub enum PatKind<'tcx> { #[type_visitable(ignore)] var: LocalVarId, ty: Ty<'tcx>, - subpattern: Option>>, + subpattern: Option, + /// Is this the leftmost occurrence of the binding, i.e., is `var` the /// `HirId` of this pattern? + /// + /// (The same binding can occur multiple times in different branches of + /// an or-pattern, but only one of them will be primary.) is_primary: bool, }, @@ -783,23 +796,23 @@ pub enum PatKind<'tcx> { adt_def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>, variant_index: VariantIdx, - subpatterns: Vec>, + subpatterns: Vec, }, /// `(...)`, `Foo(...)`, `Foo{...}`, or `Foo`, where `Foo` is a variant name from an ADT with /// a single variant. Leaf { - subpatterns: Vec>, + subpatterns: Vec, }, /// `box P`, `&P`, `&mut P`, etc. Deref { - subpattern: Box>, + subpattern: PatId, }, /// Deref pattern, written `box P` for now. DerefPattern { - subpattern: Box>, + subpattern: PatId, mutability: hir::Mutability, }, @@ -833,7 +846,7 @@ pub enum PatKind<'tcx> { /// Otherwise, the actual pattern that the constant lowered to. As with /// other constants, inline constants are matched structurally where /// possible. - subpattern: Box>, + subpattern: PatId, }, Range(Arc>), @@ -842,22 +855,22 @@ pub enum PatKind<'tcx> { /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty. /// e.g., `&[ref xs @ ..]`. Slice { - prefix: Box<[Pat<'tcx>]>, - slice: Option>>, - suffix: Box<[Pat<'tcx>]>, + prefix: Box<[PatId]>, + slice: Option, + suffix: Box<[PatId]>, }, /// Fixed match against an array; irrefutable. Array { - prefix: Box<[Pat<'tcx>]>, - slice: Option>>, - suffix: Box<[Pat<'tcx>]>, + prefix: Box<[PatId]>, + slice: Option, + suffix: Box<[PatId]>, }, /// An or-pattern, e.g. `p | q`. /// Invariant: `pats.len() >= 2`. Or { - pats: Box<[Pat<'tcx>]>, + pats: Box<[PatId]>, }, /// A never pattern `!`. @@ -1123,7 +1136,7 @@ mod size_asserts { static_assert_size!(ExprKind<'_>, 40); static_assert_size!(Pat<'_>, 64); static_assert_size!(PatKind<'_>, 48); - static_assert_size!(Stmt<'_>, 48); - static_assert_size!(StmtKind<'_>, 48); + static_assert_size!(Stmt, 44); + static_assert_size!(StmtKind, 44); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index a9df4d1625be3..01b204cd4a6bb 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -10,7 +10,7 @@ pub trait Visitor<'thir, 'tcx: 'thir>: Sized { walk_expr(self, expr); } - fn visit_stmt(&mut self, stmt: &'thir Stmt<'tcx>) { + fn visit_stmt(&mut self, stmt: &'thir Stmt) { walk_stmt(self, stmt); } @@ -18,7 +18,7 @@ pub trait Visitor<'thir, 'tcx: 'thir>: Sized { walk_block(self, block); } - fn visit_arm(&mut self, arm: &'thir Arm<'tcx>) { + fn visit_arm(&mut self, arm: &'thir Arm) { walk_arm(self, arm); } @@ -71,9 +71,9 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( PointerCoercion { source, cast: _, is_from_as_cast: _ } => { visitor.visit_expr(&visitor.thir()[source]) } - Let { expr, ref pat } => { + Let { expr, pat } => { visitor.visit_expr(&visitor.thir()[expr]); - visitor.visit_pat(pat); + visitor.visit_pat(&visitor.thir()[pat]); } Loop { body } => visitor.visit_expr(&visitor.thir()[body]), Match { scrutinee, ref arms, .. } => { @@ -184,10 +184,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } } -pub fn walk_stmt<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( - visitor: &mut V, - stmt: &'thir Stmt<'tcx>, -) { +pub fn walk_stmt<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(visitor: &mut V, stmt: &'thir Stmt) { match &stmt.kind { StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[*expr]), StmtKind::Let { @@ -202,7 +199,7 @@ pub fn walk_stmt<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( if let Some(init) = initializer { visitor.visit_expr(&visitor.thir()[*init]); } - visitor.visit_pat(pattern); + visitor.visit_pat(&visitor.thir()[*pattern]); if let Some(block) = else_block { visitor.visit_block(&visitor.thir()[*block]) } @@ -222,14 +219,11 @@ pub fn walk_block<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } } -pub fn walk_arm<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( - visitor: &mut V, - arm: &'thir Arm<'tcx>, -) { +pub fn walk_arm<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(visitor: &mut V, arm: &'thir Arm) { if let Some(expr) = arm.guard { visitor.visit_expr(&visitor.thir()[expr]) } - visitor.visit_pat(&arm.pattern); + visitor.visit_pat(&visitor.thir()[arm.pattern]); visitor.visit_expr(&visitor.thir()[arm.body]); } @@ -237,36 +231,49 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor: &mut V, pat: &'thir Pat<'tcx>, ) { - use PatKind::*; - match &pat.kind { - AscribeUserType { subpattern, ascription: _ } - | Deref { subpattern } - | DerefPattern { subpattern, .. } - | Binding { subpattern: Some(subpattern), .. } => visitor.visit_pat(subpattern), - Binding { .. } | Wild | Never | Error(_) => {} - Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => { - for subpattern in subpatterns { - visitor.visit_pat(&subpattern.pattern); + for_each_immediate_subpat(visitor.thir(), pat, |p| visitor.visit_pat(p)); +} + +/// Invokes `callback` on each immediate subpattern of `pat`, if any. +/// A building block for assembling THIR pattern visitors. +pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( + thir: &'a Thir<'tcx>, + pat: &'a Pat<'tcx>, + mut callback: impl FnMut(&'a Pat<'tcx>), +) { + let mut callback_id = |pat_id| callback(&thir[pat_id]); + + match pat.kind { + PatKind::Wild + | PatKind::Binding { subpattern: None, .. } + | PatKind::Constant { value: _ } + | PatKind::Range(_) + | PatKind::Never + | PatKind::Error(_) => {} + + PatKind::AscribeUserType { subpattern, .. } + | PatKind::Binding { subpattern: Some(subpattern), .. } + | PatKind::Deref { subpattern } + | PatKind::DerefPattern { subpattern, .. } + | PatKind::ExpandedConstant { subpattern, .. } => callback_id(subpattern), + + PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => { + for field_pat in subpatterns { + callback_id(field_pat.pattern); } } - Constant { value: _ } => {} - ExpandedConstant { def_id: _, is_inline: _, subpattern } => visitor.visit_pat(subpattern), - Range(_) => {} - Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => { - for subpattern in prefix.iter() { - visitor.visit_pat(subpattern); - } - if let Some(pat) = slice { - visitor.visit_pat(pat); - } - for subpattern in suffix.iter() { - visitor.visit_pat(subpattern); + + PatKind::Slice { ref prefix, ref slice, ref suffix } + | PatKind::Array { ref prefix, ref slice, ref suffix } => { + for &pat in prefix.iter().chain(slice).chain(suffix) { + callback_id(pat); } } - Or { pats } => { - for pat in pats.iter() { - visitor.visit_pat(pat); + + PatKind::Or { ref pats } => { + for &pat in pats { + callback_id(pat); } } - }; + } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index db9e9fbc643b2..8c182e56497f0 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -261,6 +261,7 @@ TrivialTypeTraversalImpls! { crate::mir::SourceScope, crate::mir::SourceScopeLocalData, crate::mir::SwitchTargets, + crate::thir::PatId, crate::traits::IsConstable, crate::traits::OverflowError, crate::ty::abstract_const::NotConstEvaluatable, diff --git a/compiler/rustc_mir_build/src/builder/block.rs b/compiler/rustc_mir_build/src/builder/block.rs index 7c76e02fcef6a..03d7b42b013cb 100644 --- a/compiler/rustc_mir_build/src/builder/block.rs +++ b/compiler/rustc_mir_build/src/builder/block.rs @@ -155,6 +155,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // │Continue... │ └────────────────┘ // └────────────────┘ + let pattern = &this.thir[*pattern]; let ignores_expr_result = matches!(pattern.kind, PatKind::Wild); this.block_context.push(BlockFrame::Statement { ignores_expr_result }); @@ -199,19 +200,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None, Some((Some(&destination), initializer_span)), ); - this.visit_primary_bindings( - pattern, - UserTypeProjections::none(), - &mut |this, _, _, node, span, _, _| { - this.storage_live_binding( - block, - node, - span, - OutsideGuard, - ScheduleDrops::Yes, - ); - }, - ); + this.visit_primary_bindings(pattern, &mut |this, node, span| { + this.storage_live_binding( + block, + node, + span, + OutsideGuard, + ScheduleDrops::Yes, + ); + }); let else_block_span = this.thir[*else_block].span; let (matching, failure) = this.in_if_then_scope(last_remainder_scope, else_block_span, |this| { @@ -250,6 +247,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { else_block: None, span: _, } => { + let pattern = &this.thir[*pattern]; let ignores_expr_result = matches!(pattern.kind, PatKind::Wild); this.block_context.push(BlockFrame::Statement { ignores_expr_result }); @@ -295,20 +293,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }); debug!("ast_block_stmts: pattern={:?}", pattern); - this.visit_primary_bindings( - pattern, - UserTypeProjections::none(), - &mut |this, _, _, node, span, _, _| { - this.storage_live_binding( - block, - node, - span, - OutsideGuard, - ScheduleDrops::Yes, - ); - this.schedule_drop_for_binding(node, span, OutsideGuard); - }, - ) + this.visit_primary_bindings(pattern, &mut |this, node, span| { + this.storage_live_binding( + block, + node, + span, + OutsideGuard, + ScheduleDrops::Yes, + ); + this.schedule_drop_for_binding(node, span, OutsideGuard); + }) } // Enter the visibility scope, after evaluating the initializer. diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index 91e284604b68c..db8e1af91feb5 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs @@ -83,7 +83,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { match &self.thir[stmt_id].kind { StmtKind::Expr { expr, .. } => Ok(*expr), kind @ StmtKind::Let { pattern, .. } => Err(ParseError { - span: pattern.span, + span: self.thir[*pattern].span, item_description: format!("{kind:?}"), expected: "expression".to_string(), }), @@ -93,7 +93,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { pub(crate) fn parse_args(&mut self, params: &IndexSlice>) -> PResult<()> { for param in params.iter() { let (var, span) = { - let pat = param.pat.as_ref().unwrap(); + let pat = &self.thir[param.pat.unwrap()]; match &pat.kind { PatKind::Binding { var, .. } => (*var, pat.span), _ => { @@ -198,7 +198,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { fn parse_basic_block_decl(&mut self, stmt: StmtId) -> PResult<()> { match &self.thir[stmt].kind { StmtKind::Let { pattern, initializer: Some(initializer), .. } => { - let (var, ..) = self.parse_var(pattern)?; + let (var, ..) = self.parse_var(&self.thir[*pattern])?; let data = BasicBlockData::new( None, parse_by_kind!(self, *initializer, _, "basic block declaration", @@ -277,7 +277,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { fn parse_let_statement(&mut self, stmt_id: StmtId) -> PResult<(LocalVarId, Ty<'tcx>, Span)> { let pattern = match &self.thir[stmt_id].kind { - StmtKind::Let { pattern, .. } => pattern, + StmtKind::Let { pattern, .. } => &self.thir[*pattern], StmtKind::Expr { expr, .. } => { return Err(self.expr_error(*expr, "let statement")); } @@ -286,13 +286,14 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { self.parse_var(pattern) } - fn parse_var(&mut self, mut pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> { + fn parse_var(&mut self, pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> { // Make sure we throw out any `AscribeUserType` we find + let mut pat = pat; loop { match &pat.kind { PatKind::Binding { var, ty, .. } => break Ok((*var, *ty, pat.span)), PatKind::AscribeUserType { subpattern, .. } => { - pat = subpattern; + pat = &self.thir[*subpattern]; } _ => { break Err(ParseError { diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 19669021eefb4..1b11caf2fcd09 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -130,10 +130,11 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { }; let otherwise = &self.thir[*otherwise]; - let PatKind::Wild = otherwise.pattern.kind else { + let otherwise_pattern = &self.thir[otherwise.pattern]; + let PatKind::Wild = otherwise_pattern.kind else { return Err(ParseError { span: otherwise.span, - item_description: format!("{:?}", otherwise.pattern.kind), + item_description: format!("{:?}", otherwise_pattern.kind), expected: "wildcard pattern".to_string(), }); }; @@ -143,17 +144,18 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let mut targets = Vec::new(); for arm in rest { let arm = &self.thir[*arm]; - let value = match arm.pattern.kind { + let arm_pattern = &self.thir[arm.pattern]; + let value = match arm_pattern.kind { PatKind::Constant { value } => value, PatKind::ExpandedConstant { ref subpattern, def_id: _, is_inline: false } - if let PatKind::Constant { value } = subpattern.kind => + if let PatKind::Constant { value } = self.thir[*subpattern].kind => { value } _ => { return Err(ParseError { - span: arm.pattern.span, - item_description: format!("{:?}", arm.pattern.kind), + span: arm_pattern.span, + item_description: format!("{:?}", arm_pattern.kind), expected: "constant pattern".to_string(), }); } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 10b43390eb288..3373f73b5df9f 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -17,14 +17,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn field_match_pairs( &mut self, place: PlaceBuilder<'tcx>, - subpatterns: &[FieldPat<'tcx>], + subpatterns: &[FieldPat], ) -> Vec> { subpatterns .iter() .map(|fieldpat| { - let place = - place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty)); - MatchPairTree::for_pattern(place, &fieldpat.pattern, self) + let pat = &self.thir[fieldpat.pattern]; + let place = place.clone_project(PlaceElem::Field(fieldpat.field, pat.ty)); + MatchPairTree::for_pattern(place, pat, self) }) .collect() } @@ -37,9 +37,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, match_pairs: &mut Vec>, place: &PlaceBuilder<'tcx>, - prefix: &[Pat<'tcx>], - opt_slice: &Option>>, - suffix: &[Pat<'tcx>], + prefix: &[PatId], + opt_slice: Option, + suffix: &[PatId], ) { let tcx = self.tcx; let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) { @@ -56,10 +56,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ((prefix.len() + suffix.len()).try_into().unwrap(), false) }; - match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { + match_pairs.extend(prefix.iter().enumerate().map(|(idx, &subpattern)| { let elem = ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false }; - MatchPairTree::for_pattern(place.clone_project(elem), subpattern, self) + MatchPairTree::for_pattern(place.clone_project(elem), &self.thir[subpattern], self) })); if let Some(subslice_pat) = opt_slice { @@ -69,10 +69,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { to: if exact_size { min_length - suffix_len } else { suffix_len }, from_end: !exact_size, }); - match_pairs.push(MatchPairTree::for_pattern(subslice, subslice_pat, self)); + match_pairs.push(MatchPairTree::for_pattern(subslice, &self.thir[subslice_pat], self)); } - match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| { + match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, &subpattern)| { let end_offset = (idx + 1) as u64; let elem = ProjectionElem::ConstantIndex { offset: if exact_size { min_length - end_offset } else { end_offset }, @@ -80,7 +80,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { from_end: !exact_size, }; let place = place.clone_project(elem); - MatchPairTree::for_pattern(place, subpattern, self) + MatchPairTree::for_pattern(place, &self.thir[subpattern], self) })); } } @@ -120,7 +120,10 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::Wild | PatKind::Error(_) => default_irrefutable(), PatKind::Or { ref pats } => TestCase::Or { - pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(), + pats: pats + .iter() + .map(|&pat| FlatPat::new(place_builder.clone(), &cx.thir[pat], cx)) + .collect(), }, PatKind::Range(ref range) => { @@ -135,7 +138,7 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::AscribeUserType { ascription: Ascription { ref annotation, variance }, - ref subpattern, + subpattern, .. } => { // Apply the type ascription to the value at `match_pair.place` @@ -145,11 +148,11 @@ impl<'tcx> MatchPairTree<'tcx> { variance, }); - subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx)); + subpairs.push(MatchPairTree::for_pattern(place_builder, &cx.thir[subpattern], cx)); TestCase::Irrefutable { ascription, binding: None } } - PatKind::Binding { mode, var, ref subpattern, .. } => { + PatKind::Binding { mode, var, subpattern, .. } => { let binding = place.map(|source| super::Binding { span: pattern.span, source, @@ -157,18 +160,20 @@ impl<'tcx> MatchPairTree<'tcx> { binding_mode: mode, }); - if let Some(subpattern) = subpattern.as_ref() { + if let Some(subpattern) = try { &cx.thir[subpattern?] } { // this is the `x @ P` case; have to keep matching against `P` now subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx)); } TestCase::Irrefutable { ascription: None, binding } } - PatKind::ExpandedConstant { subpattern: ref pattern, def_id: _, is_inline: false } => { + PatKind::ExpandedConstant { subpattern, def_id: _, is_inline: false } => { + let pattern = &cx.thir[subpattern]; subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx)); default_irrefutable() } - PatKind::ExpandedConstant { subpattern: ref pattern, def_id, is_inline: true } => { + PatKind::ExpandedConstant { subpattern, def_id, is_inline: true } => { + let pattern = &cx.thir[subpattern]; // Apply a type ascription for the inline constant to the value at `match_pair.place` let ascription = place.map(|source| { let span = pattern.span; @@ -196,11 +201,11 @@ impl<'tcx> MatchPairTree<'tcx> { TestCase::Irrefutable { ascription, binding: None } } - PatKind::Array { ref prefix, ref slice, ref suffix } => { + PatKind::Array { ref prefix, slice, ref suffix } => { cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); default_irrefutable() } - PatKind::Slice { ref prefix, ref slice, ref suffix } => { + PatKind::Slice { ref prefix, slice, ref suffix } => { cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); if prefix.is_empty() && slice.is_some() && suffix.is_empty() { @@ -237,12 +242,14 @@ impl<'tcx> MatchPairTree<'tcx> { default_irrefutable() } - PatKind::Deref { ref subpattern } => { + PatKind::Deref { subpattern } => { + let subpattern = &cx.thir[subpattern]; subpairs.push(MatchPairTree::for_pattern(place_builder.deref(), subpattern, cx)); default_irrefutable() } - PatKind::DerefPattern { ref subpattern, mutability } => { + PatKind::DerefPattern { subpattern, mutability } => { + let subpattern = &cx.thir[subpattern]; // Create a new temporary for each deref pattern. // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? let temp = cx.temp( diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index d05d5b151ff48..7bf3d36c887e0 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -5,6 +5,11 @@ //! This also includes code for pattern bindings in `let` statements and //! function parameters. +use std::assert_matches::assert_matches; +use std::borrow::Borrow; +use std::mem; +use std::sync::Arc; + use rustc_abi::VariantIdx; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -19,6 +24,7 @@ use tracing::{debug, instrument}; use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard}; use crate::builder::expr::as_place::PlaceBuilder; +use crate::builder::matches::user_ty::ProjectedUserTypesNode; use crate::builder::scope::DropKind; use crate::builder::{ BlockAnd, BlockAndExtension, Builder, GuardFrame, GuardFrameLocal, LocalsForNode, @@ -28,13 +34,9 @@ use crate::builder::{ mod match_pair; mod simplify; mod test; +mod user_ty; mod util; -use std::assert_matches::assert_matches; -use std::borrow::Borrow; -use std::mem; -use std::sync::Arc; - /// Arguments to [`Builder::then_else_break_inner`] that are usually forwarded /// to recursive invocations. #[derive(Clone, Copy)] @@ -197,10 +199,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) } ExprKind::Use { source } => this.then_else_break_inner(block, source, args), - ExprKind::Let { expr, ref pat } => this.lower_let_expr( + ExprKind::Let { expr, pat } => this.lower_let_expr( block, expr, - pat, + &this.thir[pat], Some(args.variable_source_info.scope), args.variable_source_info.span, args.declare_let_bindings, @@ -381,7 +383,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm = &self.thir[arm]; let has_match_guard = if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No }; - (&*arm.pattern, has_match_guard) + (&self.thir[arm.pattern], has_match_guard) }) .collect(); let built_tree = self.lower_match_tree( @@ -465,7 +467,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scope = this.declare_bindings( None, arm.span, - &arm.pattern, + &this.thir[arm.pattern], arm.guard, opt_scrutinee_place, ); @@ -529,7 +531,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { branch: MatchTreeBranch<'tcx>, fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, - arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, + arm_match_scope: Option<(&Arm, region::Scope)>, emit_storage_live: EmitStorageLive, ) -> BasicBlock { if branch.sub_branches.len() == 1 { @@ -615,14 +617,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Optimize the case of `let x: T = ...` to write directly // into `x` and then require that `T == typeof(x)`. PatKind::AscribeUserType { - ref subpattern, + subpattern, ascription: thir::Ascription { ref annotation, variance: _ }, } if let PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. - } = subpattern.kind => + } = self.thir[subpattern].kind => { let place = self.storage_live_binding( block, @@ -756,24 +758,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard: Option, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option { - self.visit_primary_bindings( + self.visit_primary_bindings_special( pattern, - UserTypeProjections::none(), - &mut |this, name, mode, var, span, ty, user_ty| { - if visibility_scope.is_none() { - visibility_scope = - Some(this.new_source_scope(scope_span, LintLevel::Inherited)); - } + &ProjectedUserTypesNode::None, + &mut |this, name, mode, var, span, ty, user_tys| { + let vis_scope = *visibility_scope + .get_or_insert_with(|| this.new_source_scope(scope_span, LintLevel::Inherited)); let source_info = SourceInfo { span, scope: this.source_scope }; - let visibility_scope = visibility_scope.unwrap(); + let user_tys = user_tys.build_user_type_projections(); + this.declare_binding( source_info, - visibility_scope, + vis_scope, name, mode, var, ty, - user_ty, + user_tys, ArmHasGuard(guard.is_some()), opt_match_place.map(|(x, y)| (x.cloned(), y)), pattern.span, @@ -796,8 +797,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { visibility_scope: Option, ) { match self.thir.exprs[guard_expr].kind { - ExprKind::Let { expr: _, pat: ref guard_pat } => { + ExprKind::Let { expr: _, pat: guard_pat } => { // FIXME: pass a proper `opt_match_place` + let guard_pat = &self.thir[guard_pat]; self.declare_bindings(visibility_scope, scope_span, guard_pat, None, None); } ExprKind::Scope { value, .. } => { @@ -849,13 +851,35 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - /// Visit all of the primary bindings in a patterns, that is, visit the - /// leftmost occurrence of each variable bound in a pattern. A variable - /// will occur more than once in an or-pattern. + /// Visits all of the "primary" bindings in a pattern, i.e. the leftmost + /// occurrence of each variable bound by the pattern. + /// See [`PatKind::Binding::is_primary`] for more context. + /// + /// This variant provides only the limited subset of binding data needed + /// by its callers, and should be a "pure" visit without side-effects. pub(super) fn visit_primary_bindings( &mut self, pattern: &Pat<'tcx>, - pattern_user_ty: UserTypeProjections, + f: &mut impl FnMut(&mut Self, LocalVarId, Span), + ) { + self.thir.walk_pat_always(pattern, |pat| { + if let PatKind::Binding { var, is_primary: true, .. } = pat.kind { + f(self, var, pat.span); + } + }) + } + + /// Visits all of the "primary" bindings in a pattern, while preparing + /// additional user-type-annotation data needed by `declare_bindings`. + /// + /// This also has the side-effect of pushing all user type annotations + /// onto `canonical_user_type_annotations`, so that they end up in MIR + /// even if they aren't associated with any bindings. + #[instrument(level = "debug", skip(self, f))] + fn visit_primary_bindings_special( + &mut self, + pattern: &Pat<'tcx>, + user_tys: &ProjectedUserTypesNode<'_>, f: &mut impl FnMut( &mut Self, Symbol, @@ -863,39 +887,36 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { LocalVarId, Span, Ty<'tcx>, - UserTypeProjections, + &ProjectedUserTypesNode<'_>, ), ) { - debug!( - "visit_primary_bindings: pattern={:?} pattern_user_ty={:?}", - pattern, pattern_user_ty - ); + // Avoid having to write the full method name at each recursive call. + let visit_subpat = |this: &mut Self, subpat: PatId, user_tys: &_, f: &mut _| { + this.visit_primary_bindings_special(&this.thir[subpat], user_tys, f) + }; + match pattern.kind { - PatKind::Binding { name, mode, var, ty, ref subpattern, is_primary, .. } => { + PatKind::Binding { name, mode, var, ty, subpattern, is_primary, .. } => { if is_primary { - f(self, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); + f(self, name, mode, var, pattern.span, ty, user_tys); } - if let Some(subpattern) = subpattern.as_ref() { - self.visit_primary_bindings(subpattern, pattern_user_ty, f); + if let Some(subpattern) = subpattern { + visit_subpat(self, subpattern, user_tys, f); } } - PatKind::Array { ref prefix, ref slice, ref suffix } - | PatKind::Slice { ref prefix, ref slice, ref suffix } => { + PatKind::Array { ref prefix, slice, ref suffix } + | PatKind::Slice { ref prefix, slice, ref suffix } => { let from = u64::try_from(prefix.len()).unwrap(); let to = u64::try_from(suffix.len()).unwrap(); - for subpattern in prefix.iter() { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); + for &subpattern in prefix { + visit_subpat(self, subpattern, &user_tys.index(), f); } if let Some(subpattern) = slice { - self.visit_primary_bindings( - subpattern, - pattern_user_ty.clone().subslice(from, to), - f, - ); + visit_subpat(self, subpattern, &user_tys.subslice(from, to), f); } - for subpattern in suffix.iter() { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f); + for &subpattern in suffix { + visit_subpat(self, subpattern, &user_tys.index(), f); } } @@ -905,16 +926,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | PatKind::Never | PatKind::Error(_) => {} - PatKind::Deref { ref subpattern } => { - self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f); + PatKind::Deref { subpattern } => { + visit_subpat(self, subpattern, &user_tys.deref(), f); } - PatKind::DerefPattern { ref subpattern, .. } => { - self.visit_primary_bindings(subpattern, UserTypeProjections::none(), f); + PatKind::DerefPattern { subpattern, .. } => { + visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f); } PatKind::AscribeUserType { - ref subpattern, + subpattern, ascription: thir::Ascription { ref annotation, variance: _ }, } => { // This corresponds to something like @@ -926,28 +947,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Note that the variance doesn't apply here, as we are tracking the effect // of `user_ty` on any bindings contained with subpattern. + // Caution: Pushing this user type here is load-bearing even for + // patterns containing no bindings, to ensure that the type ends + // up represented in MIR _somewhere_. let base_user_ty = self.canonical_user_type_annotations.push(annotation.clone()); - let subpattern_user_ty = pattern_user_ty.push_user_type(base_user_ty); - self.visit_primary_bindings(subpattern, subpattern_user_ty, f) + let subpattern_user_tys = user_tys.push_user_type(base_user_ty); + visit_subpat(self, subpattern, &subpattern_user_tys, f) } - PatKind::ExpandedConstant { ref subpattern, .. } => { - self.visit_primary_bindings(subpattern, pattern_user_ty, f) + PatKind::ExpandedConstant { subpattern, .. } => { + visit_subpat(self, subpattern, user_tys, f) } PatKind::Leaf { ref subpatterns } => { for subpattern in subpatterns { - let subpattern_user_ty = pattern_user_ty.clone().leaf(subpattern.field); - debug!("visit_primary_bindings: subpattern_user_ty={:?}", subpattern_user_ty); - self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f); + let subpattern_user_tys = user_tys.leaf(subpattern.field); + debug!("visit_primary_bindings: subpattern_user_tys={subpattern_user_tys:?}"); + visit_subpat(self, subpattern.pattern, &subpattern_user_tys, f); } } PatKind::Variant { adt_def, args: _, variant_index, ref subpatterns } => { for subpattern in subpatterns { - let subpattern_user_ty = - pattern_user_ty.clone().variant(adt_def, variant_index, subpattern.field); - self.visit_primary_bindings(&subpattern.pattern, subpattern_user_ty, f); + let subpattern_user_tys = + user_tys.variant(adt_def, variant_index, subpattern.field); + visit_subpat(self, subpattern.pattern, &subpattern_user_tys, f); } } PatKind::Or { ref pats } => { @@ -955,8 +979,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // may not all be in the leftmost subpattern. For example in // `let (x | y) = ...`, the primary binding of `y` occurs in // the right subpattern - for subpattern in pats.iter() { - self.visit_primary_bindings(subpattern, pattern_user_ty.clone(), f); + for &subpattern in pats { + visit_subpat(self, subpattern, user_tys, f); } } } @@ -1014,7 +1038,7 @@ impl<'tcx> FlatPat<'tcx> { span: pattern.span, bindings: Vec::new(), ascriptions: Vec::new(), - is_never: pattern.is_never_pattern(), + is_never: cx.thir.is_never_pattern(pattern), }; // Recursively remove irrefutable match pairs, while recording their // bindings/ascriptions, and sort or-patterns after other match pairs. @@ -2410,7 +2434,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { sub_branch: MatchTreeSubBranch<'tcx>, fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, - arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, + arm_match_scope: Option<(&Arm, region::Scope)>, schedule_drops: ScheduleDrops, emit_storage_live: EmitStorageLive, ) -> BasicBlock { @@ -2764,7 +2788,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mode: BindingMode, var_id: LocalVarId, var_ty: Ty<'tcx>, - user_ty: UserTypeProjections, + user_ty: Option>, has_guard: ArmHasGuard, opt_match_place: Option<(Option>, Span)>, pat_span: Span, @@ -2774,7 +2798,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local = LocalDecl { mutability: mode.1, ty: var_ty, - user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) }, + user_ty, source_info, local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var( VarBindingForm { diff --git a/compiler/rustc_mir_build/src/builder/matches/user_ty.rs b/compiler/rustc_mir_build/src/builder/matches/user_ty.rs new file mode 100644 index 0000000000000..df9f93ac328a6 --- /dev/null +++ b/compiler/rustc_mir_build/src/builder/matches/user_ty.rs @@ -0,0 +1,140 @@ +//! Helper code for building a linked list of user-type projections on the +//! stack while visiting a THIR pattern. +//! +//! This avoids having to repeatedly clone a partly-built [`UserTypeProjections`] +//! at every step of the traversal, which is what the previous code was doing. + +use std::assert_matches::assert_matches; +use std::iter; + +use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_middle::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections}; +use rustc_middle::ty::{AdtDef, UserTypeAnnotationIndex}; +use rustc_span::Symbol; + +/// One of a list of "operations" that can be used to lazily build projections +/// of user-specified types. +#[derive(Clone, Debug)] +pub(crate) enum ProjectedUserTypesOp { + PushUserType { base: UserTypeAnnotationIndex }, + + Index, + Subslice { from: u64, to: u64 }, + Deref, + Leaf { field: FieldIdx }, + Variant { name: Symbol, variant: VariantIdx, field: FieldIdx }, +} + +#[derive(Debug)] +pub(crate) enum ProjectedUserTypesNode<'a> { + None, + Chain { parent: &'a Self, op: ProjectedUserTypesOp }, +} + +impl<'a> ProjectedUserTypesNode<'a> { + pub(crate) fn push_user_type(&'a self, base: UserTypeAnnotationIndex) -> Self { + // Pushing a base user type always causes the chain to become non-empty. + Self::Chain { parent: self, op: ProjectedUserTypesOp::PushUserType { base } } + } + + /// Push another projection op onto the chain, but only if it is already non-empty. + fn maybe_push(&'a self, op_fn: impl FnOnce() -> ProjectedUserTypesOp) -> Self { + match self { + Self::None => Self::None, + Self::Chain { .. } => Self::Chain { parent: self, op: op_fn() }, + } + } + + pub(crate) fn index(&'a self) -> Self { + self.maybe_push(|| ProjectedUserTypesOp::Index) + } + + pub(crate) fn subslice(&'a self, from: u64, to: u64) -> Self { + self.maybe_push(|| ProjectedUserTypesOp::Subslice { from, to }) + } + + pub(crate) fn deref(&'a self) -> Self { + self.maybe_push(|| ProjectedUserTypesOp::Deref) + } + + pub(crate) fn leaf(&'a self, field: FieldIdx) -> Self { + self.maybe_push(|| ProjectedUserTypesOp::Leaf { field }) + } + + pub(crate) fn variant( + &'a self, + adt_def: AdtDef<'_>, + variant: VariantIdx, + field: FieldIdx, + ) -> Self { + self.maybe_push(|| { + let name = adt_def.variant(variant).name; + ProjectedUserTypesOp::Variant { name, variant, field } + }) + } + + /// Traverses the chain of nodes to yield each op in the chain. + /// Because this walks from child node to parent node, the ops are + /// naturally yielded in "reverse" order. + fn iter_ops_reversed(&'a self) -> impl Iterator { + let mut next = self; + iter::from_fn(move || match next { + Self::None => None, + Self::Chain { parent, op } => { + next = parent; + Some(op) + } + }) + } + + /// Assembles this chain of user-type projections into a proper data structure. + pub(crate) fn build_user_type_projections(&self) -> Option> { + // If we know there's nothing to do, just return None immediately. + if matches!(self, Self::None) { + return None; + } + + let ops_reversed = self.iter_ops_reversed().cloned().collect::>(); + // The "first" op should always be `PushUserType`. + // Other projections are only added if there is at least one user type. + assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserType { .. })); + + let mut projections = vec![]; + for op in ops_reversed.into_iter().rev() { + match op { + ProjectedUserTypesOp::PushUserType { base } => { + projections.push(UserTypeProjection { base, projs: vec![] }) + } + + ProjectedUserTypesOp::Index => { + for p in &mut projections { + p.projs.push(ProjectionElem::Index(())) + } + } + ProjectedUserTypesOp::Subslice { from, to } => { + for p in &mut projections { + p.projs.push(ProjectionElem::Subslice { from, to, from_end: true }) + } + } + ProjectedUserTypesOp::Deref => { + for p in &mut projections { + p.projs.push(ProjectionElem::Deref) + } + } + ProjectedUserTypesOp::Leaf { field } => { + for p in &mut projections { + p.projs.push(ProjectionElem::Field(field, ())) + } + } + ProjectedUserTypesOp::Variant { name, variant, field } => { + for p in &mut projections { + p.projs.push(ProjectionElem::Downcast(Some(name), variant)); + p.projs.push(ProjectionElem::Field(field, ())); + } + } + } + } + + Some(Box::new(UserTypeProjections { contents: projections })) + } +} diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 949559549345f..7bb991e854df7 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -905,15 +905,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let expr_span = self.thir[expr_id].span; // Allocate locals for the function arguments for (argument_index, param) in arguments.iter().enumerate() { - let source_info = - SourceInfo::outermost(param.pat.as_ref().map_or(self.fn_span, |pat| pat.span)); + let source_info = SourceInfo::outermost(Option::unwrap_or( + try { self.thir[param.pat?].span }, + self.fn_span, + )); let arg_local = self.local_decls.push(LocalDecl::with_source_info(param.ty, source_info)); // If this is a simple binding pattern, give debuginfo a nice name. - if let Some(ref pat) = param.pat - && let Some(name) = pat.simple_ident() - { + if let Some(name) = try { self.thir[param.pat?].simple_ident()? } { self.var_debug_info.push(VarDebugInfo { name, source_info, @@ -935,15 +935,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Make sure we drop (parts of) the argument even when not matched on. self.schedule_drop( - param.pat.as_ref().map_or(expr_span, |pat| pat.span), + Option::unwrap_or(try { self.thir[param.pat?].span }, expr_span), argument_scope, local, DropKind::Value, ); - let Some(ref pat) = param.pat else { - continue; - }; + let Some(pat) = (try { &self.thir[param.pat?] }) else { continue }; let original_source_scope = self.source_scope; let span = pat.span; if let Some(arg_hir_id) = param.hir_id { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index c2eafd0a74e65..8f4a1bd75d19e 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -221,7 +221,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { }; // params in THIR may be unsafe, e.g. a union pattern. for param in &inner_thir.params { - if let Some(param_pat) = param.pat.as_deref() { + if let Some(param_pat) = try { &inner_thir[param.pat?] } { inner_visitor.visit_pat(param_pat); } } @@ -344,7 +344,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { if let ty::Adt(adt_def, ..) = pat.ty.kind() { for pat in subpatterns { if adt_def.non_enum_variant().fields[pat.field].safety.is_unsafe() { - self.requires_unsafe(pat.pattern.span, UseOfUnsafeField); + self.requires_unsafe(self.thir[pat.pattern].span, UseOfUnsafeField); } } if adt_def.is_union() { @@ -369,7 +369,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { for pat in subpatterns { let field = &pat.field; if adt_def.variant(*variant_index).fields[*field].safety.is_unsafe() { - self.requires_unsafe(pat.pattern.span, UseOfUnsafeField); + self.requires_unsafe(self.thir[pat.pattern].span, UseOfUnsafeField); } } visit::walk_pat(self, pat); @@ -1176,7 +1176,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { }; // params in THIR may be unsafe, e.g. a union pattern. for param in &thir.params { - if let Some(param_pat) = param.pat.as_deref() { + if let Some(param_pat) = try { &thir[param.pat?] } { visitor.visit_pat(param_pat); } } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index e858b629ab14b..6e3b8bf827492 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -86,9 +86,9 @@ impl<'tcx> ThirBuildCx<'tcx> { span: ty.span, inferred_ty: self.typeck_results.node_type(ty.hir_id), }; - pattern = Box::new(Pat { - ty: pattern.ty, - span: pattern.span, + pattern = self.thir.pats.push(Pat { + ty: self.thir[pattern].ty, + span: self.thir[pattern].span, kind: PatKind::AscribeUserType { ascription: Ascription { annotation, diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 2e069cae4269f..653b017186ede 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -119,8 +119,8 @@ impl<'tcx> ThirBuildCx<'tcx> { } #[instrument(level = "debug", skip(self))] - fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> Box> { - pat_from_hir(self.tcx, self.typing_env, self.typeck_results, p) + fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> PatId { + pat_from_hir(self.tcx, &mut self.thir, self.typing_env, self.typeck_results, p) } fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option> { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index d60ae6484afb2..7714efe8c5e4e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -61,7 +61,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err }; for param in thir.params.iter() { - if let Some(box ref pattern) = param.pat { + if let Some(pattern) = try { &thir[param.pat?] } { visitor.check_binding_is_irrefutable(pattern, origin, None, None); } } @@ -110,14 +110,14 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> { } #[instrument(level = "trace", skip(self))] - fn visit_arm(&mut self, arm: &'p Arm<'tcx>) { + fn visit_arm(&mut self, arm: &'p Arm) { self.with_lint_level(arm.lint_level, |this| { if let Some(expr) = arm.guard { this.with_let_source(LetSource::IfLetGuard, |this| { this.visit_expr(&this.thir[expr]) }); } - this.visit_pat(&arm.pattern); + this.visit_pat(&self.thir[arm.pattern]); this.visit_expr(&self.thir[arm.body]); }); } @@ -154,8 +154,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> { ExprKind::Match { scrutinee, box ref arms, match_source } => { self.check_match(scrutinee, arms, match_source, ex.span); } - ExprKind::Let { box ref pat, expr } => { - self.check_let(pat, Some(expr), ex.span); + ExprKind::Let { pat, expr } => { + self.check_let(&self.thir[pat], Some(expr), ex.span); } ExprKind::LogicalOp { op: LogicalOp::And, .. } if !matches!(self.let_source, LetSource::None) => @@ -173,16 +173,14 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> { self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex)); } - fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) { + fn visit_stmt(&mut self, stmt: &'p Stmt) { match stmt.kind { - StmtKind::Let { - box ref pattern, initializer, else_block, lint_level, span, .. - } => { + StmtKind::Let { pattern, initializer, else_block, lint_level, span, .. } => { self.with_lint_level(lint_level, |this| { let let_source = if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet }; this.with_let_source(let_source, |this| { - this.check_let(pattern, initializer, span) + this.check_let(&this.thir[pattern], initializer, span) }); visit::walk_stmt(this, stmt); }); @@ -256,12 +254,12 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { ExprKind::Scope { value, lint_level, .. } => { self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value])) } - ExprKind::Let { box ref pat, expr } => { + ExprKind::Let { pat, expr } => { let expr = &self.thir()[expr]; self.with_let_source(LetSource::None, |this| { this.visit_expr(expr); }); - Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?))) + Ok(Some((ex.span, self.is_let_irrefutable(&self.thir[pat], Some(expr))?))) } _ => { self.with_let_source(LetSource::None, |this| { @@ -277,14 +275,14 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { cx: &PatCtxt<'p, 'tcx>, pat: &'p Pat<'tcx>, ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> { - if let Err(err) = pat.pat_error_reported() { + if let Err(err) = cx.thir.pat_error_reported(pat) { self.error = Err(err); Err(err) } else { // Check the pattern for some things unrelated to exhaustiveness. let refutable = if cx.refutable { Refutable } else { Irrefutable }; let mut err = Ok(()); - pat.walk_always(|pat| { + cx.thir.walk_pat_always(pat, |pat| { check_borrow_conflicts_in_at_patterns(self, pat); check_for_bindings_named_same_as_variants(self, pat, refutable); err = err.and(check_never_pattern(cx, pat)); @@ -385,6 +383,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true); PatCtxt { tcx: self.tcx, + thir: self.thir, typeck_results: self.typeck_results, typing_env: self.typing_env, module: self.tcx.parent_module(self.lint_level).to_def_id(), @@ -459,7 +458,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { for &arm in arms { let arm = &self.thir.arms[arm]; let got_error = self.with_lint_level(arm.lint_level, |this| { - let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true }; + let Ok(pat) = this.lower_pattern(&cx, &this.thir[arm.pattern]) else { return true }; let arm = MatchArm { pat, arm_data: this.lint_level, has_guard: arm.guard.is_some() }; tarms.push(arm); @@ -496,13 +495,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { && let [_, snd_arm] = *arms { // the for loop pattern is not irrefutable - let pat = &self.thir[snd_arm].pattern; + let pat = &self.thir[self.thir[snd_arm].pattern]; // `pat` should be `Some()` from a desugared for loop. debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop)); let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() }; let [pat_field] = &subpatterns[..] else { bug!() }; self.check_binding_is_irrefutable( - &pat_field.pattern, + &self.thir[pat_field.pattern], "`for` loop binding", None, None, @@ -671,8 +670,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { // These next few matches want to peek through `AscribeUserType` to see // the underlying pattern. let mut unpeeled_pat = pat; - while let PatKind::AscribeUserType { ref subpattern, .. } = unpeeled_pat.kind { - unpeeled_pat = subpattern; + while let PatKind::AscribeUserType { subpattern, .. } = unpeeled_pat.kind { + unpeeled_pat = &self.thir[subpattern]; } if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = unpeeled_pat.kind @@ -704,7 +703,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { && scrut.is_some() { let mut bindings = vec![]; - pat.each_binding(|name, _, _, _| bindings.push(name)); + self.thir.for_each_binding_in_pat(pat, |name, _, _, _| bindings.push(name)); let semi_span = span.shrink_to_hi(); let start_span = span.shrink_to_lo(); @@ -767,9 +766,10 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { /// This analysis is *not* subsumed by NLL. fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: &Pat<'tcx>) { // Extract `sub` in `binding @ sub`. - let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else { + let PatKind::Binding { name, mode, ty, subpattern: Some(sub), .. } = pat.kind else { return; }; + let sub = &cx.thir[sub]; let is_binding_by_move = |ty: Ty<'tcx>| !cx.tcx.type_is_copy_modulo_regions(cx.typing_env, ty); @@ -780,7 +780,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: ByRef::No if is_binding_by_move(ty) => { // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. let mut conflicts_ref = Vec::new(); - sub.each_binding(|_, mode, _, span| { + cx.thir.for_each_binding_in_pat(sub, |_, mode, _, span| { if matches!(mode, ByRef::Yes(_)) { conflicts_ref.push(span) } @@ -809,7 +809,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: let mut conflicts_move = Vec::new(); let mut conflicts_mut_mut = Vec::new(); let mut conflicts_mut_ref = Vec::new(); - sub.each_binding(|name, mode, ty, span| { + cx.thir.for_each_binding_in_pat(sub, |name, mode, ty, span| { match mode { ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) { // Both sides are `ref`. @@ -1299,14 +1299,15 @@ fn report_non_exhaustive_match<'p, 'tcx>( for &arm in arms { let arm = &thir.arms[arm]; - if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = arm.pattern.kind - && let Ok(snippet) = cx.tcx.sess.source_map().span_to_snippet(arm.pattern.span) + let arm_pattern = &thir[arm.pattern]; + if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = arm_pattern.kind + && let Ok(snippet) = cx.tcx.sess.source_map().span_to_snippet(arm_pattern.span) // We filter out paths with multiple path::segments. && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') { let const_name = cx.tcx.item_name(def_id); err.span_label( - arm.pattern.span, + arm_pattern.span, format!( "this pattern doesn't introduce a new catch-all binding, but rather pattern \ matches against the value of constant `{const_name}`", @@ -1314,7 +1315,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( ); err.span_note(cx.tcx.def_span(def_id), format!("constant `{const_name}` defined here")); err.span_suggestion_verbose( - arm.pattern.span.shrink_to_hi(), + arm_pattern.span.shrink_to_hi(), "if you meant to introduce a binding, use a different name", "_var".to_string(), Applicability::MaybeIncorrect, diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 667d59d858e3a..2a715f38f6aa9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -9,10 +9,8 @@ use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree, -}; +use rustc_middle::thir::{FieldPat, Pat, PatId, PatKind, Thir}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, ValTree}; use rustc_middle::{mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::{Span, sym}; @@ -36,12 +34,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// so we have to carry one ourselves. #[instrument(level = "debug", skip(self), ret)] pub(super) fn const_to_pat( - &self, + &mut self, c: ty::Const<'tcx>, ty: Ty<'tcx>, id: hir::HirId, span: Span, - ) -> Box> { + ) -> PatId { let mut convert = ConstToPat::new(self, id, span, c); match c.kind() { @@ -52,8 +50,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } } -struct ConstToPat<'tcx> { +struct ConstToPat<'a, 'tcx> { tcx: TyCtxt<'tcx>, + thir: &'a mut Thir<'tcx>, typing_env: ty::TypingEnv<'tcx>, span: Span, id: hir::HirId, @@ -63,11 +62,17 @@ struct ConstToPat<'tcx> { c: ty::Const<'tcx>, } -impl<'tcx> ConstToPat<'tcx> { - fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { +impl<'a, 'tcx> ConstToPat<'a, 'tcx> { + fn new( + pat_ctxt: &'a mut PatCtxt<'_, 'tcx>, + id: hir::HirId, + span: Span, + c: ty::Const<'tcx>, + ) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { tcx: pat_ctxt.tcx, + thir: pat_ctxt.thir, typing_env: pat_ctxt.typing_env, span, id, @@ -84,7 +89,7 @@ impl<'tcx> ConstToPat<'tcx> { } /// We errored. Signal that in the pattern, so that follow up errors can be silenced. - fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { + fn mk_err(&mut self, mut err: Diag<'_>, ty: Ty<'tcx>) -> PatId { if let ty::ConstKind::Unevaluated(uv) = self.c.kind() { let def_kind = self.tcx.def_kind(uv.def); if let hir::def::DefKind::AssocConst = def_kind @@ -100,14 +105,10 @@ impl<'tcx> ConstToPat<'tcx> { ); } } - Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) }) + self.thir.pats.push(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) }) } - fn unevaluated_to_pat( - &mut self, - uv: ty::UnevaluatedConst<'tcx>, - ty: Ty<'tcx>, - ) -> Box> { + fn unevaluated_to_pat(&mut self, uv: ty::UnevaluatedConst<'tcx>, ty: Ty<'tcx>) -> PatId { trace!(self.treat_byte_string_as_slice); // It's not *technically* correct to be revealing opaque types here as borrowcheck has @@ -187,7 +188,7 @@ impl<'tcx> ConstToPat<'tcx> { // Convert the valtree to a const. let inlined_const_as_pat = self.valtree_to_pat(valtree, ty); - if !inlined_const_as_pat.references_error() { + if !self.thir.pat_references_error(&self.thir[inlined_const_as_pat]) { // Always check for `PartialEq` if we had no other errors yet. if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl { let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty }); @@ -200,15 +201,15 @@ impl<'tcx> ConstToPat<'tcx> { } fn field_pats( - &self, + &mut self, vals: impl Iterator, Ty<'tcx>)>, - ) -> Vec> { + ) -> Vec { vals.enumerate() .map(|(idx, (val, ty))| { let field = FieldIdx::new(idx); // Patterns can only use monomorphic types. let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty); - FieldPat { field, pattern: *self.valtree_to_pat(val, ty) } + FieldPat { field, pattern: self.valtree_to_pat(val, ty) } }) .collect() } @@ -216,7 +217,7 @@ impl<'tcx> ConstToPat<'tcx> { // Recursive helper for `to_pat`; invoke that (instead of calling this directly). // FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately. #[instrument(skip(self), level = "debug")] - fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box> { + fn valtree_to_pat(&mut self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> PatId { let span = self.span; let tcx = self.tcx; let kind = match ty.kind() { @@ -277,7 +278,7 @@ impl<'tcx> ConstToPat<'tcx> { prefix: cv .unwrap_branch() .iter() - .map(|val| *self.valtree_to_pat(*val, *elem_ty)) + .map(|&val| self.valtree_to_pat(val, *elem_ty)) .collect(), slice: None, suffix: Box::new([]), @@ -286,7 +287,7 @@ impl<'tcx> ConstToPat<'tcx> { prefix: cv .unwrap_branch() .iter() - .map(|val| *self.valtree_to_pat(*val, *elem_ty)) + .map(|&val| self.valtree_to_pat(val, *elem_ty)) .collect(), slice: None, suffix: Box::new([]), @@ -363,7 +364,7 @@ impl<'tcx> ConstToPat<'tcx> { } }; - Box::new(Pat { span, ty, kind }) + self.thir.pats.push(Pat { span, ty, kind }) } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 8dc3f998e0916..d2391b7294d4e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -15,7 +15,7 @@ use rustc_hir::{self as hir, RangeEnd}; use rustc_index::Idx; use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::thir::{ - Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, + Ascription, FieldPat, LocalVarId, Pat, PatId, PatKind, PatRange, PatRangeBoundary, Thir, }; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt}; @@ -30,6 +30,7 @@ use crate::errors::*; struct PatCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, + thir: &'a mut Thir<'tcx>, typing_env: ty::TypingEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -39,12 +40,14 @@ struct PatCtxt<'a, 'tcx> { pub(super) fn pat_from_hir<'a, 'tcx>( tcx: TyCtxt<'tcx>, + thir: &'a mut Thir<'tcx>, typing_env: ty::TypingEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, pat: &'tcx hir::Pat<'tcx>, -) -> Box> { +) -> PatId { let mut pcx = PatCtxt { tcx, + thir, typing_env, typeck_results, rust_2024_migration: typeck_results @@ -53,15 +56,15 @@ pub(super) fn pat_from_hir<'a, 'tcx>( .map(PatMigration::new), }; let result = pcx.lower_pattern(pat); - debug!("pat_from_hir({:?}) = {:?}", pat, result); if let Some(m) = pcx.rust_2024_migration { m.emit(tcx, pat.hir_id); } + debug!("pat_from_hir({pat:?}) = [{result:?}] {:?}", &thir[result]); result } impl<'a, 'tcx> PatCtxt<'a, 'tcx> { - fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { + fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> PatId { let adjustments: &[Ty<'tcx>] = self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); @@ -103,8 +106,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| { debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty); - Box::new(Pat { - span: thir_pat.span, + self.thir.pats.push(Pat { + span: self.thir[thir_pat].span, ty: *ref_ty, kind: PatKind::Deref { subpattern: thir_pat }, }) @@ -137,13 +140,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { match kind { PatKind::AscribeUserType { ascription, subpattern } => { ascriptions.push(ascription); - kind = subpattern.kind; + kind = self.thir.take_pat_kind(subpattern); } PatKind::ExpandedConstant { is_inline, def_id, subpattern } => { if is_inline { inline_consts.extend(def_id.as_local()); } - kind = subpattern.kind; + kind = self.thir.take_pat_kind(subpattern); } _ => break, } @@ -271,21 +274,21 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { for ascription in ascriptions { kind = PatKind::AscribeUserType { ascription, - subpattern: Box::new(Pat { span, ty, kind }), + subpattern: self.thir.pats.push(Pat { span, ty, kind }), }; } for def in inline_consts { kind = PatKind::ExpandedConstant { def_id: def.to_def_id(), is_inline: true, - subpattern: Box::new(Pat { span, ty, kind }), + subpattern: self.thir.pats.push(Pat { span, ty, kind }), }; } Ok(kind) } #[instrument(skip(self), level = "debug")] - fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { + fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> PatId { let mut ty = self.typeck_results.node_type(pat.hir_id); let mut span = pat.span; @@ -364,7 +367,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { name: ident.name, var: LocalVarId(id), ty: var_ty, - subpattern: self.lower_opt_pattern(sub), + subpattern: try { self.lower_pattern(sub?) }, is_primary: id == pat.hir_id, } } @@ -385,7 +388,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .iter() .map(|field| FieldPat { field: self.typeck_results.field_index(field.hir_id), - pattern: *self.lower_pattern(field.pat), + pattern: self.lower_pattern(field.pat), }) .collect(); @@ -395,12 +398,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) }, // FIXME(guard_patterns): implement guard pattern lowering - hir::PatKind::Guard(pat, _) => self.lower_pattern(pat).kind, + hir::PatKind::Guard(pat, _) => { + let pat = self.lower_pattern(pat); + self.thir.take_pat_kind(pat) + } hir::PatKind::Err(guar) => PatKind::Error(guar), }; - Box::new(Pat { span, ty, kind }) + self.thir.pats.push(Pat { span, ty, kind }) } fn lower_tuple_subpats( @@ -408,22 +414,18 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { pats: &'tcx [hir::Pat<'tcx>], expected_len: usize, gap_pos: hir::DotDotPos, - ) -> Vec> { + ) -> Vec { pats.iter() .enumerate_and_adjust(expected_len, gap_pos) .map(|(i, subpattern)| FieldPat { field: FieldIdx::new(i), - pattern: *self.lower_pattern(subpattern), + pattern: self.lower_pattern(subpattern), }) .collect() } - fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Pat<'tcx>]> { - pats.iter().map(|p| *self.lower_pattern(p)).collect() - } - - fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option>> { - pat.map(|p| self.lower_pattern(p)) + fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[PatId]> { + pats.iter().map(|p| self.lower_pattern(p)).collect() } fn slice_or_array_pattern( @@ -435,7 +437,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { suffix: &'tcx [hir::Pat<'tcx>], ) -> PatKind<'tcx> { let prefix = self.lower_patterns(prefix); - let slice = self.lower_opt_pattern(slice); + let slice = try { self.lower_pattern(slice?) }; let suffix = self.lower_patterns(suffix); match ty.kind() { // Matching a slice, `[T]`. @@ -458,7 +460,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir_id: hir::HirId, span: Span, ty: Ty<'tcx>, - subpatterns: Vec>, + subpatterns: Vec, ) -> PatKind<'tcx> { let res = match res { Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => { @@ -527,7 +529,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { inferred_ty: self.typeck_results.node_type(hir_id), }; kind = PatKind::AscribeUserType { - subpattern: Box::new(Pat { span, ty, kind }), + subpattern: self.thir.pats.push(Pat { span, ty, kind }), ascription: Ascription { annotation, variance: ty::Covariant }, }; } @@ -546,7 +548,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// it to `const_to_pat`. Any other path (like enum variants without fields) /// is converted to the corresponding pattern via `lower_variant_or_leaf`. #[instrument(skip(self), level = "debug")] - fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> Box> { + fn lower_path(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) -> PatId { let ty = self.typeck_results.node_type(id); let res = self.typeck_results.qpath_res(qpath, id); @@ -560,7 +562,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // The path isn't the name of a constant, so it must actually // be a unit struct or unit variant (e.g. `Option::None`). let kind = self.lower_variant_or_leaf(res, id, span, ty, vec![]); - return Box::new(Pat { span, ty, kind }); + return self.thir.pats.push(Pat { span, ty, kind }); } }; @@ -574,7 +576,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // diagnostics in some situations, but has no effect at runtime. let mut pattern = { let kind = PatKind::ExpandedConstant { subpattern, def_id, is_inline: false }; - Box::new(Pat { span, ty, kind }) + self.thir.pats.push(Pat { span, ty, kind }) }; // If this is an associated constant with an explicit user-written @@ -594,7 +596,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { variance: ty::Contravariant, }, }; - pattern = Box::new(Pat { span, kind, ty }); + pattern = self.thir.pats.push(Pat { span, kind, ty }); } pattern @@ -633,7 +635,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> { let (lit, neg) = match &expr.kind { hir::PatExprKind::Path(qpath) => { - return self.lower_path(qpath, expr.hir_id, expr.span).kind; + let pat = self.lower_path(qpath, expr.hir_id, expr.span); + return self.thir.take_pat_kind(pat); } hir::PatExprKind::ConstBlock(anon_const) => { return self.lower_inline_const(anon_const, expr.hir_id, expr.span); @@ -644,6 +647,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ct_ty = self.typeck_results.node_type(expr.hir_id); let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; let constant = self.tcx.at(expr.span).lit_to_const(lit_input); - self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind + let pat = self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span); + self.thir.take_pat_kind(pat) } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index cd56d93afcf75..9cdf03156931a 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -85,7 +85,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { if let Some(pat) = pat { print_indented!(self, "param: Some( ", depth_lvl + 1); - self.print_pat(pat, depth_lvl + 2); + self.print_pat(*pat, depth_lvl + 2); print_indented!(self, ")", depth_lvl + 1); } else { print_indented!(self, "param: None", depth_lvl + 1); @@ -155,7 +155,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, format!("init_scope: {:?}", init_scope), depth_lvl + 2); print_indented!(self, "pattern: ", depth_lvl + 2); - self.print_pat(pattern, depth_lvl + 3); + self.print_pat(*pattern, depth_lvl + 3); print_indented!(self, ",", depth_lvl + 2); if let Some(init) = initializer { @@ -626,7 +626,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { let Arm { pattern, guard, body, lint_level, scope, span } = arm; print_indented!(self, "pattern: ", depth_lvl + 1); - self.print_pat(pattern, depth_lvl + 2); + self.print_pat(*pattern, depth_lvl + 2); if let Some(guard) = *guard { print_indented!(self, "guard: ", depth_lvl + 1); @@ -643,8 +643,8 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "}", depth_lvl); } - fn print_pat(&mut self, pat: &Pat<'tcx>, depth_lvl: usize) { - let &Pat { ty, span, ref kind } = pat; + fn print_pat(&mut self, pat_id: PatId, depth_lvl: usize) { + let &Pat { ty, span, ref kind } = &self.thir[pat_id]; print_indented!(self, "Pat: {", depth_lvl); print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1); @@ -667,7 +667,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "AscribeUserType: {", depth_lvl + 1); print_indented!(self, format!("ascription: {:?}", ascription), depth_lvl + 2); print_indented!(self, "subpattern: ", depth_lvl + 2); - self.print_pat(subpattern, depth_lvl + 3); + self.print_pat(*subpattern, depth_lvl + 3); print_indented!(self, "}", depth_lvl + 1); } PatKind::Binding { name, mode, var, ty, subpattern, is_primary } => { @@ -680,7 +680,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { if let Some(subpattern) = subpattern { print_indented!(self, "subpattern: Some( ", depth_lvl + 2); - self.print_pat(subpattern, depth_lvl + 3); + self.print_pat(*subpattern, depth_lvl + 3); print_indented!(self, ")", depth_lvl + 2); } else { print_indented!(self, "subpattern: None", depth_lvl + 2); @@ -698,7 +698,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { if subpatterns.len() > 0 { print_indented!(self, "subpatterns: [", depth_lvl + 2); for field_pat in subpatterns.iter() { - self.print_pat(&field_pat.pattern, depth_lvl + 3); + self.print_pat(field_pat.pattern, depth_lvl + 3); } print_indented!(self, "]", depth_lvl + 2); } else { @@ -711,7 +711,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "Leaf { ", depth_lvl + 1); print_indented!(self, "subpatterns: [", depth_lvl + 2); for field_pat in subpatterns.iter() { - self.print_pat(&field_pat.pattern, depth_lvl + 3); + self.print_pat(field_pat.pattern, depth_lvl + 3); } print_indented!(self, "]", depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); @@ -719,13 +719,13 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { PatKind::Deref { subpattern } => { print_indented!(self, "Deref { ", depth_lvl + 1); print_indented!(self, "subpattern:", depth_lvl + 2); - self.print_pat(subpattern, depth_lvl + 2); + self.print_pat(*subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } PatKind::DerefPattern { subpattern, .. } => { print_indented!(self, "DerefPattern { ", depth_lvl + 1); print_indented!(self, "subpattern:", depth_lvl + 2); - self.print_pat(subpattern, depth_lvl + 2); + self.print_pat(*subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } PatKind::Constant { value } => { @@ -738,7 +738,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, format!("def_id: {def_id:?}"), depth_lvl + 2); print_indented!(self, format!("is_inline: {is_inline:?}"), depth_lvl + 2); print_indented!(self, "subpattern:", depth_lvl + 2); - self.print_pat(subpattern, depth_lvl + 2); + self.print_pat(*subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } PatKind::Range(pat_range) => { @@ -749,18 +749,18 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "prefix: [", depth_lvl + 2); for prefix_pat in prefix.iter() { - self.print_pat(prefix_pat, depth_lvl + 3); + self.print_pat(*prefix_pat, depth_lvl + 3); } print_indented!(self, "]", depth_lvl + 2); if let Some(slice) = slice { print_indented!(self, "slice: ", depth_lvl + 2); - self.print_pat(slice, depth_lvl + 3); + self.print_pat(*slice, depth_lvl + 3); } print_indented!(self, "suffix: [", depth_lvl + 2); for suffix_pat in suffix.iter() { - self.print_pat(suffix_pat, depth_lvl + 3); + self.print_pat(*suffix_pat, depth_lvl + 3); } print_indented!(self, "]", depth_lvl + 2); @@ -771,18 +771,18 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "prefix: [", depth_lvl + 2); for prefix_pat in prefix.iter() { - self.print_pat(prefix_pat, depth_lvl + 3); + self.print_pat(*prefix_pat, depth_lvl + 3); } print_indented!(self, "]", depth_lvl + 2); if let Some(slice) = slice { print_indented!(self, "slice: ", depth_lvl + 2); - self.print_pat(slice, depth_lvl + 3); + self.print_pat(*slice, depth_lvl + 3); } print_indented!(self, "suffix: [", depth_lvl + 2); for suffix_pat in suffix.iter() { - self.print_pat(suffix_pat, depth_lvl + 3); + self.print_pat(*suffix_pat, depth_lvl + 3); } print_indented!(self, "]", depth_lvl + 2); @@ -792,7 +792,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "Or {", depth_lvl + 1); print_indented!(self, "pats: [", depth_lvl + 2); for pat in pats.iter() { - self.print_pat(pat, depth_lvl + 3); + self.print_pat(*pat, depth_lvl + 3); } print_indented!(self, "]", depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 88194c737a6cc..87d0e293e1b66 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::DefId; use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::{self, Const}; -use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary}; +use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary, Thir}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef, @@ -76,8 +76,9 @@ impl<'tcx> RevealedTy<'tcx> { } #[derive(Clone)] -pub struct RustcPatCtxt<'p, 'tcx: 'p> { +pub struct RustcPatCtxt<'p, 'tcx> { pub tcx: TyCtxt<'tcx>, + pub thir: &'p Thir<'tcx>, pub typeck_results: &'tcx ty::TypeckResults<'tcx>, /// The module in which the match occurs. This is necessary for /// checking inhabited-ness of types because whether a type is (visibly) @@ -447,21 +448,25 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// `rustc_mir_build::thir::pattern::check_match::MatchVisitor::lower_pattern`. pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { let cx = self; + let thir = cx.thir; let ty = cx.reveal_opaque_ty(pat.ty); let ctor; let arity; let fields: Vec<_>; match &pat.kind { PatKind::AscribeUserType { subpattern, .. } - | PatKind::ExpandedConstant { subpattern, .. } => return self.lower_pat(subpattern), - PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), + | PatKind::ExpandedConstant { subpattern, .. } + | PatKind::Binding { subpattern: Some(subpattern), .. } => { + return self.lower_pat(&thir[*subpattern]); + } + PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; fields = vec![]; arity = 0; } PatKind::Deref { subpattern } => { - fields = vec![self.lower_pat(subpattern).at_index(0)]; + fields = vec![self.lower_pat(&thir[*subpattern]).at_index(0)]; arity = 1; ctor = match ty.kind() { // This is a box pattern. @@ -488,7 +493,10 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { arity = fs.len(); fields = subpatterns .iter() - .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index())) + .map(|ipat| { + let pat = &self.thir[ipat.pattern]; + self.lower_pat(pat).at_index(ipat.field.index()) + }) .collect(); } ty::Adt(adt, _) if adt.is_box() => { @@ -506,7 +514,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // prevent mixing of those two options. let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0); if let Some(pat) = pattern { - fields = vec![self.lower_pat(&pat.pattern).at_index(0)]; + fields = vec![self.lower_pat(&self.thir[pat.pattern]).at_index(0)]; } else { fields = vec![]; } @@ -525,7 +533,10 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { arity = variant.fields.len(); fields = subpatterns .iter() - .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index())) + .map(|ipat| { + let pat = &self.thir[ipat.pattern]; + self.lower_pat(pat).at_index(ipat.field.index()) + }) .collect(); } _ => span_bug!( @@ -703,7 +714,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { fields = prefix .iter() .chain(suffix.iter()) - .map(|p| self.lower_pat(&*p)) + .map(|&p| self.lower_pat(&self.thir[p])) .enumerate() .map(|(i, p)| p.at_index(i)) .collect(); @@ -711,7 +722,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } PatKind::Or { .. } => { ctor = Or; - let pats = expand_or_pat(pat); + let pats = expand_or_pat(self.thir, pat); fields = pats .into_iter() .map(|p| self.lower_pat(p)) @@ -1058,11 +1069,11 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { } /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. -fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { - fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) { +fn expand_or_pat<'p, 'tcx>(thir: &'p Thir<'tcx>, pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { + fn expand<'p, 'tcx>(thir: &'p Thir<'tcx>, pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) { if let PatKind::Or { pats } = &pat.kind { - for pat in pats.iter() { - expand(pat, vec); + for &pat in pats.iter() { + expand(thir, &thir[pat], vec); } } else { vec.push(pat) @@ -1070,7 +1081,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { } let mut pats = Vec::new(); - expand(pat, &mut pats); + expand(thir, pat, &mut pats); pats } diff --git a/tests/mir-opt/building/user_type_annotations.let_else.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_else.built.after.mir new file mode 100644 index 0000000000000..3a515787c10b0 --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.let_else.built.after.mir @@ -0,0 +1,80 @@ +// MIR for `let_else` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:35:20: 35:45, inferred_ty: (u32, u64, &char) +| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:35:20: 35:45, inferred_ty: (u32, u64, &char) +| +fn let_else() -> () { + let mut _0: (); + let mut _1: !; + let _2: u32 as UserTypeProjection { base: UserType(0), projs: [Field(0, ())] }; + let _3: u64 as UserTypeProjection { base: UserType(0), projs: [Field(1, ())] }; + let _4: &char as UserTypeProjection { base: UserType(0), projs: [Field(2, ())] }; + let mut _5: (u32, u64, &char); + let mut _6: &char; + let _7: &char; + let _8: char; + scope 1 { + debug x => _2; + debug y => _3; + debug z => _4; + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = const 'u'; + _7 = &_8; + _6 = &(*_7); + _5 = (const 7_u32, const 12_u64, move _6); + StorageDead(_6); + PlaceMention(_5); + falseEdge -> [real: bb4, imaginary: bb3]; + } + + bb1: { + _1 = core::panicking::panic(const "internal error: entered unreachable code") -> bb6; + } + + bb2: { + unreachable; + } + + bb3: { + goto -> bb5; + } + + bb4: { + AscribeUserType(_5, +, UserTypeProjection { base: UserType(1), projs: [] }); + _2 = copy (_5.0: u32); + _3 = copy (_5.1: u64); + _4 = copy (_5.2: &char); + StorageDead(_7); + StorageDead(_5); + _0 = const (); + StorageDead(_8); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; + } + + bb5: { + StorageDead(_7); + StorageDead(_5); + StorageDead(_8); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + goto -> bb1; + } + + bb6 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/user_type_annotations.let_else_bindless.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_else_bindless.built.after.mir new file mode 100644 index 0000000000000..52a6d904d4580 --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.let_else_bindless.built.after.mir @@ -0,0 +1,62 @@ +// MIR for `let_else_bindless` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:40:20: 40:45, inferred_ty: (u32, u64, &char) +| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:40:20: 40:45, inferred_ty: (u32, u64, &char) +| +fn let_else_bindless() -> () { + let mut _0: (); + let mut _1: !; + let mut _2: (u32, u64, &char); + let mut _3: &char; + let _4: &char; + let _5: char; + scope 1 { + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + _5 = const 'u'; + _4 = &_5; + _3 = &(*_4); + _2 = (const 7_u32, const 12_u64, move _3); + StorageDead(_3); + PlaceMention(_2); + falseEdge -> [real: bb4, imaginary: bb3]; + } + + bb1: { + _1 = core::panicking::panic(const "internal error: entered unreachable code") -> bb6; + } + + bb2: { + unreachable; + } + + bb3: { + goto -> bb5; + } + + bb4: { + AscribeUserType(_2, +, UserTypeProjection { base: UserType(1), projs: [] }); + StorageDead(_4); + StorageDead(_2); + _0 = const (); + StorageDead(_5); + return; + } + + bb5: { + StorageDead(_4); + StorageDead(_2); + StorageDead(_5); + goto -> bb1; + } + + bb6 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/user_type_annotations.let_init.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_init.built.after.mir new file mode 100644 index 0000000000000..d1b8f823e9bc5 --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.let_init.built.after.mir @@ -0,0 +1,54 @@ +// MIR for `let_init` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:25:20: 25:45, inferred_ty: (u32, u64, &char) +| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:25:20: 25:45, inferred_ty: (u32, u64, &char) +| +fn let_init() -> () { + let mut _0: (); + let _1: u32 as UserTypeProjection { base: UserType(0), projs: [Field(0, ())] }; + let _2: u64 as UserTypeProjection { base: UserType(0), projs: [Field(1, ())] }; + let _3: &char as UserTypeProjection { base: UserType(0), projs: [Field(2, ())] }; + let mut _4: (u32, u64, &char); + let mut _5: &char; + let _6: &char; + let _7: char; + scope 1 { + debug x => _1; + debug y => _2; + debug z => _3; + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + StorageLive(_7); + _7 = const 'u'; + _6 = &_7; + _5 = &(*_6); + _4 = (const 7_u32, const 12_u64, move _5); + StorageDead(_5); + PlaceMention(_4); + AscribeUserType(_4, +, UserTypeProjection { base: UserType(1), projs: [] }); + StorageLive(_1); + _1 = copy (_4.0: u32); + StorageLive(_2); + _2 = copy (_4.1: u64); + StorageLive(_3); + _3 = copy (_4.2: &char); + StorageDead(_6); + StorageDead(_4); + _0 = const (); + StorageDead(_3); + StorageDead(_2); + StorageDead(_1); + StorageDead(_7); + return; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _4); + unreachable; + } +} diff --git a/tests/mir-opt/building/user_type_annotations.let_init_bindless.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_init_bindless.built.after.mir new file mode 100644 index 0000000000000..6702f9300607f --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.let_init_bindless.built.after.mir @@ -0,0 +1,39 @@ +// MIR for `let_init_bindless` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:30:20: 30:45, inferred_ty: (u32, u64, &char) +| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:30:20: 30:45, inferred_ty: (u32, u64, &char) +| +fn let_init_bindless() -> () { + let mut _0: (); + let mut _1: (u32, u64, &char); + let mut _2: &char; + let _3: &char; + let _4: char; + scope 1 { + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = const 'u'; + _3 = &_4; + _2 = &(*_3); + _1 = (const 7_u32, const 12_u64, move _2); + StorageDead(_2); + PlaceMention(_1); + AscribeUserType(_1, +, UserTypeProjection { base: UserType(1), projs: [] }); + StorageDead(_3); + StorageDead(_1); + _0 = const (); + StorageDead(_4); + return; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } +} diff --git a/tests/mir-opt/building/user_type_annotations.let_uninit.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_uninit.built.after.mir new file mode 100644 index 0000000000000..76b5938b87d2a --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.let_uninit.built.after.mir @@ -0,0 +1,27 @@ +// MIR for `let_uninit` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:15:20: 15:45, inferred_ty: (u32, u64, &char) +| +fn let_uninit() -> () { + let mut _0: (); + let _1: u32 as UserTypeProjection { base: UserType(0), projs: [Field(0, ())] }; + let _2: u64 as UserTypeProjection { base: UserType(0), projs: [Field(1, ())] }; + let _3: &char as UserTypeProjection { base: UserType(0), projs: [Field(2, ())] }; + scope 1 { + debug x => _1; + debug y => _2; + debug z => _3; + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + _0 = const (); + StorageDead(_3); + StorageDead(_2); + StorageDead(_1); + return; + } +} diff --git a/tests/mir-opt/building/user_type_annotations.let_uninit_bindless.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_uninit_bindless.built.after.mir new file mode 100644 index 0000000000000..0cd125587714d --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.let_uninit_bindless.built.after.mir @@ -0,0 +1,15 @@ +// MIR for `let_uninit_bindless` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:20:20: 20:45, inferred_ty: (u32, u64, &char) +| +fn let_uninit_bindless() -> () { + let mut _0: (); + scope 1 { + } + + bb0: { + _0 = const (); + return; + } +} diff --git a/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir b/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir new file mode 100644 index 0000000000000..c0ce6f1d06b5e --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir @@ -0,0 +1,46 @@ +// MIR for `match_assoc_const` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:54:9: 54:44, inferred_ty: u32 +| 1: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:54:9: 54:44, inferred_ty: u32 +| +fn match_assoc_const() -> () { + let mut _0: (); + let mut _1: u32; + + bb0: { + StorageLive(_1); + _1 = const 8_u32; + PlaceMention(_1); + switchInt(copy _1) -> [99: bb2, otherwise: bb1]; + } + + bb1: { + _0 = const (); + goto -> bb6; + } + + bb2: { + falseEdge -> [real: bb5, imaginary: bb1]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } + + bb5: { + AscribeUserType(_1, -, UserTypeProjection { base: UserType(1), projs: [] }); + _0 = const (); + goto -> bb6; + } + + bb6: { + StorageDead(_1); + return; + } +} diff --git a/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir b/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir new file mode 100644 index 0000000000000..3a6aa5b7c2ca4 --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir @@ -0,0 +1,74 @@ +// MIR for `match_assoc_const_range` after built + +| User Type Annotations +| 0: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:62:11: 62:46, inferred_ty: u32 +| 1: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:62:11: 62:46, inferred_ty: u32 +| 2: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:63:9: 63:44, inferred_ty: u32 +| 3: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:63:9: 63:44, inferred_ty: u32 +| +fn match_assoc_const_range() -> () { + let mut _0: (); + let mut _1: u32; + let mut _2: bool; + let mut _3: bool; + + bb0: { + StorageLive(_1); + _1 = const 8_u32; + PlaceMention(_1); + _3 = Lt(copy _1, const 99_u32); + switchInt(move _3) -> [0: bb4, otherwise: bb2]; + } + + bb1: { + _0 = const (); + goto -> bb11; + } + + bb2: { + falseEdge -> [real: bb10, imaginary: bb4]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + _2 = Le(const 99_u32, copy _1); + switchInt(move _2) -> [0: bb5, otherwise: bb6]; + } + + bb5: { + goto -> bb1; + } + + bb6: { + falseEdge -> [real: bb9, imaginary: bb1]; + } + + bb7: { + goto -> bb5; + } + + bb8: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } + + bb9: { + AscribeUserType(_1, -, UserTypeProjection { base: UserType(3), projs: [] }); + _0 = const (); + goto -> bb11; + } + + bb10: { + AscribeUserType(_1, -, UserTypeProjection { base: UserType(1), projs: [] }); + _0 = const (); + goto -> bb11; + } + + bb11: { + StorageDead(_1); + return; + } +} diff --git a/tests/mir-opt/building/user_type_annotations.rs b/tests/mir-opt/building/user_type_annotations.rs new file mode 100644 index 0000000000000..d55c678d5ae35 --- /dev/null +++ b/tests/mir-opt/building/user_type_annotations.rs @@ -0,0 +1,66 @@ +//@ edition: 2024 +// skip-filecheck + +// This test demonstrates how many user type annotations are recorded in MIR +// for various binding constructs. In particular, this makes it possible to see +// the number of duplicate user-type-annotation entries, and whether that +// number has changed. +// +// Duplicates are mostly harmless, other than being inefficient. +// "Unused" entries that are _not_ duplicates may nevertheless be necessary so +// that they are seen by MIR lifetime checks. + +// EMIT_MIR user_type_annotations.let_uninit.built.after.mir +fn let_uninit() { + let (x, y, z): (u32, u64, &'static char); +} + +// EMIT_MIR user_type_annotations.let_uninit_bindless.built.after.mir +fn let_uninit_bindless() { + let (_, _, _): (u32, u64, &'static char); +} + +// EMIT_MIR user_type_annotations.let_init.built.after.mir +fn let_init() { + let (x, y, z): (u32, u64, &'static char) = (7, 12, &'u'); +} + +// EMIT_MIR user_type_annotations.let_init_bindless.built.after.mir +fn let_init_bindless() { + let (_, _, _): (u32, u64, &'static char) = (7, 12, &'u'); +} + +// EMIT_MIR user_type_annotations.let_else.built.after.mir +fn let_else() { + let (x, y, z): (u32, u64, &'static char) = (7, 12, &'u') else { unreachable!() }; +} + +// EMIT_MIR user_type_annotations.let_else_bindless.built.after.mir +fn let_else_bindless() { + let (_, _, _): (u32, u64, &'static char) = (7, 12, &'u') else { unreachable!() }; +} + +trait MyTrait<'a> { + const FOO: u32; +} +struct MyStruct {} +impl MyTrait<'static> for MyStruct { + const FOO: u32 = 99; +} + +// EMIT_MIR user_type_annotations.match_assoc_const.built.after.mir +fn match_assoc_const() { + match 8 { + >::FOO => {} + _ => {} + } +} + +// EMIT_MIR user_type_annotations.match_assoc_const_range.built.after.mir +fn match_assoc_const_range() { + match 8 { + ..>::FOO => {} + >::FOO.. => {} + _ => {} + } +} diff --git a/tests/ui/thir-print/thir-flat-const-variant.stdout b/tests/ui/thir-print/thir-flat-const-variant.stdout index 5588cfdfa5ce0..439a34d53afea 100644 --- a/tests/ui/thir-print/thir-flat-const-variant.stdout +++ b/tests/ui/thir-print/thir-flat-const-variant.stdout @@ -81,6 +81,7 @@ Thir { ], stmts: [], params: [], + pats: [], } DefId(0:9 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR2): @@ -166,6 +167,7 @@ Thir { ], stmts: [], params: [], + pats: [], } DefId(0:10 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR3): @@ -251,6 +253,7 @@ Thir { ], stmts: [], params: [], + pats: [], } DefId(0:11 ~ thir_flat_const_variant[1f54]::{impl#0}::BAR4): @@ -336,6 +339,7 @@ Thir { ], stmts: [], params: [], + pats: [], } DefId(0:12 ~ thir_flat_const_variant[1f54]::main): @@ -388,5 +392,6 @@ Thir { ], stmts: [], params: [], + pats: [], } diff --git a/tests/ui/thir-print/thir-flat.stdout b/tests/ui/thir-print/thir-flat.stdout index 59cecfe511c24..3ec6d2c030faf 100644 --- a/tests/ui/thir-print/thir-flat.stdout +++ b/tests/ui/thir-print/thir-flat.stdout @@ -48,5 +48,6 @@ Thir { ], stmts: [], params: [], + pats: [], }