use rucc_base::Symbol;
use rucc_diag::Span;
use crate::ast::{AttrList, EnumeratorList, MemberList};
use crate::decl::TypeNameId;
use crate::expr::ExprId;
pub type DeclSpecsId = rucc_base::Idx<DeclSpecs>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DeclSpecs {
pub storage: Option<StorageClass>,
pub thread_local: bool,
pub ty: TypeSpec,
pub quals: Quals,
pub func: FuncSpecs,
pub align: Option<AlignSpec>,
pub attrs: AttrList,
pub span: Span,
}
impl DeclSpecs {
#[must_use]
pub const fn empty(span: Span) -> DeclSpecs {
DeclSpecs {
storage: None,
thread_local: false,
ty: TypeSpec::None,
quals: Quals::NONE,
func: FuncSpecs::NONE,
align: None,
attrs: AttrList::EMPTY,
span,
}
}
#[must_use]
pub const fn is_typedef(&self) -> bool {
matches!(self.storage, Some(StorageClass::Typedef))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StorageClass {
Typedef,
Extern,
Static,
Auto,
Register,
Constexpr,
}
impl StorageClass {
#[must_use]
pub const fn spelling(self) -> &'static str {
match self {
StorageClass::Typedef => "typedef",
StorageClass::Extern => "extern",
StorageClass::Static => "static",
StorageClass::Auto => "auto",
StorageClass::Register => "register",
StorageClass::Constexpr => "constexpr",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Quals(u8);
impl Quals {
pub const NONE: Quals = Quals(0);
pub const CONST: Quals = Quals(1);
pub const VOLATILE: Quals = Quals(2);
pub const RESTRICT: Quals = Quals(4);
pub const ATOMIC: Quals = Quals(8);
#[inline]
#[must_use]
pub const fn has(self, other: Quals) -> bool {
self.0 & other.0 == other.0
}
#[inline]
#[must_use]
pub const fn with(self, other: Quals) -> Quals {
Quals(self.0 | other.0)
}
#[inline]
#[must_use]
pub const fn is_none(self) -> bool {
self.0 == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FuncSpecs(u8);
impl FuncSpecs {
pub const NONE: FuncSpecs = FuncSpecs(0);
pub const INLINE: FuncSpecs = FuncSpecs(1);
pub const NORETURN: FuncSpecs = FuncSpecs(2);
#[inline]
#[must_use]
pub const fn has(self, other: FuncSpecs) -> bool {
self.0 & other.0 == other.0
}
#[inline]
#[must_use]
pub const fn with(self, other: FuncSpecs) -> FuncSpecs {
FuncSpecs(self.0 | other.0)
}
#[inline]
#[must_use]
pub const fn is_none(self) -> bool {
self.0 == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AlignSpec {
Type(TypeNameId),
Expr(ExprId),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeSpec {
None,
Builtin(Builtin),
Record {
kind: RecordKind,
tag: Option<Symbol>,
fields: Option<MemberList>,
attrs: AttrList,
},
Enum {
tag: Option<Symbol>,
enumerators: Option<EnumeratorList>,
underlying: Option<TypeNameId>,
attrs: AttrList,
},
Typedef(Symbol),
Typeof {
unqual: bool,
operand: TypeofArg,
},
BitInt(ExprId),
Atomic(TypeNameId),
Auto,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RecordKind {
Struct,
Union,
}
impl RecordKind {
#[must_use]
pub const fn spelling(self) -> &'static str {
match self {
RecordKind::Struct => "struct",
RecordKind::Union => "union",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeofArg {
Expr(ExprId),
Type(TypeNameId),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Builtin {
pub set: BuiltinSet,
pub longs: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct BuiltinSet(u32);
impl BuiltinSet {
pub const NONE: BuiltinSet = BuiltinSet(0);
pub const VOID: BuiltinSet = BuiltinSet(1 << 0);
pub const BOOL: BuiltinSet = BuiltinSet(1 << 1);
pub const CHAR: BuiltinSet = BuiltinSet(1 << 2);
pub const SHORT: BuiltinSet = BuiltinSet(1 << 3);
pub const INT: BuiltinSet = BuiltinSet(1 << 4);
pub const LONG: BuiltinSet = BuiltinSet(1 << 5);
pub const SIGNED: BuiltinSet = BuiltinSet(1 << 6);
pub const UNSIGNED: BuiltinSet = BuiltinSet(1 << 7);
pub const FLOAT: BuiltinSet = BuiltinSet(1 << 8);
pub const DOUBLE: BuiltinSet = BuiltinSet(1 << 9);
pub const COMPLEX: BuiltinSet = BuiltinSet(1 << 10);
pub const IMAGINARY: BuiltinSet = BuiltinSet(1 << 11);
pub const INT128: BuiltinSet = BuiltinSet(1 << 12);
pub const FLOAT16: BuiltinSet = BuiltinSet(1 << 13);
pub const FLOAT32: BuiltinSet = BuiltinSet(1 << 14);
pub const FLOAT64: BuiltinSet = BuiltinSet(1 << 15);
pub const FLOAT128: BuiltinSet = BuiltinSet(1 << 16);
pub const FLOAT32X: BuiltinSet = BuiltinSet(1 << 17);
pub const FLOAT64X: BuiltinSet = BuiltinSet(1 << 18);
pub const FLOAT128X: BuiltinSet = BuiltinSet(1 << 19);
pub const FLOAT80: BuiltinSet = BuiltinSet(1 << 20);
pub const DECIMAL32: BuiltinSet = BuiltinSet(1 << 21);
pub const DECIMAL64: BuiltinSet = BuiltinSet(1 << 22);
pub const DECIMAL128: BuiltinSet = BuiltinSet(1 << 23);
const DECIMALS: BuiltinSet =
BuiltinSet(Self::DECIMAL32.0 | Self::DECIMAL64.0 | Self::DECIMAL128.0);
const EXTENDED: BuiltinSet = BuiltinSet(
Self::FLOAT16.0
| Self::FLOAT32.0
| Self::FLOAT64.0
| Self::FLOAT128.0
| Self::FLOAT32X.0
| Self::FLOAT64X.0
| Self::FLOAT128X.0
| Self::FLOAT80.0,
);
const INTEGER: BuiltinSet =
BuiltinSet(Self::SHORT.0 | Self::INT.0 | Self::LONG.0 | Self::SIGNED.0 | Self::UNSIGNED.0);
#[inline]
#[must_use]
pub const fn has(self, other: BuiltinSet) -> bool {
self.0 & other.0 == other.0
}
#[inline]
#[must_use]
pub const fn has_any(self, other: BuiltinSet) -> bool {
self.0 & other.0 != 0
}
#[inline]
#[must_use]
pub const fn with(self, other: BuiltinSet) -> BuiltinSet {
BuiltinSet(self.0 | other.0)
}
#[inline]
#[must_use]
pub const fn without(self, other: BuiltinSet) -> BuiltinSet {
BuiltinSet(self.0 & !other.0)
}
#[inline]
#[must_use]
pub const fn is_none(self) -> bool {
self.0 == 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuiltinError {
Duplicate,
TooManyLongs,
}
impl Builtin {
pub const NONE: Builtin = Builtin { set: BuiltinSet::NONE, longs: 0 };
pub const fn add(self, which: BuiltinSet) -> Result<Builtin, BuiltinError> {
if which.0 == BuiltinSet::LONG.0 {
if self.longs >= 2 {
return Err(BuiltinError::TooManyLongs);
}
return Ok(Builtin { set: self.set.with(which), longs: self.longs + 1 });
}
if self.set.has(which) {
return Err(BuiltinError::Duplicate);
}
Ok(Builtin { set: self.set.with(which), longs: self.longs })
}
#[must_use]
pub const fn is_none(self) -> bool {
self.set.is_none()
}
#[must_use]
pub fn resolve(self) -> Option<Basic> {
let set = self.set;
let longs = self.longs;
let complexity = match (set.has(BuiltinSet::COMPLEX), set.has(BuiltinSet::IMAGINARY)) {
(true, true) => return None,
(true, false) => Complexity::Complex,
(false, true) => Complexity::Imaginary,
(false, false) => Complexity::Real,
};
let set = set.without(BuiltinSet::COMPLEX.with(BuiltinSet::IMAGINARY));
let basic = |scalar| Some(Basic { scalar, complexity });
let real = |scalar| {
if complexity == Complexity::Real { Some(Basic { scalar, complexity }) } else { None }
};
if set.has(BuiltinSet::SIGNED) && set.has(BuiltinSet::UNSIGNED) {
return None;
}
let unsigned = set.has(BuiltinSet::UNSIGNED);
let signs = BuiltinSet::SIGNED.with(BuiltinSet::UNSIGNED);
if set.has(BuiltinSet::VOID) {
return if set == BuiltinSet::VOID && longs == 0 { real(Scalar::Void) } else { None };
}
if set.has(BuiltinSet::BOOL) {
return if set == BuiltinSet::BOOL && longs == 0 { real(Scalar::Bool) } else { None };
}
if set.has(BuiltinSet::CHAR) {
if set.without(signs) != BuiltinSet::CHAR || longs != 0 {
return None;
}
return real(match (set.has(BuiltinSet::SIGNED), unsigned) {
(true, _) => Scalar::SignedChar,
(_, true) => Scalar::UnsignedChar,
_ => Scalar::Char,
});
}
if set.has(BuiltinSet::INT128) {
if set.without(signs) != BuiltinSet::INT128 || longs != 0 {
return None;
}
return real(if unsigned { Scalar::UnsignedInt128 } else { Scalar::Int128 });
}
if set.has_any(BuiltinSet::DECIMALS) {
if longs != 0 || complexity != Complexity::Real {
return None;
}
return match set {
s if s == BuiltinSet::DECIMAL32 => real(Scalar::Decimal32),
s if s == BuiltinSet::DECIMAL64 => real(Scalar::Decimal64),
s if s == BuiltinSet::DECIMAL128 => real(Scalar::Decimal128),
_ => None,
};
}
if set.has_any(BuiltinSet::EXTENDED) {
if longs != 0 {
return None;
}
return match set {
s if s == BuiltinSet::FLOAT16 => basic(Scalar::Float16),
s if s == BuiltinSet::FLOAT32 => basic(Scalar::Float32),
s if s == BuiltinSet::FLOAT64 => basic(Scalar::Float64),
s if s == BuiltinSet::FLOAT128 => basic(Scalar::Float128),
s if s == BuiltinSet::FLOAT32X => basic(Scalar::Float32x),
s if s == BuiltinSet::FLOAT64X => basic(Scalar::Float64x),
s if s == BuiltinSet::FLOAT128X => basic(Scalar::Float128x),
s if s == BuiltinSet::FLOAT80 => basic(Scalar::Float80),
_ => None,
};
}
if set.has(BuiltinSet::FLOAT) {
return if set == BuiltinSet::FLOAT && longs == 0 {
basic(Scalar::Float)
} else {
None
};
}
if set.has(BuiltinSet::DOUBLE) {
if set.without(BuiltinSet::LONG) != BuiltinSet::DOUBLE {
return None;
}
return match longs {
0 => basic(Scalar::Double),
1 => basic(Scalar::LongDouble),
_ => None,
};
}
if set.is_none() {
return None;
}
if !BuiltinSet::INTEGER.has(set) {
return None;
}
if set.has(BuiltinSet::SHORT) {
if longs != 0 {
return None;
}
return basic(if unsigned { Scalar::UnsignedShort } else { Scalar::Short });
}
match longs {
0 => basic(if unsigned { Scalar::UnsignedInt } else { Scalar::Int }),
1 => basic(if unsigned { Scalar::UnsignedLong } else { Scalar::Long }),
2 => basic(if unsigned { Scalar::UnsignedLongLong } else { Scalar::LongLong }),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Basic {
pub scalar: Scalar,
pub complexity: Complexity,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Complexity {
Real,
Complex,
Imaginary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Scalar {
Void,
Bool,
Char,
SignedChar,
UnsignedChar,
Short,
UnsignedShort,
Int,
UnsignedInt,
Long,
UnsignedLong,
LongLong,
UnsignedLongLong,
Int128,
UnsignedInt128,
Float,
Double,
LongDouble,
Float16,
Float32,
Float64,
Float128,
Float32x,
Float64x,
Float128x,
Float80,
Decimal32,
Decimal64,
Decimal128,
}
#[cfg(test)]
mod tests {
use super::*;
fn resolve(keywords: &[BuiltinSet]) -> Option<Basic> {
let mut b = Builtin::NONE;
for &k in keywords {
b = b.add(k).expect("keyword rejected");
}
b.resolve()
}
fn real(keywords: &[BuiltinSet]) -> Option<Scalar> {
resolve(keywords).filter(|b| b.complexity == Complexity::Real).map(|b| b.scalar)
}
#[test]
fn the_plain_integer_spellings() {
assert_eq!(real(&[BuiltinSet::INT]), Some(Scalar::Int));
assert_eq!(real(&[BuiltinSet::SIGNED]), Some(Scalar::Int));
assert_eq!(real(&[BuiltinSet::UNSIGNED]), Some(Scalar::UnsignedInt));
assert_eq!(real(&[BuiltinSet::SIGNED, BuiltinSet::INT]), Some(Scalar::Int));
assert_eq!(real(&[BuiltinSet::SHORT]), Some(Scalar::Short));
assert_eq!(real(&[BuiltinSet::SHORT, BuiltinSet::INT]), Some(Scalar::Short));
assert_eq!(
real(&[BuiltinSet::UNSIGNED, BuiltinSet::SHORT, BuiltinSet::INT]),
Some(Scalar::UnsignedShort)
);
}
#[test]
fn long_counts_rather_than_repeats() {
assert_eq!(real(&[BuiltinSet::LONG]), Some(Scalar::Long));
assert_eq!(real(&[BuiltinSet::LONG, BuiltinSet::LONG]), Some(Scalar::LongLong));
assert_eq!(
real(&[BuiltinSet::UNSIGNED, BuiltinSet::LONG, BuiltinSet::LONG, BuiltinSet::INT]),
Some(Scalar::UnsignedLongLong)
);
let three = Builtin::NONE
.add(BuiltinSet::LONG)
.and_then(|b| b.add(BuiltinSet::LONG))
.and_then(|b| b.add(BuiltinSet::LONG));
assert_eq!(three, Err(BuiltinError::TooManyLongs));
}
#[test]
fn a_repeated_keyword_is_caught_where_it_is_written() {
let twice = Builtin::NONE.add(BuiltinSet::INT).and_then(|b| b.add(BuiltinSet::INT));
assert_eq!(twice, Err(BuiltinError::Duplicate));
}
#[test]
fn plain_char_is_its_own_type() {
assert_eq!(real(&[BuiltinSet::CHAR]), Some(Scalar::Char));
assert_eq!(real(&[BuiltinSet::SIGNED, BuiltinSet::CHAR]), Some(Scalar::SignedChar));
assert_eq!(real(&[BuiltinSet::UNSIGNED, BuiltinSet::CHAR]), Some(Scalar::UnsignedChar));
assert_eq!(real(&[BuiltinSet::CHAR, BuiltinSet::INT]), None);
assert_eq!(real(&[BuiltinSet::CHAR, BuiltinSet::LONG]), None);
}
#[test]
fn long_double_is_a_double_with_one_long() {
assert_eq!(real(&[BuiltinSet::DOUBLE]), Some(Scalar::Double));
assert_eq!(real(&[BuiltinSet::LONG, BuiltinSet::DOUBLE]), Some(Scalar::LongDouble));
assert_eq!(real(&[BuiltinSet::LONG, BuiltinSet::LONG, BuiltinSet::DOUBLE]), None);
assert_eq!(real(&[BuiltinSet::LONG, BuiltinSet::FLOAT]), None);
assert_eq!(real(&[BuiltinSet::DOUBLE, BuiltinSet::INT]), None);
}
#[test]
fn complex_is_a_modifier_and_not_a_type() {
assert_eq!(
resolve(&[BuiltinSet::COMPLEX, BuiltinSet::DOUBLE]),
Some(Basic { scalar: Scalar::Double, complexity: Complexity::Complex })
);
assert_eq!(
resolve(&[BuiltinSet::LONG, BuiltinSet::DOUBLE, BuiltinSet::IMAGINARY]),
Some(Basic { scalar: Scalar::LongDouble, complexity: Complexity::Imaginary })
);
assert_eq!(
resolve(&[BuiltinSet::COMPLEX, BuiltinSet::INT]),
Some(Basic { scalar: Scalar::Int, complexity: Complexity::Complex })
);
assert_eq!(resolve(&[BuiltinSet::COMPLEX, BuiltinSet::IMAGINARY, BuiltinSet::FLOAT]), None);
assert_eq!(resolve(&[BuiltinSet::COMPLEX, BuiltinSet::DECIMAL64]), None);
}
#[test]
fn the_extended_types_stand_alone_or_with_complex() {
assert_eq!(real(&[BuiltinSet::FLOAT128]), Some(Scalar::Float128));
assert_eq!(
resolve(&[BuiltinSet::COMPLEX, BuiltinSet::FLOAT128]),
Some(Basic { scalar: Scalar::Float128, complexity: Complexity::Complex })
);
assert_eq!(real(&[BuiltinSet::FLOAT32X]), Some(Scalar::Float32x));
assert_eq!(real(&[BuiltinSet::FLOAT128X]), Some(Scalar::Float128x));
assert_eq!(real(&[BuiltinSet::FLOAT16, BuiltinSet::INT]), None);
assert_eq!(real(&[BuiltinSet::FLOAT32, BuiltinSet::FLOAT64]), None);
}
#[test]
fn the_wide_integers_take_a_sign_and_nothing_else() {
assert_eq!(real(&[BuiltinSet::INT128]), Some(Scalar::Int128));
assert_eq!(real(&[BuiltinSet::UNSIGNED, BuiltinSet::INT128]), Some(Scalar::UnsignedInt128));
assert_eq!(real(&[BuiltinSet::INT128, BuiltinSet::INT]), None);
}
#[test]
fn void_and_bool_take_nothing() {
assert_eq!(real(&[BuiltinSet::VOID]), Some(Scalar::Void));
assert_eq!(real(&[BuiltinSet::BOOL]), Some(Scalar::Bool));
assert_eq!(real(&[BuiltinSet::VOID, BuiltinSet::INT]), None);
assert_eq!(real(&[BuiltinSet::UNSIGNED, BuiltinSet::BOOL]), None);
}
#[test]
fn two_signs_name_no_type() {
assert_eq!(real(&[BuiltinSet::SIGNED, BuiltinSet::UNSIGNED]), None);
}
#[test]
fn no_keywords_at_all_names_no_type() {
assert_eq!(Builtin::NONE.resolve(), None);
assert!(Builtin::NONE.is_none());
}
#[test]
fn short_and_long_do_not_go_together() {
assert_eq!(real(&[BuiltinSet::SHORT, BuiltinSet::LONG]), None);
}
#[test]
fn qualifier_sets_add_up() {
let q = Quals::NONE.with(Quals::CONST).with(Quals::VOLATILE);
assert!(q.has(Quals::CONST));
assert!(q.has(Quals::VOLATILE));
assert!(!q.has(Quals::RESTRICT));
assert!(Quals::NONE.is_none());
assert!(!q.is_none());
}
#[test]
fn function_specifier_sets_add_up() {
let f = FuncSpecs::NONE.with(FuncSpecs::INLINE);
assert!(f.has(FuncSpecs::INLINE));
assert!(!f.has(FuncSpecs::NORETURN));
assert!(FuncSpecs::NONE.is_none());
}
}