]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/custom/mod.rs
Auto merge of #105650 - cassaundra:float-literal-suggestion, r=pnkfelix
[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_hir::HirId;
24 use rustc_index::vec::IndexVec;
25 use rustc_middle::{
26     mir::*,
27     thir::*,
28     ty::{ParamEnv, Ty, TyCtxt},
29 };
30 use rustc_span::Span;
31
32 mod parse;
33
34 pub(super) fn build_custom_mir<'tcx>(
35     tcx: TyCtxt<'tcx>,
36     did: DefId,
37     hir_id: HirId,
38     thir: &Thir<'tcx>,
39     expr: ExprId,
40     params: &IndexVec<ParamId, Param<'tcx>>,
41     return_ty: Ty<'tcx>,
42     return_ty_span: Span,
43     span: Span,
44     attr: &Attribute,
45 ) -> Body<'tcx> {
46     let mut body = Body {
47         basic_blocks: BasicBlocks::new(IndexVec::new()),
48         source: MirSource::item(did),
49         phase: MirPhase::Built,
50         source_scopes: IndexVec::new(),
51         generator: None,
52         local_decls: LocalDecls::new(),
53         user_type_annotations: IndexVec::new(),
54         arg_count: params.len(),
55         spread_arg: None,
56         var_debug_info: Vec::new(),
57         span,
58         required_consts: Vec::new(),
59         is_polymorphic: false,
60         tainted_by_errors: None,
61         injection_phase: None,
62         pass_count: 0,
63     };
64
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 {
68         span,
69         parent_scope: None,
70         inlined: None,
71         inlined_parent_scope: None,
72         local_data: ClearCrossCrate::Set(SourceScopeLocalData {
73             lint_root: hir_id,
74             safety: Safety::Safe,
75         }),
76     });
77     body.injection_phase = Some(parse_attribute(attr));
78
79     let mut pctxt = ParseCtxt {
80         tcx,
81         param_env: tcx.param_env(did),
82         thir,
83         source_scope: OUTERMOST_SOURCE_SCOPE,
84         body: &mut body,
85         local_map: FxHashMap::default(),
86         block_map: FxHashMap::default(),
87     };
88
89     let res: PResult<_> = try {
90         pctxt.parse_args(&params)?;
91         pctxt.parse_body(expr)?;
92     };
93     if let Err(err) = res {
94         tcx.sess.diagnostic().span_fatal(
95             err.span,
96             format!("Could not parse {}, found: {:?}", err.expected, err.item_description),
97         )
98     }
99
100     body
101 }
102
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;
107
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() {
112             "dialect" => {
113                 assert!(dialect.is_none());
114                 dialect = Some(value);
115             }
116             "phase" => {
117                 assert!(phase.is_none());
118                 phase = Some(value);
119             }
120             other => {
121                 panic!("Unexpected key {}", other);
122             }
123         }
124     }
125
126     let Some(dialect) = dialect else {
127         assert!(phase.is_none());
128         return MirPhase::Built;
129     };
130
131     MirPhase::parse(dialect, phase)
132 }
133
134 struct ParseCtxt<'tcx, 'body> {
135     tcx: TyCtxt<'tcx>,
136     param_env: ParamEnv<'tcx>,
137     thir: &'body Thir<'tcx>,
138     source_scope: SourceScope,
139
140     body: &'body mut Body<'tcx>,
141     local_map: FxHashMap<LocalVarId, Local>,
142     block_map: FxHashMap<LocalVarId, BasicBlock>,
143 }
144
145 struct ParseError {
146     span: Span,
147     item_description: String,
148     expected: String,
149 }
150
151 impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
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: format!("{:?}", expr.kind),
157             expected: expected.to_string(),
158         }
159     }
160 }
161
162 type PResult<T> = Result<T, ParseError>;