Skip to main content

rustc_attr_parsing/attributes/
prototype.rs

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