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