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_middle::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections};
12use rustc_middle::ty::{AdtDef, UserTypeAnnotationIndex};
13use rustc_span::Symbol;
14
15/// One of a list of "operations" that can be used to lazily build projections
16/// of user-specified types.
17#[derive(Clone, Debug)]
18pub(crate) enum ProjectedUserTypesOp {
19    PushUserType { base: UserTypeAnnotationIndex },
20
21    Index,
22    Subslice { from: u64, to: u64 },
23    Deref,
24    Leaf { field: FieldIdx },
25    Variant { name: Symbol, variant: VariantIdx, field: FieldIdx },
26}
27
28#[derive(Debug)]
29pub(crate) enum ProjectedUserTypesNode<'a> {
30    None,
31    Chain { parent: &'a Self, op: ProjectedUserTypesOp },
32}
33
34impl<'a> ProjectedUserTypesNode<'a> {
35    pub(crate) fn push_user_type(&'a self, base: UserTypeAnnotationIndex) -> Self {
36        // Pushing a base user type always causes the chain to become non-empty.
37        Self::Chain { parent: self, op: ProjectedUserTypesOp::PushUserType { base } }
38    }
39
40    /// Push another projection op onto the chain, but only if it is already non-empty.
41    fn maybe_push(&'a self, op_fn: impl FnOnce() -> ProjectedUserTypesOp) -> Self {
42        match self {
43            Self::None => Self::None,
44            Self::Chain { .. } => Self::Chain { parent: self, op: op_fn() },
45        }
46    }
47
48    pub(crate) fn index(&'a self) -> Self {
49        self.maybe_push(|| ProjectedUserTypesOp::Index)
50    }
51
52    pub(crate) fn subslice(&'a self, from: u64, to: u64) -> Self {
53        self.maybe_push(|| ProjectedUserTypesOp::Subslice { from, to })
54    }
55
56    pub(crate) fn deref(&'a self) -> Self {
57        self.maybe_push(|| ProjectedUserTypesOp::Deref)
58    }
59
60    pub(crate) fn leaf(&'a self, field: FieldIdx) -> Self {
61        self.maybe_push(|| ProjectedUserTypesOp::Leaf { field })
62    }
63
64    pub(crate) fn variant(
65        &'a self,
66        adt_def: AdtDef<'_>,
67        variant: VariantIdx,
68        field: FieldIdx,
69    ) -> Self {
70        self.maybe_push(|| {
71            let name = adt_def.variant(variant).name;
72            ProjectedUserTypesOp::Variant { name, variant, field }
73        })
74    }
75
76    /// Traverses the chain of nodes to yield each op in the chain.
77    /// Because this walks from child node to parent node, the ops are
78    /// naturally yielded in "reverse" order.
79    fn iter_ops_reversed(&'a self) -> impl Iterator<Item = &'a ProjectedUserTypesOp> {
80        let mut next = self;
81        iter::from_fn(move || match next {
82            Self::None => None,
83            Self::Chain { parent, op } => {
84                next = parent;
85                Some(op)
86            }
87        })
88    }
89
90    /// Assembles this chain of user-type projections into a proper data structure.
91    pub(crate) fn build_user_type_projections(&self) -> Option<Box<UserTypeProjections>> {
92        // If we know there's nothing to do, just return None immediately.
93        if matches!(self, Self::None) {
94            return None;
95        }
96
97        let ops_reversed = self.iter_ops_reversed().cloned().collect::<Vec<_>>();
98        // The "first" op should always be `PushUserType`.
99        // Other projections are only added if there is at least one user type.
100        assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserType { .. }));
101
102        let mut projections = vec![];
103        for op in ops_reversed.into_iter().rev() {
104            match op {
105                ProjectedUserTypesOp::PushUserType { base } => {
106                    projections.push(UserTypeProjection { base, projs: vec![] })
107                }
108
109                ProjectedUserTypesOp::Index => {
110                    for p in &mut projections {
111                        p.projs.push(ProjectionElem::Index(()))
112                    }
113                }
114                ProjectedUserTypesOp::Subslice { from, to } => {
115                    for p in &mut projections {
116                        p.projs.push(ProjectionElem::Subslice { from, to, from_end: true })
117                    }
118                }
119                ProjectedUserTypesOp::Deref => {
120                    for p in &mut projections {
121                        p.projs.push(ProjectionElem::Deref)
122                    }
123                }
124                ProjectedUserTypesOp::Leaf { field } => {
125                    for p in &mut projections {
126                        p.projs.push(ProjectionElem::Field(field, ()))
127                    }
128                }
129                ProjectedUserTypesOp::Variant { name, variant, field } => {
130                    for p in &mut projections {
131                        p.projs.push(ProjectionElem::Downcast(Some(name), variant));
132                        p.projs.push(ProjectionElem::Field(field, ()));
133                    }
134                }
135            }
136        }
137
138        Some(Box::new(UserTypeProjections { contents: projections }))
139    }
140}