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 crate::attributes::SingleAttributeParser;
9use crate::context::AcceptContext;
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 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(meta_item) = item.meta_item() else {
33                cx.adcx().expected_name_value(item.span(), None);
34                failed = true;
35                break;
36            };
37
38            if let Some(arg) = meta_item.word_is(sym::dialect) {
39                extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed);
40            } else if let Some(arg) = meta_item.word_is(sym::phase) {
41                extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed);
42            } else if let Some(..) = meta_item.path().word() {
43                cx.adcx().expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]);
44                failed = true;
45            } else {
46                cx.adcx().expected_name_value(meta_item.span(), None);
47                failed = true;
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, cx.attr_span))
60    }
61}
62
63fn extract_value(
64    cx: &mut AcceptContext<'_, '_>,
65    key: Symbol,
66    arg: &ArgParser,
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(val) = arg.name_value() else {
78        cx.adcx().expected_name_value(span, Some(key));
79        *failed = true;
80        return;
81    };
82
83    let Some(value_sym) = val.value_as_str() else {
84        cx.adcx().expected_string_literal(val.value_span, Some(val.value_as_lit()));
85        *failed = true;
86        return;
87    };
88
89    *out_val = Some((value_sym, val.value_span));
90}
91
92fn parse_dialect(
93    cx: &mut AcceptContext<'_, '_>,
94    dialect: Option<(Symbol, Span)>,
95    failed: &mut bool,
96) -> Option<(MirDialect, Span)> {
97    let (dialect, span) = dialect?;
98
99    let dialect = match dialect {
100        sym::analysis => MirDialect::Analysis,
101        sym::built => MirDialect::Built,
102        sym::runtime => MirDialect::Runtime,
103
104        _ => {
105            cx.adcx().expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]);
106            *failed = true;
107            return None;
108        }
109    };
110
111    Some((dialect, span))
112}
113
114fn parse_phase(
115    cx: &mut AcceptContext<'_, '_>,
116    phase: Option<(Symbol, Span)>,
117    failed: &mut bool,
118) -> Option<(MirPhase, Span)> {
119    let (phase, span) = phase?;
120
121    let phase = match phase {
122        sym::initial => MirPhase::Initial,
123        sym::post_cleanup => MirPhase::PostCleanup,
124        sym::optimized => MirPhase::Optimized,
125
126        _ => {
127            cx.adcx().expected_specific_argument(
128                span,
129                &[sym::initial, sym::post_cleanup, sym::optimized],
130            );
131            *failed = true;
132            return None;
133        }
134    };
135
136    Some((phase, span))
137}
138
139fn check_custom_mir(
140    cx: &mut AcceptContext<'_, '_>,
141    dialect: Option<(MirDialect, Span)>,
142    phase: Option<(MirPhase, Span)>,
143    failed: &mut bool,
144) {
145    let attr_span = cx.attr_span;
146    let Some((dialect, dialect_span)) = dialect else {
147        if let Some((_, phase_span)) = phase {
148            *failed = true;
149            cx.emit_err(session_diagnostics::CustomMirPhaseRequiresDialect {
150                attr_span,
151                phase_span,
152            });
153        }
154        return;
155    };
156
157    match dialect {
158        MirDialect::Analysis => {
159            if let Some((MirPhase::Optimized, phase_span)) = phase {
160                *failed = true;
161                cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase {
162                    dialect,
163                    phase: MirPhase::Optimized,
164                    attr_span,
165                    dialect_span,
166                    phase_span,
167                });
168            }
169        }
170
171        MirDialect::Built => {
172            if let Some((phase, phase_span)) = phase {
173                *failed = true;
174                cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase {
175                    dialect,
176                    phase,
177                    attr_span,
178                    dialect_span,
179                    phase_span,
180                });
181            }
182        }
183        MirDialect::Runtime => {}
184    }
185}