2024-12-01 07:01:44 -06:00
|
|
|
use {
|
|
|
|
crate::{
|
|
|
|
ctx_map,
|
|
|
|
lexer::TokenKind,
|
|
|
|
parser::{self, CommentOr, Expr, ExprRef, Pos},
|
|
|
|
utils::{self, Ent, EntVec},
|
|
|
|
Ident,
|
|
|
|
},
|
|
|
|
alloc::{string::String, vec::Vec},
|
|
|
|
core::{
|
|
|
|
cell::Cell,
|
|
|
|
num::NonZeroU32,
|
|
|
|
ops::{Deref, DerefMut, Range},
|
|
|
|
},
|
|
|
|
hashbrown::hash_map,
|
|
|
|
};
|
2024-12-01 08:11:38 -06:00
|
|
|
|
2024-12-01 07:01:44 -06:00
|
|
|
macro_rules! impl_deref {
|
|
|
|
($for:ty { $name:ident: $base:ty }) => {
|
|
|
|
impl Deref for $for {
|
|
|
|
type Target = $base;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.$name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DerefMut for $for {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.$name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type ArrayLen = u32;
|
2024-12-01 08:11:38 -06:00
|
|
|
pub type Offset = u32;
|
|
|
|
pub type Size = u32;
|
2024-12-01 07:01:44 -06:00
|
|
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)]
|
|
|
|
pub struct Tuple(pub u32);
|
|
|
|
|
|
|
|
impl Tuple {
|
|
|
|
const LEN_BITS: u32 = 5;
|
|
|
|
const LEN_MASK: usize = Self::MAX_LEN - 1;
|
|
|
|
const MAX_LEN: usize = 1 << Self::LEN_BITS;
|
|
|
|
|
|
|
|
pub fn new(pos: usize, len: usize) -> Option<Self> {
|
|
|
|
if len >= Self::MAX_LEN {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(Self((pos << Self::LEN_BITS | len) as u32))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn range(self) -> Range<usize> {
|
|
|
|
let start = self.0 as usize >> Self::LEN_BITS;
|
|
|
|
start..start + self.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn len(self) -> usize {
|
|
|
|
self.0 as usize & Self::LEN_MASK
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_empty(self) -> bool {
|
|
|
|
self.len() == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn empty() -> Self {
|
|
|
|
Self(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn args(self) -> ArgIter {
|
|
|
|
ArgIter(self.range())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ArgIter(Range<usize>);
|
|
|
|
|
|
|
|
pub enum Arg {
|
|
|
|
Type(Id),
|
|
|
|
Value(Id),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ArgIter {
|
|
|
|
pub(crate) fn next(&mut self, tys: &Types) -> Option<Arg> {
|
|
|
|
let ty = tys.ins.args[self.0.next()?];
|
|
|
|
if ty == Id::TYPE {
|
|
|
|
return Some(Arg::Type(tys.ins.args[self.0.next().unwrap()]));
|
|
|
|
}
|
|
|
|
Some(Arg::Value(ty))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn next_value(&mut self, tys: &Types) -> Option<Id> {
|
|
|
|
loop {
|
|
|
|
match self.next(tys)? {
|
|
|
|
Arg::Type(_) => continue,
|
|
|
|
Arg::Value(id) => break Some(id),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
|
|
|
pub struct Id(NonZeroU32);
|
|
|
|
|
|
|
|
impl From<Id> for i64 {
|
|
|
|
fn from(value: Id) -> Self {
|
|
|
|
value.0.get() as _
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl crate::ctx_map::CtxEntry for Id {
|
|
|
|
type Ctx = TypeIns;
|
|
|
|
type Key<'a> = SymKey<'a>;
|
|
|
|
|
|
|
|
fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> {
|
|
|
|
match self.expand() {
|
|
|
|
Kind::Struct(s) => {
|
|
|
|
let st = &ctx.structs[s];
|
|
|
|
debug_assert_ne!(st.pos, Pos::MAX);
|
|
|
|
SymKey::Type(st.file, st.pos, st.captured)
|
|
|
|
}
|
|
|
|
Kind::Enum(e) => {
|
|
|
|
let en = &ctx.enums[e];
|
|
|
|
debug_assert_ne!(en.pos, Pos::MAX);
|
|
|
|
SymKey::Type(en.file, en.pos, en.captured)
|
|
|
|
}
|
2024-12-01 08:11:38 -06:00
|
|
|
Kind::Union(e) => {
|
|
|
|
let en = &ctx.unions[e];
|
|
|
|
debug_assert_ne!(en.pos, Pos::MAX);
|
|
|
|
SymKey::Type(en.file, en.pos, en.captured)
|
|
|
|
}
|
2024-12-01 07:01:44 -06:00
|
|
|
Kind::Ptr(p) => SymKey::Pointer(&ctx.ptrs[p]),
|
|
|
|
Kind::Opt(p) => SymKey::Optional(&ctx.opts[p]),
|
|
|
|
Kind::Func(f) => {
|
|
|
|
let fc = &ctx.funcs[f];
|
|
|
|
if let Some(base) = fc.base {
|
|
|
|
// TODO: merge base and sig
|
|
|
|
SymKey::FuncInst(base, fc.sig.unwrap().args)
|
|
|
|
} else {
|
|
|
|
SymKey::Decl(fc.parent, fc.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Kind::Global(g) => {
|
|
|
|
let gb = &ctx.globals[g];
|
|
|
|
SymKey::Decl(gb.file.into(), gb.name)
|
|
|
|
}
|
|
|
|
Kind::Slice(s) => SymKey::Array(&ctx.slices[s]),
|
|
|
|
Kind::Module(_) | Kind::Builtin(_) => {
|
|
|
|
SymKey::Decl(Module::default().into(), Ident::INVALID)
|
|
|
|
}
|
|
|
|
Kind::Const(c) => SymKey::Constant(&ctx.consts[c]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Id {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(unsafe { NonZeroU32::new_unchecked(UNDECLARED) })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Id {
|
|
|
|
pub const DINT: Self = Self::UINT;
|
|
|
|
|
|
|
|
pub fn bin_ret(self, op: TokenKind) -> Id {
|
|
|
|
if op.is_compatison() {
|
|
|
|
Self::BOOL
|
|
|
|
} else {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_float(self) -> bool {
|
|
|
|
matches!(self.repr(), F32 | F64) || self.is_never()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_signed(self) -> bool {
|
|
|
|
matches!(self.repr(), I8..=INT) || self.is_never()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_unsigned(self) -> bool {
|
|
|
|
matches!(self.repr(), U8..=UINT) || self.is_never()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_integer(self) -> bool {
|
|
|
|
matches!(self.repr(), U8..=INT) || self.is_never()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_never(self) -> bool {
|
|
|
|
self == Self::NEVER
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn strip_pointer(self) -> Self {
|
|
|
|
match self.expand() {
|
|
|
|
Kind::Ptr(_) => Id::UINT,
|
|
|
|
_ => self,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_pointer(self) -> bool {
|
|
|
|
matches!(self.expand(), Kind::Ptr(_)) || self.is_never()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_optional(self) -> bool {
|
|
|
|
matches!(self.expand(), Kind::Opt(_)) || self.is_never()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_upcast(self, ob: Self) -> Option<Self> {
|
|
|
|
self.try_upcast_low(ob, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_upcast_low(self, ob: Self, coerce_pointer: bool) -> Option<Self> {
|
|
|
|
let (oa, ob) = (Self(self.0.min(ob.0)), Self(self.0.max(ob.0)));
|
|
|
|
let (a, b) = (oa.strip_pointer(), ob.strip_pointer());
|
|
|
|
Some(match () {
|
|
|
|
_ if oa == Id::NEVER => ob,
|
|
|
|
_ if ob == Id::NEVER => oa,
|
|
|
|
_ if oa == ob => oa,
|
|
|
|
_ if ob.is_optional() => ob,
|
|
|
|
_ if oa.is_pointer() && ob.is_pointer() => return None,
|
|
|
|
_ if a.is_signed() && b.is_signed() || a.is_unsigned() && b.is_unsigned() => ob,
|
|
|
|
_ if a.is_unsigned() && b.is_signed() && a.repr() - U8 < b.repr() - I8 => ob,
|
|
|
|
_ if a.is_unsigned() && b.is_signed() && a.repr() - U8 > b.repr() - I8 => oa,
|
|
|
|
_ if oa.is_integer() && ob.is_pointer() && coerce_pointer => ob,
|
|
|
|
_ => return None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn expand(self) -> Kind {
|
|
|
|
Kind::from_ty(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const fn repr(self) -> u32 {
|
|
|
|
self.0.get()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn simple_size(&self) -> Option<Size> {
|
|
|
|
Some(match self.expand() {
|
|
|
|
Kind::Ptr(_) => 8,
|
|
|
|
Kind::Builtin(Builtin(VOID)) => 0,
|
|
|
|
Kind::Builtin(Builtin(NEVER)) => 0,
|
|
|
|
Kind::Builtin(Builtin(INT | UINT | F64)) => 8,
|
|
|
|
Kind::Builtin(Builtin(I32 | U32 | TYPE | F32)) => 4,
|
|
|
|
Kind::Builtin(Builtin(I16 | U16)) => 2,
|
|
|
|
Kind::Builtin(Builtin(I8 | U8 | BOOL)) => 1,
|
|
|
|
_ => return None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn extend(self) -> Self {
|
|
|
|
if self.is_signed() {
|
|
|
|
Self::INT
|
|
|
|
} else if self.is_pointer() {
|
|
|
|
self
|
|
|
|
} else {
|
|
|
|
Self::UINT
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn loc(&self, tys: &Types) -> Loc {
|
|
|
|
match self.expand() {
|
|
|
|
Kind::Opt(o)
|
|
|
|
if let ty = tys.ins.opts[o].base
|
|
|
|
&& ty.loc(tys) == Loc::Reg
|
|
|
|
&& (ty.is_pointer() || tys.size_of(ty) < 8) =>
|
|
|
|
{
|
|
|
|
Loc::Reg
|
|
|
|
}
|
|
|
|
Kind::Ptr(_) | Kind::Enum(_) | Kind::Builtin(_) => Loc::Reg,
|
2024-12-01 08:11:38 -06:00
|
|
|
Kind::Struct(_) | Kind::Union(_) if tys.size_of(*self) == 0 => Loc::Reg,
|
|
|
|
Kind::Struct(_) | Kind::Union(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack,
|
2024-12-01 07:01:44 -06:00
|
|
|
c @ (Kind::Func(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_)) => {
|
|
|
|
unreachable!("{c:?}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn has_pointers(&self, tys: &Types) -> bool {
|
|
|
|
match self.expand() {
|
|
|
|
Kind::Struct(s) => tys.struct_fields(s).iter().any(|f| f.ty.has_pointers(tys)),
|
|
|
|
Kind::Ptr(_) => true,
|
|
|
|
Kind::Slice(s) => tys.ins.slices[s].len == ArrayLen::MAX,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
|
|
|
pub enum Loc {
|
|
|
|
Reg,
|
|
|
|
Stack,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<u64> for Id {
|
|
|
|
fn from(id: u64) -> Self {
|
|
|
|
Self(unsafe { NonZeroU32::new_unchecked(id as _) })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const fn array_to_lower_case<const N: usize>(array: [u8; N]) -> [u8; N] {
|
|
|
|
let mut result = [0; N];
|
|
|
|
let mut i = 0;
|
|
|
|
while i < N {
|
|
|
|
result[i] = array[i].to_ascii_lowercase();
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
result
|
|
|
|
}
|
|
|
|
// const string to lower case
|
|
|
|
|
|
|
|
macro_rules! builtin_type {
|
|
|
|
($($name:ident;)*) => {
|
|
|
|
$(const $name: u32 = ${index(0)} + 1;)*
|
|
|
|
|
|
|
|
mod __lc_names {
|
|
|
|
use super::*;
|
|
|
|
$(pub const $name: &str = unsafe {
|
|
|
|
const LCL: &[u8] = unsafe {
|
|
|
|
&array_to_lower_case(
|
|
|
|
*(stringify!($name).as_ptr() as *const [u8; stringify!($name).len()])
|
|
|
|
)
|
|
|
|
};
|
|
|
|
core::str::from_utf8_unchecked(LCL)
|
|
|
|
};)*
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Builtin {
|
|
|
|
$(pub const $name: Self = Builtin($name);)*
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Id {
|
|
|
|
$(pub const $name: Self = Kind::Builtin(Builtin($name)).compress();)*
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Kind {
|
|
|
|
$(pub const $name: Self = Kind::Builtin(Builtin($name));)*
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_str(name: &str) -> Option<Builtin> {
|
|
|
|
match name {
|
|
|
|
$(__lc_names::$name => Some(Builtin($name)),)*
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_str(ty: Builtin) -> &'static str {
|
|
|
|
match ty.0 {
|
|
|
|
$($name => __lc_names::$name,)*
|
|
|
|
v => unreachable!("invalid type: {}", v),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
builtin_type! {
|
|
|
|
UNDECLARED;
|
|
|
|
LEFT_UNREACHABLE;
|
|
|
|
RIGHT_UNREACHABLE;
|
|
|
|
NEVER;
|
|
|
|
VOID;
|
|
|
|
TYPE;
|
|
|
|
BOOL;
|
|
|
|
U8;
|
|
|
|
U16;
|
|
|
|
U32;
|
|
|
|
UINT;
|
|
|
|
I8;
|
|
|
|
I16;
|
|
|
|
I32;
|
|
|
|
INT;
|
|
|
|
F32;
|
|
|
|
F64;
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! type_kind {
|
|
|
|
($(#[$meta:meta])* $vis:vis enum $name:ident {$( $variant:ident, )*}) => {
|
|
|
|
crate::utils::decl_ent! {
|
|
|
|
$(pub struct $variant(u32);)*
|
|
|
|
}
|
|
|
|
|
|
|
|
$(#[$meta])*
|
|
|
|
$vis enum $name {
|
|
|
|
$($variant($variant),)*
|
|
|
|
}
|
|
|
|
|
|
|
|
impl $name {
|
|
|
|
const FLAG_BITS: u32 = (${count($variant)} as u32).next_power_of_two().ilog2();
|
|
|
|
const FLAG_OFFSET: u32 = core::mem::size_of::<Id>() as u32 * 8 - Self::FLAG_BITS;
|
|
|
|
const INDEX_MASK: u32 = (1 << (32 - Self::FLAG_BITS)) - 1;
|
|
|
|
|
|
|
|
$vis fn from_ty(ty: Id) -> Self {
|
|
|
|
let (flag, index) = (ty.repr() >> Self::FLAG_OFFSET, ty.repr() & Self::INDEX_MASK);
|
|
|
|
match flag {
|
|
|
|
$(${index(0)} => Self::$variant($variant(index)),)*
|
|
|
|
i => unreachable!("{i}"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$vis const fn compress(self) -> Id {
|
|
|
|
let (index, flag) = match self {
|
|
|
|
$(Self::$variant(index) => (index.0, ${index(0)}),)*
|
|
|
|
};
|
|
|
|
Id(unsafe { NonZeroU32::new_unchecked((flag << Self::FLAG_OFFSET) | index) })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$(
|
|
|
|
impl From<$variant> for $name {
|
|
|
|
fn from(value: $variant) -> Self {
|
|
|
|
Self::$variant(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<$variant> for i64 {
|
|
|
|
fn from(value: $variant) -> Self {
|
|
|
|
Id::from(value).into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<$variant> for Id {
|
|
|
|
fn from(value: $variant) -> Self {
|
|
|
|
$name::$variant(value).compress()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)*
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
type_kind! {
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub enum Kind {
|
|
|
|
Builtin,
|
|
|
|
Struct,
|
|
|
|
Enum,
|
2024-12-01 08:11:38 -06:00
|
|
|
Union,
|
2024-12-01 07:01:44 -06:00
|
|
|
Ptr,
|
|
|
|
Slice,
|
|
|
|
Opt,
|
|
|
|
Func,
|
|
|
|
Global,
|
|
|
|
Module,
|
|
|
|
Const,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-01 08:11:38 -06:00
|
|
|
impl Func {
|
|
|
|
pub const ECA: Func = Func(u32::MAX);
|
|
|
|
pub const MAIN: Func = Func(u32::MIN);
|
|
|
|
}
|
|
|
|
|
2024-12-01 07:01:44 -06:00
|
|
|
impl Module {
|
|
|
|
pub const MAIN: Self = Self(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Module {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(u32::MAX)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<Ident> for Builtin {
|
|
|
|
type Error = ();
|
|
|
|
|
|
|
|
fn try_from(value: Ident) -> Result<Self, Self::Error> {
|
|
|
|
if value.is_null() {
|
|
|
|
Ok(Self(value.len()))
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Kind {
|
|
|
|
fn default() -> Self {
|
|
|
|
Id::UNDECLARED.expand()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Display<'a> {
|
|
|
|
tys: &'a Types,
|
|
|
|
files: &'a [parser::Ast],
|
|
|
|
ty: Id,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Display<'a> {
|
|
|
|
pub fn new(tys: &'a Types, files: &'a [parser::Ast], ty: Id) -> Self {
|
|
|
|
Self { tys, files, ty }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rety(&self, ty: Id) -> Self {
|
|
|
|
Self::new(self.tys, self.files, ty)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl core::fmt::Display for Display<'_> {
|
|
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
|
|
use Kind as TK;
|
|
|
|
match TK::from_ty(self.ty) {
|
|
|
|
TK::Module(idx) => {
|
|
|
|
f.write_str("@use(\"")?;
|
|
|
|
self.files[idx.index()].path.fmt(f)?;
|
|
|
|
f.write_str(")[")?;
|
|
|
|
idx.fmt(f)?;
|
|
|
|
f.write_str("]")
|
|
|
|
}
|
|
|
|
TK::Builtin(ty) => f.write_str(to_str(ty)),
|
|
|
|
TK::Opt(ty) => {
|
|
|
|
f.write_str("?")?;
|
|
|
|
self.rety(self.tys.ins.opts[ty].base).fmt(f)
|
|
|
|
}
|
|
|
|
TK::Ptr(ty) => {
|
|
|
|
f.write_str("^")?;
|
|
|
|
self.rety(self.tys.ins.ptrs[ty].base).fmt(f)
|
|
|
|
}
|
|
|
|
TK::Struct(idx) => {
|
|
|
|
let record = &self.tys.ins.structs[idx];
|
|
|
|
if record.name.is_null() {
|
|
|
|
f.write_str("[")?;
|
|
|
|
idx.fmt(f)?;
|
|
|
|
f.write_str("]{")?;
|
|
|
|
for (i, &StructField { name, ty }) in
|
|
|
|
self.tys.struct_fields(idx).iter().enumerate()
|
|
|
|
{
|
|
|
|
if i != 0 {
|
|
|
|
f.write_str(", ")?;
|
|
|
|
}
|
|
|
|
f.write_str(self.tys.names.ident_str(name))?;
|
|
|
|
f.write_str(": ")?;
|
|
|
|
self.rety(ty).fmt(f)?;
|
|
|
|
}
|
|
|
|
f.write_str("}")
|
|
|
|
} else {
|
|
|
|
let file = &self.files[record.file.index()];
|
|
|
|
f.write_str(file.ident_str(record.name))
|
|
|
|
}
|
|
|
|
}
|
2024-12-01 08:11:38 -06:00
|
|
|
TK::Union(idx) => {
|
|
|
|
let record = &self.tys.ins.unions[idx];
|
|
|
|
if record.name.is_null() {
|
|
|
|
f.write_str("[")?;
|
|
|
|
idx.fmt(f)?;
|
|
|
|
f.write_str("]{")?;
|
|
|
|
for (i, &StructField { name, ty }) in
|
|
|
|
self.tys.union_fields(idx).iter().enumerate()
|
|
|
|
{
|
|
|
|
if i != 0 {
|
|
|
|
f.write_str(", ")?;
|
|
|
|
}
|
|
|
|
f.write_str(self.tys.names.ident_str(name))?;
|
|
|
|
f.write_str(": ")?;
|
|
|
|
self.rety(ty).fmt(f)?;
|
|
|
|
}
|
|
|
|
f.write_str("}")
|
|
|
|
} else {
|
|
|
|
let file = &self.files[record.file.index()];
|
|
|
|
f.write_str(file.ident_str(record.name))
|
|
|
|
}
|
|
|
|
}
|
2024-12-01 07:01:44 -06:00
|
|
|
TK::Enum(idx) => {
|
|
|
|
let enm = &self.tys.ins.enums[idx];
|
|
|
|
debug_assert!(!enm.name.is_null());
|
|
|
|
let file = &self.files[enm.file.index()];
|
|
|
|
f.write_str(file.ident_str(enm.name))
|
|
|
|
}
|
|
|
|
TK::Func(idx) => {
|
|
|
|
f.write_str("fn")?;
|
|
|
|
idx.fmt(f)
|
|
|
|
}
|
|
|
|
TK::Global(idx) => {
|
|
|
|
let global = &self.tys.ins.globals[idx];
|
|
|
|
let file = &self.files[global.file.index()];
|
|
|
|
f.write_str(file.ident_str(global.name))?;
|
|
|
|
f.write_str(" (global)")
|
|
|
|
}
|
|
|
|
TK::Slice(idx) => {
|
|
|
|
let array = self.tys.ins.slices[idx];
|
|
|
|
f.write_str("[")?;
|
|
|
|
self.rety(array.elem).fmt(f)?;
|
|
|
|
if array.len != ArrayLen::MAX {
|
|
|
|
f.write_str("; ")?;
|
|
|
|
array.len.fmt(f)?;
|
|
|
|
}
|
|
|
|
f.write_str("]")
|
|
|
|
}
|
|
|
|
TK::Const(idx) => {
|
|
|
|
let cnst = &self.tys.ins.consts[idx];
|
|
|
|
let file = &self.files[cnst.file.index()];
|
|
|
|
f.write_str(file.ident_str(cnst.name))?;
|
|
|
|
f.write_str(" (const)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
|
|
|
pub enum SymKey<'a> {
|
|
|
|
Pointer(&'a PtrData),
|
|
|
|
Optional(&'a OptData),
|
|
|
|
Type(Module, Pos, Tuple),
|
|
|
|
FuncInst(Func, Tuple),
|
|
|
|
Decl(Id, Ident),
|
|
|
|
Array(&'a ArrayData),
|
|
|
|
Constant(&'a ConstData),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct Sig {
|
|
|
|
pub args: Tuple,
|
|
|
|
pub ret: Id,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default, Clone, Copy)]
|
|
|
|
pub struct FuncData {
|
|
|
|
pub file: Module,
|
|
|
|
pub parent: Id,
|
|
|
|
pub name: Ident,
|
|
|
|
pub base: Option<Func>,
|
|
|
|
pub expr: ExprRef,
|
|
|
|
pub sig: Option<Sig>,
|
|
|
|
pub is_inline: bool,
|
|
|
|
pub returns_type: bool,
|
|
|
|
pub comp_state: [CompState; 2],
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default, PartialEq, Eq, Clone, Copy)]
|
|
|
|
pub enum CompState {
|
|
|
|
#[default]
|
|
|
|
Dead,
|
|
|
|
Queued(usize),
|
|
|
|
Compiled,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Default)]
|
|
|
|
pub struct GlobalData {
|
|
|
|
pub file: Module,
|
|
|
|
pub name: Ident,
|
|
|
|
pub ty: Id,
|
|
|
|
pub data: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Hash)]
|
|
|
|
pub struct ConstData {
|
|
|
|
pub ast: ExprRef,
|
|
|
|
pub name: Ident,
|
|
|
|
pub file: Module,
|
|
|
|
pub parent: Id,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EnumField {
|
|
|
|
pub name: Ident,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct TypeBase {
|
|
|
|
pub file: Module,
|
|
|
|
pub parent: Id,
|
|
|
|
pub pos: Pos,
|
|
|
|
pub name: Ident,
|
|
|
|
pub field_start: u32,
|
|
|
|
pub captured: Tuple,
|
|
|
|
pub ast: ExprRef,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct EnumData {
|
|
|
|
pub base: TypeBase,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_deref!(EnumData { base: TypeBase });
|
|
|
|
|
2024-12-01 08:11:38 -06:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct UnionData {
|
|
|
|
pub base: TypeBase,
|
|
|
|
pub size: Cell<Size>,
|
|
|
|
pub align: Cell<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_deref!(UnionData { base: TypeBase });
|
|
|
|
|
2024-12-01 07:01:44 -06:00
|
|
|
pub struct StructField {
|
|
|
|
pub name: Ident,
|
|
|
|
pub ty: Id,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct StructData {
|
|
|
|
pub base: TypeBase,
|
|
|
|
pub size: Cell<Size>,
|
|
|
|
pub align: Cell<u8>,
|
|
|
|
// TODO: make this compact
|
|
|
|
pub explicit_alignment: Option<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_deref!(StructData { base: TypeBase });
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
|
|
|
pub struct OptData {
|
|
|
|
pub base: Id,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
|
|
|
pub struct PtrData {
|
|
|
|
pub base: Id,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub struct ArrayData {
|
|
|
|
pub elem: Id,
|
|
|
|
pub len: ArrayLen,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ArrayData {
|
|
|
|
#[expect(clippy::len_without_is_empty)]
|
|
|
|
pub fn len(&self) -> Option<usize> {
|
|
|
|
(self.len != ArrayLen::MAX).then_some(self.len as usize)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ctx_map::CtxEntry for Ident {
|
|
|
|
type Ctx = str;
|
|
|
|
type Key<'a> = &'a str;
|
|
|
|
|
|
|
|
fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> {
|
|
|
|
unsafe { ctx.get_unchecked(self.range()) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct IdentInterner {
|
|
|
|
lookup: ctx_map::CtxMap<Ident>,
|
|
|
|
strings: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IdentInterner {
|
|
|
|
pub fn intern(&mut self, ident: &str) -> Ident {
|
|
|
|
let (entry, hash) = self.lookup.entry(ident, &self.strings);
|
|
|
|
match entry {
|
|
|
|
hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.value,
|
|
|
|
hash_map::RawEntryMut::Vacant(v) => {
|
|
|
|
let id = Ident::new(self.strings.len() as _, ident.len() as _).unwrap();
|
|
|
|
self.strings.push_str(ident);
|
|
|
|
v.insert(ctx_map::Key { hash, value: id }, ());
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ident_str(&self, ident: Ident) -> &str {
|
|
|
|
&self.strings[ident.range()]
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn project(&self, ident: &str) -> Option<Ident> {
|
|
|
|
self.lookup.get(ident, &self.strings).copied()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clear(&mut self) {
|
|
|
|
self.lookup.clear();
|
|
|
|
self.strings.clear()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct TypesTmp {
|
|
|
|
pub struct_fields: Vec<StructField>,
|
|
|
|
pub enum_fields: Vec<EnumField>,
|
|
|
|
pub args: Vec<Id>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct TypeIns {
|
|
|
|
pub args: Vec<Id>,
|
|
|
|
pub struct_fields: Vec<StructField>,
|
|
|
|
pub enum_fields: Vec<EnumField>,
|
|
|
|
pub funcs: EntVec<Func, FuncData>,
|
|
|
|
pub globals: EntVec<Global, GlobalData>,
|
|
|
|
pub consts: EntVec<Const, ConstData>,
|
|
|
|
pub structs: EntVec<Struct, StructData>,
|
|
|
|
pub enums: EntVec<Enum, EnumData>,
|
2024-12-01 08:11:38 -06:00
|
|
|
pub unions: EntVec<Union, UnionData>,
|
2024-12-01 07:01:44 -06:00
|
|
|
pub ptrs: EntVec<Ptr, PtrData>,
|
|
|
|
pub opts: EntVec<Opt, OptData>,
|
|
|
|
pub slices: EntVec<Slice, ArrayData>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct FTask {
|
|
|
|
pub file: Module,
|
|
|
|
pub id: Func,
|
|
|
|
pub ct: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct StringRef(pub Global);
|
|
|
|
|
|
|
|
impl ctx_map::CtxEntry for StringRef {
|
|
|
|
type Ctx = EntVec<Global, GlobalData>;
|
|
|
|
type Key<'a> = &'a [u8];
|
|
|
|
|
|
|
|
fn key<'a>(&self, ctx: &'a Self::Ctx) -> Self::Key<'a> {
|
|
|
|
&ctx[self.0].data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct Types {
|
|
|
|
pub syms: ctx_map::CtxMap<Id>,
|
|
|
|
pub names: IdentInterner,
|
|
|
|
pub strings: ctx_map::CtxMap<StringRef>,
|
|
|
|
pub ins: TypeIns,
|
|
|
|
pub tmp: TypesTmp,
|
|
|
|
pub tasks: Vec<Option<FTask>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Types {
|
|
|
|
pub fn case(&self, ty: Id) -> fn(&str) -> Result<(), &'static str> {
|
|
|
|
match ty.expand() {
|
|
|
|
Kind::NEVER => |_| Ok(()),
|
|
|
|
Kind::Enum(_)
|
|
|
|
| Kind::Struct(_)
|
2024-12-01 08:11:38 -06:00
|
|
|
| Kind::Union(_)
|
2024-12-01 07:01:44 -06:00
|
|
|
| Kind::Builtin(_)
|
|
|
|
| Kind::Ptr(_)
|
|
|
|
| Kind::Slice(_)
|
|
|
|
| Kind::Opt(_) => utils::is_pascal_case,
|
|
|
|
Kind::Func(f) if self.ins.funcs[f].returns_type => utils::is_pascal_case,
|
|
|
|
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) => utils::is_snake_case,
|
|
|
|
Kind::Const(_) => utils::is_screaming_case,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pack_args(&mut self, arg_base: usize) -> Option<Tuple> {
|
|
|
|
let base = self.ins.args.len();
|
|
|
|
self.ins.args.extend(self.tmp.args.drain(arg_base..));
|
|
|
|
let needle = &self.ins.args[base..];
|
|
|
|
if needle.is_empty() {
|
|
|
|
return Some(Tuple::empty());
|
|
|
|
}
|
|
|
|
let len = needle.len();
|
|
|
|
// FIXME: maybe later when this becomes a bottleneck we use more
|
|
|
|
// efficient search (SIMD?, indexing?)
|
|
|
|
let sp = self.ins.args.windows(needle.len()).position(|val| val == needle).unwrap();
|
|
|
|
self.ins.args.truncate((sp + needle.len()).max(base));
|
|
|
|
Tuple::new(sp, len)
|
|
|
|
}
|
|
|
|
|
2024-12-01 08:11:38 -06:00
|
|
|
pub fn union_fields(&self, union: Union) -> &[StructField] {
|
|
|
|
&self.ins.struct_fields[self.union_field_range(union)]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn union_field_range(&self, union: Union) -> Range<usize> {
|
|
|
|
let start = self.ins.unions[union].field_start as usize;
|
|
|
|
let end = self
|
|
|
|
.ins
|
|
|
|
.unions
|
|
|
|
.next(union)
|
|
|
|
.map_or(self.ins.struct_fields.len(), |s| s.field_start as usize);
|
|
|
|
start..end
|
|
|
|
}
|
|
|
|
|
2024-12-01 07:01:44 -06:00
|
|
|
pub fn struct_fields(&self, strct: Struct) -> &[StructField] {
|
|
|
|
&self.ins.struct_fields[self.struct_field_range(strct)]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn struct_field_range(&self, strct: Struct) -> Range<usize> {
|
|
|
|
let start = self.ins.structs[strct].field_start as usize;
|
|
|
|
let end = self
|
|
|
|
.ins
|
|
|
|
.structs
|
|
|
|
.next(strct)
|
|
|
|
.map_or(self.ins.struct_fields.len(), |s| s.field_start as usize);
|
|
|
|
start..end
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enum_fields(&self, enm: Enum) -> &[EnumField] {
|
|
|
|
&self.ins.enum_fields[self.enum_field_range(enm)]
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enum_field_range(&self, enm: Enum) -> Range<usize> {
|
|
|
|
let start = self.ins.enums[enm].field_start as usize;
|
|
|
|
let end =
|
|
|
|
self.ins.enums.next(enm).map_or(self.ins.enum_fields.len(), |s| s.field_start as usize);
|
|
|
|
start..end
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn make_opt(&mut self, base: Id) -> Id {
|
|
|
|
self.make_generic_ty(OptData { base }, |ins| &mut ins.opts, |e| SymKey::Optional(e))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn make_ptr(&mut self, base: Id) -> Id {
|
|
|
|
self.make_generic_ty(PtrData { base }, |ins| &mut ins.ptrs, |e| SymKey::Pointer(e))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn make_array(&mut self, elem: Id, len: ArrayLen) -> Id {
|
|
|
|
self.make_generic_ty(ArrayData { elem, len }, |ins| &mut ins.slices, |e| SymKey::Array(e))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_generic_ty<K: Ent + Into<Id>, T: Copy>(
|
|
|
|
&mut self,
|
|
|
|
ty: T,
|
|
|
|
get_col: fn(&mut TypeIns) -> &mut EntVec<K, T>,
|
|
|
|
key: fn(&T) -> SymKey,
|
|
|
|
) -> Id {
|
|
|
|
*self.syms.get_or_insert(key(&{ ty }), &mut self.ins, |ins| get_col(ins).push(ty).into())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn size_of(&self, ty: Id) -> Size {
|
|
|
|
match ty.expand() {
|
|
|
|
Kind::Slice(arr) => {
|
|
|
|
let arr = &self.ins.slices[arr];
|
|
|
|
match arr.len {
|
|
|
|
0 => 0,
|
|
|
|
ArrayLen::MAX => 16,
|
|
|
|
len => self.size_of(arr.elem) * len,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Kind::Struct(stru) => {
|
|
|
|
if self.ins.structs[stru].size.get() != 0 {
|
|
|
|
return self.ins.structs[stru].size.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut oiter = OffsetIter::new(stru, self);
|
|
|
|
while oiter.next(self).is_some() {}
|
|
|
|
self.ins.structs[stru].size.set(oiter.offset);
|
|
|
|
oiter.offset
|
|
|
|
}
|
2024-12-01 08:11:38 -06:00
|
|
|
Kind::Union(union) => {
|
|
|
|
if self.ins.unions[union].size.get() != 0 {
|
|
|
|
return self.ins.unions[union].size.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
let size =
|
|
|
|
self.union_fields(union).iter().map(|f| self.size_of(f.ty)).max().unwrap_or(0);
|
|
|
|
self.ins.unions[union].size.set(size);
|
|
|
|
size
|
|
|
|
}
|
2024-12-01 07:01:44 -06:00
|
|
|
Kind::Enum(enm) => (self.enum_field_range(enm).len().ilog2() + 7) / 8,
|
|
|
|
Kind::Opt(opt) => {
|
|
|
|
let base = self.ins.opts[opt].base;
|
|
|
|
if self.nieche_of(base).is_some() {
|
|
|
|
self.size_of(base)
|
|
|
|
} else {
|
|
|
|
self.size_of(base) + self.align_of(base)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ if let Some(size) = ty.simple_size() => size,
|
|
|
|
ty => unimplemented!("size_of: {:?}", ty),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn align_of(&self, ty: Id) -> Size {
|
|
|
|
match ty.expand() {
|
2024-12-01 08:11:38 -06:00
|
|
|
Kind::Union(union) => {
|
|
|
|
if self.ins.unions[union].align.get() != 0 {
|
|
|
|
return self.ins.unions[union].align.get() as _;
|
|
|
|
}
|
|
|
|
let align =
|
|
|
|
self.union_fields(union).iter().map(|f| self.align_of(f.ty)).max().unwrap_or(1);
|
|
|
|
self.ins.unions[union].align.set(align.try_into().unwrap());
|
|
|
|
align
|
|
|
|
}
|
2024-12-01 07:01:44 -06:00
|
|
|
Kind::Struct(stru) => {
|
|
|
|
if self.ins.structs[stru].align.get() != 0 {
|
|
|
|
return self.ins.structs[stru].align.get() as _;
|
|
|
|
}
|
|
|
|
let align = self.ins.structs[stru].explicit_alignment.map_or_else(
|
|
|
|
|| {
|
|
|
|
self.struct_fields(stru)
|
|
|
|
.iter()
|
|
|
|
.map(|&StructField { ty, .. }| self.align_of(ty))
|
|
|
|
.max()
|
|
|
|
.unwrap_or(1)
|
|
|
|
},
|
|
|
|
|a| a as _,
|
|
|
|
);
|
|
|
|
self.ins.structs[stru].align.set(align.try_into().unwrap());
|
|
|
|
align
|
|
|
|
}
|
|
|
|
Kind::Slice(arr) => {
|
|
|
|
let arr = &self.ins.slices[arr];
|
|
|
|
match arr.len {
|
|
|
|
ArrayLen::MAX => 8,
|
|
|
|
_ => self.align_of(arr.elem),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => self.size_of(ty).max(1),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn base_of(&self, ty: Id) -> Option<Id> {
|
|
|
|
match ty.expand() {
|
|
|
|
Kind::Ptr(p) => Some(self.ins.ptrs[p].base),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn inner_of(&self, ty: Id) -> Option<Id> {
|
|
|
|
match ty.expand() {
|
|
|
|
Kind::Opt(o) => Some(self.ins.opts[o].base),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn opt_layout(&self, inner_ty: Id) -> OptLayout {
|
|
|
|
match self.nieche_of(inner_ty) {
|
|
|
|
Some((_, flag_offset, flag_ty)) => {
|
|
|
|
OptLayout { flag_ty, flag_offset, payload_offset: 0 }
|
|
|
|
}
|
|
|
|
None => OptLayout {
|
|
|
|
flag_ty: Id::BOOL,
|
|
|
|
flag_offset: 0,
|
|
|
|
payload_offset: self.align_of(inner_ty),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn nieche_of(&self, ty: Id) -> Option<(bool, Offset, Id)> {
|
|
|
|
match ty.expand() {
|
|
|
|
Kind::Ptr(_) => Some((false, 0, Id::UINT)),
|
|
|
|
// TODO: cache this
|
|
|
|
Kind::Struct(s) => OffsetIter::new(s, self).into_iter(self).find_map(|(f, off)| {
|
|
|
|
self.nieche_of(f.ty).map(|(uninit, o, ty)| (uninit, o + off, ty))
|
|
|
|
}),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn find_struct_field(&self, s: Struct, name: &str) -> Option<usize> {
|
|
|
|
let name = self.names.project(name)?;
|
|
|
|
self.struct_fields(s).iter().position(|f| f.name == name)
|
|
|
|
}
|
|
|
|
|
2024-12-01 08:11:38 -06:00
|
|
|
pub fn find_union_field(&self, u: Union, name: &str) -> Option<usize> {
|
|
|
|
let name = self.names.project(name)?;
|
|
|
|
self.union_fields(u).iter().position(|f| f.name == name)
|
|
|
|
}
|
|
|
|
|
2024-12-01 07:01:44 -06:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.syms.clear();
|
|
|
|
self.names.clear();
|
|
|
|
self.strings.clear();
|
|
|
|
|
|
|
|
self.ins.funcs.clear();
|
|
|
|
self.ins.args.clear();
|
|
|
|
self.ins.globals.clear();
|
|
|
|
self.ins.structs.clear();
|
|
|
|
self.ins.struct_fields.clear();
|
|
|
|
self.ins.ptrs.clear();
|
|
|
|
self.ins.slices.clear();
|
|
|
|
|
|
|
|
debug_assert_eq!(self.tmp.struct_fields.len(), 0);
|
|
|
|
debug_assert_eq!(self.tmp.args.len(), 0);
|
|
|
|
|
|
|
|
debug_assert_eq!(self.tasks.len(), 0);
|
|
|
|
}
|
|
|
|
|
2024-12-01 08:11:38 -06:00
|
|
|
fn type_base_of(&self, id: Id) -> Option<&TypeBase> {
|
|
|
|
Some(match id.expand() {
|
|
|
|
Kind::Struct(s) => &*self.ins.structs[s],
|
|
|
|
Kind::Enum(e) => &*self.ins.enums[e],
|
|
|
|
Kind::Union(e) => &*self.ins.unions[e],
|
|
|
|
Kind::Builtin(_)
|
|
|
|
| Kind::Ptr(_)
|
|
|
|
| Kind::Slice(_)
|
|
|
|
| Kind::Opt(_)
|
|
|
|
| Kind::Func(_)
|
|
|
|
| Kind::Global(_)
|
|
|
|
| Kind::Module(_)
|
|
|
|
| Kind::Const(_) => return None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-12-01 07:01:44 -06:00
|
|
|
pub fn scope_of<'a>(&self, parent: Id, file: &'a parser::Ast) -> Option<&'a [Expr<'a>]> {
|
2024-12-01 08:11:38 -06:00
|
|
|
let base = match parent.expand() {
|
|
|
|
_ if let Some(base) = self.type_base_of(parent) => base,
|
|
|
|
Kind::Module(_) => return Some(file.exprs()),
|
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Expr::Struct { fields: [.., CommentOr::Or(Err(scope))], .. }
|
|
|
|
| Expr::Union { fields: [.., CommentOr::Or(Err(scope))], .. }
|
|
|
|
| Expr::Enum { variants: [.., CommentOr::Or(Err(scope))], .. } = base.ast.get(file)
|
|
|
|
{
|
|
|
|
Some(scope)
|
|
|
|
} else {
|
|
|
|
Some(&[])
|
2024-12-01 07:01:44 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parent_of(&self, ty: Id) -> Option<Id> {
|
2024-12-01 08:11:38 -06:00
|
|
|
self.type_base_of(ty).map(|b| b.parent)
|
2024-12-01 07:01:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn captures_of<'a>(&self, ty: Id, file: &'a parser::Ast) -> Option<(&'a [Ident], Tuple)> {
|
2024-12-01 08:11:38 -06:00
|
|
|
let base = self.type_base_of(ty)?;
|
|
|
|
|
|
|
|
let (Expr::Struct { captured, .. }
|
|
|
|
| Expr::Enum { captured, .. }
|
|
|
|
| Expr::Union { captured, .. }) = *base.ast.get(file)
|
|
|
|
else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
|
|
|
debug_assert_eq!(captured.len(), base.captured.len());
|
|
|
|
Some((captured, base.captured))
|
2024-12-01 07:01:44 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct OptLayout {
|
|
|
|
pub flag_ty: Id,
|
|
|
|
pub flag_offset: Offset,
|
|
|
|
pub payload_offset: Offset,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct OffsetIter {
|
|
|
|
strct: Struct,
|
|
|
|
offset: Offset,
|
|
|
|
fields: Range<usize>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OffsetIter {
|
|
|
|
pub fn new(strct: Struct, tys: &Types) -> Self {
|
|
|
|
Self { strct, offset: 0, fields: tys.struct_field_range(strct) }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn offset_of(tys: &Types, idx: Struct, field: &str) -> Option<(Offset, Id)> {
|
|
|
|
let field_id = tys.names.project(field)?;
|
|
|
|
OffsetIter::new(idx, tys)
|
|
|
|
.into_iter(tys)
|
|
|
|
.find(|(f, _)| f.name == field_id)
|
|
|
|
.map(|(f, off)| (off, f.ty))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a StructField, Offset)> {
|
|
|
|
let stru = &tys.ins.structs[self.strct];
|
|
|
|
let field = &tys.ins.struct_fields[self.fields.next()?];
|
|
|
|
|
|
|
|
let align = stru.explicit_alignment.map_or_else(|| tys.align_of(field.ty), |a| a as u32);
|
|
|
|
self.offset = (self.offset + align - 1) & !(align - 1);
|
|
|
|
|
|
|
|
let off = self.offset;
|
|
|
|
self.offset += tys.size_of(field.ty);
|
|
|
|
Some((field, off))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next_ty(&mut self, tys: &Types) -> Option<(Id, Offset)> {
|
|
|
|
let (field, off) = self.next(tys)?;
|
|
|
|
Some((field.ty, off))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn into_iter(mut self, tys: &Types) -> impl Iterator<Item = (&StructField, Offset)> {
|
|
|
|
core::iter::from_fn(move || self.next(tys))
|
|
|
|
}
|
|
|
|
}
|