1use rustc_ast::ptr::P;
2use rustc_ast::{self as ast, AsmMacro};
3use rustc_span::{Span, Symbol, kw};
4
5use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos};
6use crate::{PResult, Parser, errors, exp, token};
7
8pub struct AsmArg {
11 pub kind: AsmArgKind,
12 pub attributes: AsmAttrVec,
13 pub span: Span,
14}
15
16pub enum AsmArgKind {
17 Template(P<ast::Expr>),
18 Operand(Option<Symbol>, ast::InlineAsmOperand),
19 Options(Vec<AsmOption>),
20 ClobberAbi(Vec<(Symbol, Span)>),
21}
22
23pub struct AsmOption {
24 pub symbol: Symbol,
25 pub span: Span,
26 pub options: ast::InlineAsmOptions,
28 pub span_with_comma: Span,
30}
31
32pub struct AsmAttrVec(pub ast::AttrVec);
35
36impl AsmAttrVec {
37 fn parse<'a>(p: &mut Parser<'a>) -> PResult<'a, Self> {
38 let attrs = p.parse_outer_attributes()?;
39
40 p.collect_tokens(None, attrs, ForceCollect::No, |_, attrs| {
41 Ok((Self(attrs), Trailing::No, UsePreAttrPos::No))
42 })
43 }
44}
45impl ast::HasAttrs for AsmAttrVec {
46 const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false;
48
49 fn attrs(&self) -> &[rustc_ast::Attribute] {
50 &self.0
51 }
52
53 fn visit_attrs(&mut self, f: impl FnOnce(&mut rustc_ast::AttrVec)) {
54 f(&mut self.0)
55 }
56}
57
58impl ast::HasTokens for AsmAttrVec {
59 fn tokens(&self) -> Option<&rustc_ast::tokenstream::LazyAttrTokenStream> {
60 None
61 }
62
63 fn tokens_mut(&mut self) -> Option<&mut Option<rustc_ast::tokenstream::LazyAttrTokenStream>> {
64 None
65 }
66}
67
68fn eat_operand_keyword<'a>(
77 p: &mut Parser<'a>,
78 exp: ExpKeywordPair,
79 asm_macro: AsmMacro,
80) -> PResult<'a, bool> {
81 if matches!(asm_macro, AsmMacro::Asm) {
82 Ok(p.eat_keyword(exp))
83 } else {
84 let span = p.token.span;
85 if p.eat_keyword_noexpect(exp.kw) {
86 let symbol = if exp.kw == kw::In { "in" } else { exp.kw.as_str() };
88 Err(p.dcx().create_err(errors::AsmUnsupportedOperand {
89 span,
90 symbol,
91 macro_name: asm_macro.macro_name(),
92 }))
93 } else {
94 Ok(false)
95 }
96 }
97}
98
99fn parse_asm_operand<'a>(
100 p: &mut Parser<'a>,
101 asm_macro: AsmMacro,
102) -> PResult<'a, Option<ast::InlineAsmOperand>> {
103 let dcx = p.dcx();
104
105 Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? {
106 let reg = parse_reg(p)?;
107 if p.eat_keyword(exp!(Underscore)) {
108 let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
109 return Err(err);
110 }
111 let expr = p.parse_expr()?;
112 ast::InlineAsmOperand::In { reg, expr }
113 } else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
114 let reg = parse_reg(p)?;
115 let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
116 ast::InlineAsmOperand::Out { reg, expr, late: false }
117 } else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
118 let reg = parse_reg(p)?;
119 let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
120 ast::InlineAsmOperand::Out { reg, expr, late: true }
121 } else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
122 let reg = parse_reg(p)?;
123 if p.eat_keyword(exp!(Underscore)) {
124 let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
125 return Err(err);
126 }
127 let expr = p.parse_expr()?;
128 if p.eat(exp!(FatArrow)) {
129 let out_expr =
130 if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
131 ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
132 } else {
133 ast::InlineAsmOperand::InOut { reg, expr, late: false }
134 }
135 } else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
136 let reg = parse_reg(p)?;
137 if p.eat_keyword(exp!(Underscore)) {
138 let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
139 return Err(err);
140 }
141 let expr = p.parse_expr()?;
142 if p.eat(exp!(FatArrow)) {
143 let out_expr =
144 if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
145 ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
146 } else {
147 ast::InlineAsmOperand::InOut { reg, expr, late: true }
148 }
149 } else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
150 let block = p.parse_block()?;
151 ast::InlineAsmOperand::Label { block }
152 } else if p.eat_keyword(exp!(Const)) {
153 let anon_const = p.parse_expr_anon_const()?;
154 ast::InlineAsmOperand::Const { anon_const }
155 } else if p.eat_keyword(exp!(Sym)) {
156 let expr = p.parse_expr()?;
157 let ast::ExprKind::Path(qself, path) = &expr.kind else {
158 let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
159 return Err(err);
160 };
161 let sym =
162 ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() };
163 ast::InlineAsmOperand::Sym { sym }
164 } else {
165 return Ok(None);
166 }))
167}
168
169pub fn parse_asm_args<'a>(
171 p: &mut Parser<'a>,
172 sp: Span,
173 asm_macro: AsmMacro,
174) -> PResult<'a, Vec<AsmArg>> {
175 let dcx = p.dcx();
176
177 if p.token == token::Eof {
178 return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
179 }
180
181 let mut args = Vec::new();
182
183 let attributes = AsmAttrVec::parse(p)?;
184 let first_template = p.parse_expr()?;
185 args.push(AsmArg {
186 span: first_template.span,
187 kind: AsmArgKind::Template(first_template),
188 attributes,
189 });
190
191 let mut allow_templates = true;
192
193 while p.token != token::Eof {
194 if !p.eat(exp!(Comma)) {
195 if allow_templates {
196 return Err(dcx.create_err(errors::AsmExpectedComma { span: p.token.span }));
198 } else {
199 return Err(p.expect(exp!(Comma)).err().unwrap());
201 }
202 }
203
204 if p.token == token::Eof {
206 break;
207 }
208
209 let attributes = AsmAttrVec::parse(p)?;
210 let span_start = p.token.span;
211
212 if p.eat_keyword(exp!(ClobberAbi)) {
214 allow_templates = false;
215
216 args.push(AsmArg {
217 kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
218 span: span_start.to(p.prev_token.span),
219 attributes,
220 });
221
222 continue;
223 }
224
225 if p.eat_keyword(exp!(Options)) {
227 allow_templates = false;
228
229 args.push(AsmArg {
230 kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
231 span: span_start.to(p.prev_token.span),
232 attributes,
233 });
234
235 continue;
236 }
237
238 let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
240 let (ident, _) = p.token.ident().unwrap();
241 p.bump();
242 p.expect(exp!(Eq))?;
243 allow_templates = false;
244 Some(ident.name)
245 } else {
246 None
247 };
248
249 if let Some(op) = parse_asm_operand(p, asm_macro)? {
250 allow_templates = false;
251
252 args.push(AsmArg {
253 span: span_start.to(p.prev_token.span),
254 kind: AsmArgKind::Operand(name, op),
255 attributes,
256 });
257 } else if allow_templates {
258 let template = p.parse_expr()?;
259 match template.kind {
262 ast::ExprKind::Lit(token_lit)
263 if matches!(
264 token_lit.kind,
265 token::LitKind::Str | token::LitKind::StrRaw(_)
266 ) => {}
267 ast::ExprKind::MacCall(..) => {}
268 _ => {
269 let err = dcx.create_err(errors::AsmExpectedOther {
270 span: template.span,
271 is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
272 });
273 return Err(err);
274 }
275 }
276
277 args.push(AsmArg {
278 span: template.span,
279 kind: AsmArgKind::Template(template),
280 attributes,
281 });
282 } else {
283 p.unexpected_any()?
284 }
285 }
286
287 Ok(args)
288}
289
290fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec<AsmOption>> {
291 p.expect(exp!(OpenParen))?;
292
293 let mut asm_options = Vec::new();
294
295 while !p.eat(exp!(CloseParen)) {
296 const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
297 (exp!(Pure), ast::InlineAsmOptions::PURE),
298 (exp!(Nomem), ast::InlineAsmOptions::NOMEM),
299 (exp!(Readonly), ast::InlineAsmOptions::READONLY),
300 (exp!(PreservesFlags), ast::InlineAsmOptions::PRESERVES_FLAGS),
301 (exp!(Noreturn), ast::InlineAsmOptions::NORETURN),
302 (exp!(Nostack), ast::InlineAsmOptions::NOSTACK),
303 (exp!(MayUnwind), ast::InlineAsmOptions::MAY_UNWIND),
304 (exp!(AttSyntax), ast::InlineAsmOptions::ATT_SYNTAX),
305 (exp!(Raw), ast::InlineAsmOptions::RAW),
306 ];
307
308 'blk: {
309 for (exp, options) in OPTIONS {
310 let kw_matched = if asm_macro.is_supported_option(options) {
312 p.eat_keyword(exp)
313 } else {
314 p.eat_keyword_noexpect(exp.kw)
315 };
316
317 if kw_matched {
318 let span = p.prev_token.span;
319 let span_with_comma =
320 if p.token == token::Comma { span.to(p.token.span) } else { span };
321
322 asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma });
323 break 'blk;
324 }
325 }
326
327 return p.unexpected_any();
328 }
329
330 if p.eat(exp!(CloseParen)) {
332 break;
333 }
334 p.expect(exp!(Comma))?;
335 }
336
337 Ok(asm_options)
338}
339
340fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
341 p.expect(exp!(OpenParen))?;
342
343 if p.eat(exp!(CloseParen)) {
344 return Err(p.dcx().create_err(errors::NonABI { span: p.token.span }));
345 }
346
347 let mut new_abis = Vec::new();
348 while !p.eat(exp!(CloseParen)) {
349 match p.parse_str_lit() {
350 Ok(str_lit) => {
351 new_abis.push((str_lit.symbol_unescaped, str_lit.span));
352 }
353 Err(opt_lit) => {
354 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
355 return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span }));
356 }
357 };
358
359 if p.eat(exp!(CloseParen)) {
361 break;
362 }
363 p.expect(exp!(Comma))?;
364 }
365
366 Ok(new_abis)
367}
368
369fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
370 p.expect(exp!(OpenParen))?;
371 let result = match p.token.uninterpolate().kind {
372 token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
373 token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
374 ast::InlineAsmRegOrRegClass::Reg(symbol)
375 }
376 _ => {
377 return Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister {
378 span: p.token.span,
379 }));
380 }
381 };
382 p.bump();
383 p.expect(exp!(CloseParen))?;
384 Ok(result)
385}