adding stricter typechecking

This commit is contained in:
Jakub Doka 2024-10-13 14:11:17 +02:00
parent 3a2367f24f
commit 659ccbd637
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
5 changed files with 66 additions and 77 deletions

View file

@ -388,56 +388,6 @@ main := fn(major: int, minor: int): OemIdent {
### Incomplete Examples ### Incomplete Examples
#### wired_mem_swap
```hb
Color := struct {x: int}
put_trisquare := fn(pos: Vec2(int), size: Vec2(int), color: Color): void {
step := Vec2(int).(1, 1)
if size.x < 0 {
step.x = -1
}
if size.y < 0 {
step.y = -1
}
target := pos + size
loop if pos.x == target.x break else {
put_vline(pos.x, pos.y, target.y, color)
pos.x += step.x
}
return
}
put_vline := fn(x: int, y0: int, y1: int, color: Color): void {
return
}
Vec2 := fn($Expr: type): type {
return struct {x: Expr, y: Expr}
}
MemSwap := fn($Expr: type): type {
return struct {a: Expr, b: Expr}
}
memswap := fn($Expr: type, a: ^Expr, b: ^Expr): void {
MemSwap(Expr).(b, a) = MemSwap(Expr).(*a, *b)
return
}
main := fn(): int {
put_trisquare(.(0, 0), .(0, 0), .(0))
a := 10
b := 50
//memswap(int, a, b)
return a
}
```
#### comptime_pointers #### comptime_pointers
```hb ```hb
main := fn(): int { main := fn(): int {

View file

@ -7,7 +7,8 @@ use {
parser::{ parser::{
self, find_symbol, idfl, CommentOr, CtorField, Expr, ExprRef, FileId, Pos, StructField, self, find_symbol, idfl, CommentOr, CtorField, Expr, ExprRef, FileId, Pos, StructField,
}, },
ty, Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey, ty::{self, TyCheck},
Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey,
TypedReloc, Types, HEADER_SIZE, TypedReloc, Types, HEADER_SIZE,
}, },
alloc::{boxed::Box, string::String, vec::Vec}, alloc::{boxed::Box, string::String, vec::Vec},
@ -607,6 +608,7 @@ struct FTask {
struct Ctx { struct Ctx {
loc: Option<Loc>, loc: Option<Loc>,
ty: Option<ty::Id>, ty: Option<ty::Id>,
check: TyCheck,
} }
impl Ctx { impl Ctx {
@ -618,6 +620,10 @@ impl Ctx {
Self { ty: Some(ty.into()), ..self } Self { ty: Some(ty.into()), ..self }
} }
pub fn with_check(self, check: TyCheck) -> Self {
Self { check, ..self }
}
fn into_value(self) -> Option<Value> { fn into_value(self) -> Option<Value> {
Some(Value { ty: self.ty.unwrap(), loc: self.loc? }) Some(Value { ty: self.ty.unwrap(), loc: self.loc? })
} }
@ -625,7 +631,7 @@ impl Ctx {
impl From<Value> for Ctx { impl From<Value> for Ctx {
fn from(value: Value) -> Self { fn from(value: Value) -> Self {
Self { loc: Some(value.loc), ty: Some(value.ty) } Self { loc: Some(value.loc), ty: Some(value.ty), ..Default::default() }
} }
} }
@ -839,7 +845,13 @@ impl Codegen {
base_val.loc = self.make_loc_owned(base_val.loc, base_val.ty); base_val.loc = self.make_loc_owned(base_val.loc, base_val.ty);
} }
let index_val = self.expr(index)?; let index_val = self.expr(index)?;
_ = self.assert_ty(index.pos(), index_val.ty, ty::Id::INT, "subsctipt"); _ = self.assert_ty(
index.pos(),
index_val.ty,
ty::Id::INT,
TyCheck::BinOp,
"subsctipt",
);
if let Some(ty) = self.tys.base_of(base_val.ty) { if let Some(ty) = self.tys.base_of(base_val.ty) {
base_val.ty = ty; base_val.ty = ty;
@ -1179,12 +1191,6 @@ impl Codegen {
let loc = loc.as_ref().offset(offset); let loc = loc.as_ref().offset(offset);
let ctx = Ctx::default().with_loc(loc).with_ty(ty); let ctx = Ctx::default().with_loc(loc).with_ty(ty);
let value = self.expr_ctx(field, ctx)?; let value = self.expr_ctx(field, ctx)?;
std::println!(
"{} {} {}",
self.ty_display(ty),
self.ty_display(value.ty),
self.ast_display(field)
);
self.ci.free_loc(value.loc); self.ci.free_loc(value.loc);
} }
} }
@ -1290,7 +1296,6 @@ impl Codegen {
} }
E::UnOp { op: T::Xor, val, .. } => { E::UnOp { op: T::Xor, val, .. } => {
let val = self.ty(val); let val = self.ty(val);
let ptr = self.tys.make_ptr(val);
Some(Value::ty(self.tys.make_ptr(val))) Some(Value::ty(self.tys.make_ptr(val)))
} }
E::UnOp { op: T::Band, val, pos } => { E::UnOp { op: T::Band, val, pos } => {
@ -1384,7 +1389,13 @@ impl Codegen {
// TODO: pass the arg as dest // TODO: pass the arg as dest
let varg = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; let varg = self.expr_ctx(arg, Ctx::default().with_ty(ty))?;
_ = self.assert_ty(arg.pos(), varg.ty, ty, format_args!("argument({i})")); _ = self.assert_ty(
arg.pos(),
varg.ty,
ty,
TyCheck::Assign,
format_args!("argument({i})"),
);
self.pass_arg(&varg, &mut parama); self.pass_arg(&varg, &mut parama);
self.pool.arg_locs.push(varg.loc); self.pool.arg_locs.push(varg.loc);
should_momize = false; should_momize = false;
@ -1443,14 +1454,16 @@ impl Codegen {
_ => Some(Loc::reg(self.ci.ret_reg.as_ref()).into_derefed()), _ => Some(Loc::reg(self.ci.ret_reg.as_ref()).into_derefed()),
}; };
let value = if let Some(val) = val { let value = if let Some(val) = val {
self.expr_ctx(val, Ctx { ty: self.ci.ret, loc })? self.expr_ctx(val, Ctx { ty: self.ci.ret, loc, ..Default::default() })?
} else { } else {
Value::void() Value::void()
}; };
match self.ci.ret { match self.ci.ret {
None => self.ci.ret = Some(value.ty), None => self.ci.ret = Some(value.ty),
Some(ret) => _ = self.assert_ty(pos, value.ty, ret, "return type"), Some(ret) => {
_ = self.assert_ty(pos, value.ty, ret, TyCheck::Assign, "return type")
}
} }
self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4)); self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4));
@ -1613,7 +1626,13 @@ impl Codegen {
if let ty::Kind::Struct(_) = left.ty.expand() { if let ty::Kind::Struct(_) = left.ty.expand() {
let right = self.expr_ctx(right, Ctx::default().with_ty(left.ty))?; let right = self.expr_ctx(right, Ctx::default().with_ty(left.ty))?;
_ = self.assert_ty(expr.pos(), right.ty, left.ty, "right struct operand"); _ = self.assert_ty(
expr.pos(),
right.ty,
left.ty,
TyCheck::Assign,
"right struct operand",
);
return self.struct_op(op, left.ty, ctx, left.loc, right.loc); return self.struct_op(op, left.ty, ctx, left.loc, right.loc);
} }
@ -1636,10 +1655,17 @@ impl Codegen {
let lhs = self.loc_to_reg(left.loc, lsize); let lhs = self.loc_to_reg(left.loc, lsize);
(lhs.as_ref(), lhs, Loc::default()) (lhs.as_ref(), lhs, Loc::default())
}; };
let right = self.expr_ctx(right, Ctx::default().with_ty(left.ty))?; let right = self
.expr_ctx(right, Ctx::default().with_ty(left.ty).with_check(TyCheck::BinOp))?;
let rsize = self.tys.size_of(right.ty); let rsize = self.tys.size_of(right.ty);
let ty = self.assert_ty(expr.pos(), right.ty, left.ty, "right sclalar operand"); let ty = self.assert_ty(
expr.pos(),
right.ty,
left.ty,
TyCheck::BinOp,
"right sclalar operand",
);
let size = self.tys.size_of(ty); let size = self.tys.size_of(ty);
let signed = ty.is_signed(); let signed = ty.is_signed();
@ -1733,7 +1759,7 @@ impl Codegen {
}?; }?;
if let Some(ty) = ctx.ty { if let Some(ty) = ctx.ty {
_ = self.assert_ty(expr.pos(), value.ty, ty, "something"); _ = self.assert_ty(expr.pos(), value.ty, ty, ctx.check, "something");
} }
Some(match ctx.loc { Some(match ctx.loc {
@ -1870,7 +1896,7 @@ impl Codegen {
s.ci.ret_reg = reg; s.ci.ret_reg = reg;
}; };
let ctx = Ctx { loc: None, ty: s.ci.ret }; let ctx = Ctx { ty: s.ci.ret, ..Default::default() };
if s.expr_ctx(&Expr::Return { pos: 0, val: Some(expr) }, ctx).is_some() { if s.expr_ctx(&Expr::Return { pos: 0, val: Some(expr) }, ctx).is_some() {
s.report(expr.pos(), "we fucked up"); s.report(expr.pos(), "we fucked up");
}; };
@ -1956,7 +1982,7 @@ impl Codegen {
}; };
if let Some(expected) = ctx.ty { if let Some(expected) = ctx.ty {
_ = self.assert_ty(pos, ty, expected, "struct"); _ = self.assert_ty(pos, ty, expected, TyCheck::Assign, "struct");
} }
match ty.expand() { match ty.expand() {
@ -2704,8 +2730,15 @@ impl Codegen {
#[must_use] #[must_use]
#[track_caller] #[track_caller]
fn assert_ty(&self, pos: Pos, ty: ty::Id, expected: ty::Id, hint: impl Display) -> ty::Id { fn assert_ty(
if let Some(res) = ty.try_upcast(expected) { &self,
pos: Pos,
ty: ty::Id,
expected: ty::Id,
kind: TyCheck,
hint: impl Display,
) -> ty::Id {
if let Some(res) = ty.try_upcast(expected, kind) {
res res
} else { } else {
let dty = self.ty_display(ty); let dty = self.ty_display(ty);
@ -2822,7 +2855,6 @@ mod tests {
struct_return_from_module_function; struct_return_from_module_function;
//comptime_pointers; //comptime_pointers;
sort_something_viredly; sort_something_viredly;
wired_mem_swap;
hex_octal_binary_literals; hex_octal_binary_literals;
//comptime_min_reg_leak; //comptime_min_reg_leak;
// structs_in_registers; // structs_in_registers;

View file

@ -4,7 +4,7 @@ use {
lexer::{self, Lexer, TokenKind}, lexer::{self, Lexer, TokenKind},
parser::{self, CommentOr, CtorField, Expr, Poser, Radix, StructField}, parser::{self, CommentOr, CtorField, Expr, Poser, Radix, StructField},
}, },
core::{fmt, usize}, core::fmt,
}; };
pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str { pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {

View file

@ -249,7 +249,7 @@ mod ty {
lexer::TokenKind, lexer::TokenKind,
parser::{self, Pos}, parser::{self, Pos},
}, },
core::{fmt::Write, num::NonZeroU32, ops::Range}, core::{default, num::NonZeroU32, ops::Range},
}; };
pub type ArrayLen = u32; pub type ArrayLen = u32;
@ -355,7 +355,7 @@ mod ty {
matches!(Kind::from_ty(self), Kind::Ptr(_)) matches!(Kind::from_ty(self), Kind::Ptr(_))
} }
pub fn try_upcast(self, ob: Self) -> Option<Self> { pub fn try_upcast(self, ob: Self, kind: TyCheck) -> Option<Self> {
let (oa, ob) = (Self(self.0.min(ob.0)), Self(self.0.max(ob.0))); let (oa, ob) = (Self(self.0.min(ob.0)), Self(self.0.max(ob.0)));
let (a, b) = (oa.strip_pointer(), ob.strip_pointer()); let (a, b) = (oa.strip_pointer(), ob.strip_pointer());
Some(match () { Some(match () {
@ -365,7 +365,7 @@ mod ty {
_ if oa.is_pointer() && ob.is_pointer() => return None, _ 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_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 => ob,
_ if oa.is_integer() && ob.is_pointer() => ob, _ if oa.is_integer() && ob.is_pointer() && kind == TyCheck::BinOp => ob,
_ => return None, _ => return None,
}) })
} }
@ -384,6 +384,13 @@ mod ty {
} }
} }
#[derive(PartialEq, Eq, Default, Debug)]
pub enum TyCheck {
BinOp,
#[default]
Assign,
}
impl From<u64> for Id { impl From<u64> for Id {
fn from(id: u64) -> Self { fn from(id: u64) -> Self {
Self(unsafe { NonZeroU32::new_unchecked(id as _) }) Self(unsafe { NonZeroU32::new_unchecked(id as _) })

View file

@ -2140,7 +2140,7 @@ impl Codegen {
preserve_expected: bool, preserve_expected: bool,
hint: impl fmt::Display, hint: impl fmt::Display,
) -> ty::Id { ) -> ty::Id {
if let Some(res) = ty.try_upcast(expected) if let Some(res) = ty.try_upcast(expected, ty::TyCheck::BinOp)
&& (!preserve_expected || res == expected) && (!preserve_expected || res == expected)
{ {
res res