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, Stage};
10use crate::parser::ArgParser;
11use crate::session_diagnostics;
12use crate::target_checking::AllowedTargets;
13use crate::target_checking::Policy::Allow;
14
15pub(crate) struct CustomMirParser;
16
17impl<S: Stage> SingleAttributeParser<S> 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<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
25 let Some(list) = args.list() else {
26 let attr_span = cx.attr_span;
27 cx.adcx().expected_list(attr_span, args);
28 return None;
29 };
30
31 let mut dialect = None;
32 let mut phase = None;
33 let mut failed = false;
34
35 for item in list.mixed() {
36 let Some(meta_item) = item.meta_item() else {
37 cx.adcx().expected_name_value(item.span(), None);
38 failed = true;
39 break;
40 };
41
42 if let Some(arg) = meta_item.word_is(sym::dialect) {
43 extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
44 } else if let Some(arg) = meta_item.word_is(sym::phase) {
45 extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
46 } else if let Some(..) = meta_item.path().word() {
47 cx.adcx().expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]);
48 failed = true;
49 } else {
50 cx.adcx().expected_name_value(meta_item.span(), None);
51 failed = true;
52 };
53 }
54
55 let dialect = parse_dialect(cx, dialect, &mut failed);
56 let phase = parse_phase(cx, phase, &mut failed);
57 check_custom_mir(cx, dialect, phase, &mut failed);
58
59 if failed {
60 return None;
61 }
62
63 Some(AttributeKind::CustomMir(dialect, phase, cx.attr_span))
64 }
65}
66
67fn extract_value<S: Stage>(
68 cx: &mut AcceptContext<'_, '_, S>,
69 key: Symbol,
70 arg: &ArgParser,
71 span: Span,
72 out_val: &mut Option<(Symbol, Span)>,
73 failed: &mut bool,
74) {
75 if out_val.is_some() {
76 cx.adcx().duplicate_key(span, key);
77 *failed = true;
78 return;
79 }
80
81 let Some(val) = arg.name_value() else {
82 cx.adcx().expected_name_value(span, Some(key));
83 *failed = true;
84 return;
85 };
86
87 let Some(value_sym) = val.value_as_str() else {
88 cx.adcx().expected_string_literal(val.value_span, Some(val.value_as_lit()));
89 *failed = true;
90 return;
91 };
92
93 *out_val = Some((value_sym, val.value_span));
94}
95
96fn parse_dialect<S: Stage>(
97 cx: &mut AcceptContext<'_, '_, S>,
98 dialect: Option<(Symbol, Span)>,
99 failed: &mut bool,
100) -> Option<(MirDialect, Span)> {
101 let (dialect, span) = dialect?;
102
103 let dialect = match dialect {
104 sym::analysis => MirDialect::Analysis,
105 sym::built => MirDialect::Built,
106 sym::runtime => MirDialect::Runtime,
107
108 _ => {
109 cx.adcx().expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]);
110 *failed = true;
111 return None;
112 }
113 };
114
115 Some((dialect, span))
116}
117
118fn parse_phase<S: Stage>(
119 cx: &mut AcceptContext<'_, '_, S>,
120 phase: Option<(Symbol, Span)>,
121 failed: &mut bool,
122) -> Option<(MirPhase, Span)> {
123 let (phase, span) = phase?;
124
125 let phase = match phase {
126 sym::initial => MirPhase::Initial,
127 sym::post_cleanup => MirPhase::PostCleanup,
128 sym::optimized => MirPhase::Optimized,
129
130 _ => {
131 cx.adcx().expected_specific_argument(
132 span,
133 &[sym::initial, sym::post_cleanup, sym::optimized],
134 );
135 *failed = true;
136 return None;
137 }
138 };
139
140 Some((phase, span))
141}
142
143fn check_custom_mir<S: Stage>(
144 cx: &mut AcceptContext<'_, '_, S>,
145 dialect: Option<(MirDialect, Span)>,
146 phase: Option<(MirPhase, Span)>,
147 failed: &mut bool,
148) {
149 let attr_span = cx.attr_span;
150 let Some((dialect, dialect_span)) = dialect else {
151 if let Some((_, phase_span)) = phase {
152 *failed = true;
153 cx.emit_err(session_diagnostics::CustomMirPhaseRequiresDialect {
154 attr_span,
155 phase_span,
156 });
157 }
158 return;
159 };
160
161 match dialect {
162 MirDialect::Analysis => {
163 if let Some((MirPhase::Optimized, phase_span)) = phase {
164 *failed = true;
165 cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase {
166 dialect,
167 phase: MirPhase::Optimized,
168 attr_span,
169 dialect_span,
170 phase_span,
171 });
172 }
173 }
174
175 MirDialect::Built => {
176 if let Some((phase, phase_span)) = phase {
177 *failed = true;
178 cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase {
179 dialect,
180 phase,
181 attr_span,
182 dialect_span,
183 phase_span,
184 });
185 }
186 }
187 MirDialect::Runtime => {}
188 }
189}