diff --git a/cobalt-ast/src/ast.rs b/cobalt-ast/src/ast.rs index 195e4d0..1adca55 100644 --- a/cobalt-ast/src/ast.rs +++ b/cobalt-ast/src/ast.rs @@ -37,7 +37,10 @@ pub trait AST<'src>: ASTClone<'src> + std::fmt::Debug { 1 } // AST properties - fn is_const(&self) -> bool { + fn is_const(&self, _ctx: &CompCtx<'src, '_>) -> bool { + false + } + fn has_const_call(&self, _ctx: &CompCtx<'src, '_>) -> bool { false } // pretty printing diff --git a/cobalt-ast/src/ast/funcs.rs b/cobalt-ast/src/ast/funcs.rs index cd3f0b0..aa6aa32 100644 --- a/cobalt-ast/src/ast/funcs.rs +++ b/cobalt-ast/src/ast/funcs.rs @@ -1730,6 +1730,9 @@ impl<'src> AST<'src> for CallAST<'src> { fn nodes(&self) -> usize { self.target.nodes() + self.args.iter().map(|x| x.nodes()).sum::() + 1 } + fn is_const(&self, ctx: &CompCtx<'src, '_>) -> bool { + self.target.has_const_call(ctx) && self.args.iter().all(|a| a.is_const(ctx)) + } fn codegen_impl<'ctx>( &self, ctx: &CompCtx<'src, 'ctx>, @@ -1776,6 +1779,22 @@ impl<'src> AST<'src> for IntrinsicAST<'src> { fn loc(&self) -> SourceSpan { self.loc } + fn is_const(&self, _ctx: &CompCtx<'src, '_>) -> bool { + intrinsics::VALUE_INTRINSICS + .pin() + .get(&*self.name) + .map_or(false, |i| i.is_const) + || intrinsics::FUNCTION_INTRINSICS + .pin() + .get(&*self.name) + .map_or(false, |i| i.is_const) + } + fn has_const_call(&self, _ctx: &CompCtx<'src, '_>) -> bool { + intrinsics::FUNCTION_INTRINSICS + .pin() + .get(&*self.name) + .map_or(false, |i| i.is_const) + } fn codegen_impl<'ctx>( &self, _ctx: &CompCtx<'src, 'ctx>, diff --git a/cobalt-ast/src/ast/literals.rs b/cobalt-ast/src/ast/literals.rs index b230440..f3a5254 100644 --- a/cobalt-ast/src/ast/literals.rs +++ b/cobalt-ast/src/ast/literals.rs @@ -18,7 +18,7 @@ impl<'src> AST<'src> for IntLiteralAST<'src> { fn loc(&self) -> SourceSpan { self.loc } - fn is_const(&self) -> bool { + fn is_const(&self, _ctx: &CompCtx<'src, '_>) -> bool { true } fn codegen_impl<'ctx>( @@ -117,7 +117,7 @@ impl<'src> AST<'src> for FloatLiteralAST<'src> { fn loc(&self) -> SourceSpan { self.loc } - fn is_const(&self) -> bool { + fn is_const(&self, _ctx: &CompCtx<'src, '_>) -> bool { true } fn codegen_impl<'ctx>( @@ -185,7 +185,7 @@ impl<'src> AST<'src> for CharLiteralAST<'src> { fn loc(&self) -> SourceSpan { self.loc } - fn is_const(&self) -> bool { + fn is_const(&self, _ctx: &CompCtx<'src, '_>) -> bool { true } fn codegen_impl<'ctx>( @@ -293,7 +293,7 @@ impl<'src> AST<'src> for StringLiteralAST<'src> { fn loc(&self) -> SourceSpan { self.loc } - fn is_const(&self) -> bool { + fn is_const(&self, _ctx: &CompCtx<'src, '_>) -> bool { true } fn codegen_impl<'ctx>( diff --git a/cobalt-ast/src/ast/misc.rs b/cobalt-ast/src/ast/misc.rs index 7904dd6..034886a 100644 --- a/cobalt-ast/src/ast/misc.rs +++ b/cobalt-ast/src/ast/misc.rs @@ -304,8 +304,8 @@ impl<'src> AST<'src> for ParenAST<'src> { fn nodes(&self) -> usize { self.base.nodes() + 1 } - fn is_const(&self) -> bool { - self.base.is_const() + fn is_const(&self, ctx: &CompCtx<'src, '_>) -> bool { + self.base.is_const(ctx) } fn codegen_impl<'ctx>( &self, diff --git a/cobalt-ast/src/ast/ops.rs b/cobalt-ast/src/ast/ops.rs index fadfc1a..4e8ac35 100644 --- a/cobalt-ast/src/ast/ops.rs +++ b/cobalt-ast/src/ast/ops.rs @@ -277,7 +277,9 @@ impl<'src> AST<'src> for PrefixAST<'src> { return Value::error(); } if self.op == "&" { - if let Some(ty) = v.data_type.downcast::() { + if let Ok(val) = v.data_type.pre_op(v.clone(), "&", self.loc, ctx, true) { + val + } else if let Some(ty) = v.data_type.downcast::() { Value { data_type: types::Pointer::new(ty.base()), ..v diff --git a/cobalt-ast/src/ast/vars.rs b/cobalt-ast/src/ast/vars.rs index 2d2c2ab..7abcdac 100644 --- a/cobalt-ast/src/ast/vars.rs +++ b/cobalt-ast/src/ast/vars.rs @@ -477,7 +477,7 @@ impl<'src> AST<'src> for VarDefAST<'src> { Value::error() } } - } else if self.val.is_const() && self.type_.is_none() { + } else if self.val.is_const(ctx) && self.type_.is_none() { let mut val = self.val.codegen(ctx, errs); let dt = if let Some(t) = self.type_.as_ref().map(|t| { let oic = ctx.is_const.replace(true); @@ -1852,6 +1852,10 @@ impl<'src> AST<'src> for VarGetAST<'src> { fn loc(&self) -> SourceSpan { self.loc } + fn is_const<'ctx>(&self, ctx: &CompCtx<'src, '_>) -> bool { + ctx.lookup(&self.name, self.global) + .map_or(true, |v| v.0.inter_val.is_some()) + } fn codegen_impl<'ctx>( &self, ctx: &CompCtx<'src, 'ctx>, diff --git a/cobalt-ast/src/intrinsics.rs b/cobalt-ast/src/intrinsics.rs index c167fd9..c5d9e07 100644 --- a/cobalt-ast/src/intrinsics.rs +++ b/cobalt-ast/src/intrinsics.rs @@ -6,10 +6,21 @@ pub struct ValueIntrinsic { pub name: &'static str, pub wraps: ValueCallType, pub ret: fn() -> TypeRef, + pub is_const: bool, } impl ValueIntrinsic { - pub const fn new(name: &'static str, wraps: ValueCallType, ret: fn() -> TypeRef) -> Self { - Self { name, wraps, ret } + pub const fn new( + name: &'static str, + wraps: ValueCallType, + ret: fn() -> TypeRef, + is_const: bool, + ) -> Self { + Self { + name, + wraps, + ret, + is_const, + } } } @@ -17,10 +28,15 @@ impl ValueIntrinsic { pub struct FunctionIntrinsic { pub name: &'static str, pub wraps: FunctionCallType, + pub is_const: bool, } impl FunctionIntrinsic { - pub const fn new(name: &'static str, wraps: FunctionCallType) -> Self { - Self { name, wraps } + pub const fn new(name: &'static str, wraps: FunctionCallType, is_const: bool) -> Self { + Self { + name, + wraps, + is_const, + } } } @@ -48,6 +64,7 @@ pub static FUNCTION_INTRINSICS: Lazy", if _0 {"union"} else {"enum"})] +struct EnumAggregator(bool); +impl TypeSerde for EnumAggregator { + no_type_header!(); + impl_type_proxy!(bool, this => this.0, s => if s {&UNION_AGG} else {&ENUM_AGG}); +} +impl Type for EnumAggregator { + fn size(&self) -> SizeType { + SizeType::Meta + } + fn align(&self) -> u16 { + 0 + } + fn decay(&self) -> TypeRef { + types::TypeData::new() + } + fn _can_iconv_to(&self, other: TypeRef, _ctx: &CompCtx) -> bool { + other == types::TypeData::new() + } + fn _iconv_to<'src, 'ctx>( + &'static self, + val: Value<'src, 'ctx>, + target: (TypeRef, Option), + _ctx: &CompCtx<'src, 'ctx>, + ) -> Result, CobaltError<'src>> { + if target.0 == types::TypeData::new() { + if let Some(InterData::Array(vec)) = val.inter_val { + let res = vec + .into_iter() + .map(|t| { + let InterData::Spanned(_, i) = t else { + unreachable!() + }; + let InterData::Type(t) = *i else { + unreachable!() + }; + t + }) + .collect::>(); + Ok(Value::make_type(EnumOrUnion::new(res, self.0))) + } else { + unreachable!() + } + } else { + Err(cant_iconv(&val, target.0, target.1)) + } + } + fn _has_bin_lhs( + &self, + other: TypeRef, + op: &str, + ctx: &CompCtx, + _move_left: bool, + _move_right: bool, + ) -> bool { + op == "|" && other.impl_convertible(types::TypeData::new(), ctx) + } + fn _bin_lhs<'src, 'ctx>( + &'static self, + mut lhs: Value<'src, 'ctx>, + rhs: Value<'src, 'ctx>, + op: (&'static str, SourceSpan), + ctx: &CompCtx<'src, 'ctx>, + _move_left: bool, + _move_right: bool, + ) -> Result, CobaltError<'src>> { + if op.0 == "|" && rhs.data_type.impl_convertible(types::TypeData::new(), ctx) { + let curr = rhs.loc; + let rt = rhs.into_type(ctx)?; + let Some(InterData::Array(vec)) = &mut lhs.inter_val else { + unreachable!() + }; + if let Some(prev) = vec.iter().filter_map(|i| { + let InterData::Spanned(s, t) = i else { + unreachable!() + }; + let InterData::Type(t) = &**t else { + unreachable!() + }; + (rt == *t).then_some(*s) + }).next() { + Err(CobaltError::DuplicateEnumVariant { + is_union: self.0, + prev, curr, + ty: rt.to_string(), + }) + } else { + vec.push(InterData::Spanned(curr, Box::new(InterData::Type(rt)))); + Ok(lhs) + } + } else { + Err(invalid_binop(&lhs, &rhs, op.0, op.1)) + } + } +} + +inventory::submit! { + ValueIntrinsic::new("enum", |_| Value::metaval(InterData::Array(vec![]), &ENUM_AGG), || &ENUM_AGG, true) +} +inventory::submit! { + ValueIntrinsic::new("union", |_| Value::metaval(InterData::Array(vec![]), &UNION_AGG), || &UNION_AGG, true) +} + +submit_types!(EnumAggregator); diff --git a/cobalt-ast/src/intrinsics/misc.rs b/cobalt-ast/src/intrinsics/misc.rs index 9c7a0b1..76a049c 100644 --- a/cobalt-ast/src/intrinsics/misc.rs +++ b/cobalt-ast/src/intrinsics/misc.rs @@ -288,8 +288,8 @@ fn alloca<'src, 'ctx>( } } inventory::submit! { - FunctionIntrinsic::new("asm", asm) + FunctionIntrinsic::new("asm", asm, true) } inventory::submit! { - FunctionIntrinsic::new("alloca", alloca) + FunctionIntrinsic::new("alloca", alloca, false) } diff --git a/cobalt-ast/src/intrinsics/types.rs b/cobalt-ast/src/intrinsics/types.rs index a208643..c1c3bbf 100644 --- a/cobalt-ast/src/intrinsics/types.rs +++ b/cobalt-ast/src/intrinsics/types.rs @@ -2,7 +2,7 @@ use super::*; inventory::submit! { FunctionIntrinsic::new("type", |v, c, a, _| { extract_single("@type", v, c, a).map(|v| Value::make_type(v.data_type)) - }) + }, true) } inventory::submit! { FunctionIntrinsic::new("size", |v, c, a, ctx| { @@ -14,7 +14,7 @@ inventory::submit! { ) }) }) - }) + }, true) } inventory::submit! { FunctionIntrinsic::new("sized", |v, c, a, ctx| { @@ -30,7 +30,7 @@ inventory::submit! { ) }) }) - }) + }, true) } inventory::submit! { FunctionIntrinsic::new("align", |v, c, a, ctx| { @@ -42,7 +42,7 @@ inventory::submit! { ) }) }) - }) + }, true) } inventory::submit! { FunctionIntrinsic::new("typename", |v, c, a, ctx| { @@ -50,7 +50,7 @@ inventory::submit! { v.into_type(ctx) .map(|t| Value::make_str(t.to_string(), ctx)) }) - }) + }, true) } fn extract_single<'src, 'ctx>( name: &'static str, diff --git a/cobalt-ast/src/intrinsics/version.rs b/cobalt-ast/src/intrinsics/version.rs index 2c95ccb..ff4bea3 100644 --- a/cobalt-ast/src/intrinsics/version.rs +++ b/cobalt-ast/src/intrinsics/version.rs @@ -11,7 +11,8 @@ inventory::submit! { InterData::Int(*VERSION_MAJOR as _), types::IntLiteral::new(), ), - || types::IntLiteral::new() + || types::IntLiteral::new(), + true ) } inventory::submit! { @@ -21,7 +22,8 @@ inventory::submit! { InterData::Int(*VERSION_MINOR as _), types::IntLiteral::new(), ), - || types::IntLiteral::new() + || types::IntLiteral::new(), + true ) } inventory::submit! { @@ -31,14 +33,16 @@ inventory::submit! { InterData::Int(*VERSION_PATCH as _), types::IntLiteral::new(), ), - || types::IntLiteral::new() + || types::IntLiteral::new(), + true ) } inventory::submit! { ValueIntrinsic::new( "version_string", |ctx| Value::make_str(env!("CARGO_PKG_VERSION"), ctx), - || types::SizedArray::new(types::Int::unsigned(8), env!("CARGO_PKG_VERSION").len() as _) + || types::SizedArray::new(types::Int::unsigned(8), env!("CARGO_PKG_VERSION").len() as _), + true ) } inventory::submit! { @@ -56,7 +60,8 @@ inventory::submit! { .into_iter() .collect(), ) - } + }, + true ) } fn make_version<'src, 'ctx>(ctx: &CompCtx<'src, 'ctx>) -> Value<'src, 'ctx> { diff --git a/cobalt-ast/src/types.rs b/cobalt-ast/src/types.rs index 74339af..a2bd5eb 100644 --- a/cobalt-ast/src/types.rs +++ b/cobalt-ast/src/types.rs @@ -37,6 +37,9 @@ impl SizeType { self } } + pub fn is_c(self) -> bool { + !matches!(self, Static(0) | Dynamic | Meta) + } } impl Display for SizeType { fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -155,6 +158,8 @@ impl TypeSerde for T { Self::create_self() } } + +#[macro_export] macro_rules! no_type_header { () => { type Header = (); @@ -162,9 +167,11 @@ macro_rules! no_type_header { false } fn get_header() -> Self::Header {} - fn set_header(header: Self::Header) {} + fn set_header(_header: Self::Header) {} }; } + +#[macro_export] macro_rules! impl_null_type_with_new { ($ty:ty) => { impl NullType for $ty { @@ -174,6 +181,8 @@ macro_rules! impl_null_type_with_new { } }; } + +#[macro_export] macro_rules! impl_type_proxy { ($proxy:ty, $gp:pat => $ge:expr, $sp:pat => $se:expr) => { type Proxy = $proxy; @@ -664,6 +673,14 @@ pub trait Type: self.as_type_ref() }) } + + fn add_ptr(&'static self, is_mut: bool) -> TypeRef { + types::Pointer::new(if is_mut { + types::Mut::new(self.as_type_ref()) + } else { + self.as_type_ref() + }) + } } impl dyn Type { pub fn is(&self) -> bool { @@ -753,6 +770,7 @@ pub struct TypeLoader { pub load_header: fn(&mut dyn erased_serde::Deserializer) -> Result<(), erased_serde::Error>, pub load: fn(&mut dyn erased_serde::Deserializer) -> Result, } + #[macro_export] macro_rules! type_loader { ($T:ty) => { @@ -769,6 +787,8 @@ macro_rules! type_loader { } }; } + +#[macro_export] macro_rules! submit_types { ($T:ty) => ( inventory::submit! {type_loader!($T)} @@ -815,6 +835,7 @@ pub static TYPE_SERIAL_REGISTRY: Lazy> = pub mod agg; pub mod custom; +pub mod enums; pub mod float; pub mod func; pub mod int; @@ -824,6 +845,7 @@ pub mod meta; pub use agg::*; pub use custom::*; +pub use enums::*; pub use float::*; pub use func::*; pub use int::*; diff --git a/cobalt-ast/src/types/agg.rs b/cobalt-ast/src/types/agg.rs index 8bdeb50..e254aac 100644 --- a/cobalt-ast/src/types/agg.rs +++ b/cobalt-ast/src/types/agg.rs @@ -1,25 +1,33 @@ use super::*; use std::collections::BTreeMap; -fn tuple_size(types: &[TypeRef]) -> SizeType { +pub(super) fn tuple_size(types: &[TypeRef]) -> SizeType { let mut out = 0; let mut overall_align = 1; - for ty in types { - let size = ty.size(); - if let SizeType::Static(size) = size { - let align = ty.align() as u32; - out += align - 1; - out /= align; - out += size; - if align > overall_align { - overall_align = align + for (n, ty) in types.iter().enumerate() { + match ty.size() { + SizeType::Static(size) => { + let align = ty.align() as u32; + out += align - 1; + out /= align; + out += size; + if align > overall_align { + overall_align = align + } } - } else { - return size; + SizeType::Dynamic => { + return if types[n..].iter().any(|i| i.size() == SizeType::Meta) { + SizeType::Meta + } else { + SizeType::Dynamic + } + } + SizeType::Meta => return SizeType::Meta, } } out += overall_align - 1; out /= overall_align; + out *= overall_align; SizeType::Static(out) } @@ -57,7 +65,13 @@ impl TypeSerde for Tuple { } impl Type for Tuple { fn align(&self) -> u16 { - self.0.iter().map(|v| v.align()).max().unwrap_or(1) + self.0.iter().map(|v| v.align()).fold(1, |old, new| { + if old == 0 || new == 0 { + 0 + } else { + std::cmp::max(old, new) + } + }) } fn size(&self) -> SizeType { tuple_size(&self.0) diff --git a/cobalt-ast/src/types/custom.rs b/cobalt-ast/src/types/custom.rs index d3e9f74..3c2ca51 100644 --- a/cobalt-ast/src/types/custom.rs +++ b/cobalt-ast/src/types/custom.rs @@ -3,11 +3,14 @@ use once_cell::sync::Lazy; use serde::de::DeserializeSeed; use std::marker::PhantomData; use std::{cell::Ref, collections::HashMap}; + static CUSTOM_INTERN: Interner> = Interner::new(); static CUSTOM_DATA: Lazy< flurry::HashMap<&'static str, (TypeRef, bool, flurry::HashMap, usize>, usize)>, > = Lazy::new(flurry::HashMap::new); + pub type ValueRef<'a, 'src, 'ctx> = Ref<'a, Value<'src, 'ctx>>; + #[derive(Debug, ConstIdentify, Display, RefCastCustom)] #[repr(transparent)] pub struct Custom(Box); @@ -129,6 +132,7 @@ impl Custom { }); } } + #[derive(DeserializeState)] #[serde(de_parameters = "'a")] #[serde(deserialize_state = "&'a CompCtx<'src, 'ctx>")] diff --git a/cobalt-ast/src/types/enums.rs b/cobalt-ast/src/types/enums.rs new file mode 100644 index 0000000..384c9b2 --- /dev/null +++ b/cobalt-ast/src/types/enums.rs @@ -0,0 +1,554 @@ +use super::*; +use std::fmt::{self, Display, Formatter}; + +#[inline(always)] +pub fn union_align(tag: Option, types: &[TypeRef]) -> u16 { + types + .iter() + .map(|v| v.align()) + .try_fold(tag.map_or(1, |t| t.align()), |old, new| { + if new == 0 { + None + } else { + Some(std::cmp::max(old, new)) + } + }) + .unwrap_or(0) +} +#[inline(always)] +pub fn union_size(types: &[TypeRef], align: u32) -> SizeType { + types + .iter() + .try_fold(None, |old, new| { + let sz = new.size(); + if sz == SizeType::Meta { + None + } else { + Some(Some(if let Some(old) = old { + match (old, sz) { + (SizeType::Static(l), SizeType::Static(r)) => { + SizeType::Static(std::cmp::max(l, r)) + } + (SizeType::Dynamic, _) | (_, SizeType::Dynamic) => SizeType::Dynamic, + (SizeType::Meta, _) | (_, SizeType::Meta) => unreachable!(), + } + } else { + sz + })) + } + }) + .flatten() + .unwrap_or(SizeType::Meta) + .map_static(|mut s| { + s *= align; + s += align - 1; + s /= align; + s + }) +} +pub fn union_type<'ctx>( + tag: Option, + types: &[TypeRef], + ctx: &CompCtx<'_, 'ctx>, +) -> Option> { + let mut found = false; + let mut max_align = 0; + let mut max_size = 0; + let mut max_aligned = ctx.null_type; + for ty in types { + let size = ty.size().as_static()?; + let align = ty.align(); + let Some(llt) = ty.llvm_type(ctx) else { + if size == 0 { + continue; + } else { + return None; + } + }; + if align > max_align { + max_align = align; + max_size = size; + max_aligned = llt; + found = true; + } + } + found.then_some(())?; + let total_size = union_size(types, max_align as _).as_static()?; + let body = if total_size == max_size { + max_aligned + } else { + ctx.context + .struct_type( + &[ + max_aligned, + ctx.context + .i8_type() + .array_type(total_size - max_size) + .into(), + ], + false, + ) + .into() + }; + if let Some(llt) = tag + .and_then(|tag| tag.size().is_c().then(|| tag.llvm_type(ctx))) + .flatten() + { + Some(ctx.context.struct_type(&[llt, body], false).into()) + } else { + Some(body) + } +} + +#[derive(Debug, ConstIdentify, PartialEq, Eq, Hash, RefCastCustom)] +#[repr(transparent)] +pub struct EnumOrUnion((Box<[TypeRef]>, bool)); +impl EnumOrUnion { + #[ref_cast_custom] + fn from_ref(variants: &(Box<[TypeRef]>, bool)) -> &Self; + + pub fn new>>(variants: V, sorted: bool) -> &'static Self { + static INTERN: Interner<(Box<[TypeRef]>, bool)> = Interner::new(); + let mut vars = variants.into(); + if sorted { + vars.sort_by_cached_key(ToString::to_string); + } + Self::from_ref(INTERN.intern((vars, sorted))) + } + + pub fn variants(&self) -> &[TypeRef] { + &self.0 .0 + } + + pub fn is_sorted(&self) -> bool { + self.0 .1 + } + + pub fn tag_type(&self) -> TypeRef { + match self.variants().len() { + 0 | 1 => types::Null::new(), + c => types::Int::unsigned((usize::BITS - (c - 1).leading_zeros()) as _), + } + } +} +impl Display for EnumOrUnion { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(if self.0 .1 { "@union" } else { "@enum" })?; + for item in self.0 .0.iter() { + f.write_str(" | ")?; + Display::fmt(&item, f)?; + } + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Serialize, Deserialize)] +pub struct EoUShim { + sorted: bool, + variants: Cow<'static, [TypeRef]>, +} + +impl TypeSerde for EnumOrUnion { + no_type_header!(); + impl_type_proxy!(EoUShim, this => EoUShim {sorted: this.is_sorted(), variants: this.variants().into()}, EoUShim {sorted, variants} => Self::new(variants, sorted)); +} +impl Type for EnumOrUnion { + fn size(&'static self) -> SizeType { + let tag = self.tag_type(); + match self.variants() { + [] => SizeType::Meta, + [v] => v.size(), + _ => { + let align = union_align(Some(tag), self.variants()) as u32; + let (mut raw_size, mut tag_size) = + match (union_size(self.variants(), align as _), tag.size()) { + (SizeType::Static(l), SizeType::Static(r)) => (l, r), + (SizeType::Meta, _) | (_, SizeType::Meta) => return SizeType::Meta, + (SizeType::Dynamic, _) | (_, SizeType::Dynamic) => { + return SizeType::Dynamic + } + }; + raw_size *= align; + raw_size += align - 1; + raw_size /= align; + tag_size *= align; + tag_size += align - 1; + tag_size -= align; + SizeType::Static(raw_size + tag_size) + } + } + } + fn align(&'static self) -> u16 { + union_align(Some(self.tag_type()), self.variants()) + } + fn llvm_type<'ctx>(&'static self, ctx: &CompCtx<'_, 'ctx>) -> Option> { + union_type(Some(self.tag_type()), self.variants(), ctx) + } + fn has_dtor(&self, ctx: &CompCtx) -> bool { + self.variants().iter().any(|v| v.has_dtor(ctx)) + } + fn ins_dtor<'src, 'ctx>(&'static self, val: &Value<'src, 'ctx>, ctx: &CompCtx<'src, 'ctx>) { + if ctx.is_const.get() { + return; + } + let mut dtors_iter = self + .variants() + .iter() + .enumerate() + .filter(|v| v.1.has_dtor(ctx)); + let Some((fi, &ft)) = dtors_iter.by_ref().next() else { + return; + }; + if self.variants().len() == 1 { + ft.ins_dtor(val, ctx); + return; + } + let llt = self.llvm_type(ctx).unwrap(); + let Some(eptr) = val.addr(ctx) else { return }; + let ptr = ctx + .builder + .build_struct_gep(llt, eptr, 1, "enum.body") + .unwrap(); + if let Some(InterData::Array(arr)) = &val.inter_val { + if let [InterData::Int(dsc), iv] = &arr[..] { + let ty = self.variants()[*dsc as usize]; + let Some(llt) = ty.llvm_type(ctx) else { return }; + let val = ctx.builder.build_load(llt, ptr, "").unwrap(); + Value::with_addr(Some(val), Some(iv.clone()), ty, ptr).ins_dtor(ctx); + return; + } + } + let Some(start) = ctx.builder.get_insert_block() else { + return; + }; + let Some(f) = start.get_parent() else { + return; + }; + let dsc = ctx + .builder + .build_extract_value(val.value(ctx).unwrap().into_struct_value(), 0, "enum.disc") + .unwrap() + .into_int_value(); + let it = dsc.get_type(); + if dtors_iter.next().is_none() { + let then = ctx.context.append_basic_block(f, "enum.single.dtor"); + let merge = ctx.context.append_basic_block(f, "enum.dtor.merge"); + let check = ctx + .builder + .build_int_compare( + inkwell::IntPredicate::EQ, + dsc, + it.const_int(fi as _, false), + "", + ) + .unwrap(); + ctx.builder + .build_conditional_branch(check, then, merge) + .unwrap(); + ctx.builder.position_at_end(then); + let val = ctx + .builder + .build_load(ft.llvm_type(ctx).unwrap(), ptr, "") + .unwrap(); + Value::with_addr(Some(val), None, ft, ptr).ins_dtor(ctx); + ctx.builder.build_unconditional_branch(merge).unwrap(); + ctx.builder.position_at_end(merge); + return; + } + let merge = ctx.context.append_basic_block(f, "enum.dtor.merge"); + let cases = self + .variants() + .iter() + .enumerate() + .filter_map(|(n, &t)| { + t.has_dtor(ctx) + .then(|| t.llvm_type(ctx)) + .flatten() + .map(|llt| (n, t, llt)) + }) + .map(|(n, t, llt)| { + let blk = ctx + .context + .prepend_basic_block(merge, &format!("enum.dtor.{n}")); + ctx.builder.position_at_end(blk); + let val = ctx.builder.build_load(llt, ptr, "").unwrap(); + Value::with_addr(Some(val), None, t, ptr).ins_dtor(ctx); + ctx.builder.build_unconditional_branch(merge).unwrap(); + (it.const_int(n as _, false), blk) + }) + .collect::>(); + ctx.builder.position_at_end(start); + ctx.builder.build_switch(dsc, merge, &cases).unwrap(); + ctx.builder.position_at_end(merge); + } + fn _has_ref_attr(&self, attr: &str, _ctx: &CompCtx) -> bool { + matches!(attr, "__disc" | "__ptr") + } + fn _has_mut_attr(&self, attr: &str, _ctx: &CompCtx) -> bool { + matches!(attr, "__disc" | "__ptr") + } + fn _has_refmut_attr(&self, attr: &str, _ctx: &CompCtx) -> bool { + matches!(attr, "__disc" | "__ptr") + } + fn attr<'src, 'ctx>( + &'static self, + val: Value<'src, 'ctx>, + attr: (Cow<'src, str>, SourceSpan), + ctx: &CompCtx<'src, 'ctx>, + ) -> Result, CobaltError<'src>> { + match &*attr.0 { + "__disc" => { + let tag = self.tag_type(); + let res = if tag.kind() == types::Int::KIND { + let value = Value::new( + if let (Some(BasicValueEnum::StructValue(sv)), false) = + (val.value(ctx), ctx.is_const.get()) + { + ctx.builder.build_extract_value(sv, 0, "").ok() + } else { + None + }, + if let Some(InterData::Array(arr)) = &val.inter_val { + if let [InterData::Int(ix), _] = &arr[..] { + Some(InterData::Int(*ix)) + } else { + None + } + } else { + None + }, + tag, + ); + val.address.set(self.llvm_type(ctx).and_then(|llt| { + val.addr(ctx) + .and_then(|a| ctx.builder.build_struct_gep(llt, a, 0, "").ok()) + })); + val + } else { + Value { + data_type: tag, + ..Value::null() + } + }; + Ok(res) + } + "__ptr" => { + let tag = self.tag_type(); + let res = if tag.kind() == types::Int::KIND { + if !ctx.is_const.get() { + if let (Some(pv), Some(llt)) = (val.addr(ctx), self.llvm_type(ctx)) { + ctx.builder + .build_struct_gep(llt, pv, 0, "") + .ok() + .map(From::from) + } else { + None + } + } else { + None + } + } else { + val.addr(ctx).map(From::from) + }; + Ok(Value::new(res, None, types::Null::new().add_ptr(false))) + } + _ => Err(CobaltError::AttrNotDefined { + val: self.to_string(), + attr: attr.0, + vloc: val.loc, + aloc: attr.1, + }), + } + } + fn _mut_attr<'src, 'ctx>( + &'static self, + val: Value<'src, 'ctx>, + attr: (Cow<'src, str>, SourceSpan), + ctx: &CompCtx<'src, 'ctx>, + ) -> Result, CobaltError<'src>> { + match &*attr.0 { + "__disc" => { + let tag = self.tag_type(); + let res = if tag.kind() == types::Int::KIND { + Value::new( + self.llvm_type(ctx) + .and_then(|llt| { + val.addr(ctx) + .and_then(|a| ctx.builder.build_struct_gep(llt, a, 0, "").ok()) + }) + .map(From::from), + if let Some(InterData::Array(arr)) = &val.inter_val { + if let [InterData::Int(ix), _] = &arr[..] { + Some(InterData::Int(*ix)) + } else { + None + } + } else { + None + }, + types::Mut::new(tag), + ) + } else { + Value { + data_type: tag, + ..Value::null() + } + }; + Ok(res) + } + "__ptr" => { + let tag = self.tag_type(); + let res = if tag.kind() == types::Int::KIND { + if let (Some(BasicValueEnum::PointerValue(pv)), Some(llt), false) = + (val.comp_val, self.llvm_type(ctx), ctx.is_const.get()) + { + ctx.builder + .build_struct_gep(llt, pv, 0, "") + .ok() + .map(From::from) + } else { + None + } + } else { + val.addr(ctx).map(From::from) + }; + Ok(Value::new(res, None, types::Null::new().add_ptr(true))) + } + _ => Err(CobaltError::AttrNotDefined { + val: self.to_string(), + attr: attr.0, + vloc: val.loc, + aloc: attr.1, + }), + } + } + fn _ref_attr<'src, 'ctx>( + &'static self, + val: Value<'src, 'ctx>, + attr: (Cow<'src, str>, SourceSpan), + ctx: &CompCtx<'src, 'ctx>, + ) -> Result, CobaltError<'src>> { + match &*attr.0 { + "__disc" => { + let tag = self.tag_type(); + let res = if tag.kind() == types::Int::KIND { + Value::new( + self.llvm_type(ctx) + .and_then(|llt| { + val.addr(ctx) + .and_then(|a| ctx.builder.build_struct_gep(llt, a, 0, "").ok()) + }) + .map(From::from), + if let Some(InterData::Array(arr)) = &val.inter_val { + if let [InterData::Int(ix), _] = &arr[..] { + Some(InterData::Int(*ix)) + } else { + None + } + } else { + None + }, + tag.add_ref(false), + ) + } else { + Value { + data_type: tag, + ..Value::null() + } + }; + Ok(res) + } + "__ptr" => { + let tag = self.tag_type(); + let res = if tag.kind() == types::Int::KIND { + if let (Some(BasicValueEnum::PointerValue(pv)), Some(llt), false) = + (val.comp_val, self.llvm_type(ctx), ctx.is_const.get()) + { + ctx.builder + .build_struct_gep(llt, pv, 0, "") + .ok() + .map(From::from) + } else { + None + } + } else { + val.addr(ctx).map(From::from) + }; + Ok(Value::new(res, None, types::Null::new().add_ptr(false))) + } + _ => Err(CobaltError::AttrNotDefined { + val: self.to_string(), + attr: attr.0, + vloc: val.loc, + aloc: attr.1, + }), + } + } + fn _refmut_attr<'src, 'ctx>( + &'static self, + val: Value<'src, 'ctx>, + attr: (Cow<'src, str>, SourceSpan), + ctx: &CompCtx<'src, 'ctx>, + ) -> Result, CobaltError<'src>> { + match &*attr.0 { + "__disc" => { + let tag = self.tag_type(); + let res = if tag.kind() == types::Int::KIND { + Value::new( + self.llvm_type(ctx) + .and_then(|llt| { + val.addr(ctx) + .and_then(|a| ctx.builder.build_struct_gep(llt, a, 0, "").ok()) + }) + .map(From::from), + if let Some(InterData::Array(arr)) = &val.inter_val { + if let [InterData::Int(ix), _] = &arr[..] { + Some(InterData::Int(*ix)) + } else { + None + } + } else { + None + }, + tag.add_ref(true), + ) + } else { + Value { + data_type: tag, + ..Value::null() + } + }; + Ok(res) + } + "__ptr" => { + let tag = self.tag_type(); + let res = if tag.kind() == types::Int::KIND { + if let (Some(BasicValueEnum::PointerValue(pv)), Some(llt), false) = + (val.comp_val, self.llvm_type(ctx), ctx.is_const.get()) + { + ctx.builder + .build_struct_gep(llt, pv, 0, "") + .ok() + .map(From::from) + } else { + None + } + } else { + val.addr(ctx).map(From::from) + }; + Ok(Value::new(res, None, types::Null::new().add_ptr(true))) + } + _ => Err(CobaltError::AttrNotDefined { + val: self.to_string(), + attr: attr.0, + vloc: val.loc, + aloc: attr.1, + }), + } + } +} + +pub type Enum = EnumOrUnion; +pub type Union = EnumOrUnion; +submit_types!(EnumOrUnion); diff --git a/cobalt-ast/src/types/float.rs b/cobalt-ast/src/types/float.rs index 9002097..e733e77 100644 --- a/cobalt-ast/src/types/float.rs +++ b/cobalt-ast/src/types/float.rs @@ -1,5 +1,6 @@ use super::*; use inkwell::FloatPredicate::*; + #[derive( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Display, Serialize, Deserialize, )] @@ -13,10 +14,12 @@ pub enum FPType { #[display(fmt = "f128")] F128, } + static F16: Float = Float(FPType::F16); static F32: Float = Float(FPType::F32); static F64: Float = Float(FPType::F64); static F128: Float = Float(FPType::F128); + #[derive(Debug, ConstIdentify, Display)] pub struct Float(FPType); impl Float { @@ -1489,4 +1492,5 @@ impl Type for Float { } } } + submit_types!(Float); diff --git a/cobalt-ast/src/types/func.rs b/cobalt-ast/src/types/func.rs index 65817ac..35913a2 100644 --- a/cobalt-ast/src/types/func.rs +++ b/cobalt-ast/src/types/func.rs @@ -1,4 +1,5 @@ use super::*; + #[derive(Debug, ConstIdentify, RefCastCustom)] #[repr(transparent)] pub struct Function((TypeRef, Box<[(TypeRef, bool)]>)); @@ -225,6 +226,7 @@ impl Type for Function { )) } } + #[derive(Debug, ConstIdentify, RefCastCustom)] #[repr(transparent)] pub struct BoundMethod((TypeRef, Box<[(TypeRef, bool)]>)); @@ -399,4 +401,5 @@ impl TypeSerde for BoundMethod { FnProxy { ret, params } => Self::new(ret, unsafe {std::mem::transmute::<_, &[(TypeRef, bool)]>(&*params)}) ); } + submit_types!(Function, BoundMethod); diff --git a/cobalt-ast/src/types/int.rs b/cobalt-ast/src/types/int.rs index a3d1340..db892df 100644 --- a/cobalt-ast/src/types/int.rs +++ b/cobalt-ast/src/types/int.rs @@ -1,5 +1,6 @@ use super::*; use inkwell::IntPredicate::*; + #[derive(Debug, ConstIdentify, PartialEq, Eq, Hash, Display, RefCastCustom)] #[display(fmt = "{}{}", r#"if _0.1 {"u"} else {"i"}"#, "_0.0")] #[repr(transparent)] @@ -21,7 +22,7 @@ impl Int { Self::new(ctx.flags.word_size, false) } pub fn usize(ctx: &CompCtx) -> &'static Self { - Self::new(ctx.flags.word_size, false) + Self::new(ctx.flags.word_size, true) } pub fn bool() -> &'static Self { Self::new(1, true) @@ -49,7 +50,7 @@ impl Type for Int { SizeType::Static(((self.0 .0 + 7) / 8) as _) } fn align(&self) -> u16 { - 1 << std::cmp::min(16 - self.0 .0.leading_zeros(), 6) + 1 << ((15 - std::cmp::min(self.0 .0.leading_zeros(), 15)).clamp(3, 6) - 3) } fn llvm_type<'ctx>(&self, ctx: &CompCtx<'_, 'ctx>) -> Option> { Some(ctx.context.custom_width_int_type(self.bits() as _).into()) @@ -1944,6 +1945,7 @@ impl Type for Int { } } } + #[derive(Debug, ConstIdentify, Display)] #[display(fmt = "")] pub struct IntLiteral(()); @@ -2281,4 +2283,5 @@ impl Type for IntLiteral { } } } + submit_types!(Int, IntLiteral); diff --git a/cobalt-ast/src/types/intrinsic.rs b/cobalt-ast/src/types/intrinsic.rs index b55deb6..30f97ea 100644 --- a/cobalt-ast/src/types/intrinsic.rs +++ b/cobalt-ast/src/types/intrinsic.rs @@ -1,5 +1,7 @@ use super::*; + static INTRINSIC_INTERN: Interner> = Interner::new(); + #[derive(Debug, ConstIdentify, Display, RefCastCustom)] #[repr(transparent)] #[display(fmt = "@{_0}")] @@ -238,7 +240,9 @@ impl Type for Intrinsic { }) } } + static ASM_INTERN: Interner = Interner::new(); + #[derive(Debug, ConstIdentify, Display, RefCastCustom)] #[repr(transparent)] #[display(fmt = "@asm({_0})")] @@ -343,4 +347,5 @@ impl Type for InlineAsm { } } } + submit_types!(Intrinsic, InlineAsm); diff --git a/cobalt-ast/src/types/mem.rs b/cobalt-ast/src/types/mem.rs index 2bfc7d9..b376de2 100644 --- a/cobalt-ast/src/types/mem.rs +++ b/cobalt-ast/src/types/mem.rs @@ -2,6 +2,7 @@ use super::*; use inkwell::IntPredicate::*; use std::cell::Cell; use std::rc::Rc; + #[derive(Debug, ConstIdentify, Display, RefCastCustom)] #[display(fmt = "&{}", _0)] #[repr(transparent)] @@ -404,6 +405,7 @@ impl Type for Reference { self.base().subscript(val, idx, ctx) } } + #[derive(Debug, ConstIdentify, Display, RefCastCustom)] #[display(fmt = "*{}", _0)] #[repr(transparent)] @@ -946,6 +948,7 @@ impl Type for Pointer { self.base().ptr_type(ctx) } } + #[derive(Debug, ConstIdentify, Display, RefCastCustom)] #[display(fmt = "mut {}", _0)] #[repr(transparent)] @@ -1204,4 +1207,5 @@ impl Type for Mut { self.base()._has_refmut_attr(attr, ctx) } } + submit_types!(Reference, Pointer, Mut); diff --git a/cobalt-ast/src/value.rs b/cobalt-ast/src/value.rs index 8fee250..1d2a879 100644 --- a/cobalt-ast/src/value.rs +++ b/cobalt-ast/src/value.rs @@ -42,6 +42,7 @@ pub enum InterData<'src, 'ctx> { Function(#[serde(deserialize_state)] FnData<'src, 'ctx>), InlineAsm(String, String), Type(TypeRef), + Spanned(SourceSpan, #[serde(deserialize_state)] Box), Module( #[serde(deserialize_state)] HashMap, Symbol<'src, 'ctx>>, Vec<(CompoundDottedName<'src>, bool)>, diff --git a/cobalt-errors/src/error.rs b/cobalt-errors/src/error.rs index e55cdbe..0159c5a 100644 --- a/cobalt-errors/src/error.rs +++ b/cobalt-errors/src/error.rs @@ -187,6 +187,15 @@ pub enum CobaltError<'src> { #[label("error at byte {pos} of glob: {msg}")] loc: SourceSpan, }, + #[error("Duplicate {} variants", if *.is_union {"union"} else {"enum"})] + DuplicateEnumVariant { + is_union: bool, + #[label("previously defined here")] + prev: SourceSpan, + #[label("redefinition of variant {ty}")] + curr: SourceSpan, + ty: String, + }, #[error("value cannot be determined at compile-time")] NotCompileTime { #[label]