Skip to content

Commit 1ab023b

Browse files
committed
feat: add null literal and type to solve null pointers properly
1 parent 2240ad0 commit 1ab023b

File tree

11 files changed

+73
-34
lines changed

11 files changed

+73
-34
lines changed

examples/lib/std.lo

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ struct str {
66

77
// constants
88

9-
const NULL = 0 as &void
109
const u32::MAX = 4_294_967_295
1110

1211
global STATIC_DATA_SIZE = @data_size()
@@ -187,9 +186,9 @@ macro heap::Block::next!(&self): &heap::Block {
187186
(self.data!() as u32 + self.size) as &heap::Block
188187
}
189188

190-
global heap::FIRST_BLOCK = NULL as &heap::Block
189+
global heap::FIRST_BLOCK = null as &heap::Block
191190

192-
global heap::CURRENT_BLOCK = NULL as &heap::Block
191+
global heap::CURRENT_BLOCK = null as &heap::Block
193192

194193
macro heap::new!(value: infer T): &T {
195194
let ptr = heap::alloc!<T>()
@@ -209,12 +208,12 @@ macro heap::alloc_many!<T>(count: u32): *&T {
209208
fn heap::alloc(size: u32): &void {
210209
size = mem::align(size)
211210
let free_block = heap::find_free_block(size)
212-
if free_block != NULL {
211+
if free_block != null {
213212
free_block.used = true
214213
return free_block.data!()
215214
}
216215

217-
if heap::CURRENT_BLOCK == NULL {
216+
if heap::CURRENT_BLOCK == null {
218217
stack::check_init!()
219218
heap::CURRENT_BLOCK = (stack::MIN + stack::SIZE) as &heap::Block
220219
heap::FIRST_BLOCK = heap::CURRENT_BLOCK
@@ -251,8 +250,8 @@ fn heap::get_block(ptr: &void): &heap::Block {
251250
// first fit
252251
fn heap::find_free_block(size: u32): &heap::Block {
253252
let block = heap::FIRST_BLOCK
254-
if block == NULL {
255-
return NULL as &heap::Block
253+
if block == null {
254+
return null
256255
}
257256
loop {
258257
if !block.used && block.size >= size {
@@ -263,7 +262,7 @@ fn heap::find_free_block(size: u32): &heap::Block {
263262
}
264263
block = block.next!()
265264
}
266-
return NULL as &heap::Block
265+
return null
267266
}
268267

269268
// arena allocator
@@ -285,7 +284,7 @@ struct Arena::Block {
285284
fn Arena::Block::new(a: Alloc, size: u32): &Arena::Block {
286285
let mem = alloc(a, size)
287286
return new!(a, Arena::Block {
288-
next: NULL as &Arena::Block,
287+
next: null,
289288
size: size,
290289
used: 0,
291290
base: mem,
@@ -344,7 +343,7 @@ fn Arena::reset(&self) {
344343
let block = self.head
345344
loop {
346345
block.used = 0
347-
if block.next == NULL {
346+
if block.next == null {
348347
break
349348
}
350349
block = block.next
@@ -358,7 +357,7 @@ fn Arena::free(&self) {
358357
free(self.alloc, block.base, block.size)
359358
let next_block = block.next
360359
free!<Arena::Block>(self.alloc, block)
361-
if next_block == NULL {
360+
if next_block == null {
362361
break
363362
}
364363
block = next_block

examples/self-hosted/core.lo

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ include "../lib/fs.lo" with extern
33
include "../lib/wasi.lo"
44
include "../lib/print.lo"
55

6+
type VoidError = u32
7+
const VoidError::VALUE = 1 as &VoidError
8+
69
struct Pos {
710
offset: u32,
811
line: u32,
@@ -165,22 +168,22 @@ fn stderr_write(message: str) {
165168
eprint(message)
166169
}
167170

168-
global STDOUT_BUFFER = NULL as &Vec
171+
global STDOUT_BUFFER = null as &Vec
169172
const STDOUT_BUFFER_SIZE = 4096
170173

171174
fn stdout_enable_buffering() {
172175
STDOUT_BUFFER = Vec::with_capacity(STDOUT_BUFFER_SIZE, sizeof u8)
173176
}
174177

175178
fn stdout_disable_buffering() {
176-
if STDOUT_BUFFER != NULL {
179+
if STDOUT_BUFFER != null {
177180
if STDOUT_BUFFER.size != 0 {
178181
let _ = fprint_str(wasi::FD_STDOUT, STDOUT_BUFFER as &str)
179182
}
180183
}
181184

182185
STDOUT_BUFFER.free()
183-
STDOUT_BUFFER = NULL
186+
STDOUT_BUFFER = null
184187
}
185188

186189
fn stdout_writeln(message: str) {
@@ -189,7 +192,7 @@ fn stdout_writeln(message: str) {
189192
}
190193

191194
fn stdout_write(message: str) {
192-
if STDOUT_BUFFER == NULL {
195+
if STDOUT_BUFFER == null {
193196
print(message)
194197
return
195198
}

lo.wasm

508 Bytes
Binary file not shown.

src/ast.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ pub enum CodeExpr {
218218
// literals
219219
BoolLiteral(BoolLiteralExpr),
220220
CharLiteral(CharLiteralExpr),
221+
NullLiteral(NullLiteralExpr),
221222
IntLiteral(IntLiteralExpr),
222223
StringLiteral(StringLiteralExpr),
223224
StructLiteral(StructLiteralExpr),
@@ -272,6 +273,10 @@ pub struct CharLiteralExpr {
272273
pub loc: Loc,
273274
}
274275

276+
pub struct NullLiteralExpr {
277+
pub loc: Loc,
278+
}
279+
275280
pub struct IntLiteralExpr {
276281
pub repr: String,
277282
pub value: u32,
@@ -506,6 +511,7 @@ impl CodeExpr {
506511
match self {
507512
CodeExpr::BoolLiteral(e) => &e.loc,
508513
CodeExpr::CharLiteral(e) => &e.loc,
514+
CodeExpr::NullLiteral(e) => &e.loc,
509515
CodeExpr::IntLiteral(e) => &e.loc,
510516
CodeExpr::StringLiteral(e) => &e.loc,
511517
CodeExpr::ArrayLiteral(e) => &e.loc,

src/compiler.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use core::{cell::RefCell, fmt::Write};
1010
#[derive(Clone, PartialEq)]
1111
pub enum Type {
1212
Never,
13+
Null,
1314
Void,
1415
Bool,
1516
U8,
@@ -61,6 +62,7 @@ impl Type {
6162
fn format(&self, f: &mut core::fmt::Formatter<'_>, compiler: &Compiler) -> core::fmt::Result {
6263
match self {
6364
Type::Never => f.write_str("never"),
65+
Type::Null => f.write_str("null"),
6466
Type::Void => f.write_str("void"),
6567
Type::Bool => f.write_str("bool"),
6668
Type::U8 => f.write_str("u8"),
@@ -1772,6 +1774,9 @@ impl Compiler {
17721774
value: *value as i32,
17731775
});
17741776
}
1777+
CodeExpr::NullLiteral(NullLiteralExpr { loc: _ }) => {
1778+
instrs.push(WasmInstr::I32Const { value: 0 });
1779+
}
17751780
CodeExpr::IntLiteral(IntLiteralExpr {
17761781
repr: _,
17771782
value,
@@ -1828,7 +1833,7 @@ impl Compiler {
18281833
}
18291834

18301835
let field_value_type = self.get_expr_type(ctx, &field_literal.value)?;
1831-
if field_value_type != struct_field.field_type {
1836+
if !self.is_type_compatible(&struct_field.field_type, &field_value_type) {
18321837
return Err(Error {
18331838
message: format!(
18341839
"Invalid type for struct field {}.{}, expected: {}, got: {}",
@@ -2252,8 +2257,7 @@ impl Compiler {
22522257
let lhs_type = self.get_expr_type(ctx, lhs)?;
22532258
let rhs_type = self.get_expr_type(ctx, rhs)?;
22542259

2255-
// a hack to make &T == &void work
2256-
if !self.is_type_compatible(&rhs_type, &lhs_type) {
2260+
if !self.is_type_compatible(&lhs_type, &rhs_type) {
22572261
return Err(Error {
22582262
message: format!(
22592263
"Operands are not of the same type: lhs = {}, rhs = {}",
@@ -2656,7 +2660,7 @@ impl Compiler {
26562660
};
26572661

26582662
let fn_return_type = &self.functions[fn_index].fn_type.output;
2659-
if return_type != *fn_return_type && return_type != Type::Never {
2663+
if !self.is_type_compatible(fn_return_type, &return_type) {
26602664
return Err(Error {
26612665
message: format!(
26622666
"Invalid return type: {}, expected: {}",
@@ -3557,6 +3561,7 @@ impl Compiler {
35573561
}
35583562
Type::U32
35593563
| Type::I32
3564+
| Type::Null
35603565
| Type::Pointer { pointee: _ }
35613566
| Type::SequencePointer { pointee: _ } => {
35623567
if is_store {
@@ -3717,6 +3722,7 @@ impl Compiler {
37173722
match expr {
37183723
CodeExpr::BoolLiteral(_) => Ok(Type::Bool),
37193724
CodeExpr::CharLiteral(_) => Ok(Type::U8),
3725+
CodeExpr::NullLiteral(_) => Ok(Type::Null),
37203726
CodeExpr::IntLiteral(IntLiteralExpr {
37213727
repr: _,
37223728
value: _,
@@ -3881,6 +3887,7 @@ impl Compiler {
38813887
Type::U32 => Ok(Type::I32),
38823888
Type::U64 => Ok(Type::I64),
38833889
Type::Never
3890+
| Type::Null
38843891
| Type::Void
38853892
| Type::Bool
38863893
| Type::I8
@@ -4173,6 +4180,7 @@ impl Compiler {
41734180
match expr {
41744181
CodeExpr::BoolLiteral(_)
41754182
| CodeExpr::CharLiteral(_)
4183+
| CodeExpr::NullLiteral(_)
41764184
| CodeExpr::IntLiteral(_)
41774185
| CodeExpr::StringLiteral(_)
41784186
| CodeExpr::StructLiteral(_)
@@ -5007,15 +5015,15 @@ impl Compiler {
50075015
value_type: &Type,
50085016
) {
50095017
match value_type {
5010-
Type::Never => {}
5011-
Type::Void => {}
5018+
Type::Never | Type::Void => {}
50125019
Type::Bool
50135020
| Type::U8
50145021
| Type::I8
50155022
| Type::U16
50165023
| Type::I16
50175024
| Type::U32
50185025
| Type::I32
5026+
| Type::Null
50195027
| Type::Pointer { pointee: _ }
50205028
| Type::SequencePointer { pointee: _ } => instrs.push(WasmInstr::I32Const { value: 0 }),
50215029
Type::U64 | Type::I64 => instrs.push(WasmInstr::I64Const { value: 0 }),
@@ -5066,6 +5074,10 @@ impl Compiler {
50665074

50675075
fn is_type_compatible(&self, slot: &Type, value: &Type) -> bool {
50685076
if let Type::Pointer { pointee } = slot {
5077+
if *value == Type::Null {
5078+
return true;
5079+
}
5080+
50695081
if **pointee == Type::Void {
50705082
if let Type::Pointer { pointee: _ } = value {
50715083
return true;
@@ -5077,20 +5089,24 @@ impl Compiler {
50775089
}
50785090
}
50795091

5092+
if *value == Type::Never {
5093+
return true;
5094+
}
5095+
50805096
slot == value
50815097
}
50825098

50835099
fn lower_type(&self, lo_type: &Type, wasm_types: &mut Vec<WasmType>) {
50845100
match lo_type {
5085-
Type::Never => {}
5086-
Type::Void => {}
5087-
Type::Bool => wasm_types.push(WasmType::I32),
5088-
Type::U8 => wasm_types.push(WasmType::I32),
5089-
Type::I8 => wasm_types.push(WasmType::I32),
5090-
Type::U16 => wasm_types.push(WasmType::I32),
5091-
Type::I16 => wasm_types.push(WasmType::I32),
5092-
Type::U32 => wasm_types.push(WasmType::I32),
5093-
Type::I32 => wasm_types.push(WasmType::I32),
5101+
Type::Never | Type::Void => {}
5102+
Type::Null
5103+
| Type::Bool
5104+
| Type::U8
5105+
| Type::I8
5106+
| Type::U16
5107+
| Type::I16
5108+
| Type::U32
5109+
| Type::I32 => wasm_types.push(WasmType::I32),
50945110
Type::F32 => wasm_types.push(WasmType::F32),
50955111
Type::U64 => wasm_types.push(WasmType::I64),
50965112
Type::I64 => wasm_types.push(WasmType::I64),
@@ -5141,6 +5157,7 @@ impl Compiler {
51415157
Type::U32
51425158
| Type::I32
51435159
| Type::F32
5160+
| Type::Null
51445161
| Type::Pointer { pointee: _ }
51455162
| Type::SequencePointer { pointee: _ } => {
51465163
layout.primities_count += 1;

src/parser.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,12 @@ impl Parser {
658658
return Ok(CodeExpr::BoolLiteral(BoolLiteralExpr { value: false, loc }));
659659
}
660660

661+
if let Some(_) = self.eat(Symbol, "null") {
662+
let loc = self.prev().loc.clone();
663+
664+
return Ok(CodeExpr::NullLiteral(NullLiteralExpr { loc }));
665+
}
666+
661667
if let Some(char) = self.eat_any(CharLiteral).cloned() {
662668
return Ok(CodeExpr::CharLiteral(CharLiteralExpr {
663669
repr: char.get_value(self.lexer.source).to_string(),
@@ -1417,7 +1423,7 @@ impl Parser {
14171423
Some(self.next())
14181424
}
14191425

1420-
// return current or terminal token
1426+
// returns current or terminal token
14211427
fn current(&self) -> &Token {
14221428
let mut index = *self.tokens_processed.borrow();
14231429
if index >= self.lexer.tokens.len() - 1 {

src/printer.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,9 @@ impl Printer {
474474
}) => {
475475
stdout_write(repr);
476476
}
477+
CodeExpr::NullLiteral(NullLiteralExpr { loc: _ }) => {
478+
stdout_write("null");
479+
}
477480
CodeExpr::IntLiteral(IntLiteralExpr {
478481
repr,
479482
tag,

vscode-ext/lo.vsix

-9.09 KB
Binary file not shown.

vscode-ext/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vscode-ext/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"author": "glebbash",
66
"publisher": "glebbash",
77
"license": "MIT",
8-
"version": "0.0.88",
8+
"version": "0.0.90",
99
"engines": {
1010
"vscode": "^1.88.0"
1111
},

0 commit comments

Comments
 (0)