rustc_attr_parsing/attributes/
prototype.rs1use rustc_attr_ir::target::Target;
4use rustc_attr_ir::{AttributeKind, MirDialect, MirPhase};
5use rustc_feature::AttributeStability;
6use rustc_span::{Span, Symbol, sym};
7
8use crate::attributes::SingleAttributeParser;
9use crate::context::AcceptContext;
10use crate::parser::{ArgParser, NameValueParser};
11use crate::target_checking::AllowedTargets;
12use crate::target_checking::Policy::Allow;
13use crate::{AttributeTemplate, diagnostics, template, unstable};
14
15pub(crate) struct CustomMirParser;
16
17impl SingleAttributeParser for CustomMirParser {
18 const PATH: &[rustc_span::Symbol] = &[sym::custom_mir];
19 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::custom_mir,
gate_check: rustc_feature::Features::custom_mir,
notes: &[],
}unstable!(custom_mir);
20
21 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
22
23 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: Some(&[r#"dialect = "...", phase = "...""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[r#"dialect = "...", phase = "...""#]);
24
25 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
26 let list = cx.expect_list(args, cx.attr_span)?;
27
28 let mut dialect = None;
29 let mut phase = None;
30 let mut failed = false;
31
32 for item in list.mixed() {
33 let Some((path, arg)) = cx.expect_name_value(item, item.span(), None) else {
34 failed = true;
35 break;
36 };
37
38 match path.name {
39 sym::dialect => {
40 extract_value(cx, sym::dialect, arg, item.span(), &mut dialect, &mut failed)
41 }
42 sym::phase => {
43 extract_value(cx, sym::phase, arg, item.span(), &mut phase, &mut failed)
44 }
45 _ => {
46 cx.adcx().expected_specific_argument(item.span(), &[sym::dialect, sym::phase]);
47 failed = true;
48 }
49 }
50 }
51
52 let dialect = parse_dialect(cx, dialect, &mut failed);
53 let phase = parse_phase(cx, phase, &mut failed);
54 check_custom_mir(cx, dialect, phase, &mut failed);
55
56 if failed {
57 return None;
58 }
59
60 Some(AttributeKind::CustomMir(dialect, phase))
61 }
62}
63
64fn extract_value(
65 cx: &mut AcceptContext<'_, '_>,
66 key: Symbol,
67 val: &NameValueParser,
68 span: Span,
69 out_val: &mut Option<(Symbol, Span)>,
70 failed: &mut bool,
71) {
72 if out_val.is_some() {
73 cx.adcx().duplicate_key(span, key);
74 *failed = true;
75 return;
76 }
77
78 let Some(value_sym) = cx.expect_string_literal(val) else {
79 *failed = true;
80 return;
81 };
82
83 *out_val = Some((value_sym, val.value_span));
84}
85
86fn parse_dialect(
87 cx: &mut AcceptContext<'_, '_>,
88 dialect: Option<(Symbol, Span)>,
89 failed: &mut bool,
90) -> Option<(MirDialect, Span)> {
91 let (dialect, span) = dialect?;
92
93 let dialect = match dialect {
94 sym::analysis => MirDialect::Analysis,
95 sym::built => MirDialect::Built,
96 sym::runtime => MirDialect::Runtime,
97
98 _ => {
99 cx.adcx().expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]);
100 *failed = true;
101 return None;
102 }
103 };
104
105 Some((dialect, span))
106}
107
108fn parse_phase(
109 cx: &mut AcceptContext<'_, '_>,
110 phase: Option<(Symbol, Span)>,
111 failed: &mut bool,
112) -> Option<(MirPhase, Span)> {
113 let (phase, span) = phase?;
114
115 let phase = match phase {
116 sym::initial => MirPhase::Initial,
117 sym::post_cleanup => MirPhase::PostCleanup,
118 sym::optimized => MirPhase::Optimized,
119
120 _ => {
121 cx.adcx().expected_specific_argument(
122 span,
123 &[sym::initial, sym::post_cleanup, sym::optimized],
124 );
125 *failed = true;
126 return None;
127 }
128 };
129
130 Some((phase, span))
131}
132
133fn check_custom_mir(
134 cx: &mut AcceptContext<'_, '_>,
135 dialect: Option<(MirDialect, Span)>,
136 phase: Option<(MirPhase, Span)>,
137 failed: &mut bool,
138) {
139 let attr_span = cx.attr_span;
140 let Some((dialect, dialect_span)) = dialect else {
141 if let Some((_, phase_span)) = phase {
142 *failed = true;
143 cx.emit_err(diagnostics::CustomMirPhaseRequiresDialect { attr_span, phase_span });
144 }
145 return;
146 };
147
148 match dialect {
149 MirDialect::Analysis => {
150 if let Some((MirPhase::Optimized, phase_span)) = phase {
151 *failed = true;
152 cx.emit_err(diagnostics::CustomMirIncompatibleDialectAndPhase {
153 dialect,
154 phase: MirPhase::Optimized,
155 attr_span,
156 dialect_span,
157 phase_span,
158 });
159 }
160 }
161
162 MirDialect::Built => {
163 if let Some((phase, phase_span)) = phase {
164 *failed = true;
165 cx.emit_err(diagnostics::CustomMirIncompatibleDialectAndPhase {
166 dialect,
167 phase,
168 attr_span,
169 dialect_span,
170 phase_span,
171 });
172 }
173 }
174 MirDialect::Runtime => {}
175 }
176}