use rucc_ast::{
AlignSpec, AttrArg, AttrArgList, AttrList, AttrSyntax, Attribute, Builtin, BuiltinError,
BuiltinSet, DeclSpecs, DeclSpecsId, Deduction, Enumerator, EnumeratorList, ExprId, Field,
FuncSpecs, Member, MemberList, Quals, RecordKind, StorageClass, StrId, TypeSpec, TypeofArg,
};
use rucc_base::Symbol;
use rucc_diag::Span;
use rucc_lex::{Keyword, Punct, Token, TokenKind};
use rucc_session::Std;
use crate::cursor::MAX_LOOKAHEAD;
use crate::parser::Parser;
use crate::scope::{IdentKind, TagKind};
const MULTIPLE_STORAGE_CLASSES: &str = "multiple storage classes in declaration specifiers";
fn builtin_keyword(word: Keyword) -> Option<BuiltinSet> {
let set = match word {
Keyword::Void => BuiltinSet::VOID,
Keyword::Bool => BuiltinSet::BOOL,
Keyword::Char => BuiltinSet::CHAR,
Keyword::Short => BuiltinSet::SHORT,
Keyword::Int => BuiltinSet::INT,
Keyword::Long => BuiltinSet::LONG,
Keyword::Signed => BuiltinSet::SIGNED,
Keyword::Unsigned => BuiltinSet::UNSIGNED,
Keyword::Float => BuiltinSet::FLOAT,
Keyword::Double => BuiltinSet::DOUBLE,
Keyword::Complex => BuiltinSet::COMPLEX,
Keyword::Imaginary => BuiltinSet::IMAGINARY,
Keyword::Int128 => BuiltinSet::INT128,
Keyword::Int128T => BuiltinSet::INT128.with(BuiltinSet::SIGNED),
Keyword::UInt128T => BuiltinSet::INT128.with(BuiltinSet::UNSIGNED),
Keyword::Float16 => BuiltinSet::FLOAT16,
Keyword::Float32 => BuiltinSet::FLOAT32,
Keyword::Float64 => BuiltinSet::FLOAT64,
Keyword::Float128 => BuiltinSet::FLOAT128,
Keyword::Float32x => BuiltinSet::FLOAT32X,
Keyword::Float64x => BuiltinSet::FLOAT64X,
Keyword::Float128x => BuiltinSet::FLOAT128X,
Keyword::Decimal32 => BuiltinSet::DECIMAL32,
Keyword::Decimal64 => BuiltinSet::DECIMAL64,
Keyword::Decimal128 => BuiltinSet::DECIMAL128,
_ => return None,
};
Some(set)
}
fn qual_keyword(word: Keyword) -> Option<Quals> {
match word {
Keyword::Const => Some(Quals::CONST),
Keyword::Volatile => Some(Quals::VOLATILE),
Keyword::Restrict => Some(Quals::RESTRICT),
_ => None,
}
}
fn storage_keyword(word: Keyword) -> Option<StorageClass> {
match word {
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,
}
}
struct Pending<'a> {
specs: &'a mut DeclSpecs,
builtin: &'a mut Builtin,
named: &'a mut bool,
autos: &'a mut Autos,
}
#[derive(Clone, Copy)]
struct Autos {
count: u32,
span: Span,
}
fn type_keyword(word: Keyword) -> bool {
if builtin_keyword(word).is_some() || qual_keyword(word).is_some() {
return true;
}
matches!(
word,
Keyword::Struct
| Keyword::Union
| Keyword::Enum
| Keyword::Atomic
| Keyword::Typeof
| Keyword::TypeofUnqual
| Keyword::BitInt
| Keyword::Attribute
| Keyword::BuiltinVaList
)
}
impl Parser<'_> {
pub(crate) fn starts_type_name(&self, token: Token) -> bool {
match token.kind {
TokenKind::Keyword(word) => type_keyword(word),
TokenKind::Ident => self.scopes.is_typedef_name(Symbol::from_raw(token.value)),
_ => false,
}
}
pub(crate) fn starts_decl_specs(&self, token: Token) -> bool {
match token.kind {
TokenKind::Keyword(word) => {
type_keyword(word)
|| storage_keyword(word).is_some()
|| matches!(
word,
Keyword::Inline
| Keyword::Noreturn
| Keyword::Alignas
| Keyword::ThreadLocal
| Keyword::AutoType
)
}
TokenKind::Ident => self.scopes.is_typedef_name(Symbol::from_raw(token.value)),
_ => false,
}
}
pub(crate) fn at_decl_specs(&self) -> bool {
let mut ahead = 0;
while ahead < MAX_LOOKAHEAD && self.cursor.peek(ahead).keyword() == Some(Keyword::Extension)
{
ahead += 1;
}
self.starts_decl_specs(self.cursor.peek(ahead))
}
pub(crate) fn at_standard_attribute(&self) -> bool {
self.cursor.at_punct(Punct::LBracket)
&& self.cursor.peek(1).punct() == Some(Punct::LBracket)
}
pub(crate) fn at_attribute(&self) -> bool {
self.cursor.at_keyword(Keyword::Attribute) || self.at_standard_attribute()
}
pub(crate) fn attributes(&mut self) -> AttrList {
if !self.at_attribute() {
return AttrList::EMPTY;
}
let mut attrs = Vec::new();
self.collect_attributes(&mut attrs);
self.ast.add_attr_list(&attrs)
}
fn collect_attributes(&mut self, out: &mut Vec<Attribute>) {
loop {
if self.at_standard_attribute() {
self.standard_attributes(out);
} else if self.cursor.at_keyword(Keyword::Attribute) {
self.gnu_attributes(out);
} else {
return;
}
}
}
fn standard_attributes(&mut self, out: &mut Vec<Attribute>) {
self.cursor.bump();
self.cursor.bump();
loop {
if self.cursor.at_punct(Punct::RBracket) || self.cursor.is_eof() {
break;
}
let before = self.cursor.index();
if let Some(attr) = self.one_attribute(AttrSyntax::Standard) {
out.push(attr);
}
if !self.cursor.eat_punct(Punct::Comma) {
break;
}
if self.cursor.index() == before {
break;
}
}
self.expect_punct(Punct::RBracket);
self.expect_punct(Punct::RBracket);
}
fn gnu_attributes(&mut self, out: &mut Vec<Attribute>) {
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return;
}
if !self.expect_punct(Punct::LParen) {
return;
}
loop {
if self.cursor.at_punct(Punct::RParen) || self.cursor.is_eof() {
break;
}
let before = self.cursor.index();
if !self.cursor.at_punct(Punct::Comma) {
if let Some(attr) = self.one_attribute(AttrSyntax::Gnu) {
out.push(attr);
}
}
if !self.cursor.eat_punct(Punct::Comma) {
break;
}
if self.cursor.index() == before {
break;
}
}
self.expect_punct(Punct::RParen);
self.expect_punct(Punct::RParen);
}
fn one_attribute(&mut self, syntax: AttrSyntax) -> Option<Attribute> {
let start = self.cursor.span();
let mut namespace = None;
let mut name = self.attribute_name()?;
if self.cursor.eat_punct(Punct::ColonColon) {
namespace = Some(name);
name = self.attribute_name()?;
}
let args = if self.cursor.at_punct(Punct::LParen) {
self.attribute_args()
} else {
AttrArgList::EMPTY
};
Some(Attribute { namespace, name, args, syntax, span: self.span_from(start) })
}
fn attribute_name(&mut self) -> Option<Symbol> {
let token = self.cursor.current();
if matches!(token.kind, TokenKind::Ident | TokenKind::Keyword(_)) {
self.cursor.bump();
return Some(Symbol::from_raw(token.value));
}
let found = self.describe(token);
self.error("E0401", format!("expected an attribute name, found {found}"), token.span);
None
}
fn attribute_args(&mut self) -> AttrArgList {
let mut args = Vec::new();
if !self.enter() {
self.cursor.bump();
return AttrArgList::EMPTY;
}
self.cursor.bump();
while !self.cursor.at_punct(Punct::RParen) && !self.cursor.is_eof() {
let before = self.cursor.index();
let lone_ident = self.cursor.current().ident().filter(|_| {
matches!(self.cursor.peek(1).punct(), Some(Punct::Comma | Punct::RParen))
});
match lone_ident {
Some(name) => {
self.cursor.bump();
args.push(AttrArg::Ident(name));
}
None => args.push(AttrArg::Expr(self.assign_expr())),
}
if !self.cursor.eat_punct(Punct::Comma) {
break;
}
if self.cursor.index() == before {
break;
}
}
self.expect_punct(Punct::RParen);
self.leave();
self.ast.add_attr_args(&args)
}
pub(crate) fn decl_specs(&mut self) -> DeclSpecsId {
let start = self.cursor.span();
self.decl_specs_with(AttrList::EMPTY, start)
}
pub(crate) fn decl_specs_with(&mut self, leading: AttrList, start: Span) -> DeclSpecsId {
let mut specs = DeclSpecs::empty(start);
let mut builtin = Builtin::NONE;
let mut attrs = self.ast[leading].to_vec();
let mut named = false;
let mut autos = Autos { count: 0, span: start };
loop {
let token = self.cursor.current();
let span = token.span;
match token.kind {
TokenKind::Punct(Punct::LBracket) if self.at_standard_attribute() => {
self.standard_attributes(&mut attrs);
}
TokenKind::Ident => {
if named || !builtin.is_none() {
break;
}
let name = Symbol::from_raw(token.value);
if !self.scopes.is_typedef_name(name) {
break;
}
self.cursor.bump();
specs.ty = TypeSpec::Typedef(name);
named = true;
}
TokenKind::Keyword(word) => {
let mut state = Pending {
specs: &mut specs,
builtin: &mut builtin,
named: &mut named,
autos: &mut autos,
};
if !self.decl_spec_keyword(word, span, &mut state) {
break;
}
}
_ => break,
}
}
if !builtin.is_none() {
specs.ty = TypeSpec::Builtin(builtin);
}
self.settle_auto(&mut specs, autos);
specs.attrs = self.ast.add_attr_list(&attrs);
specs.span = self.span_from(start);
self.ast.add_specs(specs)
}
fn settle_auto(&mut self, specs: &mut DeclSpecs, autos: Autos) {
if autos.count == 0 {
return;
}
if autos.count > 1 {
self.error("E0406", "duplicate `auto`", autos.span);
}
let deduces = self.cx.std >= Std::C23
&& matches!(specs.ty, TypeSpec::None)
&& specs.storage != Some(StorageClass::Typedef);
if deduces {
specs.ty = TypeSpec::Auto(Deduction::Auto);
return;
}
if specs.storage.is_some() {
self.error("E0404", MULTIPLE_STORAGE_CLASSES, autos.span);
return;
}
specs.storage = Some(StorageClass::Auto);
}
fn decl_spec_keyword(&mut self, word: Keyword, span: Span, state: &mut Pending<'_>) -> bool {
let Pending { specs, builtin, named, autos } = state;
let (specs, builtin, named) = (&mut **specs, &mut **builtin, &mut **named);
if word == Keyword::Auto {
self.cursor.bump();
if autos.count == 0 {
autos.span = span;
}
autos.count += 1;
return true;
}
if let Some(set) = builtin_keyword(word) {
self.cursor.bump();
if *named {
self.two_types(span);
return true;
}
match builtin.add(set) {
Ok(next) => *builtin = next,
Err(BuiltinError::Duplicate) => {
self.error("E0406", format!("duplicate `{}`", word.as_str()), span);
}
Err(BuiltinError::TooManyLongs) => {
self.error("E0406", "`long long long` is too long for this compiler", span);
}
}
return true;
}
if let Some(qual) = qual_keyword(word) {
self.cursor.bump();
specs.quals = specs.quals.with(qual);
return true;
}
if let Some(storage) = storage_keyword(word) {
self.cursor.bump();
if let Some(previous) = specs.storage {
if previous == storage {
let message = format!("duplicate `{}`", storage.spelling());
self.error("E0406", message, span);
} else {
self.error("E0404", MULTIPLE_STORAGE_CLASSES, span);
}
}
specs.storage = Some(storage);
return true;
}
match word {
Keyword::Extension => {
self.cursor.bump();
}
Keyword::ThreadLocal => {
self.cursor.bump();
specs.thread_local = true;
}
Keyword::Inline => {
self.cursor.bump();
specs.func = specs.func.with(FuncSpecs::INLINE);
}
Keyword::Noreturn => {
self.cursor.bump();
specs.func = specs.func.with(FuncSpecs::NORETURN);
}
Keyword::Attribute => {
let mut attrs = Vec::new();
self.gnu_attributes(&mut attrs);
let list = self.ast.add_attr_list(&attrs);
specs.attrs = self.join_attrs(specs.attrs, list);
}
Keyword::Alignas => {
self.cursor.bump();
let align = self.align_spec();
if specs.align.is_none() {
specs.align = align;
}
}
Keyword::Struct | Keyword::Union => {
let kind =
if word == Keyword::Struct { RecordKind::Struct } else { RecordKind::Union };
let ty = self.record(kind);
self.set_type(specs, builtin, named, ty, span);
}
Keyword::Enum => {
let ty = self.enumeration();
self.set_type(specs, builtin, named, ty, span);
}
Keyword::Typeof | Keyword::TypeofUnqual => {
let ty = self.typeof_spec(word == Keyword::TypeofUnqual);
self.set_type(specs, builtin, named, ty, span);
}
Keyword::BitInt => {
self.cursor.bump();
let Some(width) = self.bit_int_width() else {
return true;
};
if *named {
self.two_types(span);
return true;
}
match builtin.add_bit_int(width) {
Ok(next) => *builtin = next,
Err(_) => self.two_types(span),
}
}
Keyword::AutoType => {
self.cursor.bump();
self.set_type(specs, builtin, named, TypeSpec::Auto(Deduction::AutoType), span);
}
Keyword::BuiltinVaList => {
self.cursor.bump();
self.set_type(specs, builtin, named, TypeSpec::VaList, span);
}
Keyword::Atomic => {
let constructor = !*named
&& builtin.is_none()
&& self.cursor.peek(1).punct() == Some(Punct::LParen)
&& self.starts_type_name(self.cursor.peek(2));
self.cursor.bump();
if constructor {
self.cursor.bump();
let ty = self.type_name();
self.expect_punct(Punct::RParen);
self.set_type(specs, builtin, named, TypeSpec::Atomic(ty), span);
} else {
specs.quals = specs.quals.with(Quals::ATOMIC);
}
}
_ => return false,
}
true
}
fn set_type(
&mut self,
specs: &mut DeclSpecs,
builtin: &Builtin,
named: &mut bool,
ty: TypeSpec,
span: Span,
) {
if *named || !builtin.is_none() {
self.two_types(span);
return;
}
specs.ty = ty;
*named = true;
}
fn two_types(&mut self, span: Span) {
self.error("E0405", "two or more data types in declaration specifiers", span);
}
fn join_attrs(&mut self, first: AttrList, second: AttrList) -> AttrList {
if first.is_empty() {
return second;
}
if second.is_empty() {
return first;
}
let mut both: Vec<_> = self.ast[first].to_vec();
both.extend_from_slice(&self.ast[second]);
self.ast.add_attr_list(&both)
}
fn align_spec(&mut self) -> Option<AlignSpec> {
if !self.expect_punct(Punct::LParen) {
return None;
}
let align = if self.starts_type_name(self.cursor.current()) {
AlignSpec::Type(self.type_name())
} else {
AlignSpec::Expr(self.const_expr())
};
self.expect_punct(Punct::RParen);
Some(align)
}
fn typeof_spec(&mut self, unqual: bool) -> TypeSpec {
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return TypeSpec::None;
}
let operand = if self.starts_type_name(self.cursor.current()) {
TypeofArg::Type(self.type_name())
} else {
TypeofArg::Expr(self.expr())
};
self.expect_punct(Punct::RParen);
TypeSpec::Typeof { unqual, operand }
}
fn bit_int_width(&mut self) -> Option<ExprId> {
if !self.expect_punct(Punct::LParen) {
return None;
}
let width = self.const_expr();
self.expect_punct(Punct::RParen);
Some(width)
}
fn record(&mut self, kind: RecordKind) -> TypeSpec {
self.cursor.bump();
let mut attrs = Vec::new();
self.collect_attributes(&mut attrs);
let tag = self.cursor.current().ident();
let tag_span = self.cursor.span();
if tag.is_some() {
self.cursor.bump();
}
let fields = if self.cursor.at_punct(Punct::LBrace) {
if let Some(name) = tag {
let tag_kind =
if kind == RecordKind::Struct { TagKind::Struct } else { TagKind::Union };
self.scopes.declare_tag(name, tag_kind);
}
Some(self.members())
} else {
if tag.is_none() {
let found = self.describe(self.cursor.current());
let message = format!("expected a tag or a body after `{}`, found {found}", {
kind.spelling()
});
self.error("E0407", message, tag_span);
}
None
};
self.collect_attributes(&mut attrs);
let attrs = self.ast.add_attr_list(&attrs);
TypeSpec::Record { kind, tag, fields, attrs }
}
fn members(&mut self) -> MemberList {
let mut members = Vec::new();
if !self.enter() {
self.cursor.bump();
return MemberList::EMPTY;
}
self.cursor.bump();
while !self.cursor.at_punct(Punct::RBrace) && !self.cursor.is_eof() && !self.stopped() {
let before = self.cursor.index();
self.member(&mut members);
if self.cursor.index() == before {
let found = self.describe(self.cursor.current());
let span = self.cursor.span();
self.error("E0407", format!("expected a member, found {found}"), span);
self.cursor.bump();
}
}
self.expect_punct(Punct::RBrace);
self.leave();
self.ast.add_member_list(&members)
}
fn member(&mut self, out: &mut Vec<Member>) {
let start = self.cursor.span();
if self.cursor.at_keyword(Keyword::StaticAssert) {
let (cond, message) = self.static_assert_body();
self.expect_punct(Punct::Semi);
out.push(Member::StaticAssert { cond, message, span: self.span_from(start) });
return;
}
if self.cursor.eat_punct(Punct::Semi) {
self.pedantic("E0408", "extra `;` in a member list", start);
return;
}
let specs = self.decl_specs();
if self.cursor.eat_punct(Punct::Semi) {
let attrs = self.ast[specs].attrs;
out.push(Member::Field(Field {
specs,
declarator: None,
bits: None,
attrs,
span: self.span_from(start),
}));
return;
}
loop {
let at = self.cursor.span();
let before = self.cursor.index();
let declarator =
if self.cursor.at_punct(Punct::Colon) { None } else { Some(self.declarator()) };
let bits =
if self.cursor.eat_punct(Punct::Colon) { Some(self.const_expr()) } else { None };
let attrs = self.attributes();
out.push(Member::Field(Field {
specs,
declarator,
bits,
attrs,
span: self.span_from(at),
}));
if !self.cursor.eat_punct(Punct::Comma) {
break;
}
if self.cursor.index() == before {
break;
}
}
self.expect_punct(Punct::Semi);
}
fn enumeration(&mut self) -> TypeSpec {
self.cursor.bump();
let mut attrs = Vec::new();
self.collect_attributes(&mut attrs);
let tag = self.cursor.current().ident();
let tag_span = self.cursor.span();
if tag.is_some() {
self.cursor.bump();
}
let underlying =
if self.cursor.at_punct(Punct::Colon) && self.starts_type_name(self.cursor.peek(1)) {
self.cursor.bump();
Some(self.type_name())
} else {
None
};
let enumerators = if self.cursor.at_punct(Punct::LBrace) {
if let Some(name) = tag {
self.scopes.declare_tag(name, TagKind::Enum);
}
Some(self.enumerators())
} else {
if tag.is_none() {
let found = self.describe(self.cursor.current());
let message = format!("expected a tag or a body after `enum`, found {found}");
self.error("E0407", message, tag_span);
}
None
};
self.collect_attributes(&mut attrs);
let attrs = self.ast.add_attr_list(&attrs);
TypeSpec::Enum { tag, enumerators, underlying, attrs }
}
fn enumerators(&mut self) -> EnumeratorList {
let mut out = Vec::new();
if !self.enter() {
self.cursor.bump();
return EnumeratorList::EMPTY;
}
self.cursor.bump();
while !self.cursor.at_punct(Punct::RBrace) && !self.cursor.is_eof() {
let start = self.cursor.span();
let before = self.cursor.index();
let Some((name, _)) = self.expect_ident() else { break };
self.scopes.declare(name, IdentKind::Ordinary);
let attrs = self.attributes();
let value =
if self.cursor.eat_punct(Punct::Eq) { Some(self.const_expr()) } else { None };
out.push(Enumerator { name, value, attrs, span: self.span_from(start) });
if !self.cursor.eat_punct(Punct::Comma) {
break;
}
if self.cursor.index() == before {
break;
}
}
self.expect_punct(Punct::RBrace);
self.leave();
self.ast.add_enumerator_list(&out)
}
pub(crate) fn static_assert_body(&mut self) -> (ExprId, Option<StrId>) {
let start = self.cursor.span();
self.cursor.bump();
if !self.expect_punct(Punct::LParen) {
return (self.poison_expr(start), None);
}
let cond = self.const_expr();
let mut message = None;
if self.cursor.eat_punct(Punct::Comma) {
message = self.string_literal();
}
self.expect_punct(Punct::RParen);
(cond, message)
}
}