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