diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c315..ffd11e46d98b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 250bceecbd654..8961dc47706f7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -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 CombineAttributeParser 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 CombineAttributeParser for RustcLayoutParser { } } +pub(crate) struct RustcMirParser; + +impl CombineAttributeParser for RustcMirParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_mir]; + + type Item = RustcMirKind; + + const CONVERT: ConvertFn = |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 { + 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 NoArgsAttributeParser for RustcNonConstTraitMethodParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f340b393567ec..322e189e6d123 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -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, Combine, Combine, + Combine, Combine, Combine, // 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) } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f9748542beb94..e67fc95e28062 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -524,6 +524,7 @@ pub(crate) enum AttributeParseErrorReason<'a> { ExpectedStringLiteral { byte_string: Option, }, + 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"); } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fba53c913e16a..6138ffc8d9544 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -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), + /// Represents `#[rustc_must_implement_one_of]` RustcMustImplementOneOf { attr_span: Span, fn_names: ThinVec }, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 26e5fc6e62579..7ec1920152a5e 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -122,6 +122,7 @@ impl AttributeKind { RustcLintUntrackedQueryInformation => Yes, RustcMacroTransparency(..) => Yes, RustcMain => No, + RustcMir(..) => Yes, RustcMustImplementOneOf { .. } => No, RustcNeverReturnsNullPointer => Yes, RustcNoImplicitAutorefs => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index c2ad644688fce..bd268d2c423f7 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -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 PrintAttribute for FxIndexMap { 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 { diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index 9621f9f20bdc8..e41d5da713979 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -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" } diff --git a/compiler/rustc_mir_dataflow/messages.ftl b/compiler/rustc_mir_dataflow/messages.ftl index 5698367e42ba5..3783c647b03ef 100644 --- a/compiler/rustc_mir_dataflow/messages.ftl +++ b/compiler/rustc_mir_dataflow/messages.ftl @@ -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 diff --git a/compiler/rustc_mir_dataflow/src/errors.rs b/compiler/rustc_mir_dataflow/src/errors.rs index cfacc0ec370c1..9d8c34c8a1f35 100644 --- a/compiler/rustc_mir_dataflow/src/errors.rs +++ b/compiler/rustc_mir_dataflow/src/errors.rs @@ -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)] diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 22bff3806b156..c4b9b4ce64161 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -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, - formatter: Option, + formatter: Option, } impl RustcMirAttrs { - fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result { - 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( - field: &mut Option, - tcx: TyCtxt<'_>, - name: Symbol, - attr: &ast::MetaItemInner, - mapper: impl FnOnce(Symbol) -> Result, - ) -> 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` diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index a899ec1fa8846..1c5b383616690 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -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 { - 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)); - } - - let move_data = MoveData::gather_moves(body, tcx, |_| true); - - 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::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); + } - 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 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_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 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::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)); } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bd02bc5517561..dd16092c99f96 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -309,6 +309,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcLintUntrackedQueryInformation | AttributeKind::RustcMacroTransparency(_) | AttributeKind::RustcMain + | AttributeKind::RustcMir(_) | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcNoImplicitAutorefs | AttributeKind::RustcNonConstTraitMethod