forked from AbleOS/holey-bytes
adding stricter typechecking
This commit is contained in:
parent
3a2367f24f
commit
659ccbd637
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 _) })
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue