Skip to content

Commit de9bcf0

Browse files
authored
Merge 3667338 into 915a766
2 parents 915a766 + 3667338 commit de9bcf0

File tree

12 files changed

+282
-56
lines changed

12 files changed

+282
-56
lines changed

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub(crate) mod no_implicit_prelude;
4343
pub(crate) mod non_exhaustive;
4444
pub(crate) mod path;
4545
pub(crate) mod proc_macro_attrs;
46+
pub(crate) mod prototype;
4647
pub(crate) mod repr;
4748
pub(crate) mod rustc_internal;
4849
pub(crate) mod semantics;
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//! Attributes that are only used on function prototypes.
2+
3+
use rustc_feature::{AttributeTemplate, template};
4+
use rustc_hir::attrs::{AttributeKind, MirDialect, MirPhase};
5+
use rustc_span::{Span, Symbol, sym};
6+
7+
use super::{AttributeOrder, OnDuplicate};
8+
use crate::attributes::SingleAttributeParser;
9+
use crate::context::{AcceptContext, Stage};
10+
use crate::parser::ArgParser;
11+
12+
pub(crate) struct CustomMirParser;
13+
14+
impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
15+
const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
16+
17+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
18+
19+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
20+
21+
const TEMPLATE: AttributeTemplate = template!(List: "dialect, phase");
22+
23+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
24+
let Some(list) = args.list() else {
25+
cx.expected_list(cx.attr_span);
26+
return None;
27+
};
28+
29+
let mut dialect = None;
30+
let mut phase = None;
31+
let mut failed = false;
32+
33+
for item in list.mixed() {
34+
let Some(meta_item) = item.meta_item() else {
35+
cx.expected_name_value(item.span(), None);
36+
failed = true;
37+
break;
38+
};
39+
40+
if let Some(arg) = meta_item.word_is(sym::dialect) {
41+
extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
42+
} else if let Some(arg) = meta_item.word_is(sym::phase) {
43+
extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
44+
} else if let Some(word) = meta_item.path().word() {
45+
let word = word.to_string();
46+
cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]);
47+
failed = true;
48+
} else {
49+
cx.expected_name_value(meta_item.span(), None);
50+
failed = true;
51+
};
52+
}
53+
54+
let dialect = parse_dialect(cx, dialect, &mut failed);
55+
let phase = parse_phase(cx, phase, &mut failed);
56+
57+
if failed {
58+
return None;
59+
}
60+
61+
Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span))
62+
}
63+
}
64+
65+
fn extract_value<S: Stage>(
66+
cx: &mut AcceptContext<'_, '_, S>,
67+
key: Symbol,
68+
arg: &ArgParser<'_>,
69+
span: Span,
70+
out_val: &mut Option<(Symbol, Span)>,
71+
failed: &mut bool,
72+
) {
73+
if out_val.is_some() {
74+
cx.duplicate_key(span, key);
75+
*failed = true;
76+
return;
77+
}
78+
79+
let Some(val) = arg.name_value() else {
80+
cx.expected_single_argument(arg.span().unwrap_or(span));
81+
*failed = true;
82+
return;
83+
};
84+
85+
let Some(value_sym) = val.value_as_str() else {
86+
cx.expected_string_literal(val.value_span, Some(val.value_as_lit()));
87+
*failed = true;
88+
return;
89+
};
90+
91+
*out_val = Some((value_sym, val.value_span));
92+
}
93+
94+
fn parse_dialect<S: Stage>(
95+
cx: &mut AcceptContext<'_, '_, S>,
96+
dialect: Option<(Symbol, Span)>,
97+
failed: &mut bool,
98+
) -> Option<(MirDialect, Span)> {
99+
let (dialect, span) = dialect?;
100+
101+
let dialect = match dialect.as_str() {
102+
"analysis" => MirDialect::Analysis,
103+
"built" => MirDialect::Built,
104+
"runtime" => MirDialect::Runtime,
105+
106+
_ => {
107+
cx.expected_specific_argument(span, vec!["analysis", "built", "runtime"]);
108+
*failed = true;
109+
return None;
110+
}
111+
};
112+
113+
Some((dialect, span))
114+
}
115+
116+
fn parse_phase<S: Stage>(
117+
cx: &mut AcceptContext<'_, '_, S>,
118+
phase: Option<(Symbol, Span)>,
119+
failed: &mut bool,
120+
) -> Option<(MirPhase, Span)> {
121+
let (phase, span) = phase?;
122+
123+
let phase = match phase.as_str().to_ascii_lowercase().as_str() {
124+
"initial" => MirPhase::Initial,
125+
"post_cleanup" | "post-cleanup" | "postcleanup" => MirPhase::PostCleanup,
126+
"optimized" => MirPhase::Optimized,
127+
128+
_ => {
129+
cx.expected_specific_argument(span, vec!["initial", "post_cleanup", "optimized"]);
130+
*failed = true;
131+
return None;
132+
}
133+
};
134+
135+
Some((phase, span))
136+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use crate::attributes::path::PathParser as PathAttributeParser;
4141
use crate::attributes::proc_macro_attrs::{
4242
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
4343
};
44+
use crate::attributes::prototype::CustomMirParser;
4445
use crate::attributes::repr::{AlignParser, ReprParser};
4546
use crate::attributes::rustc_internal::{
4647
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
@@ -157,6 +158,7 @@ attribute_parsers!(
157158

158159
// tidy-alphabetical-start
159160
Single<CoverageParser>,
161+
Single<CustomMirParser>,
160162
Single<DeprecationParser>,
161163
Single<DummyParser>,
162164
Single<ExportNameParser>,

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,23 @@ pub enum CfgEntry {
187187
Version(Option<RustcVersion>, Span),
188188
}
189189

190+
#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
191+
#[derive(HashStable_Generic, PrintAttribute)]
192+
pub enum MirDialect {
193+
Analysis,
194+
Built,
195+
Runtime,
196+
}
197+
198+
#[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
199+
#[derive(HashStable_Generic, PrintAttribute)]
200+
201+
pub enum MirPhase {
202+
Initial,
203+
PostCleanup,
204+
Optimized,
205+
}
206+
190207
/// Represents parsed *built-in* inert attributes.
191208
///
192209
/// ## Overview
@@ -303,6 +320,9 @@ pub enum AttributeKind {
303320
/// Represents `#[coverage(..)]`.
304321
Coverage(Span, CoverageAttrKind),
305322

323+
/// Represents `#[custom_mir]`.
324+
CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span),
325+
306326
///Represents `#[rustc_deny_explicit_impl]`.
307327
DenyExplicitImpl(Span),
308328

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ impl AttributeKind {
3030
ConstTrait(..) => No,
3131
Coroutine(..) => No,
3232
Coverage(..) => No,
33+
CustomMir(_, _, _) => Yes,
3334
DenyExplicitImpl(..) => No,
3435
Deprecation { .. } => Yes,
3536
DoNotImplementViaObject(..) => No,

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc_hir::def::{CtorKind, Namespace};
1919
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
2020
use rustc_hir::{
2121
self as hir, BindingMode, ByRef, CoroutineDesugaring, CoroutineKind, HirId, ImplicitSelfKind,
22+
attrs,
2223
};
2324
use rustc_index::bit_set::DenseBitSet;
2425
use rustc_index::{Idx, IndexSlice, IndexVec};
@@ -117,44 +118,46 @@ impl MirPhase {
117118
}
118119

119120
/// Parses a `MirPhase` from a pair of strings. Panics if this isn't possible for any reason.
120-
pub fn parse(dialect: String, phase: Option<String>) -> Self {
121-
match &*dialect.to_ascii_lowercase() {
122-
"built" => {
121+
pub fn parse(dialect: attrs::MirDialect, phase: Option<attrs::MirPhase>) -> Self {
122+
match dialect {
123+
attrs::MirDialect::Built => {
124+
// Caught during attribute checking.
123125
assert!(phase.is_none(), "Cannot specify a phase for `Built` MIR");
124126
MirPhase::Built
125127
}
126-
"analysis" => Self::Analysis(AnalysisPhase::parse(phase)),
127-
"runtime" => Self::Runtime(RuntimePhase::parse(phase)),
128-
_ => bug!("Unknown MIR dialect: '{}'", dialect),
128+
attrs::MirDialect::Analysis => Self::Analysis(AnalysisPhase::parse(phase)),
129+
attrs::MirDialect::Runtime => Self::Runtime(RuntimePhase::parse(phase)),
129130
}
130131
}
131132
}
132133

133134
impl AnalysisPhase {
134-
pub fn parse(phase: Option<String>) -> Self {
135+
pub fn parse(phase: Option<attrs::MirPhase>) -> Self {
135136
let Some(phase) = phase else {
136137
return Self::Initial;
137138
};
138139

139-
match &*phase.to_ascii_lowercase() {
140-
"initial" => Self::Initial,
141-
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
142-
_ => bug!("Unknown analysis phase: '{}'", phase),
140+
match phase {
141+
attrs::MirPhase::Initial => Self::Initial,
142+
attrs::MirPhase::PostCleanup => Self::PostCleanup,
143+
attrs::MirPhase::Optimized => {
144+
// Caught during attribute checking.
145+
bug!("`optimized` dialect is not compatible with the `analysis` dialect")
146+
}
143147
}
144148
}
145149
}
146150

147151
impl RuntimePhase {
148-
pub fn parse(phase: Option<String>) -> Self {
152+
pub fn parse(phase: Option<attrs::MirPhase>) -> Self {
149153
let Some(phase) = phase else {
150154
return Self::Initial;
151155
};
152156

153-
match &*phase.to_ascii_lowercase() {
154-
"initial" => Self::Initial,
155-
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
156-
"optimized" => Self::Optimized,
157-
_ => bug!("Unknown runtime phase: '{}'", phase),
157+
match phase {
158+
attrs::MirPhase::Initial => Self::Initial,
159+
attrs::MirPhase::PostCleanup => Self::PostCleanup,
160+
attrs::MirPhase::Optimized => Self::Optimized,
158161
}
159162
}
160163
}

compiler/rustc_mir_build/src/builder/custom/mod.rs

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@
1919
2020
use rustc_data_structures::fx::FxHashMap;
2121
use rustc_hir::def_id::DefId;
22-
use rustc_hir::{Attribute, HirId};
22+
use rustc_hir::{HirId, attrs};
2323
use rustc_index::{IndexSlice, IndexVec};
2424
use rustc_middle::mir::*;
25-
use rustc_middle::span_bug;
2625
use rustc_middle::thir::*;
2726
use rustc_middle::ty::{self, Ty, TyCtxt};
2827
use rustc_span::Span;
@@ -39,7 +38,8 @@ pub(super) fn build_custom_mir<'tcx>(
3938
return_ty: Ty<'tcx>,
4039
return_ty_span: Span,
4140
span: Span,
42-
attr: &Attribute,
41+
dialect: Option<attrs::MirDialect>,
42+
phase: Option<attrs::MirPhase>,
4343
) -> Body<'tcx> {
4444
let mut body = Body {
4545
basic_blocks: BasicBlocks::new(IndexVec::new()),
@@ -72,7 +72,7 @@ pub(super) fn build_custom_mir<'tcx>(
7272
inlined_parent_scope: None,
7373
local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }),
7474
});
75-
body.injection_phase = Some(parse_attribute(attr));
75+
body.injection_phase = Some(parse_attribute(dialect, phase));
7676

7777
let mut pctxt = ParseCtxt {
7878
tcx,
@@ -98,34 +98,7 @@ pub(super) fn build_custom_mir<'tcx>(
9898
body
9999
}
100100

101-
fn parse_attribute(attr: &Attribute) -> MirPhase {
102-
let meta_items = attr.meta_item_list().unwrap();
103-
let mut dialect: Option<String> = None;
104-
let mut phase: Option<String> = None;
105-
106-
// Not handling errors properly for this internal attribute; will just abort on errors.
107-
for nested in meta_items {
108-
let name = nested.name().unwrap();
109-
let value = nested.value_str().unwrap().as_str().to_string();
110-
match name.as_str() {
111-
"dialect" => {
112-
assert!(dialect.is_none());
113-
dialect = Some(value);
114-
}
115-
"phase" => {
116-
assert!(phase.is_none());
117-
phase = Some(value);
118-
}
119-
other => {
120-
span_bug!(
121-
nested.span(),
122-
"Unexpected key while parsing custom_mir attribute: '{}'",
123-
other
124-
);
125-
}
126-
}
127-
}
128-
101+
fn parse_attribute(dialect: Option<attrs::MirDialect>, phase: Option<attrs::MirPhase>) -> MirPhase {
129102
let Some(dialect) = dialect else {
130103
assert!(phase.is_none());
131104
return MirPhase::Built;

compiler/rustc_mir_build/src/builder/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ use rustc_ast::attr;
1111
use rustc_data_structures::fx::FxHashMap;
1212
use rustc_data_structures::sorted_map::SortedIndexMultiMap;
1313
use rustc_errors::ErrorGuaranteed;
14+
use rustc_hir::attrs::AttributeKind;
1415
use rustc_hir::def::DefKind;
1516
use rustc_hir::def_id::{DefId, LocalDefId};
16-
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node};
17+
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_attr};
1718
use rustc_index::bit_set::GrowableBitSet;
1819
use rustc_index::{Idx, IndexSlice, IndexVec};
1920
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@@ -479,8 +480,7 @@ fn construct_fn<'tcx>(
479480
ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"),
480481
};
481482

482-
if let Some(custom_mir_attr) =
483-
tcx.hir_attrs(fn_id).iter().find(|attr| attr.has_name(sym::custom_mir))
483+
if let Some((dialect, phase)) = find_attr!(tcx.hir_attrs(fn_id), AttributeKind::CustomMir(dialect, phase, _) => (dialect, phase))
484484
{
485485
return custom::build_custom_mir(
486486
tcx,
@@ -492,7 +492,8 @@ fn construct_fn<'tcx>(
492492
return_ty,
493493
return_ty_span,
494494
span_with_body,
495-
custom_mir_attr,
495+
dialect.as_ref().map(|(d, _)| *d),
496+
phase.as_ref().map(|(p, _)| *p),
496497
);
497498
}
498499

compiler/rustc_passes/messages.ftl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ passes_coverage_attribute_not_allowed =
108108
.no_body = function has no body
109109
.help = coverage attribute can be applied to a function (with body), impl block, or module
110110
111+
passes_custom_mir_incompatible_dialect_and_phase =
112+
The {$dialect} dialect is not compatible with the {$phase} phase
113+
.dialect_span = this dialect...
114+
.phase_span = ... is not compatible with this phase
115+
116+
passes_custom_mir_phase_requires_dialect =
117+
`dialect` key required
118+
.phase_span = `phase` argument requires a `dialect` argument
119+
111120
passes_dead_codes =
112121
{ $multiple ->
113122
*[true] multiple {$descr}s are

0 commit comments

Comments
 (0)