| //! Provides the implementation of the `custom_mir` attribute. | |
| //! | |
| //! Up until MIR building, this attribute has absolutely no effect. The `mir!` macro is a normal | |
| //! decl macro that expands like any other, and the code goes through parsing, name resolution and | |
| //! type checking like all other code. In MIR building we finally detect whether this attribute is | |
| //! present, and if so we branch off into this module, which implements the attribute by | |
| //! implementing a custom lowering from THIR to MIR. | |
| //! | |
| //! The result of this lowering is returned "normally" from `build_mir`, with the only | |
| //! notable difference being that the `injected` field in the body is set. Various components of the | |
| //! MIR pipeline, like borrowck and the pass manager will then consult this field (via | |
| //! `body.should_skip()`) to skip the parts of the MIR pipeline that precede the MIR phase the user | |
| //! specified. | |
| //! | |
| //! This file defines the general framework for the custom parsing. The parsing for all the | |
| //! "top-level" constructs can be found in the `parse` submodule, while the parsing for statements, | |
| //! terminators, and everything below can be found in the `parse::instruction` submodule. | |
| //! | |
| use rustc_data_structures::fx::FxHashMap; | |
| use rustc_hir::def_id::DefId; | |
| use rustc_hir::{HirId, attrs}; | |
| use rustc_index::{IndexSlice, IndexVec}; | |
| use rustc_middle::bug; | |
| use rustc_middle::mir::*; | |
| use rustc_middle::thir::*; | |
| use rustc_middle::ty::{self, Ty, TyCtxt}; | |
| use rustc_span::Span; | |
| mod parse; | |
| pub(super) fn build_custom_mir<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| did: DefId, | |
| hir_id: HirId, | |
| thir: &Thir<'tcx>, | |
| expr: ExprId, | |
| params: &IndexSlice<ParamId, Param<'tcx>>, | |
| return_ty: Ty<'tcx>, | |
| return_ty_span: Span, | |
| span: Span, | |
| dialect: Option<attrs::MirDialect>, | |
| phase: Option<attrs::MirPhase>, | |
| ) -> Body<'tcx> { | |
| let mut body = Body { | |
| basic_blocks: BasicBlocks::new(IndexVec::new()), | |
| source: MirSource::item(did), | |
| phase: MirPhase::Built, | |
| source_scopes: IndexVec::new(), | |
| coroutine: None, | |
| local_decls: IndexVec::new(), | |
| user_type_annotations: IndexVec::new(), | |
| arg_count: params.len(), | |
| spread_arg: None, | |
| var_debug_info: Vec::new(), | |
| span, | |
| required_consts: None, | |
| mentioned_items: None, | |
| is_polymorphic: false, | |
| tainted_by_errors: None, | |
| injection_phase: None, | |
| pass_count: 0, | |
| coverage_info_hi: None, | |
| function_coverage_info: None, | |
| }; | |
| body.local_decls.push(LocalDecl::new(return_ty, return_ty_span)); | |
| body.basic_blocks_mut().push(BasicBlockData::new(None, false)); | |
| body.source_scopes.push(SourceScopeData { | |
| span, | |
| parent_scope: None, | |
| inlined: None, | |
| inlined_parent_scope: None, | |
| local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }), | |
| }); | |
| body.injection_phase = Some(parse_attribute(dialect, phase)); | |
| let mut pctxt = ParseCtxt { | |
| tcx, | |
| typing_env: body.typing_env(tcx), | |
| thir, | |
| source_scope: OUTERMOST_SOURCE_SCOPE, | |
| body: &mut body, | |
| local_map: FxHashMap::default(), | |
| block_map: FxHashMap::default(), | |
| }; | |
| let res = try { | |
| pctxt.parse_args(params)?; | |
| pctxt.parse_body(expr)?; | |
| }; | |
| if let Err(err) = res { | |
| tcx.dcx().span_fatal( | |
| err.span, | |
| format!("Could not parse {}, found: {:?}", err.expected, err.item_description), | |
| ) | |
| } | |
| body | |
| } | |
| /// Turns the arguments passed to `#[custom_mir(..)]` into a proper | |
| /// [`MirPhase`]. Panics if this isn't possible for any reason. | |
| fn parse_attribute(dialect: Option<attrs::MirDialect>, phase: Option<attrs::MirPhase>) -> MirPhase { | |
| let Some(dialect) = dialect else { | |
| // Caught during attribute checking. | |
| assert!(phase.is_none()); | |
| return MirPhase::Built; | |
| }; | |
| match dialect { | |
| attrs::MirDialect::Built => { | |
| // Caught during attribute checking. | |
| assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR"); | |
| MirPhase::Built | |
| } | |
| attrs::MirDialect::Analysis => match phase { | |
| None | Some(attrs::MirPhase::Initial) => MirPhase::Analysis(AnalysisPhase::Initial), | |
| Some(attrs::MirPhase::PostCleanup) => MirPhase::Analysis(AnalysisPhase::PostCleanup), | |
| Some(attrs::MirPhase::Optimized) => { | |
| // Caught during attribute checking. | |
| bug!("`optimized` dialect is not compatible with the `analysis` dialect") | |
| } | |
| }, | |
| attrs::MirDialect::Runtime => match phase { | |
| None | Some(attrs::MirPhase::Initial) => MirPhase::Runtime(RuntimePhase::Initial), | |
| Some(attrs::MirPhase::PostCleanup) => MirPhase::Runtime(RuntimePhase::PostCleanup), | |
| Some(attrs::MirPhase::Optimized) => MirPhase::Runtime(RuntimePhase::Optimized), | |
| }, | |
| } | |
| } | |
| struct ParseCtxt<'a, 'tcx> { | |
| tcx: TyCtxt<'tcx>, | |
| typing_env: ty::TypingEnv<'tcx>, | |
| thir: &'a Thir<'tcx>, | |
| source_scope: SourceScope, | |
| body: &'a mut Body<'tcx>, | |
| local_map: FxHashMap<LocalVarId, Local>, | |
| block_map: FxHashMap<LocalVarId, BasicBlock>, | |
| } | |
| struct ParseError { | |
| span: Span, | |
| item_description: String, | |
| expected: String, | |
| } | |
| impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { | |
| fn expr_error(&self, expr: ExprId, expected: &'static str) -> ParseError { | |
| let expr = &self.thir[expr]; | |
| ParseError { | |
| span: expr.span, | |
| item_description: format!("{:?}", expr.kind), | |
| expected: expected.to_string(), | |
| } | |
| } | |
| fn stmt_error(&self, stmt: StmtId, expected: &'static str) -> ParseError { | |
| let stmt = &self.thir[stmt]; | |
| let span = match stmt.kind { | |
| StmtKind::Expr { expr, .. } => self.thir[expr].span, | |
| StmtKind::Let { span, .. } => span, | |
| }; | |
| ParseError { | |
| span, | |
| item_description: format!("{:?}", stmt.kind), | |
| expected: expected.to_string(), | |
| } | |
| } | |
| } | |
| type PResult<T> = Result<T, ParseError>; |