This commit is contained in:
Jakub Doka 2024-10-30 20:20:03 +01:00
parent acacd10ee9
commit 9ed3c7ab9e
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
5 changed files with 150 additions and 114 deletions

View file

@ -161,6 +161,27 @@ drop := fn(a: uint): void {
}
```
#### nullable_types
```hb
main := fn(): int {
a := &1
b := @as(?^uint, null)
if decide() b = a
if b == null return 9001
c := @as(?uint, *b)
if decide() c = null
if c != null return 42
return 0
}
decide := fn(): bool return true
```
#### structs
```hb
Ty := struct {
@ -1196,3 +1217,4 @@ main := fn(): int {
opaque := fn(): Foo {
return .(3, 2)
}
```

View file

@ -290,6 +290,7 @@ mod ty {
pub type Builtin = u32;
pub type Struct = u32;
pub type Opt = u32;
pub type Ptr = u32;
pub type Func = u32;
pub type Global = u32;
@ -373,6 +374,7 @@ mod ty {
crate::SymKey::Struct(st.file, st.pos, st.captures)
}
Kind::Ptr(p) => crate::SymKey::Pointer(&ctx.ptrs[p as usize]),
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p as usize]),
Kind::Func(f) => {
let fc = &ctx.funcs[f as usize];
if let Some(base) = fc.base {
@ -441,6 +443,10 @@ mod ty {
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> {
let (oa, ob) = (Self(self.0.min(ob.0)), Self(self.0.max(ob.0)));
let (a, b) = (oa.strip_pointer(), ob.strip_pointer());
@ -490,9 +496,16 @@ mod ty {
pub(crate) fn loc(&self, tys: &Types) -> Loc {
match self.expand() {
Kind::Opt(o)
if let ty = tys.ins.opts[o as usize].base
&& ty.loc(tys) == Loc::Reg
&& (ty.is_pointer() || tys.size_of(ty) < 8) =>
{
Loc::Reg
}
Kind::Ptr(_) | Kind::Builtin(_) => Loc::Reg,
Kind::Struct(_) if tys.size_of(*self) == 0 => Loc::Reg,
Kind::Struct(_) | Kind::Slice(_) => Loc::Stack,
Kind::Struct(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack,
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) => unreachable!(),
}
}
@ -633,6 +646,7 @@ mod ty {
pub enum Kind {
Builtin,
Struct,
Opt,
Ptr,
Func,
Global,
@ -675,6 +689,10 @@ mod ty {
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 as usize].base).fmt(f)
}
TK::Ptr(ty) => {
f.write_str("^")?;
self.rety(self.tys.ins.ptrs[ty as usize].base).fmt(f)
@ -730,6 +748,7 @@ type Size = u32;
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub enum SymKey<'a> {
Pointer(&'a Ptr),
Optional(&'a Opt),
Struct(FileId, Pos, ty::Tuple),
FuncInst(ty::Func, ty::Tuple),
Decl(FileId, Ident),
@ -840,7 +859,12 @@ struct Struct {
field_start: u32,
}
#[derive(PartialEq, Eq, Hash)]
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct Opt {
base: ty::Id,
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct Ptr {
base: ty::Id,
}
@ -935,6 +959,7 @@ pub struct TypeIns {
structs: Vec<Struct>,
fields: Vec<Field>,
ptrs: Vec<Ptr>,
opts: Vec<Opt>,
slices: Vec<Array>,
}
@ -1065,6 +1090,10 @@ trait TypeParser {
let base = self.parse_ty(file, val, None, files);
self.tys().make_ptr(base)
}
Expr::UnOp { op: TokenKind::Que, val, .. } => {
let base = self.parse_ty(file, val, None, files);
self.tys().make_opt(base)
}
Expr::Ident { id, .. } if id.is_null() => id.len().into(),
Expr::Ident { id, pos, .. } => self.find_type(pos, file, file, Ok(id), files),
Expr::Field { target, pos, name }
@ -1206,64 +1235,44 @@ impl Types {
(ret, iter)
}
fn make_opt(&mut self, base: ty::Id) -> ty::Id {
self.make_generic_ty(
Opt { base },
|ins| &mut ins.opts,
|e| SymKey::Optional(e),
ty::Kind::Opt,
)
}
fn make_ptr(&mut self, base: ty::Id) -> ty::Id {
ty::Kind::Ptr(self.make_ptr_low(base)).compress()
self.make_generic_ty(
Ptr { base },
|ins| &mut ins.ptrs,
|e| SymKey::Pointer(e),
ty::Kind::Ptr,
)
}
fn make_ptr_low(&mut self, base: ty::Id) -> ty::Ptr {
let ptr = Ptr { base };
let (entry, hash) = self.syms.entry(SymKey::Pointer(&ptr), &self.ins);
match entry {
hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.value,
hash_map::RawEntryMut::Vacant(v) => {
self.ins.ptrs.push(ptr);
v.insert(
ctx_map::Key {
value: ty::Kind::Ptr(self.ins.ptrs.len() as u32 - 1).compress(),
hash,
},
(),
)
.0
.value
}
}
.expand()
.inner()
fn make_array(&mut self, elem: ty::Id, len: ArrayLen) -> ty::Id {
self.make_generic_ty(
Array { elem, len },
|ins| &mut ins.slices,
|e| SymKey::Array(e),
ty::Kind::Slice,
)
}
fn make_array(&mut self, ty: ty::Id, len: ArrayLen) -> ty::Id {
ty::Kind::Slice(self.make_array_low(ty, len)).compress()
}
fn make_array_low(&mut self, ty: ty::Id, len: ArrayLen) -> ty::Slice {
self.syms
.get_or_insert(SymKey::Array(&Array { elem: ty, len }), &mut self.ins, |ins| {
ins.slices.push(Array { elem: ty, len });
ty::Kind::Slice(ins.slices.len() as u32 - 1).compress()
})
.expand()
.inner()
//let array = Array { ty, len };
//let (entry, hash) = self.syms.entry(SymKey::Array(&array), &self.ins);
//match entry {
// hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.value,
// hash_map::RawEntryMut::Vacant(v) => {
// self.ins.arrays.push(array);
// v.insert(
// ctx_map::Key {
// value: ty::Kind::Slice(self.ins.ptrs.len() as u32 - 1).compress(),
// hash,
// },
// (),
// )
// .0
// .value
// }
//}
//.expand()
//.inner()
fn make_generic_ty<T: Copy>(
&mut self,
ty: T,
get_col: fn(&mut TypeIns) -> &mut Vec<T>,
key: fn(&T) -> SymKey,
kind: fn(u32) -> ty::Kind,
) -> ty::Id {
*self.syms.get_or_insert(key(&{ ty }), &mut self.ins, |ins| {
get_col(ins).push(ty);
kind(get_col(ins).len() as u32 - 1).compress()
})
}
fn size_of(&self, ty: ty::Id) -> Size {
@ -1328,6 +1337,20 @@ impl Types {
}
}
fn inner_of(&self, ty: ty::Id) -> Option<ty::Id> {
match ty.expand() {
ty::Kind::Opt(o) => Some(self.ins.opts[o as usize].base),
_ => None,
}
}
fn opt_flag_field(&self, ty: ty::Id) -> (Offset, ty::Id) {
match ty.expand() {
ty::Kind::Ptr(_) => (0, ty::Id::UINT),
_ => todo!("{ty:?}"),
}
}
fn find_struct_field(&self, s: ty::Struct, name: &str) -> Option<usize> {
let name = self.names.project(name)?;
self.struct_fields(s).iter().position(|f| f.name == name)

View file

@ -443,7 +443,7 @@ impl<'a, 'b> Parser<'a, 'b> {
pos
},
},
T::Band | T::Mul | T::Xor | T::Sub => E::UnOp {
T::Band | T::Mul | T::Xor | T::Sub | T::Que => E::UnOp {
pos,
op: token.kind,
val: {

View file

@ -654,6 +654,14 @@ impl Nodes {
}
}
fn new_const(&mut self, ty: ty::Id, value: impl Into<i64>) -> Nid {
self.new_node_nop(ty, Kind::CInt { value: value.into() }, [VOID])
}
fn new_const_lit(&mut self, ty: ty::Id, value: impl Into<i64>) -> Value {
self.new_node_lit(ty, Kind::CInt { value: value.into() }, [VOID])
}
fn new_node_lit(&mut self, ty: ty::Id, kind: Kind, inps: impl Into<Vc>) -> Value {
Value::new(self.new_node(ty, kind, inps)).ty(ty)
}
@ -772,18 +780,14 @@ impl Nodes {
if let (&K::CInt { value: a }, &K::CInt { value: b }) =
(&self[lhs].kind, &self[rhs].kind)
{
return Some(self.new_node(
ty,
K::CInt { value: op.apply_binop(a, b, is_float) },
[ctrl],
));
return Some(self.new_const(ty, op.apply_binop(a, b, is_float)));
}
if lhs == rhs {
match op {
T::Sub => return Some(self.new_node(ty, K::CInt { value: 0 }, [ctrl])),
T::Sub => return Some(self.new_const(ty, 0)),
T::Add => {
let rhs = self.new_node_nop(ty, K::CInt { value: 2 }, [ctrl]);
let rhs = self.new_const(ty, 2);
return Some(
self.new_node(ty, K::BinOp { op: T::Mul }, [ctrl, lhs, rhs]),
);
@ -813,11 +817,7 @@ impl Nodes {
&& let K::CInt { value: bv } = self[rhs].kind
{
// (a op #b) op #c => a op (#b op #c)
let new_rhs = self.new_node_nop(
ty,
K::CInt { value: op.apply_binop(av, bv, is_float) },
[ctrl],
);
let new_rhs = self.new_const(ty, op.apply_binop(av, bv, is_float));
return Some(self.new_node(ty, K::BinOp { op }, [ctrl, a, new_rhs]));
}
@ -834,7 +834,7 @@ impl Nodes {
&& let K::CInt { value } = self[self[lhs].inputs[2]].kind
{
// a * #n + a => a * (#n + 1)
let new_rhs = self.new_node_nop(ty, K::CInt { value: value + 1 }, [ctrl]);
let new_rhs = self.new_const(ty, value + 1);
return Some(self.new_node(ty, K::BinOp { op: T::Mul }, [ctrl, rhs, new_rhs]));
}
@ -843,7 +843,7 @@ impl Nodes {
&& let K::CInt { value: a } = self[rhs].kind
&& let K::CInt { value: b } = self[self[lhs].inputs[2]].kind
{
let new_rhs = self.new_node_nop(ty, K::CInt { value: b - a }, [ctrl]);
let new_rhs = self.new_const(ty, b - a);
return Some(self.new_node(ty, K::BinOp { op: T::Add }, [
ctrl,
self[lhs].inputs[1],
@ -864,17 +864,13 @@ impl Nodes {
}
}
K::UnOp { op } => {
let &[ctrl, oper] = self[target].inputs.as_slice() else { unreachable!() };
let &[_, oper] = self[target].inputs.as_slice() else { unreachable!() };
let ty = self[target].ty;
let is_float = self[oper].ty.is_float();
if let K::CInt { value } = self[oper].kind {
return Some(self.new_node(
ty,
K::CInt { value: op.apply_unop(value, is_float) },
[ctrl],
));
return Some(self.new_const(ty, op.apply_unop(value, is_float)));
}
}
K::If => {
@ -2307,21 +2303,31 @@ impl<'a> Codegen<'a> {
// ordered by complexity of the expression
match *expr {
Expr::Null { pos } => {
inference!(ty, ctx, self, pos, "null pointer", "@as(^<ty>, null)");
inference!(oty, ctx, self, pos, "null pointer", "@as(^<ty>, null)");
if !ty.is_pointer() {
let Some(ty) = self.tys.inner_of(oty) else {
self.report(
pos,
fa!(
"'null' expression was inferred to be '{}',
which is not a pointer (and that is not supported yet)",
self.ty_display(ty)
which is not optional",
self.ty_display(oty)
),
);
return Value::NEVER;
}
};
Some(self.ci.nodes.new_node_lit(ty, Kind::CInt { value: 0 }, [VOID]))
match oty.loc(self.tys) {
Loc::Reg => Some(self.ci.nodes.new_const_lit(oty, 0)),
Loc::Stack => {
let (off, flag_ty) = self.tys.opt_flag_field(ty);
let stack = self.new_stack(oty);
let offset = self.offset(stack, off);
let value = self.ci.nodes.new_const(flag_ty, 0);
self.store_mem(offset, flag_ty, value);
Some(Value::ptr(stack).ty(oty))
}
}
}
Expr::Idk { pos } => {
inference!(ty, ctx, self, pos, "value", "@as(<ty>, idk)");
@ -2340,20 +2346,14 @@ impl<'a> Codegen<'a> {
Value::NEVER
}
}
Expr::Bool { value, .. } => Some(self.ci.nodes.new_node_lit(
ty::Id::BOOL,
Kind::CInt { value: value as i64 },
[VOID],
)),
Expr::Number { value, .. } => Some(self.ci.nodes.new_node_lit(
Expr::Bool { value, .. } => Some(self.ci.nodes.new_const_lit(ty::Id::BOOL, value)),
Expr::Number { value, .. } => Some(self.ci.nodes.new_const_lit(
ctx.ty.filter(|ty| ty.is_integer()).unwrap_or(ty::Id::DEFAULT_INT),
Kind::CInt { value },
[VOID],
value,
)),
Expr::Float { value, .. } => Some(self.ci.nodes.new_node_lit(
Expr::Float { value, .. } => Some(self.ci.nodes.new_const_lit(
ctx.ty.filter(|ty| ty.is_float()).unwrap_or(ty::Id::F32),
Kind::CInt { value: value as _ },
[VOID],
value as i64,
)),
Expr::Ident { id, .. }
if let Some(index) = self.ci.scope.vars.iter().rposition(|v| v.id == id) =>
@ -2551,11 +2551,7 @@ impl<'a> Codegen<'a> {
if val.ty.is_integer() {
Some(self.ci.nodes.new_node_lit(val.ty, Kind::UnOp { op }, [VOID, val.id]))
} else if val.ty.is_float() {
let value = self.ci.nodes.new_node_nop(
val.ty,
Kind::CInt { value: (-1f64).to_bits() as _ },
[VOID],
);
let value = self.ci.nodes.new_const(val.ty, (-1f64).to_bits() as i64);
Some(self.ci.nodes.new_node_lit(val.ty, Kind::BinOp { op: TokenKind::Mul }, [
VOID, val.id, value,
]))
@ -2669,8 +2665,7 @@ impl<'a> Codegen<'a> {
let elem = self.tys.ins.slices[s as usize].elem;
let mut idx = self.expr_ctx(index, Ctx::default().with_ty(ty::Id::DEFAULT_INT))?;
self.assert_ty(index.pos(), &mut idx, ty::Id::DEFAULT_INT, "subscript");
let value = self.tys.size_of(elem) as i64;
let size = self.ci.nodes.new_node_nop(ty::Id::INT, Kind::CInt { value }, [VOID]);
let size = self.ci.nodes.new_const(ty::Id::INT, self.tys.size_of(elem));
let inps = [VOID, idx.id, size];
let offset =
self.ci.nodes.new_node(ty::Id::INT, Kind::BinOp { op: TokenKind::Mul }, inps);
@ -2690,18 +2685,16 @@ impl<'a> Codegen<'a> {
}
Expr::Directive { name: "sizeof", args: [ty], .. } => {
let ty = self.ty(ty);
Some(self.ci.nodes.new_node_lit(
Some(self.ci.nodes.new_const_lit(
ctx.ty.filter(|ty| ty.is_integer()).unwrap_or(ty::Id::DEFAULT_INT),
Kind::CInt { value: self.tys.size_of(ty) as _ },
[VOID],
self.tys.size_of(ty),
))
}
Expr::Directive { name: "alignof", args: [ty], .. } => {
let ty = self.ty(ty);
Some(self.ci.nodes.new_node_lit(
Some(self.ci.nodes.new_const_lit(
ctx.ty.filter(|ty| ty.is_integer()).unwrap_or(ty::Id::DEFAULT_INT),
Kind::CInt { value: self.tys.align_of(ty) as _ },
[VOID],
self.tys.align_of(ty),
))
}
Expr::Directive { name: "bitcast", args: [val], pos } => {
@ -3166,8 +3159,8 @@ impl<'a> Codegen<'a> {
}
}
Expr::Struct { .. } => {
let value = self.ty(expr).repr() as i64;
Some(self.ci.nodes.new_node_lit(ty::Id::TYPE, Kind::CInt { value }, [VOID]))
let value = self.ty(expr).repr();
Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, value))
}
Expr::Ctor { pos, ty, fields, .. } => {
ctx.ty = ty.map(|ty| self.ty(ty)).or(ctx.ty);
@ -3729,7 +3722,7 @@ impl<'a> Codegen<'a> {
return val;
}
let off = self.ci.nodes.new_node_nop(ty::Id::INT, Kind::CInt { value: off as i64 }, [VOID]);
let off = self.ci.nodes.new_const(ty::Id::INT, off);
let (aclass, mem) = self.ci.nodes.aclass_index(val);
let inps = [VOID, val, off];
let seted = self.ci.nodes.new_node(ty::Id::INT, Kind::BinOp { op: TokenKind::Add }, inps);
@ -3914,9 +3907,7 @@ impl<'a> Codegen<'a> {
if matches!(op, TokenKind::Add | TokenKind::Sub)
&& let Some(elem) = self.tys.base_of(upcasted)
{
let value = self.tys.size_of(elem) as i64;
let cnst =
self.ci.nodes.new_node_nop(ty::Id::INT, Kind::CInt { value }, [VOID]);
let cnst = self.ci.nodes.new_const(ty::Id::INT, self.tys.size_of(elem));
oper.id =
self.ci.nodes.new_node(upcasted, Kind::BinOp { op: TokenKind::Mul }, [
VOID, oper.id, cnst,
@ -3975,9 +3966,8 @@ impl<'a> Codegen<'a> {
fn extend(&mut self, value: &mut Value, to: ty::Id) {
self.strip_ptr(value);
let val = (1i64 << (self.tys.size_of(value.ty) * 8)) - 1;
value.ty = to;
let mask = self.ci.nodes.new_node_nop(to, Kind::CInt { value: val }, [VOID]);
let mask = self.ci.nodes.new_const(to, (1i64 << (self.tys.size_of(value.ty) * 8)) - 1);
let inps = [VOID, value.id, mask];
*value = self.ci.nodes.new_node_lit(to, Kind::BinOp { op: TokenKind::Band }, inps);
}
@ -4137,6 +4127,7 @@ mod tests {
variables;
loops;
pointers;
nullable_types;
structs;
hex_octal_binary_literals;
struct_operators;

View file