1 //! Provides the implementation of the `custom_mir` attribute.
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.
9 //! The result of this lowering is returned "normally" from the `mir_built` query, 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
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.
20 use rustc_ast::Attribute;
21 use rustc_data_structures::fx::FxHashMap;
22 use rustc_hir::def_id::DefId;
24 use rustc_index::vec::IndexVec;
28 ty::{ParamEnv, Ty, TyCtxt},
34 pub(super) fn build_custom_mir<'tcx>(
40 params: &IndexVec<ParamId, Param<'tcx>>,
47 basic_blocks: BasicBlocks::new(IndexVec::new()),
48 source: MirSource::item(did),
49 phase: MirPhase::Built,
50 source_scopes: IndexVec::new(),
52 local_decls: LocalDecls::new(),
53 user_type_annotations: IndexVec::new(),
54 arg_count: params.len(),
56 var_debug_info: Vec::new(),
58 required_consts: Vec::new(),
59 is_polymorphic: false,
60 tainted_by_errors: None,
61 injection_phase: None,
65 body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));
66 body.basic_blocks_mut().push(BasicBlockData::new(None));
67 body.source_scopes.push(SourceScopeData {
71 inlined_parent_scope: None,
72 local_data: ClearCrossCrate::Set(SourceScopeLocalData {
77 body.injection_phase = Some(parse_attribute(attr));
79 let mut pctxt = ParseCtxt {
81 param_env: tcx.param_env(did),
83 source_scope: OUTERMOST_SOURCE_SCOPE,
85 local_map: FxHashMap::default(),
86 block_map: FxHashMap::default(),
89 let res: PResult<_> = try {
90 pctxt.parse_args(¶ms)?;
91 pctxt.parse_body(expr)?;
93 if let Err(err) = res {
94 tcx.sess.diagnostic().span_fatal(
96 format!("Could not parse {}, found: {:?}", err.expected, err.item_description),
103 fn parse_attribute(attr: &Attribute) -> MirPhase {
104 let meta_items = attr.meta_item_list().unwrap();
105 let mut dialect: Option<String> = None;
106 let mut phase: Option<String> = None;
108 for nested in meta_items {
109 let name = nested.name_or_empty();
110 let value = nested.value_str().unwrap().as_str().to_string();
111 match name.as_str() {
113 assert!(dialect.is_none());
114 dialect = Some(value);
117 assert!(phase.is_none());
121 panic!("Unexpected key {}", other);
126 let Some(dialect) = dialect else {
127 assert!(phase.is_none());
128 return MirPhase::Built;
131 MirPhase::parse(dialect, phase)
134 struct ParseCtxt<'tcx, 'body> {
136 param_env: ParamEnv<'tcx>,
137 thir: &'body Thir<'tcx>,
138 source_scope: SourceScope,
140 body: &'body mut Body<'tcx>,
141 local_map: FxHashMap<LocalVarId, Local>,
142 block_map: FxHashMap<LocalVarId, BasicBlock>,
147 item_description: String,
151 impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
152 fn expr_error(&self, expr: ExprId, expected: &'static str) -> ParseError {
153 let expr = &self.thir[expr];
156 item_description: format!("{:?}", expr.kind),
157 expected: expected.to_string(),
162 type PResult<T> = Result<T, ParseError>;