rustc_mir_build/builder/custom/
mod.rs

1//! Provides the implementation of the `custom_mir` attribute.
2//!
3//! Up until MIR building, this attribute has absolutely no effect. The `mir!` macro is a normal
4//! decl macro that expands like any other, and the code goes through parsing, name resolution and
5//! type checking like all other code. In MIR building we finally detect whether this attribute is
6//! present, and if so we branch off into this module, which implements the attribute by
7//! implementing a custom lowering from THIR to MIR.
8//!
9//! The result of this lowering is returned "normally" from `build_mir`, with the only
10//! notable difference being that the `injected` field in the body is set. Various components of the
11//! MIR pipeline, like borrowck and the pass manager will then consult this field (via
12//! `body.should_skip()`) to skip the parts of the MIR pipeline that precede the MIR phase the user
13//! specified.
14//!
15//! This file defines the general framework for the custom parsing. The parsing for all the
16//! "top-level" constructs can be found in the `parse` submodule, while the parsing for statements,
17//! terminators, and everything below can be found in the `parse::instruction` submodule.
18//!
19
20use rustc_data_structures::fx::FxHashMap;
21use rustc_hir::def_id::DefId;
22use rustc_hir::{HirId, attrs};
23use rustc_index::{IndexSlice, IndexVec};
24use rustc_middle::bug;
25use rustc_middle::mir::*;
26use rustc_middle::thir::*;
27use rustc_middle::ty::{self, Ty, TyCtxt};
28use rustc_span::Span;
29
30mod parse;
31
32pub(super) fn build_custom_mir<'tcx>(
33    tcx: TyCtxt<'tcx>,
34    did: DefId,
35    hir_id: HirId,
36    thir: &Thir<'tcx>,
37    expr: ExprId,
38    params: &IndexSlice<ParamId, Param<'tcx>>,
39    return_ty: Ty<'tcx>,
40    return_ty_span: Span,
41    span: Span,
42    dialect: Option<attrs::MirDialect>,
43    phase: Option<attrs::MirPhase>,
44) -> Body<'tcx> {
45    let mut body = Body {
46        basic_blocks: BasicBlocks::new(IndexVec::new()),
47        source: MirSource::item(did),
48        phase: MirPhase::Built,
49        source_scopes: IndexVec::new(),
50        coroutine: None,
51        local_decls: IndexVec::new(),
52        user_type_annotations: IndexVec::new(),
53        arg_count: params.len(),
54        spread_arg: None,
55        var_debug_info: Vec::new(),
56        span,
57        required_consts: None,
58        mentioned_items: None,
59        is_polymorphic: false,
60        tainted_by_errors: None,
61        injection_phase: None,
62        pass_count: 0,
63        coverage_info_hi: None,
64        function_coverage_info: None,
65    };
66
67    body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));
68    body.basic_blocks_mut().push(BasicBlockData::new(None, false));
69    body.source_scopes.push(SourceScopeData {
70        span,
71        parent_scope: None,
72        inlined: None,
73        inlined_parent_scope: None,
74        local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }),
75    });
76    body.injection_phase = Some(parse_attribute(dialect, phase));
77
78    let mut pctxt = ParseCtxt {
79        tcx,
80        typing_env: body.typing_env(tcx),
81        thir,
82        source_scope: OUTERMOST_SOURCE_SCOPE,
83        body: &mut body,
84        local_map: FxHashMap::default(),
85        block_map: FxHashMap::default(),
86    };
87
88    let res: PResult<_> = try {
89        pctxt.parse_args(params)?;
90        pctxt.parse_body(expr)?;
91    };
92    if let Err(err) = res {
93        tcx.dcx().span_fatal(
94            err.span,
95            format!("Could not parse {}, found: {:?}", err.expected, err.item_description),
96        )
97    }
98
99    body
100}
101
102/// Turns the arguments passed to `#[custom_mir(..)]` into a proper
103/// [`MirPhase`]. Panics if this isn't possible for any reason.
104fn parse_attribute(dialect: Option<attrs::MirDialect>, phase: Option<attrs::MirPhase>) -> MirPhase {
105    let Some(dialect) = dialect else {
106        // Caught during attribute checking.
107        assert!(phase.is_none());
108        return MirPhase::Built;
109    };
110
111    match dialect {
112        attrs::MirDialect::Built => {
113            // Caught during attribute checking.
114            assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR");
115            MirPhase::Built
116        }
117        attrs::MirDialect::Analysis => match phase {
118            None | Some(attrs::MirPhase::Initial) => MirPhase::Analysis(AnalysisPhase::Initial),
119
120            Some(attrs::MirPhase::PostCleanup) => MirPhase::Analysis(AnalysisPhase::PostCleanup),
121
122            Some(attrs::MirPhase::Optimized) => {
123                // Caught during attribute checking.
124                bug!("`optimized` dialect is not compatible with the `analysis` dialect")
125            }
126        },
127
128        attrs::MirDialect::Runtime => match phase {
129            None | Some(attrs::MirPhase::Initial) => MirPhase::Runtime(RuntimePhase::Initial),
130            Some(attrs::MirPhase::PostCleanup) => MirPhase::Runtime(RuntimePhase::PostCleanup),
131            Some(attrs::MirPhase::Optimized) => MirPhase::Runtime(RuntimePhase::Optimized),
132        },
133    }
134}
135
136struct ParseCtxt<'a, 'tcx> {
137    tcx: TyCtxt<'tcx>,
138    typing_env: ty::TypingEnv<'tcx>,
139    thir: &'a Thir<'tcx>,
140    source_scope: SourceScope,
141    body: &'a mut Body<'tcx>,
142    local_map: FxHashMap<LocalVarId, Local>,
143    block_map: FxHashMap<LocalVarId, BasicBlock>,
144}
145
146struct ParseError {
147    span: Span,
148    item_description: String,
149    expected: String,
150}
151
152impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
153    fn expr_error(&self, expr: ExprId, expected: &'static str) -> ParseError {
154        let expr = &self.thir[expr];
155        ParseError {
156            span: expr.span,
157            item_description: format!("{:?}", expr.kind),
158            expected: expected.to_string(),
159        }
160    }
161
162    fn stmt_error(&self, stmt: StmtId, expected: &'static str) -> ParseError {
163        let stmt = &self.thir[stmt];
164        let span = match stmt.kind {
165            StmtKind::Expr { expr, .. } => self.thir[expr].span,
166            StmtKind::Let { span, .. } => span,
167        };
168        ParseError {
169            span,
170            item_description: format!("{:?}", stmt.kind),
171            expected: expected.to_string(),
172        }
173    }
174}
175
176type PResult<T> = Result<T, ParseError>;