rustc_mir_build/builder/custom/
parse.rs

1use rustc_index::IndexSlice;
2use rustc_middle::mir::*;
3use rustc_middle::thir::*;
4use rustc_middle::ty::{self, Ty};
5use rustc_span::Span;
6
7use super::{PResult, ParseCtxt, ParseError};
8
9mod instruction;
10
11/// Helper macro for parsing custom MIR.
12///
13/// Example usage looks something like:
14/// ```rust,ignore (incomplete example)
15/// parse_by_kind!(
16///     self, // : &ParseCtxt
17///     expr_id, // what you're matching against
18///     "assignment", // the thing you're trying to parse
19///     @call("mir_assign", args) => { args[0] }, // match invocations of the `mir_assign` special function
20///     ExprKind::Assign { lhs, .. } => { lhs }, // match thir assignment expressions
21///     // no need for fallthrough case - reasonable error is automatically generated
22/// )
23/// ```
24macro_rules! parse_by_kind {
25    (
26        $self:ident,
27        $expr_id:expr,
28        $expr_name:pat,
29        $expected:literal,
30        $(
31            @call($name:ident, $args:ident) => $call_expr:expr,
32        )*
33        $(
34            @variant($adt:ident, $variant:ident) => $variant_expr:expr,
35        )*
36        $(
37            $pat:pat $(if $guard:expr)? => $expr:expr,
38        )*
39    ) => {{
40        let expr_id = $self.preparse($expr_id);
41        let expr = &$self.thir[expr_id];
42        tracing::debug!("Trying to parse {:?} as {}", expr.kind, $expected);
43        let $expr_name = expr;
44        match &expr.kind {
45            $(
46                ExprKind::Call { ty, fun: _, args: $args, .. } if {
47                    match ty.kind() {
48                        ty::FnDef(did, _) => {
49                            $self.tcx.is_diagnostic_item(rustc_span::sym::$name, *did)
50                        }
51                        _ => false,
52                    }
53                } => $call_expr,
54            )*
55            $(
56                ExprKind::Adt(box AdtExpr { adt_def, variant_index, .. }) if {
57                    $self.tcx.is_diagnostic_item(rustc_span::sym::$adt, adt_def.did()) &&
58                    adt_def.variants()[*variant_index].name == rustc_span::sym::$variant
59                } => $variant_expr,
60            )*
61            $(
62                $pat $(if $guard)? => $expr,
63            )*
64            #[allow(unreachable_patterns)]
65            _ => return Err($self.expr_error(expr_id, $expected))
66        }
67    }};
68}
69pub(crate) use parse_by_kind;
70
71impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
72    /// Expressions should only ever be matched on after preparsing them. This removes extra scopes
73    /// we don't care about.
74    fn preparse(&self, expr_id: ExprId) -> ExprId {
75        let expr = &self.thir[expr_id];
76        match expr.kind {
77            ExprKind::Scope { value, .. } => self.preparse(value),
78            _ => expr_id,
79        }
80    }
81
82    fn statement_as_expr(&self, stmt_id: StmtId) -> PResult<ExprId> {
83        match &self.thir[stmt_id].kind {
84            StmtKind::Expr { expr, .. } => Ok(*expr),
85            kind @ StmtKind::Let { pattern, .. } => Err(ParseError {
86                span: pattern.span,
87                item_description: format!("{kind:?}"),
88                expected: "expression".to_string(),
89            }),
90        }
91    }
92
93    pub(crate) fn parse_args(&mut self, params: &IndexSlice<ParamId, Param<'tcx>>) -> PResult<()> {
94        for param in params.iter() {
95            let (var, span) = {
96                let pat = param.pat.as_ref().unwrap();
97                match &pat.kind {
98                    PatKind::Binding { var, .. } => (*var, pat.span),
99                    _ => {
100                        return Err(ParseError {
101                            span: pat.span,
102                            item_description: format!("{:?}", pat.kind),
103                            expected: "local".to_string(),
104                        });
105                    }
106                }
107            };
108            let decl = LocalDecl::new(param.ty, span);
109            let local = self.body.local_decls.push(decl);
110            self.local_map.insert(var, local);
111        }
112
113        Ok(())
114    }
115
116    /// Bodies are of the form:
117    ///
118    /// ```text
119    /// {
120    ///     let bb1: BasicBlock;
121    ///     let bb2: BasicBlock;
122    ///     {
123    ///         let RET: _;
124    ///         let local1;
125    ///         let local2;
126    ///
127    ///         {
128    ///             { // entry block
129    ///                 statement1;
130    ///                 terminator1
131    ///             };
132    ///
133    ///             bb1 = {
134    ///                 statement2;
135    ///                 terminator2
136    ///             };
137    ///
138    ///             bb2 = {
139    ///                 statement3;
140    ///                 terminator3
141    ///             }
142    ///
143    ///             RET
144    ///         }
145    ///     }
146    /// }
147    /// ```
148    ///
149    /// This allows us to easily parse the basic blocks declarations, local declarations, and
150    /// basic block definitions in order.
151    pub(crate) fn parse_body(&mut self, expr_id: ExprId) -> PResult<()> {
152        let body = parse_by_kind!(self, expr_id, _, "whole body",
153            ExprKind::Block { block } => self.thir[*block].expr.unwrap(),
154        );
155        let (block_decls, rest) = parse_by_kind!(self, body, _, "body with block decls",
156            ExprKind::Block { block } => {
157                let block = &self.thir[*block];
158                (&block.stmts, block.expr.unwrap())
159            },
160        );
161        self.parse_block_decls(block_decls.iter().copied())?;
162
163        let (local_decls, rest) = parse_by_kind!(self, rest, _, "body with local decls",
164            ExprKind::Block { block } => {
165                let block = &self.thir[*block];
166                (&block.stmts, block.expr.unwrap())
167            },
168        );
169        self.parse_local_decls(local_decls.iter().copied())?;
170
171        let (debuginfo, rest) = parse_by_kind!(self, rest, _, "body with debuginfo",
172            ExprKind::Block { block } => {
173                let block = &self.thir[*block];
174                (&block.stmts, block.expr.unwrap())
175            },
176        );
177        self.parse_debuginfo(debuginfo.iter().copied())?;
178
179        let block_defs = parse_by_kind!(self, rest, _, "body with block defs",
180            ExprKind::Block { block } => &self.thir[*block].stmts,
181        );
182        for (i, block_def) in block_defs.iter().enumerate() {
183            let is_cleanup = self.body.basic_blocks_mut()[BasicBlock::from_usize(i)].is_cleanup;
184            let block = self.parse_block_def(self.statement_as_expr(*block_def)?, is_cleanup)?;
185            self.body.basic_blocks_mut()[BasicBlock::from_usize(i)] = block;
186        }
187
188        Ok(())
189    }
190
191    fn parse_block_decls(&mut self, stmts: impl Iterator<Item = StmtId>) -> PResult<()> {
192        for stmt in stmts {
193            self.parse_basic_block_decl(stmt)?;
194        }
195        Ok(())
196    }
197
198    fn parse_basic_block_decl(&mut self, stmt: StmtId) -> PResult<()> {
199        match &self.thir[stmt].kind {
200            StmtKind::Let { pattern, initializer: Some(initializer), .. } => {
201                let (var, ..) = self.parse_var(pattern)?;
202                let data = BasicBlockData::new(
203                    None,
204                    parse_by_kind!(self, *initializer, _, "basic block declaration",
205                        @variant(mir_basic_block, Normal) => false,
206                        @variant(mir_basic_block, Cleanup) => true,
207                    ),
208                );
209                let block = self.body.basic_blocks_mut().push(data);
210                self.block_map.insert(var, block);
211                Ok(())
212            }
213            _ => Err(self.stmt_error(stmt, "let statement with an initializer")),
214        }
215    }
216
217    fn parse_local_decls(&mut self, mut stmts: impl Iterator<Item = StmtId>) -> PResult<()> {
218        let (ret_var, ..) = self.parse_let_statement(stmts.next().unwrap())?;
219        self.local_map.insert(ret_var, Local::ZERO);
220
221        for stmt in stmts {
222            let (var, ty, span) = self.parse_let_statement(stmt)?;
223            let decl = LocalDecl::new(ty, span);
224            let local = self.body.local_decls.push(decl);
225            self.local_map.insert(var, local);
226        }
227
228        Ok(())
229    }
230
231    fn parse_debuginfo(&mut self, stmts: impl Iterator<Item = StmtId>) -> PResult<()> {
232        for stmt in stmts {
233            let stmt = &self.thir[stmt];
234            let expr = match stmt.kind {
235                StmtKind::Let { span, .. } => {
236                    return Err(ParseError {
237                        span,
238                        item_description: format!("{:?}", stmt),
239                        expected: "debuginfo".to_string(),
240                    });
241                }
242                StmtKind::Expr { expr, .. } => expr,
243            };
244            let span = self.thir[expr].span;
245            let (name, operand) = parse_by_kind!(self, expr, _, "debuginfo",
246                @call(mir_debuginfo, args) => {
247                    (args[0], args[1])
248                },
249            );
250            let name = parse_by_kind!(self, name, _, "debuginfo",
251                ExprKind::Literal { lit, neg: false } => lit,
252            );
253            let Some(name) = name.node.str() else {
254                return Err(ParseError {
255                    span,
256                    item_description: format!("{:?}", name),
257                    expected: "string".to_string(),
258                });
259            };
260            let operand = self.parse_operand(operand)?;
261            let value = match operand {
262                Operand::Constant(c) => VarDebugInfoContents::Const(*c),
263                Operand::Copy(p) | Operand::Move(p) => VarDebugInfoContents::Place(p),
264            };
265            let dbginfo = VarDebugInfo {
266                name,
267                source_info: SourceInfo { span, scope: self.source_scope },
268                composite: None,
269                argument_index: None,
270                value,
271            };
272            self.body.var_debug_info.push(dbginfo);
273        }
274
275        Ok(())
276    }
277
278    fn parse_let_statement(&mut self, stmt_id: StmtId) -> PResult<(LocalVarId, Ty<'tcx>, Span)> {
279        let pattern = match &self.thir[stmt_id].kind {
280            StmtKind::Let { pattern, .. } => pattern,
281            StmtKind::Expr { expr, .. } => {
282                return Err(self.expr_error(*expr, "let statement"));
283            }
284        };
285
286        self.parse_var(pattern)
287    }
288
289    fn parse_var(&mut self, mut pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> {
290        // Make sure we throw out any `AscribeUserType` we find
291        loop {
292            match &pat.kind {
293                PatKind::Binding { var, ty, .. } => break Ok((*var, *ty, pat.span)),
294                PatKind::AscribeUserType { subpattern, .. } => {
295                    pat = subpattern;
296                }
297                _ => {
298                    break Err(ParseError {
299                        span: pat.span,
300                        item_description: format!("{:?}", pat.kind),
301                        expected: "local".to_string(),
302                    });
303                }
304            }
305        }
306    }
307
308    fn parse_block_def(&self, expr_id: ExprId, is_cleanup: bool) -> PResult<BasicBlockData<'tcx>> {
309        let block = parse_by_kind!(self, expr_id, _, "basic block",
310            ExprKind::Block { block } => &self.thir[*block],
311        );
312
313        let mut data = BasicBlockData::new(None, is_cleanup);
314        for stmt_id in &*block.stmts {
315            let stmt = self.statement_as_expr(*stmt_id)?;
316            let span = self.thir[stmt].span;
317            let statement = self.parse_statement(stmt)?;
318            data.statements.push(Statement {
319                source_info: SourceInfo { span, scope: self.source_scope },
320                kind: statement,
321            });
322        }
323
324        let Some(trailing) = block.expr else { return Err(self.expr_error(expr_id, "terminator")) };
325        let span = self.thir[trailing].span;
326        let terminator = self.parse_terminator(trailing)?;
327        data.terminator = Some(Terminator {
328            source_info: SourceInfo { span, scope: self.source_scope },
329            kind: terminator,
330        });
331
332        Ok(data)
333    }
334}