rustc_mir_build/builder/matches/
user_ty.rs

1//! Helper code for building a linked list of user-type projections on the
2//! stack while visiting a THIR pattern.
3//!
4//! This avoids having to repeatedly clone a partly-built [`UserTypeProjections`]
5//! at every step of the traversal, which is what the previous code was doing.
6
7use std::assert_matches::assert_matches;
8use std::iter;
9
10use rustc_abi::{FieldIdx, VariantIdx};
11use rustc_data_structures::smallvec::SmallVec;
12use rustc_middle::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections};
13use rustc_middle::ty::{AdtDef, UserTypeAnnotationIndex};
14use rustc_span::Symbol;
15
16/// A single `thir::Pat` node should almost never have more than 0-2 user types.
17/// We can store up to 4 inline in the same size as an ordinary `Vec`.
18pub(crate) type UserTypeIndices = SmallVec<[UserTypeAnnotationIndex; 4]>;
19
20/// One of a list of "operations" that can be used to lazily build projections
21/// of user-specified types.
22#[derive(Debug)]
23pub(crate) enum ProjectedUserTypesOp {
24    PushUserTypes { base_types: UserTypeIndices },
25
26    Index,
27    Subslice { from: u64, to: u64 },
28    Deref,
29    Leaf { field: FieldIdx },
30    Variant { name: Symbol, variant: VariantIdx, field: FieldIdx },
31}
32
33#[derive(Debug)]
34pub(crate) enum ProjectedUserTypesNode<'a> {
35    None,
36    Chain { parent: &'a Self, op: ProjectedUserTypesOp },
37}
38
39impl<'a> ProjectedUserTypesNode<'a> {
40    pub(crate) fn push_user_types(&'a self, base_types: UserTypeIndices) -> Self {
41        assert!(!base_types.is_empty());
42        // Pushing one or more base user types always causes the chain to become non-empty.
43        Self::Chain { parent: self, op: ProjectedUserTypesOp::PushUserTypes { base_types } }
44    }
45
46    /// Push another projection op onto the chain, but only if it is already non-empty.
47    fn maybe_push(&'a self, op_fn: impl FnOnce() -> ProjectedUserTypesOp) -> Self {
48        match self {
49            Self::None => Self::None,
50            Self::Chain { .. } => Self::Chain { parent: self, op: op_fn() },
51        }
52    }
53
54    pub(crate) fn index(&'a self) -> Self {
55        self.maybe_push(|| ProjectedUserTypesOp::Index)
56    }
57
58    pub(crate) fn subslice(&'a self, from: u64, to: u64) -> Self {
59        self.maybe_push(|| ProjectedUserTypesOp::Subslice { from, to })
60    }
61
62    pub(crate) fn deref(&'a self) -> Self {
63        self.maybe_push(|| ProjectedUserTypesOp::Deref)
64    }
65
66    pub(crate) fn leaf(&'a self, field: FieldIdx) -> Self {
67        self.maybe_push(|| ProjectedUserTypesOp::Leaf { field })
68    }
69
70    pub(crate) fn variant(
71        &'a self,
72        adt_def: AdtDef<'_>,
73        variant: VariantIdx,
74        field: FieldIdx,
75    ) -> Self {
76        self.maybe_push(|| {
77            let name = adt_def.variant(variant).name;
78            ProjectedUserTypesOp::Variant { name, variant, field }
79        })
80    }
81
82    /// Traverses the chain of nodes to yield each op in the chain.
83    /// Because this walks from child node to parent node, the ops are
84    /// naturally yielded in "reverse" order.
85    fn iter_ops_reversed(&'a self) -> impl Iterator<Item = &'a ProjectedUserTypesOp> {
86        let mut next = self;
87        iter::from_fn(move || match next {
88            Self::None => None,
89            Self::Chain { parent, op } => {
90                next = parent;
91                Some(op)
92            }
93        })
94    }
95
96    /// Assembles this chain of user-type projections into a proper data structure.
97    pub(crate) fn build_user_type_projections(&self) -> Option<Box<UserTypeProjections>> {
98        // If we know there's nothing to do, just return None immediately.
99        if matches!(self, Self::None) {
100            return None;
101        }
102
103        let ops_reversed = self.iter_ops_reversed().collect::<Vec<_>>();
104        // The "first" op should always be `PushUserType`.
105        // Other projections are only added if there is at least one user type.
106        assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserTypes { .. }));
107
108        let mut projections = vec![];
109        for op in ops_reversed.into_iter().rev() {
110            match *op {
111                ProjectedUserTypesOp::PushUserTypes { ref base_types } => {
112                    assert!(!base_types.is_empty());
113                    for &base in base_types {
114                        projections.push(UserTypeProjection { base, projs: vec![] })
115                    }
116                }
117
118                ProjectedUserTypesOp::Index => {
119                    for p in &mut projections {
120                        p.projs.push(ProjectionElem::Index(()))
121                    }
122                }
123                ProjectedUserTypesOp::Subslice { from, to } => {
124                    for p in &mut projections {
125                        p.projs.push(ProjectionElem::Subslice { from, to, from_end: true })
126                    }
127                }
128                ProjectedUserTypesOp::Deref => {
129                    for p in &mut projections {
130                        p.projs.push(ProjectionElem::Deref)
131                    }
132                }
133                ProjectedUserTypesOp::Leaf { field } => {
134                    for p in &mut projections {
135                        p.projs.push(ProjectionElem::Field(field, ()))
136                    }
137                }
138                ProjectedUserTypesOp::Variant { name, variant, field } => {
139                    for p in &mut projections {
140                        p.projs.push(ProjectionElem::Downcast(Some(name), variant));
141                        p.projs.push(ProjectionElem::Field(field, ()));
142                    }
143                }
144            }
145        }
146
147        Some(Box::new(UserTypeProjections { contents: projections }))
148    }
149}