forked from AbleOS/holey-bytes
adding unreachable
This commit is contained in:
parent
843fbddf3b
commit
9de631234d
|
@ -190,6 +190,11 @@ main := fn(): uint {
|
|||
|
||||
if bar != null return 420
|
||||
|
||||
g := @as(?^uint, null)
|
||||
g = a
|
||||
|
||||
_rd := *g
|
||||
|
||||
return d - *f.a
|
||||
}
|
||||
|
||||
|
@ -458,6 +463,16 @@ main := fn(): uint {
|
|||
}
|
||||
```
|
||||
|
||||
#### die
|
||||
```hb
|
||||
main := fn(): never {
|
||||
// simply emmits 'un' instruction that immediately terminates the execution
|
||||
// the expression evaluates into `never` type that can coerce into any other
|
||||
// type
|
||||
die
|
||||
}
|
||||
```
|
||||
|
||||
### Incomplete Examples
|
||||
|
||||
#### comptime_pointers
|
||||
|
@ -478,7 +493,7 @@ modify := fn($num: ^uint): void {
|
|||
MALLOC_SYS_CALL := 69
|
||||
FREE_SYS_CALL := 96
|
||||
|
||||
malloc := fn(size: uint, align: uint): ^void return @eca(MALLOC_SYS_CALL, size, align)
|
||||
malloc := fn(size: uint, align: uint): ?^void return @eca(MALLOC_SYS_CALL, size, align)
|
||||
free := fn(ptr: ^void, size: uint, align: uint): void return @eca(FREE_SYS_CALL, ptr, size, align)
|
||||
|
||||
Vec := fn($Elem: type): type {
|
||||
|
@ -497,7 +512,7 @@ deinit := fn($Elem: type, vec: ^Vec(Elem)): void {
|
|||
return
|
||||
}
|
||||
|
||||
push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ^Elem {
|
||||
push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ?^Elem {
|
||||
if vec.len == vec.cap {
|
||||
if vec.cap == 0 {
|
||||
vec.cap = 1
|
||||
|
@ -505,11 +520,11 @@ push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ^Elem {
|
|||
vec.cap *= 2
|
||||
}
|
||||
|
||||
new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))))
|
||||
if new_alloc == 0 return @bitcast(0)
|
||||
new_alloc := @as(?^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))))
|
||||
if new_alloc == null return null
|
||||
|
||||
src_cursor := vec.data
|
||||
dst_cursor := new_alloc
|
||||
dst_cursor := @as(^Elem, new_alloc)
|
||||
end := vec.data + vec.len
|
||||
|
||||
loop if src_cursor == end break else {
|
||||
|
|
|
@ -371,6 +371,7 @@ impl<'a> Formatter<'a> {
|
|||
}
|
||||
Expr::Bool { value, .. } => f.write_str(if value { "true" } else { "false" }),
|
||||
Expr::Idk { .. } => f.write_str("idk"),
|
||||
Expr::Die { .. } => f.write_str("die"),
|
||||
Expr::Null { .. } => f.write_str("null"),
|
||||
Expr::BinOp {
|
||||
left,
|
||||
|
|
|
@ -134,6 +134,7 @@ pub enum TokenKind {
|
|||
False,
|
||||
Null,
|
||||
Idk,
|
||||
Die,
|
||||
|
||||
Ctor,
|
||||
Tupl,
|
||||
|
@ -306,6 +307,7 @@ gen_token_kind! {
|
|||
False = b"false",
|
||||
Null = b"null",
|
||||
Idk = b"idk",
|
||||
Die = b"die",
|
||||
#[punkt]
|
||||
Ctor = ".{",
|
||||
Tupl = ".(",
|
||||
|
|
|
@ -1023,16 +1023,14 @@ trait TypeParser {
|
|||
|
||||
let Some((Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else {
|
||||
return match id {
|
||||
Ok(name) => {
|
||||
let name = files[from_file as usize].ident_str(name);
|
||||
self.report(from_file, pos, format_args!("undefined indentifier: {name}"))
|
||||
}
|
||||
Ok(_) => ty::Id::NEVER,
|
||||
Err("main") => self.report(
|
||||
from_file,
|
||||
pos,
|
||||
format_args!(
|
||||
"missing main function in '{}', compiler can't \
|
||||
emmit libraries since such concept is not defined",
|
||||
emmit libraries since such concept is not defined \
|
||||
(minimal main function: `main := fn(): void {{}}`)",
|
||||
f.path
|
||||
),
|
||||
),
|
||||
|
|
|
@ -337,6 +337,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
T::False => E::Bool { pos, value: false },
|
||||
T::Null => E::Null { pos },
|
||||
T::Idk => E::Idk { pos },
|
||||
T::Die => E::Die { pos },
|
||||
T::DQuote => E::String { pos, literal: self.tok_str(token) },
|
||||
T::Packed => {
|
||||
self.packed = true;
|
||||
|
@ -903,6 +904,10 @@ generate_expr! {
|
|||
Idk {
|
||||
pos: Pos,
|
||||
},
|
||||
/// `'die'`
|
||||
Die {
|
||||
pos: Pos,
|
||||
},
|
||||
/// `'@' Ident List('(', ',', ')', Expr)`
|
||||
Directive {
|
||||
pos: Pos,
|
||||
|
|
|
@ -138,7 +138,7 @@ impl Nodes {
|
|||
}
|
||||
depth
|
||||
}
|
||||
Kind::Start | Kind::End => 1,
|
||||
Kind::Start | Kind::End | Kind::Die => 1,
|
||||
u => unreachable!("{u:?}"),
|
||||
};
|
||||
|
||||
|
@ -805,6 +805,12 @@ impl Nodes {
|
|||
|
||||
if let K::CInt { value } = self[rhs].kind {
|
||||
match (op, value) {
|
||||
(T::Eq, 0) if self[lhs].ty.is_pointer() || self[lhs].kind == Kind::Stck => {
|
||||
return Some(self.new_const(ty::Id::BOOL, 0));
|
||||
}
|
||||
(T::Ne, 0) if self[lhs].ty.is_pointer() || self[lhs].kind == Kind::Stck => {
|
||||
return Some(self.new_const(ty::Id::BOOL, 1));
|
||||
}
|
||||
(T::Add | T::Sub | T::Shl, 0) | (T::Mul | T::Div, 1) => return Some(lhs),
|
||||
(T::Mul, 0) => return Some(rhs),
|
||||
_ => {}
|
||||
|
@ -1294,6 +1300,7 @@ impl Nodes {
|
|||
Kind::If => write!(out, " if: "),
|
||||
Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"),
|
||||
Kind::Return => write!(out, " ret: "),
|
||||
Kind::Die => write!(out, " die: "),
|
||||
Kind::CInt { value } => write!(out, "cint: #{value:<4}"),
|
||||
Kind::Phi => write!(out, " phi: "),
|
||||
Kind::Arg => write!(
|
||||
|
@ -1380,7 +1387,7 @@ impl Nodes {
|
|||
}
|
||||
node = cfg_index;
|
||||
}
|
||||
Kind::Return => {
|
||||
Kind::Return | Kind::Die => {
|
||||
node = self[node].outputs[0];
|
||||
}
|
||||
Kind::Then | Kind::Else | Kind::Entry => {
|
||||
|
@ -1578,6 +1585,8 @@ pub enum Kind {
|
|||
// [ctrl, ?value]
|
||||
Return,
|
||||
// [ctrl]
|
||||
Die,
|
||||
// [ctrl]
|
||||
CInt {
|
||||
value: i64,
|
||||
},
|
||||
|
@ -1620,6 +1629,7 @@ impl Kind {
|
|||
Self::Start
|
||||
| Self::End
|
||||
| Self::Return
|
||||
| Self::Die
|
||||
| Self::Entry
|
||||
| Self::Then
|
||||
| Self::Else
|
||||
|
@ -1631,7 +1641,7 @@ impl Kind {
|
|||
}
|
||||
|
||||
fn ends_basic_block(&self) -> bool {
|
||||
matches!(self, Self::Return | Self::If | Self::End)
|
||||
matches!(self, Self::Return | Self::If | Self::End | Self::Die)
|
||||
}
|
||||
|
||||
fn is_peeped(&self) -> bool {
|
||||
|
@ -2478,6 +2488,16 @@ impl<'a> Codegen<'a> {
|
|||
|
||||
None
|
||||
}
|
||||
Expr::Die { .. } => {
|
||||
self.ci.ctrl.set(
|
||||
self.ci.nodes.new_node_nop(ty::Id::VOID, Kind::Die, [self.ci.ctrl.get()]),
|
||||
&mut self.ci.nodes,
|
||||
);
|
||||
|
||||
self.ci.nodes[NEVER].inputs.push(self.ci.ctrl.get());
|
||||
self.ci.nodes[self.ci.ctrl.get()].outputs.push(NEVER);
|
||||
None
|
||||
}
|
||||
Expr::Field { target, name, pos } => {
|
||||
let mut vtarget = self.raw_expr(target)?;
|
||||
self.strip_var(&mut vtarget);
|
||||
|
@ -2553,20 +2573,20 @@ impl<'a> Codegen<'a> {
|
|||
}
|
||||
Expr::UnOp { op: TokenKind::Mul, val, pos } => {
|
||||
let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) };
|
||||
let mut val = self.expr_ctx(val, ctx)?;
|
||||
let mut vl = self.expr_ctx(val, ctx)?;
|
||||
|
||||
self.unwrap_opt(pos, &mut val);
|
||||
self.unwrap_opt(val.pos(), &mut vl);
|
||||
|
||||
let Some(base) = self.tys.base_of(val.ty) else {
|
||||
let Some(base) = self.tys.base_of(vl.ty) else {
|
||||
self.report(
|
||||
pos,
|
||||
fa!("the '{}' can not be dereferneced", self.ty_display(val.ty)),
|
||||
fa!("the '{}' can not be dereferneced", self.ty_display(vl.ty)),
|
||||
);
|
||||
return Value::NEVER;
|
||||
};
|
||||
val.ptr = true;
|
||||
val.ty = base;
|
||||
Some(val)
|
||||
vl.ptr = true;
|
||||
vl.ty = base;
|
||||
Some(vl)
|
||||
}
|
||||
Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => {
|
||||
let val =
|
||||
|
@ -2784,6 +2804,25 @@ impl<'a> Codegen<'a> {
|
|||
val.ty = ty;
|
||||
Some(val)
|
||||
}
|
||||
Expr::Directive { name: "unwrap", args: [expr], .. } => {
|
||||
let mut val = self.raw_expr(expr)?;
|
||||
self.strip_var(&mut val);
|
||||
|
||||
let Some(ty) = self.tys.inner_of(val.ty) else {
|
||||
self.report(
|
||||
expr.pos(),
|
||||
fa!(
|
||||
"only optional types can be unwrapped ('{}' is not optional)",
|
||||
self.ty_display(val.ty)
|
||||
),
|
||||
);
|
||||
return Value::NEVER;
|
||||
};
|
||||
|
||||
self.unwrap_opt_unchecked(ty, val.ty, &mut val);
|
||||
val.ty = ty;
|
||||
Some(val)
|
||||
}
|
||||
Expr::Directive { name: "intcast", args: [expr], pos } => {
|
||||
let mut val = self.expr(expr)?;
|
||||
|
||||
|
@ -4063,18 +4102,7 @@ impl<'a> Codegen<'a> {
|
|||
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);
|
||||
}
|
||||
}
|
||||
self.unwrap_opt_unchecked(ty, oty, opt);
|
||||
}
|
||||
ty::Id::RIGHT_UNREACHABLE => {
|
||||
self.report(pos, "the value is always null, some checks might need to be inverted");
|
||||
|
@ -4083,12 +4111,29 @@ impl<'a> Codegen<'a> {
|
|||
self.report(
|
||||
pos,
|
||||
"can't prove the value is not 'null', \
|
||||
there is not nice syntax for bypassing this, sorry",
|
||||
use '@unwrap(<opt>)' if you believe compiler is stupid, \
|
||||
or explicitly check for null and handle it \
|
||||
('if <opt> == null { /* handle */ } else { /* use opt */ }')",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_opt_unchecked(&mut self, ty: ty::Id, oty: ty::Id, opt: &mut Value) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -4335,6 +4380,7 @@ mod tests {
|
|||
inline;
|
||||
idk;
|
||||
generic_functions;
|
||||
die;
|
||||
|
||||
// Incomplete Examples;
|
||||
//comptime_pointers;
|
||||
|
|
|
@ -308,6 +308,9 @@ impl ItemCtx {
|
|||
self.emit(instrs::jmp(0));
|
||||
}
|
||||
}
|
||||
Kind::Die => {
|
||||
self.emit(instrs::un());
|
||||
}
|
||||
Kind::CInt { value } if node.ty.is_float() => {
|
||||
self.emit(match node.ty {
|
||||
ty::Id::F32 => instrs::li32(
|
||||
|
@ -617,7 +620,9 @@ impl ItemCtx {
|
|||
self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, (pushed + stack) as _));
|
||||
}
|
||||
self.relocs.iter_mut().for_each(|r| r.reloc.offset -= stripped_prelude_size as u32);
|
||||
self.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0));
|
||||
if sig.ret != ty::Id::NEVER {
|
||||
self.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -822,6 +827,10 @@ impl<'a> Function<'a> {
|
|||
self.add_instr(nid, ops);
|
||||
self.emit_node(node.outputs[0], nid);
|
||||
}
|
||||
Kind::Die => {
|
||||
self.add_instr(nid, vec![]);
|
||||
self.emit_node(node.outputs[0], nid);
|
||||
}
|
||||
Kind::CInt { .. }
|
||||
if node.outputs.iter().all(|&o| {
|
||||
let ond = &self.nodes[o];
|
||||
|
@ -1167,7 +1176,7 @@ impl regalloc2::Function for Function<'_> {
|
|||
}
|
||||
|
||||
fn is_ret(&self, insn: regalloc2::Inst) -> bool {
|
||||
self.nodes[self.instrs[insn.index()].nid].kind == Kind::Return
|
||||
matches!(self.nodes[self.instrs[insn.index()].nid].kind, Kind::Return | Kind::Die)
|
||||
}
|
||||
|
||||
fn is_branch(&self, insn: regalloc2::Inst) -> bool {
|
||||
|
|
5
lang/tests/son_tests_die.txt
Normal file
5
lang/tests/son_tests_die.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
main:
|
||||
UN
|
||||
code size: 9
|
||||
ret: 0
|
||||
status: Err(Unreachable)
|
|
@ -75,52 +75,53 @@ push:
|
|||
MUL64 r2, r36, r37
|
||||
CP r3, r37
|
||||
JAL r31, r0, :malloc
|
||||
CP r38, r34
|
||||
ST r36, r38, 16a, 8h
|
||||
JNE r1, r35, :3
|
||||
CP r1, r35
|
||||
CP r38, r1
|
||||
CP r39, r34
|
||||
ST r36, r39, 16a, 8h
|
||||
LI64 r1, 0d
|
||||
CP r7, r38
|
||||
JNE r7, r1, :3
|
||||
JMP :4
|
||||
3: CP r39, r1
|
||||
CP r1, r35
|
||||
LD r6, r38, 8a, 8h
|
||||
MULI64 r8, r6, 8d
|
||||
LD r12, r38, 0a, 8h
|
||||
ADD64 r11, r12, r8
|
||||
CP r3, r39
|
||||
9: LD r2, r38, 0a, 8h
|
||||
LD r8, r38, 8a, 8h
|
||||
JNE r11, r12, :5
|
||||
JEQ r8, r1, :6
|
||||
3: CP r38, r7
|
||||
LD r8, r39, 8a, 8h
|
||||
MULI64 r10, r8, 8d
|
||||
LD r3, r39, 0a, 8h
|
||||
ADD64 r7, r3, r10
|
||||
CP r5, r38
|
||||
9: LD r2, r39, 0a, 8h
|
||||
LD r10, r39, 8a, 8h
|
||||
JNE r7, r3, :5
|
||||
JEQ r10, r35, :6
|
||||
CP r4, r37
|
||||
MUL64 r3, r8, r4
|
||||
MUL64 r3, r10, r4
|
||||
JAL r31, r0, :free
|
||||
CP r5, r39
|
||||
CP r6, r38
|
||||
JMP :7
|
||||
6: CP r5, r39
|
||||
7: ST r5, r38, 0a, 8h
|
||||
6: CP r6, r38
|
||||
7: ST r6, r39, 0a, 8h
|
||||
JMP :8
|
||||
5: CP r4, r37
|
||||
CP r5, r39
|
||||
ADDI64 r6, r3, 8d
|
||||
ADDI64 r7, r12, 8d
|
||||
LD r8, r12, 0a, 8h
|
||||
ST r8, r3, 0a, 8h
|
||||
CP r3, r6
|
||||
CP r12, r7
|
||||
CP r6, r38
|
||||
ADDI64 r8, r5, 8d
|
||||
ADDI64 r9, r3, 8d
|
||||
LD r10, r3, 0a, 8h
|
||||
ST r10, r5, 0a, 8h
|
||||
CP r3, r9
|
||||
CP r5, r8
|
||||
JMP :9
|
||||
0: CP r38, r34
|
||||
8: LD r3, r38, 8a, 8h
|
||||
MULI64 r5, r3, 8d
|
||||
LD r4, r38, 0a, 8h
|
||||
ADD64 r1, r4, r5
|
||||
0: CP r39, r34
|
||||
8: LD r5, r39, 8a, 8h
|
||||
MULI64 r7, r5, 8d
|
||||
LD r6, r39, 0a, 8h
|
||||
ADD64 r1, r6, r7
|
||||
CP r3, r32
|
||||
ST r3, r1, 0a, 8h
|
||||
LD r11, r38, 8a, 8h
|
||||
ADD64 r2, r11, r33
|
||||
ST r2, r38, 8a, 8h
|
||||
LD r2, r39, 8a, 8h
|
||||
ADD64 r3, r2, r33
|
||||
ST r3, r39, 8a, 8h
|
||||
4: LD r31, r254, 0a, 72h
|
||||
ADDI64 r254, r254, 72d
|
||||
JALA r0, r31, 0a
|
||||
code size: 945
|
||||
code size: 955
|
||||
ret: 69
|
||||
status: Ok(())
|
||||
|
|
Loading…
Reference in a new issue