Port rustc_mir to attribute parser

This commit is contained in:
Jamie Hill-Daniel 2026-01-30 03:06:46 +00:00
parent 46c86aef65
commit b668057d79
13 changed files with 186 additions and 161 deletions

View file

@ -4338,11 +4338,11 @@ dependencies = [
"polonius-engine",
"regex",
"rustc_abi",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_graphviz",
"rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_middle",

View file

@ -1,5 +1,7 @@
use std::path::PathBuf;
use rustc_ast::{LitIntType, LitKind, MetaItemLit};
use rustc_hir::attrs::RustcLayoutType;
use rustc_hir::attrs::{BorrowckGraphvizFormatKind, RustcLayoutType, RustcMirKind};
use rustc_session::errors;
use super::prelude::*;
@ -357,7 +359,6 @@ impl<S: Stage> CombineAttributeParser<S> for RustcLayoutParser {
const TEMPLATE: AttributeTemplate =
template!(List: &["abi", "align", "size", "homogenous_aggregate", "debug"]);
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
@ -397,6 +398,94 @@ impl<S: Stage> CombineAttributeParser<S> for RustcLayoutParser {
}
}
pub(crate) struct RustcMirParser;
impl<S: Stage> CombineAttributeParser<S> for RustcMirParser {
const PATH: &[rustc_span::Symbol] = &[sym::rustc_mir];
type Item = RustcMirKind;
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::RustcMir(items);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::Method(MethodKind::Trait { body: true })),
]);
const TEMPLATE: AttributeTemplate = template!(List: &["arg1, arg2, ..."]);
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
return ThinVec::new();
};
list.mixed()
.filter_map(|arg| arg.meta_item())
.filter_map(|mi| {
if let Some(ident) = mi.ident() {
match ident.name {
sym::rustc_peek_maybe_init => Some(RustcMirKind::PeekMaybeInit),
sym::rustc_peek_maybe_uninit => Some(RustcMirKind::PeekMaybeUninit),
sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness),
sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow),
sym::borrowck_graphviz_postflow => {
let Some(nv) = mi.args().name_value() else {
cx.expected_name_value(
mi.span(),
Some(sym::borrowck_graphviz_postflow),
);
return None;
};
let Some(path) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, None);
return None;
};
let path = PathBuf::from(path.to_string());
if path.file_name().is_some() {
Some(RustcMirKind::BorrowckGraphvizPostflow { path })
} else {
cx.expected_filename_literal(nv.value_span);
None
}
}
sym::borrowck_graphviz_format => {
let Some(nv) = mi.args().name_value() else {
cx.expected_name_value(
mi.span(),
Some(sym::borrowck_graphviz_format),
);
return None;
};
let Some(format) = nv.value_as_ident() else {
cx.expected_identifier(nv.value_span);
return None;
};
match format.name {
sym::two_phase => Some(RustcMirKind::BorrowckGraphvizFormat {
format: BorrowckGraphvizFormatKind::TwoPhase,
}),
_ => {
cx.expected_specific_argument(format.span, &[sym::two_phase]);
None
}
}
}
_ => None,
}
} else {
None
}
})
.collect()
}
}
pub(crate) struct RustcNonConstTraitMethodParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcNonConstTraitMethodParser {

View file

@ -81,7 +81,7 @@ use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser,
RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser,
RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser,
RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser,
RustcMirParser, RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser,
RustcNoImplicitAutorefsParser, RustcNonConstTraitMethodParser, RustcNounwindParser,
RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser,
RustcSimdMonomorphizeLaneLimitParser,
@ -202,6 +202,7 @@ attribute_parsers!(
Combine<LinkParser>,
Combine<ReprParser>,
Combine<RustcLayoutParser>,
Combine<RustcMirParser>,
Combine<TargetFeatureParser>,
Combine<UnstableFeatureBoundParser>,
// tidy-alphabetical-end
@ -517,6 +518,11 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
)
}
/// Error that a filename string literal was expected.
pub(crate) fn expected_filename_literal(&self, span: Span) {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedFilenameLiteral);
}
pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral)
}

View file

@ -524,6 +524,7 @@ pub(crate) enum AttributeParseErrorReason<'a> {
ExpectedStringLiteral {
byte_string: Option<Span>,
},
ExpectedFilenameLiteral,
ExpectedIntegerLiteral,
ExpectedIntegerLiteralInRange {
lower_bound: isize,
@ -597,6 +598,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
diag.span_label(self.span, "expected a string literal here");
}
}
AttributeParseErrorReason::ExpectedFilenameLiteral => {
diag.span_label(self.span, "expected a filename string literal here");
}
AttributeParseErrorReason::ExpectedIntegerLiteral => {
diag.span_label(self.span, "expected an integer literal here");
}

View file

@ -699,6 +699,21 @@ pub enum RustcLayoutType {
Debug,
}
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq, Eq)]
pub enum RustcMirKind {
PeekMaybeInit,
PeekMaybeUninit,
PeekLiveness,
StopAfterDataflow,
BorrowckGraphvizPostflow { path: PathBuf },
BorrowckGraphvizFormat { format: BorrowckGraphvizFormatKind },
}
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq, Eq)]
pub enum BorrowckGraphvizFormatKind {
TwoPhase,
}
/// Represents parsed *built-in* inert attributes.
///
/// ## Overview
@ -1090,6 +1105,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_main]`.
RustcMain,
/// Represents `#[rustc_mir]`.
RustcMir(ThinVec<RustcMirKind>),
/// Represents `#[rustc_must_implement_one_of]`
RustcMustImplementOneOf { attr_span: Span, fn_names: ThinVec<Ident> },

View file

@ -122,6 +122,7 @@ impl AttributeKind {
RustcLintUntrackedQueryInformation => Yes,
RustcMacroTransparency(..) => Yes,
RustcMain => No,
RustcMir(..) => Yes,
RustcMustImplementOneOf { .. } => No,
RustcNeverReturnsNullPointer => Yes,
RustcNoImplicitAutorefs => Yes,

View file

@ -1,5 +1,6 @@
use std::num::NonZero;
use std::ops::Deref;
use std::path::PathBuf;
use rustc_abi::Align;
use rustc_ast::attr::data_structures::CfgEntry;
@ -96,7 +97,15 @@ impl<T: PrintAttribute> PrintAttribute for FxIndexMap<T, Span> {
p.word("]");
}
}
impl PrintAttribute for PathBuf {
fn should_render(&self) -> bool {
true
}
fn print_attribute(&self, p: &mut Printer) {
p.word(self.display().to_string());
}
}
macro_rules! print_skip {
($($t: ty),* $(,)?) => {$(
impl PrintAttribute for $t {

View file

@ -8,11 +8,11 @@ edition = "2024"
polonius-engine = "0.13.0"
regex = "1"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_graphviz = { path = "../rustc_graphviz" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }

View file

@ -1,9 +1,3 @@
mir_dataflow_duplicate_values_for =
duplicate values for `{$name}`
mir_dataflow_path_must_end_in_filename =
path must end in a filename
mir_dataflow_peek_argument_not_a_local =
rustc_peek: argument was not a local
@ -19,11 +13,5 @@ mir_dataflow_peek_must_be_not_temporary =
mir_dataflow_peek_must_be_place_or_ref_place =
rustc_peek: argument expression must be either `place` or `&place`
mir_dataflow_requires_an_argument =
`{$name}` requires an argument
mir_dataflow_stop_after_dataflow_ended_compilation =
stop_after_dataflow ended compilation
mir_dataflow_unknown_formatter =
unknown formatter

View file

@ -1,35 +1,5 @@
use rustc_macros::Diagnostic;
use rustc_span::{Span, Symbol};
#[derive(Diagnostic)]
#[diag(mir_dataflow_path_must_end_in_filename)]
pub(crate) struct PathMustEndInFilename {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_dataflow_unknown_formatter)]
pub(crate) struct UnknownFormatter {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_dataflow_duplicate_values_for)]
pub(crate) struct DuplicateValuesFor {
#[primary_span]
pub span: Span,
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag(mir_dataflow_requires_an_argument)]
pub(crate) struct RequiresAnArgument {
#[primary_span]
pub span: Span,
pub name: Symbol,
}
use rustc_span::Span;
#[derive(Diagnostic)]
#[diag(mir_dataflow_stop_after_dataflow_ended_compilation)]

View file

@ -7,6 +7,9 @@ use std::sync::OnceLock;
use std::{io, ops, str};
use regex::Regex;
use rustc_graphviz as dot;
use rustc_hir::attrs::{AttributeKind, BorrowckGraphvizFormatKind, RustcMirKind};
use rustc_hir::find_attr;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::mir::{
self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal,
@ -14,17 +17,12 @@ use rustc_middle::mir::{
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_span::def_id::DefId;
use rustc_span::{Symbol, sym};
use tracing::debug;
use {rustc_ast as ast, rustc_graphviz as dot};
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
use super::{
Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor, visit_results,
};
use crate::errors::{
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
};
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
@ -43,10 +41,7 @@ where
use std::io::Write;
let def_id = body.source.def_id();
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
return Ok(());
};
let attrs = RustcMirAttrs::parse(tcx, def_id);
let file = try {
match attrs.output_path(A::NAME) {
@ -72,10 +67,7 @@ where
Err(e) => return Err(e),
};
let style = match attrs.formatter {
Some(sym::two_phase) => OutputStyle::BeforeAndAfter,
_ => OutputStyle::AfterOnly,
};
let style = attrs.formatter.unwrap_or(OutputStyle::AfterOnly);
let mut buf = Vec::new();
@ -98,71 +90,33 @@ where
#[derive(Default)]
struct RustcMirAttrs {
basename_and_suffix: Option<PathBuf>,
formatter: Option<Symbol>,
formatter: Option<OutputStyle>,
}
impl RustcMirAttrs {
fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
let mut result = Ok(());
fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Self {
let mut ret = RustcMirAttrs::default();
let rustc_mir_attrs = tcx
.get_attrs(def_id, sym::rustc_mir)
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
for attr in rustc_mir_attrs {
let attr_result = match attr.name() {
Some(name @ sym::borrowck_graphviz_postflow) => {
Self::set_field(&mut ret.basename_and_suffix, tcx, name, &attr, |s| {
let path = PathBuf::from(s.to_string());
match path.file_name() {
Some(_) => Ok(path),
None => {
tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() });
Err(())
let attrs = tcx.get_all_attrs(def_id);
if let Some(rustc_mir_attrs) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) {
for attr in rustc_mir_attrs {
match attr {
RustcMirKind::BorrowckGraphvizPostflow { path } => {
ret.basename_and_suffix = Some(path.clone());
}
RustcMirKind::BorrowckGraphvizFormat { format } => {
ret.formatter = match format {
BorrowckGraphvizFormatKind::TwoPhase => {
Some(OutputStyle::BeforeAndAfter)
}
}
})
}
Some(name @ sym::borrowck_graphviz_format) => {
Self::set_field(&mut ret.formatter, tcx, name, &attr, |s| match s {
sym::two_phase => Ok(s),
_ => {
tcx.dcx().emit_err(UnknownFormatter { span: attr.span() });
Err(())
}
})
}
_ => Ok(()),
};
result = result.and(attr_result);
};
}
_ => (),
};
}
}
result.map(|()| ret)
}
fn set_field<T>(
field: &mut Option<T>,
tcx: TyCtxt<'_>,
name: Symbol,
attr: &ast::MetaItemInner,
mapper: impl FnOnce(Symbol) -> Result<T, ()>,
) -> Result<(), ()> {
if field.is_some() {
tcx.dcx().emit_err(DuplicateValuesFor { span: attr.span(), name });
return Err(());
}
if let Some(s) = attr.value_str() {
*field = Some(mapper(s)?);
Ok(())
} else {
tcx.dcx()
.emit_err(RequiresAnArgument { span: attr.span(), name: attr.name().unwrap() });
Err(())
}
ret
}
/// Returns the path where dataflow results should be written, or `None`

View file

@ -1,8 +1,8 @@
use rustc_ast::MetaItem;
use rustc_hir::attrs::{AttributeKind, RustcMirKind};
use rustc_hir::find_attr;
use rustc_middle::mir::{self, Body, Local, Location};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_span::{Span, Symbol, sym};
use rustc_span::{Span, sym};
use tracing::{debug, info};
use crate::errors::{
@ -14,52 +14,37 @@ use crate::impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPl
use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
use crate::{Analysis, JoinSemiLattice, ResultsCursor};
fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option<MetaItem> {
for attr in tcx.get_attrs(def_id, sym::rustc_mir) {
let items = attr.meta_item_list();
for item in items.iter().flat_map(|l| l.iter()) {
match item.meta_item() {
Some(mi) if mi.has_name(name) => return Some(mi.clone()),
_ => continue,
}
}
}
None
}
pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let def_id = body.source.def_id();
if !tcx.has_attr(def_id, sym::rustc_mir) {
debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
return;
} else {
let attrs = tcx.get_all_attrs(def_id);
if let Some(kind) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) {
let move_data = MoveData::gather_moves(body, tcx, |_| true);
debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
}
if kind.contains(&RustcMirKind::PeekMaybeInit) {
let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body);
sanity_check_via_rustc_peek(tcx, flow_inits);
}
let move_data = MoveData::gather_moves(body, tcx, |_| true);
if kind.contains(&RustcMirKind::PeekMaybeUninit) {
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data)
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body);
sanity_check_via_rustc_peek(tcx, flow_uninits);
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body);
sanity_check_via_rustc_peek(tcx, flow_inits);
}
if kind.contains(&RustcMirKind::PeekLiveness) {
let flow_liveness =
MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
sanity_check_via_rustc_peek(tcx, flow_liveness);
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() {
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data)
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body);
sanity_check_via_rustc_peek(tcx, flow_uninits);
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
let flow_liveness =
MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
sanity_check_via_rustc_peek(tcx, flow_liveness);
}
if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation);
if kind.contains(&RustcMirKind::StopAfterDataflow) {
tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation);
}
} else {
debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
}
}

View file

@ -309,6 +309,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::RustcLintUntrackedQueryInformation
| AttributeKind::RustcMacroTransparency(_)
| AttributeKind::RustcMain
| AttributeKind::RustcMir(_)
| AttributeKind::RustcNeverReturnsNullPointer
| AttributeKind::RustcNoImplicitAutorefs
| AttributeKind::RustcNonConstTraitMethod