adding unreachable

This commit is contained in:
Jakub Doka 2024-11-03 10:15:03 +01:00
parent 843fbddf3b
commit 9de631234d
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
9 changed files with 152 additions and 70 deletions

View file

@ -190,6 +190,11 @@ main := fn(): uint {
if bar != null return 420 if bar != null return 420
g := @as(?^uint, null)
g = a
_rd := *g
return d - *f.a 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 ### Incomplete Examples
#### comptime_pointers #### comptime_pointers
@ -478,7 +493,7 @@ modify := fn($num: ^uint): void {
MALLOC_SYS_CALL := 69 MALLOC_SYS_CALL := 69
FREE_SYS_CALL := 96 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) free := fn(ptr: ^void, size: uint, align: uint): void return @eca(FREE_SYS_CALL, ptr, size, align)
Vec := fn($Elem: type): type { Vec := fn($Elem: type): type {
@ -497,7 +512,7 @@ deinit := fn($Elem: type, vec: ^Vec(Elem)): void {
return 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.len == vec.cap {
if vec.cap == 0 { if vec.cap == 0 {
vec.cap = 1 vec.cap = 1
@ -505,11 +520,11 @@ push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ^Elem {
vec.cap *= 2 vec.cap *= 2
} }
new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem)))) new_alloc := @as(?^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))))
if new_alloc == 0 return @bitcast(0) if new_alloc == null return null
src_cursor := vec.data src_cursor := vec.data
dst_cursor := new_alloc dst_cursor := @as(^Elem, new_alloc)
end := vec.data + vec.len end := vec.data + vec.len
loop if src_cursor == end break else { loop if src_cursor == end break else {

View file

@ -371,6 +371,7 @@ impl<'a> Formatter<'a> {
} }
Expr::Bool { value, .. } => f.write_str(if value { "true" } else { "false" }), Expr::Bool { value, .. } => f.write_str(if value { "true" } else { "false" }),
Expr::Idk { .. } => f.write_str("idk"), Expr::Idk { .. } => f.write_str("idk"),
Expr::Die { .. } => f.write_str("die"),
Expr::Null { .. } => f.write_str("null"), Expr::Null { .. } => f.write_str("null"),
Expr::BinOp { Expr::BinOp {
left, left,

View file

@ -134,6 +134,7 @@ pub enum TokenKind {
False, False,
Null, Null,
Idk, Idk,
Die,
Ctor, Ctor,
Tupl, Tupl,
@ -306,6 +307,7 @@ gen_token_kind! {
False = b"false", False = b"false",
Null = b"null", Null = b"null",
Idk = b"idk", Idk = b"idk",
Die = b"die",
#[punkt] #[punkt]
Ctor = ".{", Ctor = ".{",
Tupl = ".(", Tupl = ".(",

View file

@ -1023,16 +1023,14 @@ trait TypeParser {
let Some((Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else { let Some((Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else {
return match id { return match id {
Ok(name) => { Ok(_) => ty::Id::NEVER,
let name = files[from_file as usize].ident_str(name);
self.report(from_file, pos, format_args!("undefined indentifier: {name}"))
}
Err("main") => self.report( Err("main") => self.report(
from_file, from_file,
pos, pos,
format_args!( format_args!(
"missing main function in '{}', compiler can't \ "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 f.path
), ),
), ),

View file

@ -337,6 +337,7 @@ impl<'a, 'b> Parser<'a, 'b> {
T::False => E::Bool { pos, value: false }, T::False => E::Bool { pos, value: false },
T::Null => E::Null { pos }, T::Null => E::Null { pos },
T::Idk => E::Idk { pos }, T::Idk => E::Idk { pos },
T::Die => E::Die { pos },
T::DQuote => E::String { pos, literal: self.tok_str(token) }, T::DQuote => E::String { pos, literal: self.tok_str(token) },
T::Packed => { T::Packed => {
self.packed = true; self.packed = true;
@ -903,6 +904,10 @@ generate_expr! {
Idk { Idk {
pos: Pos, pos: Pos,
}, },
/// `'die'`
Die {
pos: Pos,
},
/// `'@' Ident List('(', ',', ')', Expr)` /// `'@' Ident List('(', ',', ')', Expr)`
Directive { Directive {
pos: Pos, pos: Pos,

View file

@ -138,7 +138,7 @@ impl Nodes {
} }
depth depth
} }
Kind::Start | Kind::End => 1, Kind::Start | Kind::End | Kind::Die => 1,
u => unreachable!("{u:?}"), u => unreachable!("{u:?}"),
}; };
@ -805,6 +805,12 @@ impl Nodes {
if let K::CInt { value } = self[rhs].kind { if let K::CInt { value } = self[rhs].kind {
match (op, value) { 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::Add | T::Sub | T::Shl, 0) | (T::Mul | T::Div, 1) => return Some(lhs),
(T::Mul, 0) => return Some(rhs), (T::Mul, 0) => return Some(rhs),
_ => {} _ => {}
@ -1294,6 +1300,7 @@ impl Nodes {
Kind::If => write!(out, " if: "), Kind::If => write!(out, " if: "),
Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"), Kind::Region | Kind::Loop => writeln!(out, " goto: {node}"),
Kind::Return => write!(out, " ret: "), Kind::Return => write!(out, " ret: "),
Kind::Die => write!(out, " die: "),
Kind::CInt { value } => write!(out, "cint: #{value:<4}"), Kind::CInt { value } => write!(out, "cint: #{value:<4}"),
Kind::Phi => write!(out, " phi: "), Kind::Phi => write!(out, " phi: "),
Kind::Arg => write!( Kind::Arg => write!(
@ -1380,7 +1387,7 @@ impl Nodes {
} }
node = cfg_index; node = cfg_index;
} }
Kind::Return => { Kind::Return | Kind::Die => {
node = self[node].outputs[0]; node = self[node].outputs[0];
} }
Kind::Then | Kind::Else | Kind::Entry => { Kind::Then | Kind::Else | Kind::Entry => {
@ -1578,6 +1585,8 @@ pub enum Kind {
// [ctrl, ?value] // [ctrl, ?value]
Return, Return,
// [ctrl] // [ctrl]
Die,
// [ctrl]
CInt { CInt {
value: i64, value: i64,
}, },
@ -1620,6 +1629,7 @@ impl Kind {
Self::Start Self::Start
| Self::End | Self::End
| Self::Return | Self::Return
| Self::Die
| Self::Entry | Self::Entry
| Self::Then | Self::Then
| Self::Else | Self::Else
@ -1631,7 +1641,7 @@ impl Kind {
} }
fn ends_basic_block(&self) -> bool { 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 { fn is_peeped(&self) -> bool {
@ -2478,6 +2488,16 @@ impl<'a> Codegen<'a> {
None 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 } => { Expr::Field { target, name, pos } => {
let mut vtarget = self.raw_expr(target)?; let mut vtarget = self.raw_expr(target)?;
self.strip_var(&mut vtarget); self.strip_var(&mut vtarget);
@ -2553,20 +2573,20 @@ impl<'a> Codegen<'a> {
} }
Expr::UnOp { op: TokenKind::Mul, val, pos } => { Expr::UnOp { op: TokenKind::Mul, val, pos } => {
let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) }; 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( self.report(
pos, 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; return Value::NEVER;
}; };
val.ptr = true; vl.ptr = true;
val.ty = base; vl.ty = base;
Some(val) Some(vl)
} }
Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => { Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => {
let val = let val =
@ -2784,6 +2804,25 @@ impl<'a> Codegen<'a> {
val.ty = ty; val.ty = ty;
Some(val) 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 } => { Expr::Directive { name: "intcast", args: [expr], pos } => {
let mut val = self.expr(expr)?; let mut val = self.expr(expr)?;
@ -4063,6 +4102,24 @@ impl<'a> Codegen<'a> {
let oty = mem::replace(&mut opt.ty, ty); let oty = mem::replace(&mut opt.ty, ty);
match ctrl_ty { match ctrl_ty {
ty::Id::LEFT_UNREACHABLE => { ty::Id::LEFT_UNREACHABLE => {
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");
}
_ => {
self.report(
pos,
"can't prove the value is not 'null', \
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() { if self.tys.nieche_of(ty).is_some() {
return; return;
} }
@ -4076,18 +4133,6 @@ impl<'a> Codegen<'a> {
} }
} }
} }
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 { 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); let OptLayout { flag_ty, flag_offset, .. } = self.tys.opt_layout(ty);
@ -4335,6 +4380,7 @@ mod tests {
inline; inline;
idk; idk;
generic_functions; generic_functions;
die;
// Incomplete Examples; // Incomplete Examples;
//comptime_pointers; //comptime_pointers;

View file

@ -308,6 +308,9 @@ impl ItemCtx {
self.emit(instrs::jmp(0)); self.emit(instrs::jmp(0));
} }
} }
Kind::Die => {
self.emit(instrs::un());
}
Kind::CInt { value } if node.ty.is_float() => { Kind::CInt { value } if node.ty.is_float() => {
self.emit(match node.ty { self.emit(match node.ty {
ty::Id::F32 => instrs::li32( ty::Id::F32 => instrs::li32(
@ -617,8 +620,10 @@ impl ItemCtx {
self.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, (pushed + stack) as _)); 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.relocs.iter_mut().for_each(|r| r.reloc.offset -= stripped_prelude_size as u32);
if sig.ret != ty::Id::NEVER {
self.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0)); self.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0));
} }
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -822,6 +827,10 @@ impl<'a> Function<'a> {
self.add_instr(nid, ops); self.add_instr(nid, ops);
self.emit_node(node.outputs[0], nid); self.emit_node(node.outputs[0], nid);
} }
Kind::Die => {
self.add_instr(nid, vec![]);
self.emit_node(node.outputs[0], nid);
}
Kind::CInt { .. } Kind::CInt { .. }
if node.outputs.iter().all(|&o| { if node.outputs.iter().all(|&o| {
let ond = &self.nodes[o]; let ond = &self.nodes[o];
@ -1167,7 +1176,7 @@ impl regalloc2::Function for Function<'_> {
} }
fn is_ret(&self, insn: regalloc2::Inst) -> bool { 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 { fn is_branch(&self, insn: regalloc2::Inst) -> bool {

View file

@ -0,0 +1,5 @@
main:
UN
code size: 9
ret: 0
status: Err(Unreachable)

View file

@ -75,52 +75,53 @@ push:
MUL64 r2, r36, r37 MUL64 r2, r36, r37
CP r3, r37 CP r3, r37
JAL r31, r0, :malloc JAL r31, r0, :malloc
CP r38, r34 CP r38, r1
ST r36, r38, 16a, 8h CP r39, r34
JNE r1, r35, :3 ST r36, r39, 16a, 8h
CP r1, r35 LI64 r1, 0d
CP r7, r38
JNE r7, r1, :3
JMP :4 JMP :4
3: CP r39, r1 3: CP r38, r7
CP r1, r35 LD r8, r39, 8a, 8h
LD r6, r38, 8a, 8h MULI64 r10, r8, 8d
MULI64 r8, r6, 8d LD r3, r39, 0a, 8h
LD r12, r38, 0a, 8h ADD64 r7, r3, r10
ADD64 r11, r12, r8 CP r5, r38
CP r3, r39 9: LD r2, r39, 0a, 8h
9: LD r2, r38, 0a, 8h LD r10, r39, 8a, 8h
LD r8, r38, 8a, 8h JNE r7, r3, :5
JNE r11, r12, :5 JEQ r10, r35, :6
JEQ r8, r1, :6
CP r4, r37 CP r4, r37
MUL64 r3, r8, r4 MUL64 r3, r10, r4
JAL r31, r0, :free JAL r31, r0, :free
CP r5, r39 CP r6, r38
JMP :7 JMP :7
6: CP r5, r39 6: CP r6, r38
7: ST r5, r38, 0a, 8h 7: ST r6, r39, 0a, 8h
JMP :8 JMP :8
5: CP r4, r37 5: CP r4, r37
CP r5, r39 CP r6, r38
ADDI64 r6, r3, 8d ADDI64 r8, r5, 8d
ADDI64 r7, r12, 8d ADDI64 r9, r3, 8d
LD r8, r12, 0a, 8h LD r10, r3, 0a, 8h
ST r8, r3, 0a, 8h ST r10, r5, 0a, 8h
CP r3, r6 CP r3, r9
CP r12, r7 CP r5, r8
JMP :9 JMP :9
0: CP r38, r34 0: CP r39, r34
8: LD r3, r38, 8a, 8h 8: LD r5, r39, 8a, 8h
MULI64 r5, r3, 8d MULI64 r7, r5, 8d
LD r4, r38, 0a, 8h LD r6, r39, 0a, 8h
ADD64 r1, r4, r5 ADD64 r1, r6, r7
CP r3, r32 CP r3, r32
ST r3, r1, 0a, 8h ST r3, r1, 0a, 8h
LD r11, r38, 8a, 8h LD r2, r39, 8a, 8h
ADD64 r2, r11, r33 ADD64 r3, r2, r33
ST r2, r38, 8a, 8h ST r3, r39, 8a, 8h
4: LD r31, r254, 0a, 72h 4: LD r31, r254, 0a, 72h
ADDI64 r254, r254, 72d ADDI64 r254, r254, 72d
JALA r0, r31, 0a JALA r0, r31, 0a
code size: 945 code size: 955
ret: 69 ret: 69
status: Ok(()) status: Ok(())