Skip to main content

rustc_attr_parsing/attributes/
prototype.rs

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