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