Skip to main content

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