diff --git a/lang/README.md b/lang/README.md index e82e146..5edd986 100644 --- a/lang/README.md +++ b/lang/README.md @@ -161,6 +161,27 @@ drop := fn(a: uint): void { } ``` +#### nullable_types +```hb +main := fn(): int { + a := &1 + + b := @as(?^uint, null) + if decide() b = a + + if b == null return 9001 + + c := @as(?uint, *b) + if decide() c = null + + if c != null return 42 + + return 0 +} + +decide := fn(): bool return true +``` + #### structs ```hb Ty := struct { @@ -1196,3 +1217,4 @@ main := fn(): int { opaque := fn(): Foo { return .(3, 2) } +``` diff --git a/lang/src/lib.rs b/lang/src/lib.rs index f76141f..b35648d 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -290,6 +290,7 @@ mod ty { pub type Builtin = u32; pub type Struct = u32; + pub type Opt = u32; pub type Ptr = u32; pub type Func = u32; pub type Global = u32; @@ -373,6 +374,7 @@ mod ty { crate::SymKey::Struct(st.file, st.pos, st.captures) } Kind::Ptr(p) => crate::SymKey::Pointer(&ctx.ptrs[p as usize]), + Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p as usize]), Kind::Func(f) => { let fc = &ctx.funcs[f as usize]; if let Some(base) = fc.base { @@ -441,6 +443,10 @@ mod ty { matches!(self.expand(), Kind::Ptr(_)) || self.is_never() } + pub fn is_optional(self) -> bool { + matches!(self.expand(), Kind::Opt(_)) || self.is_never() + } + pub fn try_upcast(self, ob: Self) -> Option { let (oa, ob) = (Self(self.0.min(ob.0)), Self(self.0.max(ob.0))); let (a, b) = (oa.strip_pointer(), ob.strip_pointer()); @@ -490,9 +496,16 @@ mod ty { pub(crate) fn loc(&self, tys: &Types) -> Loc { match self.expand() { + Kind::Opt(o) + if let ty = tys.ins.opts[o as usize].base + && ty.loc(tys) == Loc::Reg + && (ty.is_pointer() || tys.size_of(ty) < 8) => + { + Loc::Reg + } Kind::Ptr(_) | Kind::Builtin(_) => Loc::Reg, Kind::Struct(_) if tys.size_of(*self) == 0 => Loc::Reg, - Kind::Struct(_) | Kind::Slice(_) => Loc::Stack, + Kind::Struct(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack, Kind::Func(_) | Kind::Global(_) | Kind::Module(_) => unreachable!(), } } @@ -633,6 +646,7 @@ mod ty { pub enum Kind { Builtin, Struct, + Opt, Ptr, Func, Global, @@ -675,6 +689,10 @@ mod ty { f.write_str("]") } TK::Builtin(ty) => f.write_str(to_str(ty)), + TK::Opt(ty) => { + f.write_str("?")?; + self.rety(self.tys.ins.opts[ty as usize].base).fmt(f) + } TK::Ptr(ty) => { f.write_str("^")?; self.rety(self.tys.ins.ptrs[ty as usize].base).fmt(f) @@ -730,6 +748,7 @@ type Size = u32; #[derive(PartialEq, Eq, Hash, Clone, Copy)] pub enum SymKey<'a> { Pointer(&'a Ptr), + Optional(&'a Opt), Struct(FileId, Pos, ty::Tuple), FuncInst(ty::Func, ty::Tuple), Decl(FileId, Ident), @@ -840,7 +859,12 @@ struct Struct { field_start: u32, } -#[derive(PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub struct Opt { + base: ty::Id, +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] pub struct Ptr { base: ty::Id, } @@ -935,6 +959,7 @@ pub struct TypeIns { structs: Vec, fields: Vec, ptrs: Vec, + opts: Vec, slices: Vec, } @@ -1065,6 +1090,10 @@ trait TypeParser { let base = self.parse_ty(file, val, None, files); self.tys().make_ptr(base) } + Expr::UnOp { op: TokenKind::Que, val, .. } => { + let base = self.parse_ty(file, val, None, files); + self.tys().make_opt(base) + } Expr::Ident { id, .. } if id.is_null() => id.len().into(), Expr::Ident { id, pos, .. } => self.find_type(pos, file, file, Ok(id), files), Expr::Field { target, pos, name } @@ -1206,64 +1235,44 @@ impl Types { (ret, iter) } + fn make_opt(&mut self, base: ty::Id) -> ty::Id { + self.make_generic_ty( + Opt { base }, + |ins| &mut ins.opts, + |e| SymKey::Optional(e), + ty::Kind::Opt, + ) + } + fn make_ptr(&mut self, base: ty::Id) -> ty::Id { - ty::Kind::Ptr(self.make_ptr_low(base)).compress() + self.make_generic_ty( + Ptr { base }, + |ins| &mut ins.ptrs, + |e| SymKey::Pointer(e), + ty::Kind::Ptr, + ) } - fn make_ptr_low(&mut self, base: ty::Id) -> ty::Ptr { - let ptr = Ptr { base }; - let (entry, hash) = self.syms.entry(SymKey::Pointer(&ptr), &self.ins); - match entry { - hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.value, - hash_map::RawEntryMut::Vacant(v) => { - self.ins.ptrs.push(ptr); - v.insert( - ctx_map::Key { - value: ty::Kind::Ptr(self.ins.ptrs.len() as u32 - 1).compress(), - hash, - }, - (), - ) - .0 - .value - } - } - .expand() - .inner() + fn make_array(&mut self, elem: ty::Id, len: ArrayLen) -> ty::Id { + self.make_generic_ty( + Array { elem, len }, + |ins| &mut ins.slices, + |e| SymKey::Array(e), + ty::Kind::Slice, + ) } - fn make_array(&mut self, ty: ty::Id, len: ArrayLen) -> ty::Id { - ty::Kind::Slice(self.make_array_low(ty, len)).compress() - } - - fn make_array_low(&mut self, ty: ty::Id, len: ArrayLen) -> ty::Slice { - self.syms - .get_or_insert(SymKey::Array(&Array { elem: ty, len }), &mut self.ins, |ins| { - ins.slices.push(Array { elem: ty, len }); - ty::Kind::Slice(ins.slices.len() as u32 - 1).compress() - }) - .expand() - .inner() - - //let array = Array { ty, len }; - //let (entry, hash) = self.syms.entry(SymKey::Array(&array), &self.ins); - //match entry { - // hash_map::RawEntryMut::Occupied(o) => o.get_key_value().0.value, - // hash_map::RawEntryMut::Vacant(v) => { - // self.ins.arrays.push(array); - // v.insert( - // ctx_map::Key { - // value: ty::Kind::Slice(self.ins.ptrs.len() as u32 - 1).compress(), - // hash, - // }, - // (), - // ) - // .0 - // .value - // } - //} - //.expand() - //.inner() + fn make_generic_ty( + &mut self, + ty: T, + get_col: fn(&mut TypeIns) -> &mut Vec, + key: fn(&T) -> SymKey, + kind: fn(u32) -> ty::Kind, + ) -> ty::Id { + *self.syms.get_or_insert(key(&{ ty }), &mut self.ins, |ins| { + get_col(ins).push(ty); + kind(get_col(ins).len() as u32 - 1).compress() + }) } fn size_of(&self, ty: ty::Id) -> Size { @@ -1328,6 +1337,20 @@ impl Types { } } + fn inner_of(&self, ty: ty::Id) -> Option { + match ty.expand() { + ty::Kind::Opt(o) => Some(self.ins.opts[o as usize].base), + _ => None, + } + } + + fn opt_flag_field(&self, ty: ty::Id) -> (Offset, ty::Id) { + match ty.expand() { + ty::Kind::Ptr(_) => (0, ty::Id::UINT), + _ => todo!("{ty:?}"), + } + } + fn find_struct_field(&self, s: ty::Struct, name: &str) -> Option { let name = self.names.project(name)?; self.struct_fields(s).iter().position(|f| f.name == name) diff --git a/lang/src/parser.rs b/lang/src/parser.rs index 91de4b5..998e7e3 100644 --- a/lang/src/parser.rs +++ b/lang/src/parser.rs @@ -443,7 +443,7 @@ impl<'a, 'b> Parser<'a, 'b> { pos }, }, - T::Band | T::Mul | T::Xor | T::Sub => E::UnOp { + T::Band | T::Mul | T::Xor | T::Sub | T::Que => E::UnOp { pos, op: token.kind, val: { diff --git a/lang/src/son.rs b/lang/src/son.rs index 128fb74..92b664c 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -654,6 +654,14 @@ impl Nodes { } } + fn new_const(&mut self, ty: ty::Id, value: impl Into) -> Nid { + self.new_node_nop(ty, Kind::CInt { value: value.into() }, [VOID]) + } + + fn new_const_lit(&mut self, ty: ty::Id, value: impl Into) -> Value { + self.new_node_lit(ty, Kind::CInt { value: value.into() }, [VOID]) + } + fn new_node_lit(&mut self, ty: ty::Id, kind: Kind, inps: impl Into) -> Value { Value::new(self.new_node(ty, kind, inps)).ty(ty) } @@ -772,18 +780,14 @@ impl Nodes { if let (&K::CInt { value: a }, &K::CInt { value: b }) = (&self[lhs].kind, &self[rhs].kind) { - return Some(self.new_node( - ty, - K::CInt { value: op.apply_binop(a, b, is_float) }, - [ctrl], - )); + return Some(self.new_const(ty, op.apply_binop(a, b, is_float))); } if lhs == rhs { match op { - T::Sub => return Some(self.new_node(ty, K::CInt { value: 0 }, [ctrl])), + T::Sub => return Some(self.new_const(ty, 0)), T::Add => { - let rhs = self.new_node_nop(ty, K::CInt { value: 2 }, [ctrl]); + let rhs = self.new_const(ty, 2); return Some( self.new_node(ty, K::BinOp { op: T::Mul }, [ctrl, lhs, rhs]), ); @@ -813,11 +817,7 @@ impl Nodes { && let K::CInt { value: bv } = self[rhs].kind { // (a op #b) op #c => a op (#b op #c) - let new_rhs = self.new_node_nop( - ty, - K::CInt { value: op.apply_binop(av, bv, is_float) }, - [ctrl], - ); + let new_rhs = self.new_const(ty, op.apply_binop(av, bv, is_float)); return Some(self.new_node(ty, K::BinOp { op }, [ctrl, a, new_rhs])); } @@ -834,7 +834,7 @@ impl Nodes { && let K::CInt { value } = self[self[lhs].inputs[2]].kind { // a * #n + a => a * (#n + 1) - let new_rhs = self.new_node_nop(ty, K::CInt { value: value + 1 }, [ctrl]); + let new_rhs = self.new_const(ty, value + 1); return Some(self.new_node(ty, K::BinOp { op: T::Mul }, [ctrl, rhs, new_rhs])); } @@ -843,7 +843,7 @@ impl Nodes { && let K::CInt { value: a } = self[rhs].kind && let K::CInt { value: b } = self[self[lhs].inputs[2]].kind { - let new_rhs = self.new_node_nop(ty, K::CInt { value: b - a }, [ctrl]); + let new_rhs = self.new_const(ty, b - a); return Some(self.new_node(ty, K::BinOp { op: T::Add }, [ ctrl, self[lhs].inputs[1], @@ -864,17 +864,13 @@ impl Nodes { } } K::UnOp { op } => { - let &[ctrl, oper] = self[target].inputs.as_slice() else { unreachable!() }; + let &[_, oper] = self[target].inputs.as_slice() else { unreachable!() }; let ty = self[target].ty; let is_float = self[oper].ty.is_float(); if let K::CInt { value } = self[oper].kind { - return Some(self.new_node( - ty, - K::CInt { value: op.apply_unop(value, is_float) }, - [ctrl], - )); + return Some(self.new_const(ty, op.apply_unop(value, is_float))); } } K::If => { @@ -2307,21 +2303,31 @@ impl<'a> Codegen<'a> { // ordered by complexity of the expression match *expr { Expr::Null { pos } => { - inference!(ty, ctx, self, pos, "null pointer", "@as(^, null)"); + inference!(oty, ctx, self, pos, "null pointer", "@as(^, null)"); - if !ty.is_pointer() { + let Some(ty) = self.tys.inner_of(oty) else { self.report( pos, fa!( "'null' expression was inferred to be '{}', - which is not a pointer (and that is not supported yet)", - self.ty_display(ty) + which is not optional", + self.ty_display(oty) ), ); return Value::NEVER; - } + }; - Some(self.ci.nodes.new_node_lit(ty, Kind::CInt { value: 0 }, [VOID])) + 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 stack = self.new_stack(oty); + let offset = self.offset(stack, off); + let value = self.ci.nodes.new_const(flag_ty, 0); + self.store_mem(offset, flag_ty, value); + Some(Value::ptr(stack).ty(oty)) + } + } } Expr::Idk { pos } => { inference!(ty, ctx, self, pos, "value", "@as(, idk)"); @@ -2340,20 +2346,14 @@ impl<'a> Codegen<'a> { Value::NEVER } } - Expr::Bool { value, .. } => Some(self.ci.nodes.new_node_lit( - ty::Id::BOOL, - Kind::CInt { value: value as i64 }, - [VOID], - )), - Expr::Number { value, .. } => Some(self.ci.nodes.new_node_lit( + 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), - Kind::CInt { value }, - [VOID], + value, )), - Expr::Float { value, .. } => Some(self.ci.nodes.new_node_lit( + Expr::Float { value, .. } => Some(self.ci.nodes.new_const_lit( ctx.ty.filter(|ty| ty.is_float()).unwrap_or(ty::Id::F32), - Kind::CInt { value: value as _ }, - [VOID], + value as i64, )), Expr::Ident { id, .. } if let Some(index) = self.ci.scope.vars.iter().rposition(|v| v.id == id) => @@ -2551,11 +2551,7 @@ impl<'a> Codegen<'a> { if val.ty.is_integer() { Some(self.ci.nodes.new_node_lit(val.ty, Kind::UnOp { op }, [VOID, val.id])) } else if val.ty.is_float() { - let value = self.ci.nodes.new_node_nop( - val.ty, - Kind::CInt { value: (-1f64).to_bits() as _ }, - [VOID], - ); + let value = self.ci.nodes.new_const(val.ty, (-1f64).to_bits() as i64); Some(self.ci.nodes.new_node_lit(val.ty, Kind::BinOp { op: TokenKind::Mul }, [ VOID, val.id, value, ])) @@ -2669,8 +2665,7 @@ impl<'a> Codegen<'a> { let elem = self.tys.ins.slices[s as usize].elem; let mut idx = self.expr_ctx(index, Ctx::default().with_ty(ty::Id::DEFAULT_INT))?; self.assert_ty(index.pos(), &mut idx, ty::Id::DEFAULT_INT, "subscript"); - let value = self.tys.size_of(elem) as i64; - let size = self.ci.nodes.new_node_nop(ty::Id::INT, Kind::CInt { value }, [VOID]); + let size = self.ci.nodes.new_const(ty::Id::INT, self.tys.size_of(elem)); let inps = [VOID, idx.id, size]; let offset = self.ci.nodes.new_node(ty::Id::INT, Kind::BinOp { op: TokenKind::Mul }, inps); @@ -2690,18 +2685,16 @@ impl<'a> Codegen<'a> { } Expr::Directive { name: "sizeof", args: [ty], .. } => { let ty = self.ty(ty); - Some(self.ci.nodes.new_node_lit( + Some(self.ci.nodes.new_const_lit( ctx.ty.filter(|ty| ty.is_integer()).unwrap_or(ty::Id::DEFAULT_INT), - Kind::CInt { value: self.tys.size_of(ty) as _ }, - [VOID], + self.tys.size_of(ty), )) } Expr::Directive { name: "alignof", args: [ty], .. } => { let ty = self.ty(ty); - Some(self.ci.nodes.new_node_lit( + Some(self.ci.nodes.new_const_lit( ctx.ty.filter(|ty| ty.is_integer()).unwrap_or(ty::Id::DEFAULT_INT), - Kind::CInt { value: self.tys.align_of(ty) as _ }, - [VOID], + self.tys.align_of(ty), )) } Expr::Directive { name: "bitcast", args: [val], pos } => { @@ -3166,8 +3159,8 @@ impl<'a> Codegen<'a> { } } Expr::Struct { .. } => { - let value = self.ty(expr).repr() as i64; - Some(self.ci.nodes.new_node_lit(ty::Id::TYPE, Kind::CInt { value }, [VOID])) + let value = self.ty(expr).repr(); + Some(self.ci.nodes.new_const_lit(ty::Id::TYPE, value)) } Expr::Ctor { pos, ty, fields, .. } => { ctx.ty = ty.map(|ty| self.ty(ty)).or(ctx.ty); @@ -3729,7 +3722,7 @@ impl<'a> Codegen<'a> { return val; } - let off = self.ci.nodes.new_node_nop(ty::Id::INT, Kind::CInt { value: off as i64 }, [VOID]); + let off = self.ci.nodes.new_const(ty::Id::INT, off); let (aclass, mem) = self.ci.nodes.aclass_index(val); let inps = [VOID, val, off]; let seted = self.ci.nodes.new_node(ty::Id::INT, Kind::BinOp { op: TokenKind::Add }, inps); @@ -3914,9 +3907,7 @@ impl<'a> Codegen<'a> { if matches!(op, TokenKind::Add | TokenKind::Sub) && let Some(elem) = self.tys.base_of(upcasted) { - let value = self.tys.size_of(elem) as i64; - let cnst = - self.ci.nodes.new_node_nop(ty::Id::INT, Kind::CInt { value }, [VOID]); + let cnst = self.ci.nodes.new_const(ty::Id::INT, self.tys.size_of(elem)); oper.id = self.ci.nodes.new_node(upcasted, Kind::BinOp { op: TokenKind::Mul }, [ VOID, oper.id, cnst, @@ -3975,9 +3966,8 @@ impl<'a> Codegen<'a> { fn extend(&mut self, value: &mut Value, to: ty::Id) { self.strip_ptr(value); - let val = (1i64 << (self.tys.size_of(value.ty) * 8)) - 1; value.ty = to; - let mask = self.ci.nodes.new_node_nop(to, Kind::CInt { value: val }, [VOID]); + 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); } @@ -4137,6 +4127,7 @@ mod tests { variables; loops; pointers; + nullable_types; structs; hex_octal_binary_literals; struct_operators; diff --git a/lang/tests/son_tests_nullable_types.txt b/lang/tests/son_tests_nullable_types.txt new file mode 100644 index 0000000..e69de29