//! 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>, return_ty: Ty<'tcx>, return_ty_span: Span, span: Span, dialect: Option, phase: Option, ) -> 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: PResult<_> = 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, phase: Option) -> 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, block_map: FxHashMap, } 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 = Result;