Skip to main content

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::mir::*;
25use rustc_middle::thir::*;
26use rustc_middle::ty::{self, Ty, TyCtxt};
27use rustc_span::{Span, bug};
28
29mod parse;
30
31pub(super) fn build_custom_mir<'tcx>(
32    tcx: TyCtxt<'tcx>,
33    did: DefId,
34    hir_id: HirId,
35    thir: &Thir<'tcx>,
36    expr: ExprId,
37    params: &IndexSlice<ParamId, Param<'tcx>>,
38    return_ty: Ty<'tcx>,
39    return_ty_span: Span,
40    span: Span,
41    dialect: Option<attrs::MirDialect>,
42    phase: Option<attrs::MirPhase>,
43) -> Body<'tcx> {
44    let mut body = Body {
45        basic_blocks: BasicBlocks::new(IndexVec::new()),
46        source: MirSource::item(did),
47        phase: MirPhase::Built,
48        source_scopes: IndexVec::new(),
49        coroutine: None,
50        local_decls: IndexVec::new(),
51        user_type_annotations: IndexVec::new(),
52        arg_count: params.len(),
53        spread_arg: None,
54        var_debug_info: Vec::new(),
55        span,
56        required_consts: None,
57        mentioned_items: None,
58        is_polymorphic: false,
59        tainted_by_errors: None,
60        injection_phase: None,
61        pass_count: 0,
62        coverage_early_info: None,
63        coverage_mir_info: None,
64    };
65
66    body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));
67    body.basic_blocks_mut().push(BasicBlockData::new(None, false));
68    body.source_scopes.push(SourceScopeData {
69        span,
70        parent_scope: None,
71        inlined: None,
72        inlined_parent_scope: None,
73        local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }),
74    });
75    body.injection_phase = Some(parse_attribute(dialect, phase));
76
77    let mut pctxt = ParseCtxt {
78        tcx,
79        typing_env: body.typing_env(tcx),
80        thir,
81        source_scope: OUTERMOST_SOURCE_SCOPE,
82        body: &mut body,
83        local_map: FxHashMap::default(),
84        block_map: FxHashMap::default(),
85    };
86
87    let res = try {
88        pctxt.parse_args(params)?;
89        pctxt.parse_body(expr)?;
90    };
91    if let Err(err) = res {
92        tcx.dcx().span_fatal(
93            err.span,
94            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Could not parse {0}, found: {1:?}",
                err.expected, err.item_description))
    })format!("Could not parse {}, found: {:?}", err.expected, err.item_description),
95        )
96    }
97
98    body
99}
100
101/// Turns the arguments passed to `#[custom_mir(..)]` into a proper
102/// [`MirPhase`]. Panics if this isn't possible for any reason.
103fn parse_attribute(dialect: Option<attrs::MirDialect>, phase: Option<attrs::MirPhase>) -> MirPhase {
104    let Some(dialect) = dialect else {
105        // Caught during attribute checking.
106        if !phase.is_none() {
    ::core::panicking::panic("assertion failed: phase.is_none()")
};assert!(phase.is_none());
107        return MirPhase::Built;
108    };
109
110    match dialect {
111        attrs::MirDialect::Built => {
112            // Caught during attribute checking.
113            if !phase.is_none() {
    {
        ::core::panicking::panic_fmt(format_args!("Cannot specify a phase for `Built` MIR"));
    }
};assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR");
114            MirPhase::Built
115        }
116        attrs::MirDialect::Analysis => match phase {
117            None | Some(attrs::MirPhase::Initial) => MirPhase::Analysis(AnalysisPhase::Initial),
118
119            Some(attrs::MirPhase::PostCleanup) => MirPhase::Analysis(AnalysisPhase::PostCleanup),
120
121            Some(attrs::MirPhase::Optimized) => {
122                // Caught during attribute checking.
123                bug_impl(None,
    format_args!("`optimized` dialect is not compatible with the `analysis` dialect"),
    Location::caller())bug!("`optimized` dialect is not compatible with the `analysis` dialect")
124            }
125        },
126
127        attrs::MirDialect::Runtime => match phase {
128            None | Some(attrs::MirPhase::Initial) => MirPhase::Runtime(RuntimePhase::Initial),
129            Some(attrs::MirPhase::PostCleanup) => MirPhase::Runtime(RuntimePhase::PostCleanup),
130            Some(attrs::MirPhase::Optimized) => MirPhase::Runtime(RuntimePhase::Optimized),
131        },
132    }
133}
134
135struct ParseCtxt<'a, 'tcx> {
136    tcx: TyCtxt<'tcx>,
137    typing_env: ty::TypingEnv<'tcx>,
138    thir: &'a Thir<'tcx>,
139    source_scope: SourceScope,
140    body: &'a mut Body<'tcx>,
141    local_map: FxHashMap<LocalVarId, Local>,
142    block_map: FxHashMap<LocalVarId, BasicBlock>,
143}
144
145struct ParseError {
146    span: Span,
147    item_description: String,
148    expected: String,
149}
150
151impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
152    fn expr_error(&self, expr: ExprId, expected: &'static str) -> ParseError {
153        let expr = &self.thir[expr];
154        ParseError {
155            span: expr.span,
156            item_description: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}", expr.kind))
    })format!("{:?}", expr.kind),
157            expected: expected.to_string(),
158        }
159    }
160
161    fn stmt_error(&self, stmt: StmtId, expected: &'static str) -> ParseError {
162        let stmt = &self.thir[stmt];
163        let span = match stmt.kind {
164            StmtKind::Expr { expr, .. } => self.thir[expr].span,
165            StmtKind::Let { span, .. } => span,
166        };
167        ParseError {
168            span,
169            item_description: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}", stmt.kind))
    })format!("{:?}", stmt.kind),
170            expected: expected.to_string(),
171        }
172    }
173}
174
175type PResult<T> = Result<T, ParseError>;