about summary refs log tree commit diff
path: root/compiler/rustc_mir_build/src/builder/matches/user_ty.rs
blob: df9f93ac328a672bf9253769f2f6a4d8b73f7465 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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<Item = &'a ProjectedUserTypesOp> {
        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<Box<UserTypeProjections>> {
        // 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::<Vec<_>>();
        // 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 }))
    }
}