use std::collections::HashMap;
use crate::ast::*;
use crate::capture::SourceRange;
use crate::diag::{Diagnostic, Diagnostics};
use crate::gnu;
use crate::ir::{INT128_TYPEDEF_NAMES, VA_LIST_NAMES, X86_VECTOR_TYPEDEF_NAMES};
use crate::lex::{Keyword, Punct, StrKind, StrLit, TokenKind};
use crate::pp::{Origin, PackMap, TargetOptionMap, Token};
use crate::target::Arch;
use crate::{Gating, Options, Standard};
pub const NORETURN_BUILTIN: &str = "__cinrs_noreturn";
fn int_expr(value: u128, range: SourceRange) -> Expr {
Expr {
kind: ExprKind::Int(crate::lex::IntLit {
value,
base: crate::lex::NumBase::Decimal,
unsigned: false,
long: crate::lex::LongKind::None,
text: value.to_string(),
}),
range,
}
}
#[derive(Debug)]
#[must_use]
pub struct Bail;
type PResult<T> = Result<T, Bail>;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum SymKind {
Typedef,
Ordinary,
}
#[derive(Default)]
struct Scope {
syms: HashMap<String, SymKind>,
}
pub fn parse(
tokens: &[Token],
unit_range: SourceRange,
packing: &PackMap,
targets: &TargetOptionMap,
options: &Options,
diags: &mut Diagnostics,
) -> TranslationUnit {
let patched: Vec<Token>;
let tokens: &[Token] = if tokens.last().is_some_and(Token::is_eof) {
tokens
} else {
patched = tokens
.iter()
.cloned()
.chain(std::iter::once(Token {
kind: TokenKind::Eof,
range: SourceRange::at(unit_range.end),
origin: Origin::Source,
}))
.collect();
&patched
};
let last_range = tokens.first().map_or(unit_range, |t| t.range);
let mut builtins = Scope::default();
for name in VA_LIST_NAMES {
builtins.syms.insert((*name).to_owned(), SymKind::Typedef);
}
for (name, _) in INT128_TYPEDEF_NAMES {
builtins.syms.insert((*name).to_owned(), SymKind::Typedef);
}
if matches!(options.target.arch, Arch::X86 | Arch::X86_64) {
for (name, _) in X86_VECTOR_TYPEDEF_NAMES {
builtins.syms.insert((*name).to_owned(), SymKind::Typedef);
}
}
let mut parser = Parser {
tokens,
pos: 0,
diags,
scopes: vec![builtins],
standard: options.standard,
gating: options.gating(),
in_extension: false,
packing,
targets,
decl_start: 0,
last_range,
depth: 0,
records: Vec::new(),
enums: Vec::new(),
typeofs: Vec::new(),
label_addrs: 0,
};
parser.parse_translation_unit(unit_range)
}
const MAX_RECURSION_DEPTH: u32 = 200;
const MAX_LABEL_CHAIN: usize = 4096;
enum PendingLabel {
Ident { label: Ident },
Case { value: Expr, upper: Option<Expr> },
Default,
}
struct Parser<'a> {
tokens: &'a [Token],
pos: usize,
diags: &'a mut Diagnostics,
scopes: Vec<Scope>,
standard: Standard,
gating: Gating,
in_extension: bool,
packing: &'a PackMap,
targets: &'a TargetOptionMap,
decl_start: usize,
last_range: SourceRange,
depth: u32,
records: Vec<RecordSpec>,
enums: Vec<EnumSpec>,
typeofs: Vec<TypeofOperand>,
label_addrs: u32,
}
impl Parser<'_> {
fn peek(&self) -> &Token {
&self.tokens[self.pos]
}
fn nth(&self, n: usize) -> &Token {
let i = (self.pos + n).min(self.tokens.len() - 1);
&self.tokens[i]
}
fn cur_range(&self) -> SourceRange {
self.peek().range
}
fn describe_cur(&self) -> String {
self.peek().kind.describe()
}
fn at_eof(&self) -> bool {
self.peek().is_eof()
}
fn at_punct(&self, p: Punct) -> bool {
self.peek().is_punct(p)
}
fn at_keyword(&self, k: Keyword) -> bool {
self.peek().is_keyword(k)
}
fn advance(&mut self) {
self.last_range = self.tokens[self.pos].range;
if self.pos + 1 < self.tokens.len() {
self.pos += 1;
}
}
fn bump_range(&mut self) -> SourceRange {
let range = self.cur_range();
self.advance();
range
}
fn eat_punct(&mut self, p: Punct) -> Option<SourceRange> {
self.at_punct(p).then(|| self.bump_range())
}
fn eat_keyword(&mut self, k: Keyword) -> Option<SourceRange> {
self.at_keyword(k).then(|| self.bump_range())
}
fn eat_ident(&mut self) -> Option<Ident> {
let name = self.peek().ident()?.to_owned();
let range = self.bump_range();
Some(Ident { name, range })
}
fn error(&mut self, range: SourceRange, message: impl Into<String>) {
self.diags.error(range, message);
}
fn error_bail(&mut self, range: SourceRange, message: impl Into<String>) -> Bail {
self.diags.error(range, message);
Bail
}
fn expect_punct(&mut self, p: Punct, ctx: &str) -> PResult<SourceRange> {
if self.at_punct(p) {
return Ok(self.bump_range());
}
let range = self.cur_range();
let found = self.describe_cur();
Err(self.error_bail(
range,
format!("expected '{}'{ctx}, found {found}", p.as_str()),
))
}
fn expect_ident(&mut self, ctx: &str) -> PResult<Ident> {
if let Some(id) = self.eat_ident() {
return Ok(id);
}
let range = self.cur_range();
let found = self.describe_cur();
Err(self.error_bail(range, format!("expected identifier{ctx}, found {found}")))
}
fn span_to_here(&self, start: SourceRange) -> SourceRange {
start.join(self.last_range)
}
fn enter(&mut self) -> PResult<()> {
self.depth += 1;
if self.depth > MAX_RECURSION_DEPTH {
let range = self.cur_range();
return Err(self.error_bail(range, "this construct nests too deeply"));
}
Ok(())
}
fn leave(&mut self) {
self.depth = self.depth.saturating_sub(1);
}
fn require_standard(&mut self, needed: Standard, what: &str, range: SourceRange) {
if self.in_extension {
return;
}
if let Some(message) = self.gating.requires(what, needed) {
self.error(range, message);
}
}
fn require_keyword(&mut self, k: Keyword, range: SourceRange) {
let needed = k.since();
self.require_standard(needed, &format!("'{}'", k.as_str()), range);
}
fn newer_keyword_here(&self) -> Option<String> {
self.gating.newer_keyword(self.peek().ident()?)
}
fn gnu_leniency(&self) -> bool {
self.gating.dialect.is_gnu()
}
fn gnu_note(&self) -> String {
format!(
"GCC accepts this with a warning; write {} for the same leniency",
self.gating.standard.macro_name_in(crate::Dialect::Gnu)
)
}
fn error_gnu(&mut self, range: SourceRange, message: impl Into<String>) {
let note = self.gnu_note();
self.diags
.push(Diagnostic::error(range, message).with_note(note));
}
}
impl Parser<'_> {
fn push_scope(&mut self) {
self.scopes.push(Scope::default());
}
fn pop_scope(&mut self) {
self.scopes.pop();
}
fn declare(&mut self, name: &str, kind: SymKind) {
if let Some(scope) = self.scopes.last_mut() {
scope.syms.insert(name.to_owned(), kind);
}
}
fn is_typedef_name(&self, name: &str) -> bool {
for scope in self.scopes.iter().rev() {
if let Some(kind) = scope.syms.get(name) {
return *kind == SymKind::Typedef;
}
}
false
}
}
impl Parser<'_> {
fn at_attributes(&self) -> bool {
(self.at_punct(Punct::LBracket) && self.nth(1).is_punct(Punct::LBracket))
|| self.at_keyword(Keyword::Attribute)
}
fn parse_attributes(&mut self) -> PResult<Attributes> {
let mut attrs = Attributes::default();
loop {
if self.at_keyword(Keyword::Attribute) {
let start = self.bump_range();
self.expect_punct(Punct::LParen, " after '__attribute__'")?;
self.expect_punct(Punct::LParen, " after '__attribute__('")?;
self.parse_attribute_list(&mut attrs, Punct::RParen)?;
self.expect_punct(Punct::RParen, " to close '__attribute__'")?;
self.expect_punct(Punct::RParen, " to close '__attribute__'")?;
let _ = start;
continue;
}
if self.at_punct(Punct::LBracket) && self.nth(1).is_punct(Punct::LBracket) {
let start = self.cur_range();
self.require_standard(Standard::C23, "an attribute specifier", start);
self.advance(); self.advance(); self.parse_attribute_list(&mut attrs, Punct::RBracket)?;
self.expect_punct(Punct::RBracket, " to close an attribute specifier")?;
self.expect_punct(Punct::RBracket, " to close an attribute specifier")?;
continue;
}
return Ok(attrs);
}
}
fn parse_attribute_list(&mut self, attrs: &mut Attributes, close: Punct) -> PResult<()> {
loop {
if self.at_punct(close) || self.at_eof() {
return Ok(());
}
if self.eat_punct(Punct::Comma).is_some() {
continue;
}
self.parse_one_attribute(attrs, close)?;
if self.eat_punct(Punct::Comma).is_none() {
return Ok(());
}
}
}
fn parse_one_attribute(&mut self, attrs: &mut Attributes, close: Punct) -> PResult<()> {
let start = self.cur_range();
let mut name = match &self.peek().kind {
TokenKind::Ident(name) => name.clone(),
TokenKind::Keyword(k) => k.as_str().to_owned(),
_ => {
let found = self.describe_cur();
return Err(self.error_bail(start, format!("expected an attribute, found {found}")));
}
};
self.advance();
let mut ours = false;
if self.at_punct(Punct::Colon) && self.nth(1).is_punct(Punct::Colon) {
self.advance();
self.advance();
let prefix = std::mem::take(&mut name);
name = match &self.peek().kind {
TokenKind::Ident(name) => name.clone(),
TokenKind::Keyword(k) => k.as_str().to_owned(),
_ => {
let found = self.describe_cur();
return Err(
self.error_bail(start, format!("expected an attribute, found {found}"))
);
}
};
self.advance();
if prefix == "cinrs" {
if gnu::cinrs_attribute(&name).is_none() {
self.skip_attribute_args()?;
let range = self.span_to_here(start);
self.error(
range,
format!(
"unknown 'cinrs' attribute '{name}'; the ones this crate has are {}",
Self::list_of_names(gnu::CINRS_ATTRIBUTES)
),
);
return Ok(());
}
ours = true;
} else if prefix != "gnu" && prefix != "clang" {
self.skip_attribute_args()?;
return Ok(());
}
}
let known = if ours {
gnu::cinrs_attribute(&name)
} else {
gnu::attribute(&name)
};
match known {
Some(gnu::Attribute::Aligned) => {
let alignment = if self.at_punct(Punct::LParen) {
self.advance();
let expr = self.parse_conditional_expr()?;
self.expect_punct(Punct::RParen, " after the alignment")?;
AlignmentKind::Expr(expr)
} else {
AlignmentKind::Expr(int_expr(16, start))
};
let range = self.span_to_here(start);
attrs.aligned = Some(Alignment {
kind: alignment,
from_attribute: true,
range,
});
return Ok(());
}
Some(gnu::Attribute::Deprecated) => {
let message = self.attribute_string()?;
let range = self.span_to_here(start);
attrs.deprecated = Some(Spanned::new(message, range));
return Ok(());
}
Some(gnu::Attribute::Cleanup) => {
let func = self.attribute_identifier()?;
let range = self.span_to_here(start);
attrs.cleanup = Some(Cleanup { func, range });
return Ok(());
}
Some(gnu::Attribute::Mode) => {
let mode = self.attribute_identifier()?;
let range = self.span_to_here(start);
match mode {
Some(mode) => attrs.mode = Some(Spanned::new(mode.name, range)),
None => self.error(range, "'mode' takes one machine mode name"),
}
return Ok(());
}
Some(gnu::Attribute::Section) => {
let name = self.attribute_string()?;
let range = self.span_to_here(start);
match name {
Some(name) => attrs.section = Some(Spanned::new(name, range)),
None => self.error(range, "'section' takes one string literal"),
}
return Ok(());
}
Some(gnu::Attribute::Target) => {
let text = self.attribute_strings()?;
let range = self.span_to_here(start);
if text.is_empty() {
self.error(
range,
"'target' takes one or more string literals naming an instruction set, \
as in target(\"avx2\")",
);
return Ok(());
}
for part in text.iter().flat_map(|s| s.split(',')) {
let part = part.trim();
if !part.is_empty() {
attrs.target.push(Spanned::new(part.to_owned(), range));
}
}
return Ok(());
}
_ => {}
}
self.skip_attribute_args()?;
let range = self.span_to_here(start);
match known {
Some(gnu::Attribute::Noreturn) => attrs.noreturn = attrs.noreturn.or(Some(range)),
Some(gnu::Attribute::AlwaysInline) => {
attrs.always_inline = attrs.always_inline.or(Some(range));
}
Some(gnu::Attribute::NoInline) => attrs.noinline = attrs.noinline.or(Some(range)),
Some(gnu::Attribute::Cold) => attrs.cold = attrs.cold.or(Some(range)),
Some(gnu::Attribute::Hot) => attrs.cold = None,
Some(gnu::Attribute::Packed) => attrs.packed = attrs.packed.or(Some(range)),
Some(gnu::Attribute::Constructor) => {
attrs.constructor = attrs.constructor.or(Some(range));
}
Some(gnu::Attribute::Destructor) => {
attrs.destructor = attrs.destructor.or(Some(range));
}
Some(gnu::Attribute::Safe) => attrs.safe = attrs.safe.or(Some(range)),
Some(gnu::Attribute::Weak) => attrs.weak = attrs.weak.or(Some(range)),
Some(gnu::Attribute::Fallthrough) | Some(gnu::Attribute::Ignored) => {}
Some(gnu::Attribute::Unsupported) => {
let reason = gnu::unsupported_reason(&name).unwrap_or("is not supported");
self.error(range, format!("'{name}' {reason}"));
}
_ => {}
}
let _ = close;
Ok(())
}
fn attribute_identifier(&mut self) -> PResult<Option<Ident>> {
if !self.at_punct(Punct::LParen) {
return Ok(None);
}
self.advance();
let name = match &self.peek().kind {
TokenKind::Ident(name) => {
let ident = Ident {
name: name.clone(),
range: self.cur_range(),
};
self.advance();
self.at_punct(Punct::RParen).then_some(ident)
}
_ => None,
};
let mut depth = 1i32;
while depth > 0 && !self.at_eof() {
if self.at_punct(Punct::LParen) {
depth += 1;
} else if self.at_punct(Punct::RParen) {
depth -= 1;
if depth == 0 {
self.advance();
break;
}
}
self.advance();
}
Ok(name)
}
fn attribute_strings(&mut self) -> PResult<Vec<String>> {
let mut out = Vec::new();
if !self.at_punct(Punct::LParen) {
return Ok(out);
}
self.advance();
let mut depth = 1i32;
while depth > 0 && !self.at_eof() {
if let TokenKind::Str(lit) = self.peek().kind.clone() {
let range = self.cur_range();
let literal = self.parse_string_literal(lit, range);
if let ExprKind::Str(lit) = literal.kind
&& let Ok(text) =
String::from_utf8(lit.values.iter().map(|v| *v as u8).collect())
{
out.push(text);
}
continue;
}
if self.at_punct(Punct::LParen) {
depth += 1;
} else if self.at_punct(Punct::RParen) {
depth -= 1;
if depth == 0 {
self.advance();
break;
}
}
self.advance();
}
Ok(out)
}
fn attribute_string(&mut self) -> PResult<Option<String>> {
if !self.at_punct(Punct::LParen) {
return Ok(None);
}
self.advance();
let mut text = None;
if let TokenKind::Str(lit) = self.peek().kind.clone() {
let range = self.cur_range();
let literal = self.parse_string_literal(lit, range);
if let ExprKind::Str(lit) = literal.kind {
text = String::from_utf8(lit.values.iter().map(|v| *v as u8).collect()).ok();
}
}
let mut depth = 1i32;
while depth > 0 && !self.at_eof() {
if self.at_punct(Punct::LParen) {
depth += 1;
} else if self.at_punct(Punct::RParen) {
depth -= 1;
if depth == 0 {
self.advance();
break;
}
}
self.advance();
}
Ok(text)
}
fn skip_attribute_args(&mut self) -> PResult<()> {
if !self.at_punct(Punct::LParen) {
return Ok(());
}
let start = self.cur_range();
let mut depth = 0i32;
while !self.at_eof() {
if self.at_punct(Punct::LParen) {
depth += 1;
} else if self.at_punct(Punct::RParen) {
depth -= 1;
if depth == 0 {
self.advance();
return Ok(());
}
}
self.advance();
}
Err(self.error_bail(start, "unterminated attribute argument list"))
}
fn list_of_names(names: &[&str]) -> String {
let quoted: Vec<String> = names.iter().map(|name| format!("'{name}'")).collect();
match quoted.split_last() {
None => String::new(),
Some((last, [])) => last.clone(),
Some((last, rest)) => format!("{} and {last}", rest.join(", ")),
}
}
fn at_static_assert(&self) -> bool {
matches!(
self.peek().keyword(),
Some(Keyword::StaticAssert | Keyword::StaticAssertName)
)
}
fn parse_static_assert(&mut self) -> PResult<StaticAssert> {
let start = self.cur_range();
let keyword = self.peek().keyword().expect("the caller checked");
self.require_keyword(keyword, start);
self.advance();
let name = keyword.as_str();
self.expect_punct(Punct::LParen, &format!(" after '{name}'"))?;
let cond = self.parse_conditional_expr()?;
let mut message = None;
if self.eat_punct(Punct::Comma).is_some() {
let range = self.cur_range();
let TokenKind::Str(first) = self.peek().kind.clone() else {
let found = self.describe_cur();
return Err(self.error_bail(
range,
format!("expected a string literal as the message of '{name}', found {found}"),
));
};
let literal = self.parse_string_literal(first, range);
if let ExprKind::Str(lit) = literal.kind {
message = Some(lit.text);
}
} else {
self.require_standard(
Standard::C23,
&format!("'{name}' without a message"),
self.span_to_here(start),
);
}
self.expect_punct(Punct::RParen, &format!(" to close '{name}'"))?;
self.expect_punct(Punct::Semi, &format!(" after '{name}'"))?;
Ok(StaticAssert {
cond,
message,
range: self.span_to_here(start),
})
}
}
impl Parser<'_> {
fn parse_translation_unit(&mut self, range: SourceRange) -> TranslationUnit {
let mut items = Vec::new();
while !self.at_eof() {
let before = self.pos;
self.depth = 0;
if self.at_punct(Punct::Semi) {
let range = self.bump_range();
if !self.gnu_leniency() {
self.error_gnu(range, "expected a declaration, found ';'");
}
continue;
}
self.decl_start = before;
match self.parse_external_decl() {
Ok(item) => items.push(item),
Err(Bail) => self.recover_top_level(before),
}
if self.pos == before {
self.advance();
}
}
TranslationUnit {
items,
records: std::mem::take(&mut self.records),
enums: std::mem::take(&mut self.enums),
typeofs: std::mem::take(&mut self.typeofs),
range,
}
}
fn depth_from(&self, start: usize) -> i32 {
let mut depth = 0i32;
for tok in &self.tokens[start.min(self.pos)..self.pos] {
match &tok.kind {
TokenKind::Punct(Punct::LBrace | Punct::LParen | Punct::LBracket) => depth += 1,
TokenKind::Punct(Punct::RBrace | Punct::RParen | Punct::RBracket) => depth -= 1,
_ => {}
}
}
depth.max(0)
}
fn recover_top_level(&mut self, decl_start: usize) {
let mut depth = self.depth_from(decl_start);
while !self.at_eof() {
match &self.peek().kind {
TokenKind::Punct(Punct::LBrace | Punct::LParen | Punct::LBracket) => {
depth += 1;
self.advance();
}
TokenKind::Punct(Punct::RBrace | Punct::RParen | Punct::RBracket) => {
let paren = self.at_punct(Punct::RParen) || self.at_punct(Punct::RBracket);
depth -= 1;
self.advance();
if depth <= 0 {
if self.eat_punct(Punct::Semi).is_some() {
return;
}
if paren && self.at_punct(Punct::LBrace) {
depth = 0;
continue;
}
return;
}
}
TokenKind::Punct(Punct::Semi) => {
self.advance();
if depth <= 0 {
return;
}
}
_ => self.advance(),
}
}
}
fn parse_external_decl(&mut self) -> PResult<ExternalDecl> {
let start = self.cur_range();
self.in_extension = false;
while self.eat_keyword(Keyword::Extension).is_some() {
self.in_extension = true;
}
let attrs = self.parse_attributes()?;
if self.at_static_assert() {
return Ok(ExternalDecl::StaticAssert(self.parse_static_assert()?));
}
let mut specs = self.parse_decl_specifiers(true)?;
specs.attrs.merge(attrs);
specs.noreturn = specs.noreturn.or(specs.attrs.noreturn);
if let Some(semi) = self.eat_punct(Punct::Semi) {
return Ok(ExternalDecl::Decl(Decl {
specifiers: specs,
declarators: Vec::new(),
range: start.join(semi),
}));
}
let mut first = self.parse_declarator(specs.base.clone(), false)?;
self.parse_declarator_tail(&mut first)?;
let looks_like_definition = matches!(first.ty.kind, TypeKind::Function(_))
&& (self.at_punct(Punct::LBrace) || self.starts_declaration());
if looks_like_definition && !specs.is_typedef() {
return self.finish_function_def(specs, first, start);
}
let decl = self.finish_declaration(specs, Some(first), start)?;
Ok(ExternalDecl::Decl(decl))
}
fn parse_declarator_tail(&mut self, declarator: &mut DeclaratorResult) -> PResult<()> {
loop {
if self.at_keyword(Keyword::Asm) {
let start = self.cur_range();
self.advance();
self.expect_punct(Punct::LParen, " after 'asm'")?;
let range = self.cur_range();
let TokenKind::Str(lit) = self.peek().kind.clone() else {
let found = self.describe_cur();
return Err(self.error_bail(
range,
format!("expected the symbol name as a string literal, found {found}"),
));
};
let literal = self.parse_string_literal(lit, range);
self.expect_punct(Punct::RParen, " after the symbol name")?;
if let ExprKind::Str(lit) = literal.kind
&& let Ok(name) =
String::from_utf8(lit.values.iter().map(|v| *v as u8).collect())
{
declarator.asm_label = Some(Spanned::new(name, self.span_to_here(start)));
}
continue;
}
if self.at_attributes() {
let attrs = self.parse_attributes()?;
declarator.attrs.merge(attrs);
continue;
}
return Ok(());
}
}
fn parse_kr_declaration_list(&mut self) -> PResult<Vec<Decl>> {
let mut decls = Vec::new();
loop {
if self.at_static_assert() {
let range = self.cur_range();
self.error(
range,
"a static assertion is not allowed in the declaration list of an old-style \
function definition; every declaration there has to declare one of the \
parameters (C99 6.9.1p6)",
);
self.parse_static_assert()?;
continue;
}
if !self.starts_declaration() {
return Ok(decls);
}
decls.push(self.parse_declaration()?);
}
}
fn finish_function_def(
&mut self,
specs: DeclSpecifiers,
declarator: DeclaratorResult,
start: SourceRange,
) -> PResult<ExternalDecl> {
let Some(name) = declarator.name.clone() else {
return Err(self.error_bail(declarator.range, "function definition requires a name"));
};
self.declare(&name.name, SymKind::Ordinary);
self.push_scope();
if let TypeKind::Function(ft) = &declarator.ty.kind {
for param in &ft.params {
if let Some(pname) = ¶m.name {
self.scopes
.last_mut()
.expect("scope stack is never empty")
.syms
.insert(pname.name.clone(), SymKind::Ordinary);
}
}
for kr in &ft.kr_names {
self.scopes
.last_mut()
.expect("scope stack is never empty")
.syms
.insert(kr.name.clone(), SymKind::Ordinary);
}
}
let kr_decls = match self.parse_kr_declaration_list() {
Ok(decls) => decls,
Err(bail) => {
self.pop_scope();
return Err(bail);
}
};
let before = self.label_addrs;
let body = match self.parse_compound_stmt() {
Ok(body) => body,
Err(bail) => {
self.pop_scope();
return Err(bail);
}
};
self.pop_scope();
let mut attrs = declarator.attrs;
if attrs.target.is_empty() && specs.attrs.target.is_empty() && !self.targets.is_empty() {
for (feature, range) in self.targets.at(self.decl_start) {
attrs.target.push(Spanned::new(feature.clone(), *range));
}
}
Ok(ExternalDecl::Function(FunctionDef {
specifiers: specs,
name,
ty: declarator.ty,
kr_decls,
attrs,
asm_label: declarator.asm_label,
body,
uses_label_addrs: self.label_addrs != before,
range: self.span_to_here(start),
}))
}
fn finish_declaration(
&mut self,
specs: DeclSpecifiers,
first: Option<DeclaratorResult>,
start: SourceRange,
) -> PResult<Decl> {
let is_typedef = specs.is_typedef();
let mut declarators = Vec::new();
let mut pending = first;
loop {
let mut declarator = match pending.take() {
Some(d) => d,
None => {
let mut d = self.parse_declarator(specs.base.clone(), false)?;
self.parse_declarator_tail(&mut d)?;
d
}
};
if let Some(name) = &declarator.name {
let kind = if is_typedef {
SymKind::Typedef
} else {
SymKind::Ordinary
};
self.declare(&name.name.clone(), kind);
}
let init = if self.eat_punct(Punct::Assign).is_some() {
Some(self.parse_initializer()?)
} else {
None
};
if self.at_attributes() {
let attrs = self.parse_attributes()?;
declarator.attrs.merge(attrs);
}
let range = self.span_to_here(declarator.range);
declarators.push(InitDeclarator {
name: declarator.name,
ty: declarator.ty,
init,
attrs: declarator.attrs,
asm_label: declarator.asm_label,
range,
});
if self.eat_punct(Punct::Comma).is_none() {
break;
}
}
let semi = self.expect_punct(Punct::Semi, " after declaration")?;
Ok(Decl {
specifiers: specs,
declarators,
range: start.join(semi),
})
}
fn parse_declaration(&mut self) -> PResult<Decl> {
let enclosing = std::mem::take(&mut self.in_extension);
let result = self.parse_declaration_inner();
self.in_extension = enclosing;
result
}
fn parse_declaration_inner(&mut self) -> PResult<Decl> {
let (specs, start) = self.parse_declaration_head()?;
if let Some(semi) = self.eat_punct(Punct::Semi) {
return Ok(Decl {
specifiers: specs,
declarators: Vec::new(),
range: start.join(semi),
});
}
self.finish_declaration(specs, None, start)
}
fn parse_declaration_head(&mut self) -> PResult<(DeclSpecifiers, SourceRange)> {
let start = self.cur_range();
while self.eat_keyword(Keyword::Extension).is_some() {
self.in_extension = true;
}
let attrs = self.parse_attributes()?;
let mut specs = self.parse_decl_specifiers(true)?;
specs.attrs.merge(attrs);
specs.noreturn = specs.noreturn.or(specs.attrs.noreturn);
Ok((specs, start))
}
fn parse_block_declaration(&mut self) -> PResult<BlockItem> {
let enclosing = std::mem::take(&mut self.in_extension);
let result = self.parse_block_declaration_inner();
self.in_extension = enclosing;
result
}
fn parse_block_declaration_inner(&mut self) -> PResult<BlockItem> {
let (specs, start) = self.parse_declaration_head()?;
if let Some(semi) = self.eat_punct(Punct::Semi) {
return Ok(BlockItem::Decl(Decl {
specifiers: specs,
declarators: Vec::new(),
range: start.join(semi),
}));
}
let mut first = self.parse_declarator(specs.base.clone(), false)?;
self.parse_declarator_tail(&mut first)?;
if self.at_nested_function_body(&specs, &first) {
return self
.parse_nested_function(specs, first, start)
.map(BlockItem::NestedFunction);
}
self.finish_declaration(specs, Some(first), start)
.map(BlockItem::Decl)
}
fn at_nested_function_body(
&self,
specs: &DeclSpecifiers,
declarator: &DeclaratorResult,
) -> bool {
declarator.name.is_some()
&& !specs.is_typedef()
&& matches!(declarator.ty.kind, TypeKind::Function(_))
&& (self.at_punct(Punct::LBrace) || self.at_kr_declaration_list())
}
fn at_kr_declaration_list(&self) -> bool {
let mut n = 0;
while self.starts_decl_specifier(n) {
let mut depth = 0i32;
loop {
let tok = self.nth(n);
if tok.is_eof() {
return false;
}
n += 1;
match &tok.kind {
TokenKind::Punct(Punct::LBrace | Punct::LParen | Punct::LBracket) => depth += 1,
TokenKind::Punct(Punct::RBrace | Punct::RParen | Punct::RBracket) => {
depth -= 1;
if depth < 0 {
return false;
}
}
TokenKind::Punct(Punct::Semi) if depth == 0 => break,
_ => {}
}
}
}
n > 0 && self.nth(n).is_punct(Punct::LBrace)
}
fn parse_nested_function(
&mut self,
specs: DeclSpecifiers,
declarator: DeclaratorResult,
start: SourceRange,
) -> PResult<FunctionDef> {
let name = declarator
.name
.clone()
.expect("at_nested_function_body requires a name");
self.declare(&name.name, SymKind::Ordinary);
self.push_scope();
if let TypeKind::Function(ft) = &declarator.ty.kind {
for param in &ft.params {
if let Some(pname) = ¶m.name {
self.scopes
.last_mut()
.expect("scope stack is never empty")
.syms
.insert(pname.name.clone(), SymKind::Ordinary);
}
}
for kr in &ft.kr_names {
self.scopes
.last_mut()
.expect("scope stack is never empty")
.syms
.insert(kr.name.clone(), SymKind::Ordinary);
}
}
let kr_decls = match self.parse_kr_declaration_list() {
Ok(decls) => decls,
Err(bail) => {
self.pop_scope();
return Err(bail);
}
};
let before = self.label_addrs;
let body = match self.parse_compound_stmt() {
Ok(body) => body,
Err(bail) => {
self.pop_scope();
return Err(bail);
}
};
self.pop_scope();
let uses_label_addrs = self.label_addrs != before;
self.label_addrs = before;
Ok(FunctionDef {
specifiers: specs,
name,
ty: declarator.ty,
kr_decls,
attrs: declarator.attrs,
asm_label: declarator.asm_label,
body,
uses_label_addrs,
range: self.span_to_here(start),
})
}
}
#[derive(Default)]
struct SpecCounts {
void: u32,
char: u32,
short: u32,
int: u32,
long: u32,
float: u32,
double: u32,
signed: u32,
unsigned: u32,
bool: u32,
complex: u32,
imaginary: u32,
int128: u32,
floatn: u32,
floatn_size: Option<FloatSize>,
}
impl SpecCounts {
fn any(&self) -> bool {
self.void
+ self.char
+ self.short
+ self.int
+ self.long
+ self.float
+ self.double
+ self.signed
+ self.unsigned
+ self.bool
+ self.complex
+ self.imaginary
+ self.int128
+ self.floatn
> 0
}
}
impl Parser<'_> {
fn starts_declaration(&self) -> bool {
self.starts_decl_specifier(0)
}
fn starts_declarator(&self) -> bool {
let tok = self.peek();
tok.ident().is_some() || tok.is_punct(Punct::Star) || tok.is_punct(Punct::LParen)
}
fn starts_decl_specifier(&self, n: usize) -> bool {
let tok = self.nth(n);
if let Some(k) = tok.keyword() {
return matches!(
k,
Keyword::Typedef
| Keyword::Extern
| Keyword::Static
| Keyword::Auto
| Keyword::Register
| Keyword::Const
| Keyword::Volatile
| Keyword::Restrict
| Keyword::Inline
| Keyword::Void
| Keyword::Char
| Keyword::Short
| Keyword::Int
| Keyword::Long
| Keyword::Float
| Keyword::Double
| Keyword::Signed
| Keyword::Unsigned
| Keyword::Bool
| Keyword::Complex
| Keyword::Imaginary
| Keyword::Struct
| Keyword::Union
| Keyword::Enum
| Keyword::Alignas
| Keyword::AlignasName
| Keyword::Atomic
| Keyword::BitInt
| Keyword::Noreturn
| Keyword::ThreadLocal
| Keyword::ThreadLocalName
| Keyword::Constexpr
| Keyword::Typeof
| Keyword::TypeofUnqual
| Keyword::BoolName
| Keyword::Attribute
| Keyword::Extension
| Keyword::TypeofGnu
| Keyword::TypeofUnqualGnu
| Keyword::AutoType
| Keyword::ThreadGnu
| Keyword::Int128
| Keyword::InlineGnu
| Keyword::RestrictGnu
);
}
match tok.ident() {
Some(NORETURN_BUILTIN) => true,
Some(name) if floatn_type(name).is_some() && !self.is_typedef_name(name) => true,
Some(name) => self.is_typedef_name(name) && !self.nth(n + 1).is_punct(Punct::Colon),
None => false,
}
}
fn eat_type_qualifier(&mut self) -> Option<TypeQualifiers> {
let keyword = self.peek().keyword()?;
let q = match keyword {
Keyword::Const => TypeQualifiers {
is_const: true,
..TypeQualifiers::NONE
},
Keyword::Volatile => TypeQualifiers {
is_volatile: true,
..TypeQualifiers::NONE
},
Keyword::Restrict | Keyword::RestrictGnu => TypeQualifiers {
is_restrict: true,
..TypeQualifiers::NONE
},
Keyword::Atomic if !self.at_atomic_specifier(0) => TypeQualifiers {
is_atomic: true,
..TypeQualifiers::NONE
},
_ => return None,
};
if keyword == Keyword::Restrict {
let range = self.cur_range();
self.require_standard(Standard::C99, "'restrict'", range);
}
if keyword == Keyword::Atomic {
let range = self.cur_range();
self.require_keyword(Keyword::Atomic, range);
}
self.advance();
Some(q)
}
fn at_atomic_specifier(&self, n: usize) -> bool {
self.nth(n).is_keyword(Keyword::Atomic)
&& self.nth(n + 1).is_punct(Punct::LParen)
&& self.starts_decl_specifier(n + 2)
}
fn parse_type_qualifiers(&mut self) -> TypeQualifiers {
let mut quals = TypeQualifiers::NONE;
while let Some(q) = self.eat_type_qualifier() {
quals = quals.merge(q);
}
quals
}
fn parse_decl_specifiers(&mut self, allow_storage: bool) -> PResult<DeclSpecifiers> {
let start = self.cur_range();
let mut storage: Option<Spanned<StorageClass>> = None;
let mut thread_local: Option<SourceRange> = None;
let mut inline = false;
let mut noreturn: Option<SourceRange> = None;
let mut alignas: Vec<Alignment> = Vec::new();
let mut attributes = Attributes::default();
let mut quals = TypeQualifiers::NONE;
let mut counts = SpecCounts::default();
let mut tag: Option<Type> = None;
let mut typedef_name: Option<Ident> = None;
let mut auto_type: Option<SourceRange> = None;
let mut auto_kw: Option<SourceRange> = None;
let mut consumed_any = false;
loop {
let has_type = counts.any() || tag.is_some() || typedef_name.is_some();
if self.at_attributes() {
let attrs = self.parse_attributes()?;
noreturn = noreturn.or(attrs.noreturn);
attributes.merge(attrs);
consumed_any = true;
continue;
}
if self.at_keyword(Keyword::Extension) {
self.advance();
self.in_extension = true;
consumed_any = true;
continue;
}
if let Some(k) = self.peek().keyword() {
if matches!(
k,
Keyword::ThreadLocal | Keyword::ThreadLocalName | Keyword::ThreadGnu
) {
let range = self.bump_range();
self.require_keyword(k, range);
consumed_any = true;
if !allow_storage {
self.error(
range,
format!("storage class '{}' is not allowed here", k.as_str()),
);
} else if thread_local.is_none() {
thread_local = Some(range);
}
continue;
}
let storage_class = match k {
Keyword::Typedef => Some(StorageClass::Typedef),
Keyword::Extern => Some(StorageClass::Extern),
Keyword::Static => Some(StorageClass::Static),
Keyword::Auto => Some(StorageClass::Auto),
Keyword::Register => Some(StorageClass::Register),
Keyword::Constexpr => Some(StorageClass::Constexpr),
_ => None,
};
if let Some(sc) = storage_class {
let range = self.bump_range();
self.require_keyword(k, range);
consumed_any = true;
let c23_auto = allow_storage
&& self.standard >= Standard::C23
&& (sc == StorageClass::Auto
|| matches!(
storage,
Some(Spanned {
node: StorageClass::Auto,
..
})
))
&& sc != StorageClass::Typedef
&& !matches!(
storage,
Some(Spanned {
node: StorageClass::Typedef,
..
})
);
let pairs_with_constexpr = |s: StorageClass| {
matches!(
s,
StorageClass::Static | StorageClass::Register | StorageClass::Auto
)
};
let c23_constexpr = allow_storage
&& self.standard >= Standard::C23
&& match (sc, storage.as_ref().map(|s| s.node)) {
(StorageClass::Constexpr, Some(prev)) => pairs_with_constexpr(prev),
(other, Some(StorageClass::Constexpr)) => pairs_with_constexpr(other),
_ => false,
};
if sc == StorageClass::Auto {
auto_kw = auto_kw.or(Some(range));
}
if !allow_storage {
self.error(
range,
format!("storage class '{}' is not allowed here", sc.as_str()),
);
} else if c23_constexpr {
if sc == StorageClass::Constexpr {
storage = Some(Spanned::new(sc, range));
}
} else if c23_auto {
if sc != StorageClass::Auto || storage.is_none() {
storage = Some(Spanned::new(sc, range));
}
} else if let Some(prev) = &storage {
self.error(
range,
format!(
"cannot combine storage class '{}' with '{}'",
sc.as_str(),
prev.node.as_str()
),
);
} else {
storage = Some(Spanned::new(sc, range));
}
continue;
}
if let Some(q) = self.eat_type_qualifier() {
quals = quals.merge(q);
consumed_any = true;
continue;
}
if matches!(k, Keyword::Inline | Keyword::InlineGnu) {
let range = self.bump_range();
if k == Keyword::Inline {
self.require_standard(Standard::C99, "'inline'", range);
}
inline = true;
consumed_any = true;
continue;
}
if k == Keyword::Noreturn {
let range = self.bump_range();
self.require_keyword(k, range);
noreturn = noreturn.or(Some(range));
consumed_any = true;
continue;
}
if matches!(k, Keyword::Alignas | Keyword::AlignasName) {
let spec = self.parse_alignment_specifier(k)?;
alignas.push(spec);
consumed_any = true;
continue;
}
if k == Keyword::AutoType {
let range = self.bump_range();
auto_type = auto_type.or(Some(range));
consumed_any = true;
continue;
}
if matches!(
k,
Keyword::Typeof
| Keyword::TypeofUnqual
| Keyword::TypeofGnu
| Keyword::TypeofUnqualGnu
) {
let ty = self.parse_typeof_specifier(k)?;
if tag.is_some() || has_type {
self.error(ty.range, "two or more data types in declaration specifiers");
} else {
tag = Some(ty);
}
consumed_any = true;
continue;
}
if k == Keyword::Atomic {
let range = self.bump_range();
self.require_keyword(k, range);
self.expect_punct(Punct::LParen, " after '_Atomic'")?;
let inner = self.parse_type_name()?;
self.expect_punct(Punct::RParen, " after the type name")?;
if tag.is_some() || has_type {
self.error(range, "two or more data types in declaration specifiers");
} else {
tag = Some(inner.ty);
}
quals = quals.merge(TypeQualifiers {
is_atomic: true,
..TypeQualifiers::NONE
});
consumed_any = true;
continue;
}
if k == Keyword::BitInt {
let range = self.bump_range();
self.error(range, format!("'{}' is not supported yet", k.as_str()));
if self.at_punct(Punct::LParen) {
self.advance();
let _ = self.parse_conditional_expr()?;
self.expect_punct(Punct::RParen, " after the operand")?;
}
counts.int += 1;
consumed_any = true;
continue;
}
let counter = match k {
Keyword::Void => Some(&mut counts.void),
Keyword::Char => Some(&mut counts.char),
Keyword::Short => Some(&mut counts.short),
Keyword::Int => Some(&mut counts.int),
Keyword::Long => Some(&mut counts.long),
Keyword::Float => Some(&mut counts.float),
Keyword::Double => Some(&mut counts.double),
Keyword::Signed => Some(&mut counts.signed),
Keyword::Unsigned => Some(&mut counts.unsigned),
Keyword::Bool | Keyword::BoolName => Some(&mut counts.bool),
Keyword::Complex => Some(&mut counts.complex),
Keyword::Imaginary => Some(&mut counts.imaginary),
Keyword::Int128 => Some(&mut counts.int128),
_ => None,
};
if let Some(c) = counter {
*c += 1;
self.advance();
consumed_any = true;
continue;
}
if matches!(k, Keyword::Struct | Keyword::Union) {
let ty = self.parse_record_specifier()?;
if tag.is_some() || has_type {
self.error(ty.range, "two or more data types in declaration specifiers");
} else {
tag = Some(ty);
}
consumed_any = true;
continue;
}
if k == Keyword::Enum {
let ty = self.parse_enum_specifier()?;
if tag.is_some() || has_type {
self.error(ty.range, "two or more data types in declaration specifiers");
} else {
tag = Some(ty);
}
consumed_any = true;
continue;
}
break;
}
if self.peek().ident() == Some(NORETURN_BUILTIN) {
let range = self.bump_range();
noreturn = noreturn.or(Some(range));
consumed_any = true;
continue;
}
if tag.is_none()
&& typedef_name.is_none()
&& !counts.any_besides(&["_Complex"])
&& let Some(name) = self.peek().ident()
&& !self.is_typedef_name(name)
&& let Some(size) = floatn_type(name)
{
self.advance();
counts.floatn += 1;
counts.floatn_size = Some(size);
consumed_any = true;
continue;
}
if !has_type
&& let Some(name) = self.peek().ident()
&& !self.is_typedef_name(name)
&& let Some(what) = extended_float_type(name)
{
let range = self.cur_range();
return Err(self.error_bail(
range,
format!(
"'{name}' is not supported: {what} has no Rust type to become, and \
mapping it onto 'double' would compute and pass the wrong values"
),
));
}
let is_typedef_use = match self.peek().ident() {
Some(name) => !has_type && self.is_typedef_name(name),
None => false,
};
if is_typedef_use {
let id = self.eat_ident().expect("checked above");
typedef_name = Some(id);
consumed_any = true;
continue;
}
break;
}
if !consumed_any {
let range = self.cur_range();
if let Some(message) = self.newer_keyword_here() {
return Err(self.error_bail(range, message));
}
if !self.starts_declarator() {
let found = self.describe_cur();
return Err(
self.error_bail(range, format!("expected a declaration, found {found}"))
);
}
}
let specs_range = if consumed_any {
self.span_to_here(start)
} else {
self.cur_range()
};
let no_type = !counts.any() && tag.is_none() && typedef_name.is_none();
let inferred = no_type
&& (auto_type.is_some() || (self.standard >= Standard::C23 && auto_kw.is_some()));
let base = if inferred {
Type::new(TypeKind::Auto, quals, specs_range)
} else {
self.build_base_type(&counts, tag, typedef_name, quals, specs_range)
};
if let Some(aligned) = attributes.aligned.clone() {
alignas.push(aligned);
}
Ok(DeclSpecifiers {
storage,
thread_local,
inline,
noreturn,
alignas,
attrs: attributes,
base,
range: specs_range,
})
}
fn parse_alignment_specifier(&mut self, keyword: Keyword) -> PResult<Alignment> {
let start = self.cur_range();
self.require_keyword(keyword, start);
self.advance();
let name = keyword.as_str();
self.expect_punct(Punct::LParen, &format!(" after '{name}'"))?;
let kind = if self.starts_declaration() {
AlignmentKind::Type(Box::new(self.parse_type_name()?))
} else {
AlignmentKind::Expr(self.parse_conditional_expr()?)
};
self.expect_punct(Punct::RParen, &format!(" after the operand of '{name}'"))?;
Ok(Alignment {
kind,
from_attribute: false,
range: self.span_to_here(start),
})
}
fn parse_typeof_specifier(&mut self, keyword: Keyword) -> PResult<Type> {
let start = self.cur_range();
self.require_keyword(keyword, start);
self.advance();
let name = keyword.as_str();
self.expect_punct(Punct::LParen, &format!(" after '{name}'"))?;
let operand = if self.starts_declaration() {
TypeofOperand::Type(self.parse_type_name()?)
} else {
TypeofOperand::Expr(self.parse_expr()?)
};
self.expect_punct(Punct::RParen, &format!(" after the operand of '{name}'"))?;
let range = self.span_to_here(start);
let id = self.add_typeof(operand);
let unqual = matches!(keyword, Keyword::TypeofUnqual | Keyword::TypeofUnqualGnu);
Ok(Type::plain(TypeKind::Typeof { id, unqual }, range))
}
fn build_base_type(
&mut self,
counts: &SpecCounts,
tag: Option<Type>,
typedef_name: Option<Ident>,
quals: TypeQualifiers,
range: SourceRange,
) -> Type {
if let Some(mut ty) = tag {
if counts.any() || typedef_name.is_some() {
self.error(range, "two or more data types in declaration specifiers");
}
ty.qualifiers = ty.qualifiers.merge(quals);
return ty;
}
if let Some(name) = typedef_name {
if counts.any() {
self.error(range, "two or more data types in declaration specifiers");
}
return Type::new(TypeKind::Typedef(name), quals, range);
}
if !counts.any() {
if !self.gating.implicit_int() {
self.error(
range,
"type specifier missing; C99 does not support implicit 'int'",
);
}
return Type::new(
TypeKind::Int {
sign: Sign::Signed,
size: IntSize::Int,
},
quals,
range,
);
}
if counts.bool > 0 {
self.require_standard(Standard::C99, "'_Bool'", range);
}
if counts.complex > 0 {
self.require_standard(Standard::C99, "'_Complex'", range);
}
if counts.imaginary > 0 {
self.require_standard(Standard::C99, "'_Imaginary'", range);
}
if counts.long > 1 {
self.require_standard(Standard::C99, "'long long'", range);
}
if (counts.complex > 0 || counts.imaginary > 0)
&& counts.float == 0
&& counts.double == 0
&& counts.int
+ counts.char
+ counts.short
+ counts.long
+ counts.signed
+ counts.unsigned
+ counts.int128
+ counts.bool
+ counts.void
> 0
{
self.error(
range,
"a complex integer type is a GNU extension that cinrs does not support; \
the complex types are 'float _Complex', 'double _Complex' and \
'long double _Complex'",
);
let kind = if counts.complex > 0 {
TypeKind::Complex(FloatSize::Double)
} else {
TypeKind::Imaginary(FloatSize::Double)
};
return Type::new(kind, quals, range);
}
let sign = if counts.unsigned > 0 {
Some(Sign::Unsigned)
} else if counts.signed > 0 {
Some(Sign::Signed)
} else {
None
};
if counts.signed > 0 && counts.unsigned > 0 {
self.error(range, "cannot combine 'signed' with 'unsigned'");
}
let kind = if counts.void > 0 {
if counts.void > 1 || counts.any_besides(&["void"]) {
self.error(range, "cannot combine 'void' with other type specifiers");
}
TypeKind::Void
} else if counts.bool > 0 {
if counts.any_besides(&["_Bool"]) {
self.error(range, "cannot combine '_Bool' with other type specifiers");
}
TypeKind::Bool
} else if counts.char > 0 {
if counts.any_besides(&["char", "signed", "unsigned"]) {
self.error(range, "cannot combine 'char' with other type specifiers");
}
TypeKind::Char(sign)
} else if counts.floatn > 0 {
let size = counts.floatn_size.expect("set with the count");
if counts.floatn > 1 || counts.any_besides(&["_FloatN", "_Complex"]) {
self.error(
range,
format!(
"cannot combine '{}' with other type specifiers",
size.as_str()
),
);
}
if counts.complex > 0 {
TypeKind::Complex(size)
} else {
TypeKind::Float(size)
}
} else if counts.float > 0 || counts.double > 0 {
let size = if counts.float > 0 {
if counts.double > 0 {
self.error(range, "cannot combine 'float' with 'double'");
}
if counts.long > 0 {
self.error(range, "cannot combine 'long' with 'float'");
}
FloatSize::Float
} else if counts.long > 0 {
FloatSize::LongDouble
} else {
FloatSize::Double
};
if sign.is_some() {
self.error(
range,
"cannot combine 'signed' or 'unsigned' with a floating type",
);
}
if counts.complex > 0 {
TypeKind::Complex(size)
} else if counts.imaginary > 0 {
TypeKind::Imaginary(size)
} else {
TypeKind::Float(size)
}
} else if counts.complex > 0 || counts.imaginary > 0 {
if counts.complex > 0 {
TypeKind::Complex(FloatSize::Double)
} else {
TypeKind::Imaginary(FloatSize::Double)
}
} else if counts.int128 > 0 {
if counts.int128 > 1 || counts.any_besides(&["__int128", "signed", "unsigned"]) {
self.error(
range,
"cannot combine '__int128' with other type specifiers",
);
}
TypeKind::Int {
sign: sign.unwrap_or(Sign::Signed),
size: IntSize::Int128,
}
} else {
let size = if counts.short > 0 {
if counts.long > 0 {
self.error(range, "cannot combine 'short' with 'long'");
}
IntSize::Short
} else {
match counts.long {
0 => IntSize::Int,
1 => IntSize::Long,
2 => IntSize::LongLong,
_ => {
self.error(range, "'long long long' is too long for cinrs");
IntSize::LongLong
}
}
};
TypeKind::Int {
sign: sign.unwrap_or(Sign::Signed),
size,
}
};
Type::new(kind, quals, range)
}
}
impl SpecCounts {
fn any_besides(&self, allowed: &[&str]) -> bool {
let all: [(&str, u32); 14] = [
("__int128", self.int128),
("_FloatN", self.floatn),
("void", self.void),
("char", self.char),
("short", self.short),
("int", self.int),
("long", self.long),
("float", self.float),
("double", self.double),
("signed", self.signed),
("unsigned", self.unsigned),
("_Bool", self.bool),
("_Complex", self.complex),
("_Imaginary", self.imaginary),
];
all.iter()
.any(|(name, count)| *count > 0 && !allowed.contains(name))
}
}
impl Parser<'_> {
fn add_record(&mut self, spec: RecordSpec) -> RecordSpecId {
let id = RecordSpecId(self.records.len() as u32);
self.records.push(spec);
id
}
fn add_enum(&mut self, spec: EnumSpec) -> EnumSpecId {
let id = EnumSpecId(self.enums.len() as u32);
self.enums.push(spec);
id
}
fn add_typeof(&mut self, operand: TypeofOperand) -> TypeofId {
let id = TypeofId(self.typeofs.len() as u32);
self.typeofs.push(operand);
id
}
fn parse_record_specifier(&mut self) -> PResult<Type> {
self.enter()?;
let result = self.parse_record_specifier_inner();
self.leave();
result
}
fn parse_record_specifier_inner(&mut self) -> PResult<Type> {
let start = self.cur_range();
let pack = self.packing.at(self.pos);
let kind = match self.peek().keyword() {
Some(Keyword::Struct) => RecordKind::Struct,
Some(Keyword::Union) => RecordKind::Union,
_ => unreachable!("caller checked the keyword"),
};
self.advance();
let mut attrs = self.parse_attributes()?;
let name = self.eat_ident();
let mut asserts = Vec::new();
let fields = if self.at_punct(Punct::LBrace) {
let (fields, found) = self.parse_struct_body()?;
asserts = found;
Some(fields)
} else {
if name.is_none() {
let range = self.cur_range();
let found = self.describe_cur();
return Err(self.error_bail(
range,
format!(
"expected identifier or '{{' after '{}', found {found}",
kind.as_str()
),
));
}
None
};
if self.at_attributes() {
let after = self.parse_attributes()?;
attrs.merge(after);
}
let range = self.span_to_here(start);
let id = self.add_record(RecordSpec {
kind,
name,
fields,
asserts,
attrs,
pack,
range,
});
Ok(Type::plain(TypeKind::Record(id), range))
}
fn parse_struct_body(&mut self) -> PResult<(Vec<FieldDecl>, Vec<StaticAssert>)> {
self.expect_punct(Punct::LBrace, " to open a member list")?;
let mut fields = Vec::new();
let mut asserts = Vec::new();
while !self.at_punct(Punct::RBrace) && !self.at_eof() {
let before = self.pos;
if self.eat_punct(Punct::Semi).is_some() {
continue;
}
if self.at_static_assert() {
asserts.push(self.parse_static_assert()?);
continue;
}
let start = self.cur_range();
while self.eat_keyword(Keyword::Extension).is_some() {
self.in_extension = true;
}
let leading = self.parse_attributes()?;
let mut specs = self.parse_decl_specifiers(false)?;
specs.attrs.merge(leading);
if self.at_punct(Punct::Semi) {
let range = self.span_to_here(start);
self.require_standard(Standard::C11, "an anonymous struct or union member", range);
fields.push(FieldDecl {
ty: specs.base.clone(),
attrs: specs.attrs.clone(),
specifiers: specs,
name: None,
bit_width: None,
range,
});
self.expect_punct(Punct::Semi, " after member declaration")?;
continue;
}
loop {
let (name, ty, dstart, mut attrs) = if self.at_punct(Punct::Colon) {
(
None,
specs.base.clone(),
self.cur_range(),
Attributes::default(),
)
} else {
let mut d = self.parse_declarator(specs.base.clone(), true)?;
self.parse_declarator_tail(&mut d)?;
(d.name, d.ty, d.range, d.attrs)
};
let bit_width = if self.eat_punct(Punct::Colon).is_some() {
Some(self.parse_conditional_expr()?)
} else {
None
};
if self.at_attributes() {
let after = self.parse_attributes()?;
attrs.merge(after);
}
attrs.merge(specs.attrs.clone());
let range = self.span_to_here(dstart);
fields.push(FieldDecl {
specifiers: specs.clone(),
name,
ty,
bit_width,
attrs,
range,
});
if self.eat_punct(Punct::Comma).is_none() {
break;
}
}
self.expect_punct(Punct::Semi, " after member declaration")?;
if self.pos == before {
self.advance();
}
}
self.expect_punct(Punct::RBrace, " to close a member list")?;
Ok((fields, asserts))
}
fn parse_enum_specifier(&mut self) -> PResult<Type> {
let start = self.cur_range();
self.advance(); let _ = self.parse_attributes()?;
let name = self.eat_ident();
let underlying = if self.at_punct(Punct::Colon) && self.starts_decl_specifier(1) {
let colon = self.bump_range();
self.require_standard(Standard::C23, "an enum with a fixed underlying type", colon);
let specs = self.parse_decl_specifiers(false)?;
Some(specs.base)
} else {
None
};
let enumerators = if self.at_punct(Punct::LBrace) {
self.advance();
let mut list = Vec::new();
while !self.at_punct(Punct::RBrace) && !self.at_eof() {
let ename = self.expect_ident(" in enumerator list")?;
self.declare(&ename.name.clone(), SymKind::Ordinary);
let _ = self.parse_attributes()?;
let value = if self.eat_punct(Punct::Assign).is_some() {
Some(self.parse_conditional_expr()?)
} else {
None
};
let range = self.span_to_here(ename.range);
list.push(Enumerator {
name: ename,
value,
range,
});
let Some(comma) = self.eat_punct(Punct::Comma) else {
break;
};
if self.at_punct(Punct::RBrace) {
self.require_standard(
Standard::C99,
"a trailing comma in an enumerator list",
comma,
);
}
}
self.expect_punct(Punct::RBrace, " to close an enumerator list")?;
Some(list)
} else {
if name.is_none() {
let range = self.cur_range();
let found = self.describe_cur();
return Err(self.error_bail(
range,
format!("expected identifier or '{{' after 'enum', found {found}"),
));
}
None
};
let range = self.span_to_here(start);
let id = self.add_enum(EnumSpec {
name,
enumerators,
underlying,
range,
});
Ok(Type::plain(TypeKind::Enum(id), range))
}
}
#[derive(Clone, Debug)]
pub struct DeclaratorResult {
pub name: Option<Ident>,
pub ty: Type,
pub attrs: Attributes,
pub asm_label: Option<Spanned<String>>,
pub range: SourceRange,
}
impl Parser<'_> {
fn parse_declarator(&mut self, base: Type, allow_abstract: bool) -> PResult<DeclaratorResult> {
self.enter()?;
let result = self.parse_declarator_inner(base, allow_abstract);
self.leave();
result
}
fn parse_declarator_inner(
&mut self,
base: Type,
allow_abstract: bool,
) -> PResult<DeclaratorResult> {
let start = self.cur_range();
let leading = self.parse_attributes()?;
let mut ty = base;
while self.at_punct(Punct::Star) {
let star = self.bump_range();
let mut quals = self.parse_type_qualifiers();
while self.at_attributes() {
let _ = self.parse_attributes()?;
quals = quals.merge(self.parse_type_qualifiers());
}
let range = self.span_to_here(star);
ty = Type::new(TypeKind::Pointer(Box::new(ty)), quals, range);
}
if self.at_punct(Punct::LParen) && self.is_grouping_paren() {
let save = self.pos;
let balanced = self.skip_balanced_parens();
if !balanced {
let range = self.cur_range();
return Err(self.error_bail(range, "unbalanced '(' in declarator"));
}
let rparen = self.pos - 1;
ty = self.parse_type_suffix(ty)?;
let after = self.pos;
self.pos = save + 1;
let inner = self.parse_declarator(ty, allow_abstract)?;
if self.pos != rparen {
let range = self.cur_range();
let found = self.describe_cur();
return Err(self.error_bail(
range,
format!("expected ')' after declarator, found {found}"),
));
}
self.pos = after;
self.last_range = self.tokens[after - 1].range;
let mut attrs = inner.attrs;
attrs.merge(leading);
return Ok(DeclaratorResult {
name: inner.name,
ty: inner.ty,
attrs,
asm_label: inner.asm_label,
range: self.span_to_here(start),
});
}
let name = match self.eat_ident() {
Some(id) => Some(id),
None if allow_abstract => None,
None => {
let range = self.cur_range();
let found = self.describe_cur();
return Err(self.error_bail(
range,
format!("expected identifier in declarator, found {found}"),
));
}
};
let mut attrs = self.parse_attributes()?;
attrs.merge(leading);
let ty = self.parse_type_suffix(ty)?;
Ok(DeclaratorResult {
name,
ty,
attrs,
asm_label: None,
range: self.span_to_here(start),
})
}
fn is_grouping_paren(&self) -> bool {
let after = self.after_attributes(1);
!self.nth(after).is_punct(Punct::RParen) && !self.starts_decl_specifier(after)
}
fn after_attributes(&self, mut n: usize) -> usize {
loop {
let brackets =
self.nth(n).is_punct(Punct::LBracket) && self.nth(n + 1).is_punct(Punct::LBracket);
if !self.nth(n).is_keyword(Keyword::Attribute) && !brackets {
return n;
}
let (open, close) = if brackets {
(Punct::LBracket, Punct::RBracket)
} else {
(Punct::LParen, Punct::RParen)
};
let mut i = if brackets { n } else { n + 1 };
let mut depth = 0i32;
while !self.nth(i).is_eof() {
if self.nth(i).is_punct(open) {
depth += 1;
} else if self.nth(i).is_punct(close) {
depth -= 1;
if depth == 0 {
i += 1;
break;
}
}
i += 1;
}
if i <= n {
return n;
}
n = i;
}
}
fn skip_balanced_parens(&mut self) -> bool {
let mut depth = 0i32;
while !self.at_eof() {
if self.at_punct(Punct::LParen) {
depth += 1;
} else if self.at_punct(Punct::RParen) {
depth -= 1;
if depth == 0 {
self.advance();
return true;
}
}
self.advance();
}
false
}
fn parse_type_suffix(&mut self, ty: Type) -> PResult<Type> {
if let Some(lb) = self.eat_punct(Punct::LBracket) {
let mut is_static = false;
let mut quals = TypeQualifiers::NONE;
loop {
if self.at_keyword(Keyword::Static) {
self.advance();
is_static = true;
continue;
}
match self.eat_type_qualifier() {
Some(q) => quals = quals.merge(q),
None => break,
}
}
if is_static {
self.require_standard(
Standard::C99,
"'static' in an array parameter declarator",
lb,
);
}
let size = if self.at_punct(Punct::RBracket) {
ArraySize::Unspecified
} else if self.at_punct(Punct::Star) && self.nth(1).is_punct(Punct::RBracket) {
let star = self.bump_range();
self.require_standard(Standard::C99, "'[*]'", star);
ArraySize::Star
} else {
ArraySize::Expr(Box::new(self.parse_assignment_expr()?))
};
let rb = self.expect_punct(Punct::RBracket, " after array bound")?;
let elem = self.parse_type_suffix(ty)?;
return Ok(Type::plain(
TypeKind::Array {
elem: Box::new(elem),
size,
qualifiers: quals,
is_static,
},
lb.join(rb),
));
}
if let Some(lp) = self.eat_punct(Punct::LParen) {
let list = self.parse_param_list()?;
let rp = self.expect_punct(Punct::RParen, " after parameter list")?;
let ret = self.parse_type_suffix(ty)?;
return Ok(Type::plain(
TypeKind::Function(Box::new(FunctionType {
ret,
params: list.params,
variadic: list.ellipsis.is_some(),
ellipsis: list.ellipsis,
has_prototype: list.has_prototype,
kr_names: list.kr_names,
old_style: false,
})),
lp.join(rp),
));
}
Ok(ty)
}
fn parse_param_list(&mut self) -> PResult<ParamList> {
if self.at_punct(Punct::RParen) {
return Ok(ParamList::default());
}
if self.at_keyword(Keyword::Void) && self.nth(1).is_punct(Punct::RParen) {
self.advance();
return Ok(ParamList {
has_prototype: true,
..ParamList::default()
});
}
let kr = match self.peek().ident() {
Some(name) => !self.is_typedef_name(name) && floatn_type(name).is_none(),
None => false,
};
if kr {
let mut names = Vec::new();
loop {
names.push(self.expect_ident(" in parameter list")?);
if self.eat_punct(Punct::Comma).is_none() {
break;
}
}
return Ok(ParamList {
kr_names: names,
..ParamList::default()
});
}
self.push_scope();
let result = self.parse_prototype_params();
self.pop_scope();
result
}
fn parse_prototype_params(&mut self) -> PResult<ParamList> {
let mut params = Vec::new();
let mut ellipsis = None;
loop {
if self.at_punct(Punct::Ellipsis) {
ellipsis = Some(self.bump_range());
break;
}
let start = self.cur_range();
let specs = self.parse_decl_specifiers(true)?;
let declarator = self.parse_declarator(specs.base.clone(), true)?;
if let Some(name) = &declarator.name {
self.declare(&name.name.clone(), SymKind::Ordinary);
}
let range = self.span_to_here(start);
for cleanup in [&declarator.attrs.cleanup, &specs.attrs.cleanup]
.into_iter()
.flatten()
{
self.error(
cleanup.range,
"'cleanup' attribute ignored on a parameter: it calls the function when \
the object goes out of scope, and only an object with automatic storage \
duration ever does",
);
}
params.push(ParamDecl {
specifiers: specs,
name: declarator.name,
ty: declarator.ty,
range,
});
if self.eat_punct(Punct::Comma).is_none() {
break;
}
}
Ok(ParamList {
params,
ellipsis,
has_prototype: true,
kr_names: Vec::new(),
})
}
fn parse_type_name(&mut self) -> PResult<TypeName> {
let start = self.cur_range();
let specs = self.parse_decl_specifiers(false)?;
let declarator = self.parse_declarator(specs.base.clone(), true)?;
if let Some(name) = &declarator.name {
let range = name.range;
self.error(range, "a type name must not declare an identifier");
}
Ok(TypeName {
specifiers: specs,
ty: declarator.ty,
range: self.span_to_here(start),
})
}
}
#[derive(Default)]
struct ParamList {
params: Vec<ParamDecl>,
ellipsis: Option<SourceRange>,
has_prototype: bool,
kr_names: Vec<Ident>,
}
impl Parser<'_> {
fn parse_initializer(&mut self) -> PResult<Initializer> {
self.enter()?;
let result = self.parse_initializer_inner();
self.leave();
result
}
fn parse_initializer_inner(&mut self) -> PResult<Initializer> {
if self.at_punct(Punct::LBrace) {
let start = self.cur_range();
let items = self.parse_initializer_list()?;
return Ok(Initializer {
kind: InitializerKind::List(items),
range: self.span_to_here(start),
});
}
let expr = self.parse_assignment_expr()?;
Ok(Initializer {
range: expr.range,
kind: InitializerKind::Expr(expr),
})
}
fn parse_initializer_list(&mut self) -> PResult<Vec<InitItem>> {
let brace = self.expect_punct(Punct::LBrace, " to open an initializer list")?;
if self.at_punct(Punct::RBrace) {
let range = brace.join(self.cur_range());
self.require_standard(Standard::C23, "an empty initializer", range);
}
let mut items = Vec::new();
while !self.at_punct(Punct::RBrace) && !self.at_eof() {
let start = self.cur_range();
let mut designators = Vec::new();
let mut old_style = false;
if self.peek().ident().is_some() && self.nth(1).is_punct(Punct::Colon) {
let field = self.eat_ident().expect("checked above");
self.advance();
designators.push(Designator::Field(field));
old_style = true;
}
loop {
if old_style {
break;
}
if self.eat_punct(Punct::Dot).is_some() {
let field = self.expect_ident(" after '.' in designator")?;
designators.push(Designator::Field(field));
} else if self.eat_punct(Punct::LBracket).is_some() {
let index = self.parse_conditional_expr()?;
if self.eat_punct(Punct::Ellipsis).is_some() {
let high = self.parse_conditional_expr()?;
self.expect_punct(Punct::RBracket, " after array designator")?;
designators.push(Designator::Range(index, high));
} else {
self.expect_punct(Punct::RBracket, " after array designator")?;
designators.push(Designator::Index(index));
}
} else {
break;
}
}
if !designators.is_empty() {
let at = self.span_to_here(start);
self.require_standard(Standard::C99, "a designated initializer", at);
}
if !designators.is_empty() && !old_style {
self.expect_punct(Punct::Assign, " after designator")?;
}
let init = self.parse_initializer()?;
items.push(InitItem {
designators,
init,
range: self.span_to_here(start),
});
if self.eat_punct(Punct::Comma).is_none() {
break;
}
}
self.expect_punct(Punct::RBrace, " to close an initializer list")?;
Ok(items)
}
}
impl Parser<'_> {
fn parse_compound_stmt(&mut self) -> PResult<Block> {
let start = self.expect_punct(Punct::LBrace, " to open a block")?;
self.push_scope();
let mut local_labels = Vec::new();
while self.at_keyword(Keyword::Label) {
self.advance();
loop {
match self.expect_ident(" in a '__label__' declaration") {
Ok(name) => local_labels.push(name),
Err(bail) => {
self.pop_scope();
return Err(bail);
}
}
if self.eat_punct(Punct::Comma).is_none() {
break;
}
}
if let Err(bail) = self.expect_punct(Punct::Semi, " after '__label__'") {
self.pop_scope();
return Err(bail);
}
}
let mut items = Vec::new();
let mut saw_statement = false;
while !self.at_punct(Punct::RBrace) && !self.at_eof() {
let before = self.pos;
if saw_statement && self.starts_declaration() {
let at = self.cur_range();
self.require_standard(Standard::C99, "a declaration after a statement", at);
}
let item = if self.at_static_assert() {
self.parse_static_assert().map(BlockItem::StaticAssert)
} else {
match self.parse_attributes() {
Ok(attrs) => {
if self.starts_declaration() {
self.parse_block_declaration().map(|mut item| {
let specifiers = match &mut item {
BlockItem::Decl(decl) => Some(&mut decl.specifiers),
BlockItem::NestedFunction(def) => Some(&mut def.specifiers),
_ => None,
};
if let Some(specifiers) = specifiers {
specifiers.noreturn = specifiers.noreturn.or(attrs.noreturn);
specifiers.attrs.merge(attrs);
}
item
})
} else {
self.parse_stmt().map(BlockItem::Stmt)
}
}
Err(bail) => Err(bail),
}
};
match item {
Ok(item) => {
saw_statement |= matches!(item, BlockItem::Stmt(_));
items.push(item);
}
Err(bail) => {
self.pop_scope();
return Err(bail);
}
}
if self.pos == before {
self.advance();
}
}
self.pop_scope();
let end = self.expect_punct(Punct::RBrace, " to close a block")?;
Ok(Block {
items,
local_labels,
range: start.join(end),
})
}
fn parse_stmt(&mut self) -> PResult<Stmt> {
self.enter()?;
let result = self.parse_stmt_inner();
self.leave();
result
}
fn parse_stmt_inner(&mut self) -> PResult<Stmt> {
let mut labels: Vec<(PendingLabel, SourceRange)> = Vec::new();
let start = loop {
let start = self.cur_range();
let _ = self.parse_attributes()?;
while self.eat_keyword(Keyword::Extension).is_some() {}
let label = if self.peek().ident().is_some() && self.nth(1).is_punct(Punct::Colon) {
let label = self.eat_ident().expect("checked above");
self.advance(); PendingLabel::Ident { label }
} else if self.at_keyword(Keyword::Case) {
self.advance();
let value = self.parse_conditional_expr()?;
let upper = if self.eat_punct(Punct::Ellipsis).is_some() {
Some(self.parse_conditional_expr()?)
} else {
None
};
self.expect_punct(Punct::Colon, " after 'case' label")?;
PendingLabel::Case { value, upper }
} else if self.at_keyword(Keyword::Default) {
self.advance();
self.expect_punct(Punct::Colon, " after 'default' label")?;
PendingLabel::Default
} else {
break start;
};
labels.push((label, start));
if labels.len() > MAX_LABEL_CHAIN {
let range = self.cur_range();
return Err(self.error_bail(
range,
format!("more than {MAX_LABEL_CHAIN} labels on one statement"),
));
}
};
if !labels.is_empty() {
return self.finish_labeled_stmt(labels);
}
self.parse_unlabeled_stmt(start)
}
fn parse_unlabeled_stmt(&mut self, start: SourceRange) -> PResult<Stmt> {
if self.at_keyword(Keyword::Asm) {
return self.parse_asm_stmt();
}
if self.at_punct(Punct::LBrace) {
let block = self.parse_compound_stmt()?;
return Ok(Stmt {
range: block.range,
kind: StmtKind::Compound(block),
});
}
if let Some(k) = self.peek().keyword() {
match k {
Keyword::If => return self.parse_if_stmt(),
Keyword::Switch => {
self.advance();
self.expect_punct(Punct::LParen, " after 'switch'")?;
let cond = self.parse_expr()?;
self.expect_punct(Punct::RParen, " after switch condition")?;
let body = self.parse_stmt()?;
return Ok(Stmt {
kind: StmtKind::Switch {
cond,
body: Box::new(body),
},
range: self.span_to_here(start),
});
}
Keyword::While => {
self.advance();
self.expect_punct(Punct::LParen, " after 'while'")?;
let cond = self.parse_expr()?;
self.expect_punct(Punct::RParen, " after loop condition")?;
let body = self.parse_stmt()?;
return Ok(Stmt {
kind: StmtKind::While {
cond,
body: Box::new(body),
},
range: self.span_to_here(start),
});
}
Keyword::Do => {
self.advance();
let body = self.parse_stmt()?;
self.expect_keyword(Keyword::While, " after 'do' body")?;
self.expect_punct(Punct::LParen, " after 'while'")?;
let cond = self.parse_expr()?;
self.expect_punct(Punct::RParen, " after loop condition")?;
self.expect_punct(Punct::Semi, " after 'do' statement")?;
return Ok(Stmt {
kind: StmtKind::DoWhile {
body: Box::new(body),
cond,
},
range: self.span_to_here(start),
});
}
Keyword::For => return self.parse_for_stmt(),
Keyword::Goto => {
self.advance();
if self.eat_punct(Punct::Star).is_some() {
let target = self.parse_expr()?;
self.expect_punct(Punct::Semi, " after 'goto' statement")?;
return Ok(Stmt {
kind: StmtKind::GotoPtr(target),
range: self.span_to_here(start),
});
}
let label = self.expect_ident(" after 'goto'")?;
self.expect_punct(Punct::Semi, " after 'goto' statement")?;
return Ok(Stmt {
kind: StmtKind::Goto(label),
range: self.span_to_here(start),
});
}
Keyword::Continue => {
self.advance();
self.expect_punct(Punct::Semi, " after 'continue'")?;
return Ok(Stmt {
kind: StmtKind::Continue,
range: self.span_to_here(start),
});
}
Keyword::Break => {
self.advance();
self.expect_punct(Punct::Semi, " after 'break'")?;
return Ok(Stmt {
kind: StmtKind::Break,
range: self.span_to_here(start),
});
}
Keyword::Return => {
self.advance();
let value = if self.at_punct(Punct::Semi) {
None
} else {
Some(self.parse_expr()?)
};
self.expect_punct(Punct::Semi, " after 'return' statement")?;
return Ok(Stmt {
kind: StmtKind::Return(value),
range: self.span_to_here(start),
});
}
_ => {}
}
}
if let Some(semi) = self.eat_punct(Punct::Semi) {
return Ok(Stmt {
kind: StmtKind::Expr(None),
range: semi,
});
}
let expr = self.parse_expr()?;
self.expect_punct(Punct::Semi, " after expression")?;
Ok(Stmt {
kind: StmtKind::Expr(Some(expr)),
range: self.span_to_here(start),
})
}
fn finish_labeled_stmt(&mut self, labels: Vec<(PendingLabel, SourceRange)>) -> PResult<Stmt> {
let colon = self.last_range;
let (_, label_start) = labels.last().expect("a label chain is never empty");
let what = if self.at_punct(Punct::RBrace) {
Some("a label at the end of a compound statement")
} else if self.starts_declaration() || self.at_static_assert() {
Some("a label before a declaration")
} else {
None
};
let trailing = what.map(|what| (what, label_start.join(colon), colon));
let mut stmt = match trailing {
Some((what, at, colon)) => {
self.require_standard(Standard::C23, what, at);
Stmt {
kind: StmtKind::Expr(None),
range: colon,
}
}
None => {
let start = self.cur_range();
self.parse_unlabeled_stmt(start)?
}
};
let end = self.last_range;
for (label, start) in labels.into_iter().rev() {
let body = Box::new(stmt);
let kind = match label {
PendingLabel::Ident { label, .. } => StmtKind::Labeled { label, body },
PendingLabel::Case { value, upper } => StmtKind::Case { value, upper, body },
PendingLabel::Default => StmtKind::Default { body },
};
stmt = Stmt {
kind,
range: start.join(end),
};
}
Ok(stmt)
}
fn parse_asm_stmt(&mut self) -> PResult<Stmt> {
let start = self.cur_range();
self.advance();
let (mut volatile, mut inline, mut goto) = (false, false, false);
loop {
match self.peek().keyword() {
Some(Keyword::Volatile) => volatile = true,
Some(Keyword::Inline | Keyword::InlineGnu) => inline = true,
Some(Keyword::Goto) => goto = true,
Some(Keyword::Const) => {}
_ => break,
}
self.advance();
}
self.expect_punct(Punct::LParen, " after 'asm'")?;
let template = self.parse_asm_string("the assembler template")?;
let mut asm = AsmStmt {
volatile,
inline,
goto,
template,
extended: false,
outputs: Vec::new(),
inputs: Vec::new(),
clobbers: Vec::new(),
labels: Vec::new(),
};
let mut section = 0;
while section < 4 && self.eat_punct(Punct::Colon).is_some() {
asm.extended = true;
section += 1;
if self.at_punct(Punct::Colon) || self.at_punct(Punct::RParen) {
continue;
}
loop {
match section {
1 => asm.outputs.push(self.parse_asm_operand()?),
2 => asm.inputs.push(self.parse_asm_operand()?),
3 => asm.clobbers.push(self.parse_asm_string("a clobber")?),
_ => asm
.labels
.push(self.expect_ident(" as an 'asm goto' label")?),
}
if self.eat_punct(Punct::Comma).is_none() {
break;
}
}
}
self.expect_punct(Punct::RParen, " after the 'asm' operands")?;
self.expect_punct(Punct::Semi, " after the 'asm' statement")?;
Ok(Stmt {
kind: StmtKind::Asm(Box::new(asm)),
range: self.span_to_here(start),
})
}
fn parse_asm_operand(&mut self) -> PResult<AsmOperand> {
let name = if self.eat_punct(Punct::LBracket).is_some() {
let name = self.expect_ident(" as the operand's symbolic name")?;
self.expect_punct(Punct::RBracket, " after the operand's symbolic name")?;
Some(name)
} else {
None
};
let constraint = self.parse_asm_string("the operand's constraint")?;
self.expect_punct(Punct::LParen, " before the operand")?;
let expr = self.parse_expr()?;
self.expect_punct(Punct::RParen, " after the operand")?;
Ok(AsmOperand {
name,
constraint,
expr,
})
}
fn parse_asm_string(&mut self, what: &str) -> PResult<Spanned<String>> {
let range = self.cur_range();
let TokenKind::Str(lit) = self.peek().kind.clone() else {
let found = self.describe_cur();
return Err(self.error_bail(
range,
format!("expected {what} as a string literal, found {found}"),
));
};
let literal = self.parse_string_literal(lit, range);
let ExprKind::Str(lit) = literal.kind else {
unreachable!("parse_string_literal always yields a string literal");
};
if lit.kind != StrKind::Narrow {
self.error(
literal.range,
format!(
"{what} must be an ordinary string literal, not a '{}' one",
lit.kind.prefix()
),
);
}
let bytes: Vec<u8> = lit.values.iter().map(|v| *v as u8).collect();
let text = String::from_utf8_lossy(&bytes).into_owned();
Ok(Spanned::new(text, literal.range))
}
fn parse_if_stmt(&mut self) -> PResult<Stmt> {
let start = self.cur_range();
self.advance(); self.expect_punct(Punct::LParen, " after 'if'")?;
let cond = self.parse_expr()?;
self.expect_punct(Punct::RParen, " after if condition")?;
let then_branch = Box::new(self.parse_stmt()?);
let else_branch = if self.eat_keyword(Keyword::Else).is_some() {
Some(Box::new(self.parse_stmt()?))
} else {
None
};
Ok(Stmt {
kind: StmtKind::If {
cond,
then_branch,
else_branch,
},
range: self.span_to_here(start),
})
}
fn parse_for_stmt(&mut self) -> PResult<Stmt> {
let start = self.cur_range();
self.advance(); self.expect_punct(Punct::LParen, " after 'for'")?;
self.push_scope();
let result = (|parser: &mut Self| {
let init = if parser.at_punct(Punct::Semi) {
parser.advance();
ForInit::None
} else if parser.at_static_assert() {
ForInit::StaticAssert(parser.parse_static_assert()?)
} else if parser.starts_declaration() {
let at = parser.cur_range();
parser.require_standard(Standard::C99, "a declaration in a 'for' clause", at);
ForInit::Decl(Box::new(parser.parse_declaration()?))
} else {
let expr = parser.parse_expr()?;
parser.expect_punct(Punct::Semi, " after 'for' initializer")?;
ForInit::Expr(expr)
};
let cond = if parser.at_punct(Punct::Semi) {
None
} else {
Some(parser.parse_expr()?)
};
parser.expect_punct(Punct::Semi, " after 'for' condition")?;
let step = if parser.at_punct(Punct::RParen) {
None
} else {
Some(parser.parse_expr()?)
};
parser.expect_punct(Punct::RParen, " after 'for' clauses")?;
let body = parser.parse_stmt()?;
Ok(StmtKind::For {
init,
cond,
step,
body: Box::new(body),
})
})(self);
self.pop_scope();
Ok(Stmt {
kind: result?,
range: self.span_to_here(start),
})
}
fn expect_keyword(&mut self, k: Keyword, ctx: &str) -> PResult<SourceRange> {
if self.at_keyword(k) {
return Ok(self.bump_range());
}
let range = self.cur_range();
let found = self.describe_cur();
Err(self.error_bail(
range,
format!("expected '{}'{ctx}, found {found}", k.as_str()),
))
}
}
fn binary_op(kind: &TokenKind) -> Option<(BinaryOp, u8)> {
let TokenKind::Punct(p) = kind else {
return None;
};
Some(match p {
Punct::PipePipe => (BinaryOp::LogOr, 1),
Punct::AmpAmp => (BinaryOp::LogAnd, 2),
Punct::Pipe => (BinaryOp::BitOr, 3),
Punct::Caret => (BinaryOp::BitXor, 4),
Punct::Amp => (BinaryOp::BitAnd, 5),
Punct::EqEq => (BinaryOp::Eq, 6),
Punct::Ne => (BinaryOp::Ne, 6),
Punct::Lt => (BinaryOp::Lt, 7),
Punct::Gt => (BinaryOp::Gt, 7),
Punct::Le => (BinaryOp::Le, 7),
Punct::Ge => (BinaryOp::Ge, 7),
Punct::Shl => (BinaryOp::Shl, 8),
Punct::Shr => (BinaryOp::Shr, 8),
Punct::Plus => (BinaryOp::Add, 9),
Punct::Minus => (BinaryOp::Sub, 9),
Punct::Star => (BinaryOp::Mul, 10),
Punct::Slash => (BinaryOp::Div, 10),
Punct::Percent => (BinaryOp::Rem, 10),
_ => return None,
})
}
fn assign_op(kind: &TokenKind) -> Option<Option<BinaryOp>> {
let TokenKind::Punct(p) = kind else {
return None;
};
Some(match p {
Punct::Assign => None,
Punct::StarAssign => Some(BinaryOp::Mul),
Punct::SlashAssign => Some(BinaryOp::Div),
Punct::PercentAssign => Some(BinaryOp::Rem),
Punct::PlusAssign => Some(BinaryOp::Add),
Punct::MinusAssign => Some(BinaryOp::Sub),
Punct::ShlAssign => Some(BinaryOp::Shl),
Punct::ShrAssign => Some(BinaryOp::Shr),
Punct::AmpAssign => Some(BinaryOp::BitAnd),
Punct::CaretAssign => Some(BinaryOp::BitXor),
Punct::PipeAssign => Some(BinaryOp::BitOr),
_ => return None,
})
}
impl Parser<'_> {
pub(crate) fn parse_expr(&mut self) -> PResult<Expr> {
self.enter()?;
let result = self.parse_expr_inner();
self.leave();
result
}
fn parse_expr_inner(&mut self) -> PResult<Expr> {
let mut lhs = self.parse_assignment_expr()?;
while self.eat_punct(Punct::Comma).is_some() {
let rhs = self.parse_assignment_expr()?;
let range = lhs.range.join(rhs.range);
lhs = Expr {
kind: ExprKind::Comma {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
},
range,
};
}
Ok(lhs)
}
fn parse_assignment_expr(&mut self) -> PResult<Expr> {
let mut charged = 0u32;
let result = self.assignment_chain(&mut charged);
for _ in 0..charged {
self.leave();
}
result
}
fn assignment_chain(&mut self, charged: &mut u32) -> PResult<Expr> {
let mut pending: Vec<(Expr, Option<BinaryOp>)> = Vec::new();
let mut value = loop {
let lhs = self.parse_conditional_expr()?;
let Some(op) = assign_op(&self.peek().kind) else {
break lhs;
};
self.advance();
self.enter()?;
*charged += 1;
pending.push((lhs, op));
};
for (lhs, op) in pending.into_iter().rev() {
let range = lhs.range.join(value.range);
value = Expr {
kind: ExprKind::Assign {
op,
lhs: Box::new(lhs),
rhs: Box::new(value),
},
range,
};
}
Ok(value)
}
fn parse_conditional_expr(&mut self) -> PResult<Expr> {
let mut charged = 0u32;
let result = self.conditional_chain(&mut charged);
for _ in 0..charged {
self.leave();
}
result
}
fn conditional_chain(&mut self, charged: &mut u32) -> PResult<Expr> {
#[allow(clippy::type_complexity)]
let mut pending: Vec<(Expr, Option<Box<Expr>>)> = Vec::new();
let mut value = loop {
let cond = self.parse_binary_expr(1)?;
if self.eat_punct(Punct::Question).is_none() {
break cond;
}
let then_expr = if self.at_punct(Punct::Colon) {
None
} else {
Some(Box::new(self.parse_expr()?))
};
self.expect_punct(Punct::Colon, " in conditional expression")?;
self.enter()?;
*charged += 1;
pending.push((cond, then_expr));
};
for (cond, then_expr) in pending.into_iter().rev() {
let range = cond.range.join(value.range);
value = Expr {
kind: ExprKind::Conditional {
cond: Box::new(cond),
then_expr,
else_expr: Box::new(value),
},
range,
};
}
Ok(value)
}
fn parse_binary_expr(&mut self, min_prec: u8) -> PResult<Expr> {
let mut lhs = self.parse_cast_expr()?;
while let Some((op, prec)) = binary_op(&self.peek().kind) {
if prec < min_prec {
break;
}
self.advance();
let rhs = self.parse_binary_expr(prec + 1)?;
let range = lhs.range.join(rhs.range);
lhs = Expr {
kind: ExprKind::Binary {
op,
lhs: Box::new(lhs),
rhs: Box::new(rhs),
},
range,
};
}
Ok(lhs)
}
fn at_paren_type_name(&self) -> bool {
if !self.at_punct(Punct::LParen) {
return false;
}
let mut n = 1;
while self.nth(n).keyword() == Some(Keyword::Extension) {
n += 1;
}
self.starts_decl_specifier(n)
}
fn parse_cast_expr(&mut self) -> PResult<Expr> {
self.enter()?;
let result = self.parse_cast_expr_inner();
self.leave();
result
}
fn parse_cast_expr_inner(&mut self) -> PResult<Expr> {
if !self.at_paren_type_name() {
return self.parse_unary_expr();
}
let start = self.cur_range();
self.advance(); let ty = self.parse_type_name()?;
self.expect_punct(Punct::RParen, " after type name")?;
if self.at_punct(Punct::LBrace) {
let at = self.span_to_here(start);
self.require_standard(Standard::C99, "a compound literal", at);
let items = self.parse_initializer_list()?;
let expr = Expr {
kind: ExprKind::CompoundLiteral {
ty: Box::new(ty),
init: items,
},
range: self.span_to_here(start),
};
return self.parse_postfix_suffixes(expr);
}
let expr = self.parse_cast_expr()?;
let range = start.join(expr.range);
Ok(Expr {
kind: ExprKind::Cast {
ty: Box::new(ty),
expr: Box::new(expr),
},
range,
})
}
fn parse_unary_expr(&mut self) -> PResult<Expr> {
let start = self.cur_range();
if let Some(p) = match &self.peek().kind {
TokenKind::Punct(p) => Some(*p),
_ => None,
} {
let unary = match p {
Punct::Amp => Some(UnaryOp::AddrOf),
Punct::Star => Some(UnaryOp::Deref),
Punct::Plus => Some(UnaryOp::Plus),
Punct::Minus => Some(UnaryOp::Minus),
Punct::Tilde => Some(UnaryOp::BitNot),
Punct::Bang => Some(UnaryOp::LogNot),
_ => None,
};
if let Some(op) = unary {
self.advance();
let operand = self.parse_cast_expr()?;
let range = start.join(operand.range);
return Ok(Expr {
kind: ExprKind::Unary {
op,
operand: Box::new(operand),
},
range,
});
}
if p == Punct::AmpAmp {
self.advance();
let label = self.expect_ident(" after '&&'")?;
self.label_addrs += 1;
let range = start.join(label.range);
return Ok(Expr {
kind: ExprKind::LabelAddr(label),
range,
});
}
if matches!(p, Punct::PlusPlus | Punct::MinusMinus) {
self.advance();
let op = if p == Punct::PlusPlus {
IncDec::Inc
} else {
IncDec::Dec
};
let operand = self.parse_unary_expr()?;
let range = start.join(operand.range);
return Ok(Expr {
kind: ExprKind::PreIncDec {
op,
operand: Box::new(operand),
},
range,
});
}
}
if self.at_keyword(Keyword::Sizeof) {
self.advance();
if self.at_paren_type_name() {
self.advance(); let ty = self.parse_type_name()?;
self.expect_punct(Punct::RParen, " after type name")?;
if self.at_punct(Punct::LBrace) {
let at = self.span_to_here(start);
self.require_standard(Standard::C99, "a compound literal", at);
let items = self.parse_initializer_list()?;
let literal = Expr {
kind: ExprKind::CompoundLiteral {
ty: Box::new(ty),
init: items,
},
range: self.span_to_here(start),
};
let operand = self.parse_postfix_suffixes(literal)?;
let range = start.join(operand.range);
return Ok(Expr {
kind: ExprKind::SizeofExpr(Box::new(operand)),
range,
});
}
return Ok(Expr {
kind: ExprKind::SizeofType(Box::new(ty)),
range: self.span_to_here(start),
});
}
let operand = self.parse_unary_expr()?;
let range = start.join(operand.range);
return Ok(Expr {
kind: ExprKind::SizeofExpr(Box::new(operand)),
range,
});
}
if self.eat_keyword(Keyword::Extension).is_some() {
return self.parse_unary_expr();
}
if let Some(k @ (Keyword::RealGnu | Keyword::ImagGnu)) = self.peek().keyword() {
self.advance();
let operand = self.parse_cast_expr()?;
let range = start.join(operand.range);
return Ok(Expr {
kind: ExprKind::ComplexPart {
real: k == Keyword::RealGnu,
operand: Box::new(operand),
},
range,
});
}
if let Some(k @ (Keyword::Alignof | Keyword::AlignofName | Keyword::AlignofGnu)) =
self.peek().keyword()
{
self.require_keyword(k, start);
self.advance();
if self.at_paren_type_name() {
self.advance(); let ty = self.parse_type_name()?;
self.expect_punct(Punct::RParen, " after type name")?;
return Ok(Expr {
kind: ExprKind::AlignofType(Box::new(ty)),
range: self.span_to_here(start),
});
}
let operand = self.parse_unary_expr()?;
let range = start.join(operand.range);
return Ok(Expr {
kind: ExprKind::AlignofExpr(Box::new(operand)),
range,
});
}
if self.at_va_arg() {
return self.parse_va_arg();
}
if self.at_builtin("__builtin_offsetof") {
return self.parse_offsetof();
}
if self.at_builtin("__builtin_types_compatible_p") {
return self.parse_types_compatible();
}
if self.at_builtin("__builtin_choose_expr") {
return self.parse_choose_expr();
}
self.parse_postfix_expr()
}
fn parse_types_compatible(&mut self) -> PResult<Expr> {
let start = self.cur_range();
self.advance(); self.advance(); let lhs = self.parse_type_name()?;
self.expect_punct(
Punct::Comma,
" after the first type of '__builtin_types_compatible_p'",
)?;
let rhs = self.parse_type_name()?;
self.expect_punct(
Punct::RParen,
" after the second type of '__builtin_types_compatible_p'",
)?;
let expr = Expr {
kind: ExprKind::TypesCompatible {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
},
range: self.span_to_here(start),
};
self.parse_postfix_suffixes(expr)
}
fn parse_choose_expr(&mut self) -> PResult<Expr> {
let start = self.cur_range();
self.advance(); self.advance(); let cond = self.parse_assignment_expr()?;
self.expect_punct(
Punct::Comma,
" after the condition of '__builtin_choose_expr'",
)?;
let then_expr = self.parse_assignment_expr()?;
self.expect_punct(Punct::Comma, " in '__builtin_choose_expr'")?;
let else_expr = self.parse_assignment_expr()?;
self.expect_punct(Punct::RParen, " to close '__builtin_choose_expr'")?;
let expr = Expr {
kind: ExprKind::ChooseExpr {
cond: Box::new(cond),
then_expr: Box::new(then_expr),
else_expr: Box::new(else_expr),
},
range: self.span_to_here(start),
};
self.parse_postfix_suffixes(expr)
}
fn at_builtin(&self, name: &str) -> bool {
self.peek().ident() == Some(name) && self.nth(1).is_punct(Punct::LParen)
}
fn at_va_arg(&self) -> bool {
self.at_builtin("__builtin_va_arg")
}
fn parse_offsetof(&mut self) -> PResult<Expr> {
let start = self.cur_range();
self.advance(); self.advance(); let ty = self.parse_type_name()?;
self.expect_punct(Punct::Comma, " after the type of 'offsetof'")?;
let member = self.expect_ident(" as the member of 'offsetof'")?;
let mut path = Vec::new();
loop {
if self.eat_punct(Punct::Dot).is_some() {
path.push(Designator::Field(self.expect_ident(
" after '.' in the member designator of 'offsetof'",
)?));
continue;
}
if self.eat_punct(Punct::LBracket).is_some() {
path.push(Designator::Index(self.parse_expr()?));
self.expect_punct(
Punct::RBracket,
" after the subscript in the member designator of 'offsetof'",
)?;
continue;
}
break;
}
self.expect_punct(Punct::RParen, " after the member of 'offsetof'")?;
let expr = Expr {
kind: ExprKind::OffsetOf {
ty: Box::new(ty),
member,
path,
},
range: self.span_to_here(start),
};
self.parse_postfix_suffixes(expr)
}
fn parse_va_arg(&mut self) -> PResult<Expr> {
let start = self.cur_range();
self.advance(); self.advance(); let ap = self.parse_assignment_expr()?;
self.expect_punct(Punct::Comma, " after the argument list of 'va_arg'")?;
let ty = self.parse_type_name()?;
self.expect_punct(Punct::RParen, " after the type of 'va_arg'")?;
let expr = Expr {
kind: ExprKind::VaArg {
ap: Box::new(ap),
ty: Box::new(ty),
},
range: self.span_to_here(start),
};
self.parse_postfix_suffixes(expr)
}
fn parse_postfix_expr(&mut self) -> PResult<Expr> {
let primary = self.parse_primary_expr()?;
self.parse_postfix_suffixes(primary)
}
fn parse_postfix_suffixes(&mut self, expr: Expr) -> PResult<Expr> {
let mut charged = 0u32;
let result = self.postfix_suffixes(expr, &mut charged);
for _ in 0..charged {
self.leave();
}
result
}
fn postfix_suffixes(&mut self, mut expr: Expr, charged: &mut u32) -> PResult<Expr> {
let mut suffixes = 0usize;
loop {
if suffixes > 0 {
self.enter()?;
*charged += 1;
}
suffixes += 1;
if self.eat_punct(Punct::LBracket).is_some() {
let index = self.parse_expr()?;
let rb = self.expect_punct(Punct::RBracket, " after subscript")?;
expr = Expr {
range: expr.range.join(rb),
kind: ExprKind::Index {
base: Box::new(expr),
index: Box::new(index),
},
};
continue;
}
if self.eat_punct(Punct::LParen).is_some() {
let mut args = Vec::new();
if !self.at_punct(Punct::RParen) {
loop {
args.push(self.parse_assignment_expr()?);
if self.eat_punct(Punct::Comma).is_none() {
break;
}
}
}
let rp = self.expect_punct(Punct::RParen, " after argument list")?;
expr = Expr {
range: expr.range.join(rp),
kind: ExprKind::Call {
callee: Box::new(expr),
args,
},
};
continue;
}
let arrow = if self.at_punct(Punct::Dot) {
false
} else if self.at_punct(Punct::Arrow) {
true
} else if self.at_punct(Punct::PlusPlus) || self.at_punct(Punct::MinusMinus) {
let op = if self.at_punct(Punct::PlusPlus) {
IncDec::Inc
} else {
IncDec::Dec
};
let range = expr.range.join(self.bump_range());
expr = Expr {
kind: ExprKind::PostIncDec {
op,
operand: Box::new(expr),
},
range,
};
continue;
} else {
break;
};
self.advance();
let field = self.expect_ident(if arrow { " after '->'" } else { " after '.'" })?;
expr = Expr {
range: expr.range.join(field.range),
kind: ExprKind::Member {
base: Box::new(expr),
arrow,
field,
},
};
}
Ok(expr)
}
fn parse_generic_selection(&mut self) -> PResult<Expr> {
let start = self.cur_range();
self.require_keyword(Keyword::Generic, start);
self.advance();
self.expect_punct(Punct::LParen, " after '_Generic'")?;
let controlling = self.parse_assignment_expr()?;
let mut assocs = Vec::new();
while self.eat_punct(Punct::Comma).is_some() {
let astart = self.cur_range();
let ty = if self.eat_keyword(Keyword::Default).is_some() {
None
} else {
Some(self.parse_type_name()?)
};
self.expect_punct(Punct::Colon, " after the type of a '_Generic' association")?;
let value = self.parse_assignment_expr()?;
assocs.push(GenericAssoc {
ty,
value,
range: self.span_to_here(astart),
});
}
let rparen = self.expect_punct(Punct::RParen, " to close '_Generic'")?;
if assocs.is_empty() {
self.error(
start.join(rparen),
"'_Generic' requires at least one association",
);
}
Ok(Expr {
kind: ExprKind::Generic {
controlling: Box::new(controlling),
assocs,
},
range: start.join(rparen),
})
}
fn parse_primary_expr(&mut self) -> PResult<Expr> {
let range = self.cur_range();
match self.peek().kind.clone() {
TokenKind::Keyword(Keyword::Generic) => self.parse_generic_selection(),
TokenKind::Keyword(k @ (Keyword::True | Keyword::False)) => {
self.advance();
Ok(Expr {
kind: ExprKind::Bool(k == Keyword::True),
range,
})
}
TokenKind::Keyword(Keyword::Nullptr) => {
self.advance();
Ok(Expr {
kind: ExprKind::Nullptr,
range,
})
}
TokenKind::Ident(name) => {
self.advance();
Ok(Expr {
kind: ExprKind::Ident(Ident { name, range }),
range,
})
}
TokenKind::Int(lit) => {
self.advance();
Ok(Expr {
kind: ExprKind::Int(lit),
range,
})
}
TokenKind::Float(lit) => {
self.advance();
Ok(Expr {
kind: ExprKind::Float(lit),
range,
})
}
TokenKind::Char(lit) => {
self.advance();
Ok(Expr {
kind: ExprKind::Char(lit),
range,
})
}
TokenKind::Str(first) => Ok(self.parse_string_literal(first, range)),
TokenKind::Punct(Punct::LParen) => {
self.advance();
if self.at_punct(Punct::LBrace) {
let block = self.parse_compound_stmt()?;
let rp =
self.expect_punct(Punct::RParen, " to close a statement expression")?;
return Ok(Expr {
kind: ExprKind::StmtExpr(Box::new(block)),
range: range.join(rp),
});
}
let inner = self.parse_expr()?;
let rp = self.expect_punct(Punct::RParen, " after parenthesized expression")?;
Ok(Expr {
kind: inner.kind,
range: range.join(rp),
})
}
_ => {
let found = self.describe_cur();
Err(self.error_bail(range, format!("expected expression, found {found}")))
}
}
}
fn parse_string_literal(&mut self, first: StrLit, first_range: SourceRange) -> Expr {
self.advance();
let mut kind = first.kind;
let mut values = first.values;
let mut text = first.text;
let mut range = first_range;
while let TokenKind::Str(next) = self.peek().kind.clone() {
let piece_range = self.cur_range();
if next.kind != kind {
if kind == StrKind::Narrow {
values = recode_from_narrow(&values, next.kind);
kind = next.kind;
} else if next.kind != StrKind::Narrow {
self.error(
piece_range,
format!(
"cannot concatenate a '{}' string literal with a '{}' one",
kind.prefix(),
next.kind.prefix()
),
);
}
}
if next.kind == kind || next.kind != StrKind::Narrow {
values.extend_from_slice(&next.values);
} else {
values.extend(recode_from_narrow(&next.values, kind));
}
text.push(' ');
text.push_str(&next.text);
range = range.join(piece_range);
self.advance();
}
Expr {
kind: ExprKind::Str(StrLit { kind, values, text }),
range,
}
}
}
fn floatn_type(name: &str) -> Option<FloatSize> {
Some(match name {
"_Float32" => FloatSize::Float32,
"_Float64" => FloatSize::Float64,
"_Float32x" => FloatSize::Float32x,
"_Float64x" => FloatSize::Float64x,
"_Float128" | "__float128" => FloatSize::Float128,
_ => return None,
})
}
fn extended_float_type(name: &str) -> Option<&'static str> {
match name {
"_Float128x" => Some("an extended binary128 format (GCC has none either)"),
"__fp16" | "_Float16" => Some("binary16"),
"__bf16" | "__bfloat16" => Some("bfloat16"),
_ => None,
}
}
fn recode_from_narrow(values: &[u32], kind: StrKind) -> Vec<u32> {
if kind == StrKind::Narrow || kind == StrKind::Utf8 {
return values.to_vec();
}
let bytes: Vec<u8> = values.iter().map(|v| *v as u8).collect();
let text = String::from_utf8_lossy(&bytes);
let mut out = Vec::with_capacity(values.len());
for ch in text.chars() {
let value = ch as u32;
if kind == StrKind::Utf16 && value > 0xffff {
let v = value - 0x1_0000;
out.push(0xd800 + (v >> 10));
out.push(0xdc00 + (v & 0x3ff));
} else {
out.push(value);
}
}
out
}