adding optional values
This commit is contained in:
parent
9ed3c7ab9e
commit
3f9f99ff65
|
@ -135,9 +135,6 @@ fib := fn(n: uint): uint {
|
|||
#### pointers
|
||||
```hb
|
||||
main := fn(): uint {
|
||||
n := @as(^uint, null)
|
||||
if n != null return 9001
|
||||
|
||||
a := 1
|
||||
b := &a
|
||||
|
||||
|
@ -176,7 +173,12 @@ main := fn(): int {
|
|||
|
||||
if c != null return 42
|
||||
|
||||
return 0
|
||||
d := @as(?u16, null)
|
||||
if decide() d = 0
|
||||
|
||||
if d == null return 69
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
decide := fn(): bool return true
|
||||
|
|
|
@ -454,6 +454,7 @@ mod ty {
|
|||
_ if oa == Self::from(NEVER) => ob,
|
||||
_ if ob == Self::from(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,
|
||||
|
@ -646,12 +647,12 @@ mod ty {
|
|||
pub enum Kind {
|
||||
Builtin,
|
||||
Struct,
|
||||
Opt,
|
||||
Ptr,
|
||||
Slice,
|
||||
Opt,
|
||||
Func,
|
||||
Global,
|
||||
Module,
|
||||
Slice,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1295,6 +1296,14 @@ impl Types {
|
|||
self.ins.structs[stru as usize].size.set(oiter.offset);
|
||||
oiter.offset
|
||||
}
|
||||
ty::Kind::Opt(opt) => {
|
||||
let base = self.ins.opts[opt as usize].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),
|
||||
}
|
||||
|
@ -1344,10 +1353,27 @@ impl Types {
|
|||
}
|
||||
}
|
||||
|
||||
fn opt_flag_field(&self, ty: ty::Id) -> (Offset, ty::Id) {
|
||||
fn opt_layout(&self, inner_ty: 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: ty::Id::BOOL,
|
||||
flag_offset: 0,
|
||||
payload_offset: self.align_of(inner_ty),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn nieche_of(&self, ty: ty::Id) -> Option<(bool, Offset, ty::Id)> {
|
||||
match ty.expand() {
|
||||
ty::Kind::Ptr(_) => (0, ty::Id::UINT),
|
||||
_ => todo!("{ty:?}"),
|
||||
ty::Kind::Ptr(_) => Some((false, 0, ty::Id::UINT)),
|
||||
// TODO: cache this
|
||||
ty::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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1379,6 +1405,12 @@ impl Types {
|
|||
}
|
||||
}
|
||||
|
||||
struct OptLayout {
|
||||
flag_ty: ty::Id,
|
||||
flag_offset: Offset,
|
||||
payload_offset: Offset,
|
||||
}
|
||||
|
||||
struct OffsetIter {
|
||||
strct: ty::Struct,
|
||||
offset: Offset,
|
||||
|
|
225
lang/src/son.rs
225
lang/src/son.rs
|
@ -12,8 +12,8 @@ use {
|
|||
task,
|
||||
ty::{self, Arg, ArrayLen, Loc, Tuple},
|
||||
utils::{BitSet, Vc},
|
||||
FTask, Func, Global, Ident, Offset, OffsetIter, Reloc, Sig, StringRef, SymKey, TypeParser,
|
||||
TypedReloc, Types,
|
||||
FTask, Func, Global, Ident, Offset, OffsetIter, OptLayout, Reloc, Sig, StringRef, SymKey,
|
||||
TypeParser, TypedReloc, Types,
|
||||
},
|
||||
alloc::{string::String, vec::Vec},
|
||||
core::{
|
||||
|
@ -2320,9 +2320,9 @@ impl<'a> Codegen<'a> {
|
|||
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 OptLayout { flag_ty, flag_offset, .. } = self.tys.opt_layout(ty);
|
||||
let stack = self.new_stack(oty);
|
||||
let offset = self.offset(stack, off);
|
||||
let offset = self.offset(stack, flag_offset);
|
||||
let value = self.ci.nodes.new_const(flag_ty, 0);
|
||||
self.store_mem(offset, flag_ty, value);
|
||||
Some(Value::ptr(stack).ty(oty))
|
||||
|
@ -2347,14 +2347,24 @@ impl<'a> Codegen<'a> {
|
|||
}
|
||||
}
|
||||
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),
|
||||
value,
|
||||
)),
|
||||
Expr::Float { value, .. } => Some(self.ci.nodes.new_const_lit(
|
||||
ctx.ty.filter(|ty| ty.is_float()).unwrap_or(ty::Id::F32),
|
||||
value as i64,
|
||||
)),
|
||||
Expr::Number { value, .. } => Some(
|
||||
self.ci.nodes.new_const_lit(
|
||||
ctx.ty
|
||||
.map(|ty| self.tys.inner_of(ty).unwrap_or(ty))
|
||||
.filter(|ty| ty.is_integer())
|
||||
.unwrap_or(ty::Id::DEFAULT_INT),
|
||||
value,
|
||||
),
|
||||
),
|
||||
Expr::Float { value, .. } => Some(
|
||||
self.ci.nodes.new_const_lit(
|
||||
ctx.ty
|
||||
.map(|ty| self.tys.inner_of(ty).unwrap_or(ty))
|
||||
.filter(|ty| ty.is_float())
|
||||
.unwrap_or(ty::Id::F32),
|
||||
value as i64,
|
||||
),
|
||||
),
|
||||
Expr::Ident { id, .. }
|
||||
if let Some(index) = self.ci.scope.vars.iter().rposition(|v| v.id == id) =>
|
||||
{
|
||||
|
@ -2534,6 +2544,8 @@ impl<'a> Codegen<'a> {
|
|||
let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) };
|
||||
let mut val = self.expr_ctx(val, ctx)?;
|
||||
|
||||
self.unwrap_opt(pos, &mut val);
|
||||
|
||||
let Some(base) = self.tys.base_of(val.ty) else {
|
||||
self.report(
|
||||
pos,
|
||||
|
@ -2596,6 +2608,25 @@ impl<'a> Codegen<'a> {
|
|||
|
||||
Some(Value::VOID)
|
||||
}
|
||||
Expr::BinOp { left: &Expr::Null { pos }, .. } => {
|
||||
self.report(pos, "'null' must always be no the right side of an expression");
|
||||
Value::NEVER
|
||||
}
|
||||
Expr::BinOp {
|
||||
left,
|
||||
op: op @ (TokenKind::Eq | TokenKind::Ne),
|
||||
right: Expr::Null { .. },
|
||||
..
|
||||
} => {
|
||||
let mut cmped = self.raw_expr(left)?;
|
||||
self.strip_var(&mut cmped);
|
||||
|
||||
let Some(ty) = self.tys.inner_of(cmped.ty) else {
|
||||
return Some(self.ci.nodes.new_const_lit(ty::Id::BOOL, 1));
|
||||
};
|
||||
|
||||
Some(Value::new(self.gen_null_check(cmped, ty, op)).ty(ty::BOOL))
|
||||
}
|
||||
Expr::BinOp { left, pos, op, right }
|
||||
if !matches!(op, TokenKind::Assign | TokenKind::Decl) =>
|
||||
{
|
||||
|
@ -2685,17 +2716,27 @@ impl<'a> Codegen<'a> {
|
|||
}
|
||||
Expr::Directive { name: "sizeof", args: [ty], .. } => {
|
||||
let ty = self.ty(ty);
|
||||
Some(self.ci.nodes.new_const_lit(
|
||||
ctx.ty.filter(|ty| ty.is_integer()).unwrap_or(ty::Id::DEFAULT_INT),
|
||||
self.tys.size_of(ty),
|
||||
))
|
||||
Some(
|
||||
self.ci.nodes.new_const_lit(
|
||||
ctx.ty
|
||||
.map(|ty| self.tys.inner_of(ty).unwrap_or(ty))
|
||||
.filter(|ty| ty.is_integer())
|
||||
.unwrap_or(ty::Id::DEFAULT_INT),
|
||||
self.tys.size_of(ty),
|
||||
),
|
||||
)
|
||||
}
|
||||
Expr::Directive { name: "alignof", args: [ty], .. } => {
|
||||
let ty = self.ty(ty);
|
||||
Some(self.ci.nodes.new_const_lit(
|
||||
ctx.ty.filter(|ty| ty.is_integer()).unwrap_or(ty::Id::DEFAULT_INT),
|
||||
self.tys.align_of(ty),
|
||||
))
|
||||
Some(
|
||||
self.ci.nodes.new_const_lit(
|
||||
ctx.ty
|
||||
.map(|ty| self.tys.inner_of(ty).unwrap_or(ty))
|
||||
.filter(|ty| ty.is_integer())
|
||||
.unwrap_or(ty::Id::DEFAULT_INT),
|
||||
self.tys.align_of(ty),
|
||||
),
|
||||
)
|
||||
}
|
||||
Expr::Directive { name: "bitcast", args: [val], pos } => {
|
||||
let mut val = self.raw_expr(val)?;
|
||||
|
@ -3929,6 +3970,103 @@ impl<'a> Codegen<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn wrap_in_opt(&mut self, val: &mut Value) {
|
||||
debug_assert!(!val.var);
|
||||
|
||||
let oty = self.tys.make_opt(val.ty);
|
||||
|
||||
if let Some((uninit, ..)) = self.tys.nieche_of(val.ty) {
|
||||
self.strip_ptr(val);
|
||||
val.ty = oty;
|
||||
assert!(!uninit, "TODO");
|
||||
return;
|
||||
}
|
||||
|
||||
let OptLayout { flag_ty, flag_offset, payload_offset } = self.tys.opt_layout(val.ty);
|
||||
|
||||
match oty.loc(self.tys) {
|
||||
Loc::Reg => {
|
||||
self.strip_ptr(val);
|
||||
// registers have inverted offsets so that accessing the inner type is a noop
|
||||
let flag_offset = self.tys.size_of(oty) - flag_offset - 1;
|
||||
let fill = self.ci.nodes.new_const(oty, 1i64 << (flag_offset * 8 - 1));
|
||||
val.id = self
|
||||
.ci
|
||||
.nodes
|
||||
.new_node(oty, Kind::BinOp { op: TokenKind::Bor }, [VOID, val.id, fill]);
|
||||
val.ty = oty;
|
||||
}
|
||||
Loc::Stack if val.ty.loc(self.tys) == Loc::Reg => {
|
||||
self.strip_ptr(val);
|
||||
let stack = self.new_stack(oty);
|
||||
let fill = self.ci.nodes.new_const(flag_ty, 1);
|
||||
self.store_mem(stack, flag_ty, fill);
|
||||
let off = self.offset(stack, payload_offset);
|
||||
self.store_mem(off, val.ty, val.id);
|
||||
val.id = stack;
|
||||
val.ptr = true;
|
||||
val.ty = oty;
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_opt(&mut self, pos: Pos, opt: &mut Value) {
|
||||
let Some(ty) = self.tys.inner_of(opt.ty) else { return };
|
||||
let null_check = self.gen_null_check(*opt, ty, TokenKind::Eq);
|
||||
|
||||
// TODO: extract the if check int a fucntion
|
||||
let ctrl = self.ci.nodes.new_node(ty::Id::VOID, Kind::If, [self.ci.ctrl.get(), null_check]);
|
||||
let ctrl_ty = self.ci.nodes[ctrl].ty;
|
||||
self.ci.nodes.remove(ctrl);
|
||||
let oty = mem::replace(&mut opt.ty, ty);
|
||||
match ctrl_ty {
|
||||
ty::Id::LEFT_UNREACHABLE => {
|
||||
if self.tys.nieche_of(ty).is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let OptLayout { payload_offset, .. } = self.tys.opt_layout(ty);
|
||||
|
||||
match oty.loc(self.tys) {
|
||||
Loc::Reg => {}
|
||||
Loc::Stack => {
|
||||
opt.id = self.offset(opt.id, payload_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::Id::RIGHT_UNREACHABLE => {
|
||||
self.report(pos, "the value is always null, some checks might need to be inverted");
|
||||
}
|
||||
_ => {
|
||||
self.report(
|
||||
pos,
|
||||
"can't prove the value is not 'null', \
|
||||
there is not nice syntax for bypassing this, sorry",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_null_check(&mut self, mut cmped: Value, ty: ty::Id, op: TokenKind) -> Nid {
|
||||
let OptLayout { flag_ty, flag_offset, .. } = self.tys.opt_layout(ty);
|
||||
|
||||
match cmped.ty.loc(self.tys) {
|
||||
Loc::Reg => {
|
||||
self.strip_ptr(&mut cmped);
|
||||
let inps = [VOID, cmped.id, self.ci.nodes.new_const(cmped.ty, 0)];
|
||||
self.ci.nodes.new_node(ty::Id::BOOL, Kind::BinOp { op }, inps)
|
||||
}
|
||||
Loc::Stack => {
|
||||
cmped.id = self.offset(cmped.id, flag_offset);
|
||||
cmped.ty = flag_ty;
|
||||
self.strip_ptr(&mut cmped);
|
||||
let inps = [VOID, cmped.id, self.ci.nodes.new_const(ty, 0)];
|
||||
self.ci.nodes.new_node(ty::Id::BOOL, Kind::BinOp { op }, inps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_ty(
|
||||
&mut self,
|
||||
|
@ -3940,24 +4078,43 @@ impl<'a> Codegen<'a> {
|
|||
if let Some(upcasted) = src.ty.try_upcast(expected)
|
||||
&& upcasted == expected
|
||||
{
|
||||
if src.ty.is_never() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if src.ty != upcasted {
|
||||
debug_assert!(
|
||||
src.ty.is_integer() || src.ty == ty::Id::NEVER,
|
||||
"{} {}",
|
||||
self.ty_display(src.ty),
|
||||
self.ty_display(upcasted)
|
||||
);
|
||||
debug_assert!(
|
||||
upcasted.is_integer() || src.ty == ty::Id::NEVER,
|
||||
"{} {}",
|
||||
self.ty_display(src.ty),
|
||||
self.ty_display(upcasted)
|
||||
);
|
||||
self.extend(src, upcasted);
|
||||
if let Some(inner) = self.tys.inner_of(upcasted) {
|
||||
if inner != src.ty {
|
||||
self.assert_ty(pos, src, inner, hint);
|
||||
}
|
||||
self.wrap_in_opt(src);
|
||||
} else {
|
||||
debug_assert!(
|
||||
src.ty.is_integer() || src.ty == ty::Id::NEVER,
|
||||
"{} {}",
|
||||
self.ty_display(src.ty),
|
||||
self.ty_display(upcasted)
|
||||
);
|
||||
debug_assert!(
|
||||
upcasted.is_integer() || src.ty == ty::Id::NEVER,
|
||||
"{} {}",
|
||||
self.ty_display(src.ty),
|
||||
self.ty_display(upcasted)
|
||||
);
|
||||
self.extend(src, upcasted);
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
if let Some(inner) = self.tys.inner_of(src.ty)
|
||||
&& inner.try_upcast(expected) == Some(expected)
|
||||
{
|
||||
self.unwrap_opt(pos, src);
|
||||
return self.assert_ty(pos, src, expected, hint);
|
||||
}
|
||||
|
||||
let ty = self.ty_display(src.ty);
|
||||
|
||||
let expected = self.ty_display(expected);
|
||||
self.report(pos, fa!("expected {hint} to be of type {expected}, got {ty}"));
|
||||
false
|
||||
|
@ -3966,10 +4123,10 @@ impl<'a> Codegen<'a> {
|
|||
|
||||
fn extend(&mut self, value: &mut Value, to: ty::Id) {
|
||||
self.strip_ptr(value);
|
||||
value.ty = to;
|
||||
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);
|
||||
value.ty = to;
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
|
@ -226,17 +226,20 @@ impl ItemCtx {
|
|||
let node = &fuc.nodes[nid];
|
||||
|
||||
let mut extend = |base: ty::Id, dest: ty::Id, from: usize, to: usize| {
|
||||
if base.simple_size() == dest.simple_size() {
|
||||
let (bsize, dsize) = (tys.size_of(base), tys.size_of(dest));
|
||||
debug_assert!(bsize <= 8);
|
||||
debug_assert!(dsize <= 8);
|
||||
if bsize == dsize {
|
||||
return Default::default();
|
||||
}
|
||||
match (base.is_signed(), dest.is_signed()) {
|
||||
(true, true) => {
|
||||
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32]
|
||||
[base.simple_size().unwrap().ilog2() as usize];
|
||||
[bsize.ilog2() as usize];
|
||||
op(atr(allocs[to]), atr(allocs[from]))
|
||||
}
|
||||
_ => {
|
||||
let mask = (1u64 << (base.simple_size().unwrap() * 8)) - 1;
|
||||
let mask = (1u64 << (bsize * 8)) - 1;
|
||||
instrs::andi(atr(allocs[to]), atr(allocs[from]), mask)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
decide:
|
||||
LI8 r1, 1b
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 24a, 16h
|
||||
JAL r31, r0, :decide
|
||||
LI64 r3, 0d
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :0
|
||||
CP r32, r3
|
||||
JMP :1
|
||||
0: ADDI64 r32, r254, 16d
|
||||
1: JNE r32, r3, :2
|
||||
LI64 r1, 9001d
|
||||
JMP :3
|
||||
2: JAL r31, r0, :decide
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :4
|
||||
LI8 r6, 1b
|
||||
ST r6, r254, 0a, 1h
|
||||
LD r7, r32, 0a, 8h
|
||||
ST r7, r254, 8a, 8h
|
||||
JMP :5
|
||||
4: LI8 r1, 0b
|
||||
ST r1, r254, 0a, 1h
|
||||
5: LI64 r6, 0d
|
||||
LD r7, r254, 0a, 1h
|
||||
ANDI r7, r7, 255d
|
||||
JEQ r7, r6, :6
|
||||
LI64 r1, 42d
|
||||
JMP :3
|
||||
6: JAL r31, r0, :decide
|
||||
LI32 r2, 0w
|
||||
ANDI r1, r1, 255d
|
||||
JNE r1, r0, :7
|
||||
CP r8, r2
|
||||
JMP :8
|
||||
7: LI32 r8, 8388608w
|
||||
8: ANDI r8, r8, 4294967295d
|
||||
ANDI r2, r2, 4294967295d
|
||||
JNE r8, r2, :9
|
||||
LI64 r1, 69d
|
||||
JMP :3
|
||||
9: ANDI r1, r8, 65535d
|
||||
3: LD r31, r254, 24a, 16h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 389
|
||||
ret: 0
|
||||
status: Ok(())
|
Loading…
Reference in a new issue