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, 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}