]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/custom/mod.rs
Rollup merge of #105174 - chenyukang:yukang/fix-105028-unused, r=eholk
[rust.git] / compiler / rustc_mir_build / src / build / 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 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
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
20 use rustc_ast::Attribute;
21 use rustc_data_structures::fx::FxHashMap;
22 use rustc_hir::def_id::DefId;
23 use rustc_index::vec::IndexVec;
24 use rustc_middle::{
25     mir::*,
26     thir::*,
27     ty::{Ty, TyCtxt},
28 };
29 use rustc_span::Span;
30
31 mod parse;
32
33 pub(super) fn build_custom_mir<'tcx>(
34     tcx: TyCtxt<'tcx>,
35     did: DefId,
36     thir: &Thir<'tcx>,
37     expr: ExprId,
38     params: &IndexVec<ParamId, Param<'tcx>>,
39     return_ty: Ty<'tcx>,
40     return_ty_span: Span,
41     span: Span,
42     attr: &Attribute,
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         generator: None,
50         local_decls: LocalDecls::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: Vec::new(),
57         is_polymorphic: false,
58         tainted_by_errors: None,
59         injection_phase: None,
60         pass_count: 0,
61     };
62
63     body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));
64     body.basic_blocks_mut().push(BasicBlockData::new(None));
65     body.source_scopes.push(SourceScopeData {
66         span,
67         parent_scope: None,
68         inlined: None,
69         inlined_parent_scope: None,
70         local_data: ClearCrossCrate::Clear,
71     });
72     body.injection_phase = Some(parse_attribute(attr));
73
74     let mut pctxt = ParseCtxt {
75         tcx,
76         thir,
77         source_scope: OUTERMOST_SOURCE_SCOPE,
78         body: &mut body,
79         local_map: FxHashMap::default(),
80         block_map: FxHashMap::default(),
81     };
82
83     let res = (|| {
84         pctxt.parse_args(&params)?;
85         pctxt.parse_body(expr)
86     })();
87     if let Err(err) = res {
88         tcx.sess.diagnostic().span_fatal(
89             err.span,
90             format!("Could not parse {}, found: {:?}", err.expected, err.item_description),
91         )
92     }
93
94     body
95 }
96
97 fn parse_attribute(attr: &Attribute) -> MirPhase {
98     let meta_items = attr.meta_item_list().unwrap();
99     let mut dialect: Option<String> = None;
100     let mut phase: Option<String> = None;
101
102     for nested in meta_items {
103         let name = nested.name_or_empty();
104         let value = nested.value_str().unwrap().as_str().to_string();
105         match name.as_str() {
106             "dialect" => {
107                 assert!(dialect.is_none());
108                 dialect = Some(value);
109             }
110             "phase" => {
111                 assert!(phase.is_none());
112                 phase = Some(value);
113             }
114             other => {
115                 panic!("Unexpected key {}", other);
116             }
117         }
118     }
119
120     let Some(dialect) = dialect else {
121         assert!(phase.is_none());
122         return MirPhase::Built;
123     };
124
125     MirPhase::parse(dialect, phase)
126 }
127
128 struct ParseCtxt<'tcx, 'body> {
129     tcx: TyCtxt<'tcx>,
130     thir: &'body Thir<'tcx>,
131     source_scope: SourceScope,
132
133     body: &'body mut Body<'tcx>,
134     local_map: FxHashMap<LocalVarId, Local>,
135     block_map: FxHashMap<LocalVarId, BasicBlock>,
136 }
137
138 struct ParseError {
139     span: Span,
140     item_description: String,
141     expected: String,
142 }
143
144 impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
145     fn expr_error(&self, expr: ExprId, expected: &'static str) -> ParseError {
146         let expr = &self.thir[expr];
147         ParseError {
148             span: expr.span,
149             item_description: format!("{:?}", expr.kind),
150             expected: expected.to_string(),
151         }
152     }
153 }
154
155 type PResult<T> = Result<T, ParseError>;