Merge pull request #214 from birth-software/assert
Implement 'assert' builtin
This commit is contained in:
commit
02de0f1eb3
4
TODOLIST
4
TODOLIST
@ -1,6 +1,4 @@
|
|||||||
arrays
|
|
||||||
assert
|
assert
|
||||||
c abi
|
|
||||||
orelse
|
orelse
|
||||||
size
|
size
|
||||||
trailing zeroes
|
trailing zeroes
|
||||||
@ -10,3 +8,5 @@ returns inside loops (else conditional)
|
|||||||
returns inside loops (non-conditional)
|
returns inside loops (non-conditional)
|
||||||
function pointers
|
function pointers
|
||||||
for loops
|
for loops
|
||||||
|
arrays
|
||||||
|
c abi
|
||||||
|
@ -193,6 +193,38 @@ const Parser = struct{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_intrinsic(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File) void {
|
||||||
|
const src = file.source_code;
|
||||||
|
parser.expect_character(src, '#');
|
||||||
|
|
||||||
|
// TODO: make it more efficient
|
||||||
|
const identifier = parser.parse_raw_identifier(src);
|
||||||
|
if (identifier[0] == '"') {
|
||||||
|
unreachable;
|
||||||
|
} else {
|
||||||
|
const intrinsic_id = inline for (@typeInfo(Intrinsic).Enum.fields) |i_field| {
|
||||||
|
if (byte_equal(i_field.name, identifier)) {
|
||||||
|
break @field(Intrinsic, i_field.name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exit_with_error("Unknown intrinsic");
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (intrinsic_id) {
|
||||||
|
.assert => {
|
||||||
|
const condition = parser.parse_condition(analyzer, thread, file);
|
||||||
|
const assert_true_block = create_basic_block(thread);
|
||||||
|
const assert_false_block = create_basic_block(thread);
|
||||||
|
_ = emit_branch(analyzer, thread, condition, assert_true_block, assert_false_block);
|
||||||
|
analyzer.current_basic_block = assert_false_block;
|
||||||
|
emit_unreachable(analyzer, thread);
|
||||||
|
analyzer.current_basic_block = assert_true_block;
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_raw_identifier(parser: *Parser, file: []const u8) []const u8 {
|
fn parse_raw_identifier(parser: *Parser, file: []const u8) []const u8 {
|
||||||
const identifier_start = parser.i;
|
const identifier_start = parser.i;
|
||||||
const is_string_literal_identifier = file[identifier_start] == '"';
|
const is_string_literal_identifier = file[identifier_start] == '"';
|
||||||
@ -1215,6 +1247,7 @@ const Parser = struct{
|
|||||||
},
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
|
|
||||||
return compare;
|
return compare;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1509,6 +1542,12 @@ const Keyword = enum{
|
|||||||
@"break",
|
@"break",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Intrinsic = enum{
|
||||||
|
assert,
|
||||||
|
trap,
|
||||||
|
@"unreachable",
|
||||||
|
};
|
||||||
|
|
||||||
fn parse_keyword(identifier: []const u8) u32 {
|
fn parse_keyword(identifier: []const u8) u32 {
|
||||||
assert(identifier.len > 0);
|
assert(identifier.len > 0);
|
||||||
if (identifier[0] != '"') {
|
if (identifier[0] != '"') {
|
||||||
@ -1730,6 +1769,7 @@ const Instruction = struct{
|
|||||||
ret,
|
ret,
|
||||||
ret_void,
|
ret_void,
|
||||||
store,
|
store,
|
||||||
|
@"unreachable",
|
||||||
};
|
};
|
||||||
|
|
||||||
const id_to_instruction_map = std.EnumArray(Id, type).init(.{
|
const id_to_instruction_map = std.EnumArray(Id, type).init(.{
|
||||||
@ -1745,6 +1785,7 @@ const Instruction = struct{
|
|||||||
.ret = Return,
|
.ret = Return,
|
||||||
.ret_void = void,
|
.ret_void = void,
|
||||||
.store = Store,
|
.store = Store,
|
||||||
|
.@"unreachable" = Unreachable,
|
||||||
});
|
});
|
||||||
|
|
||||||
fn get_payload(instruction: *Instruction, comptime id: Id) *id_to_instruction_map.get(id) {
|
fn get_payload(instruction: *Instruction, comptime id: Id) *id_to_instruction_map.get(id) {
|
||||||
@ -1846,6 +1887,10 @@ const Return = struct{
|
|||||||
value: *Value,
|
value: *Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Unreachable = struct{
|
||||||
|
instruction: Instruction,
|
||||||
|
};
|
||||||
|
|
||||||
const Import = struct {
|
const Import = struct {
|
||||||
global_declaration: GlobalDeclaration,
|
global_declaration: GlobalDeclaration,
|
||||||
hash: u32,
|
hash: u32,
|
||||||
@ -1891,6 +1936,7 @@ const Thread = struct{
|
|||||||
local_symbols: PinnedArray(LocalSymbol) = .{},
|
local_symbols: PinnedArray(LocalSymbol) = .{},
|
||||||
argument_symbols: PinnedArray(ArgumentSymbol) = .{},
|
argument_symbols: PinnedArray(ArgumentSymbol) = .{},
|
||||||
global_variables: PinnedArray(GlobalVariable) = .{},
|
global_variables: PinnedArray(GlobalVariable) = .{},
|
||||||
|
unreachables: PinnedArray(Unreachable) = .{},
|
||||||
analyzed_file_count: u32 = 0,
|
analyzed_file_count: u32 = 0,
|
||||||
assigned_file_count: u32 = 0,
|
assigned_file_count: u32 = 0,
|
||||||
llvm: struct {
|
llvm: struct {
|
||||||
@ -3231,6 +3277,10 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
|
|||||||
_ = llvm_phi_nodes.append(phi_node);
|
_ = llvm_phi_nodes.append(phi_node);
|
||||||
break :block phi_node.toValue();
|
break :block phi_node.toValue();
|
||||||
},
|
},
|
||||||
|
.@"unreachable" => block: {
|
||||||
|
const ur = builder.createUnreachable();
|
||||||
|
break :block ur.toValue();
|
||||||
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3837,6 +3887,11 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
|
|||||||
parser.i = statement_start_ch_index;
|
parser.i = statement_start_ch_index;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'#' => {
|
||||||
|
parser.parse_intrinsic(analyzer, thread, file);
|
||||||
|
parser.skip_space(src);
|
||||||
|
parser.expect_character(src, ';');
|
||||||
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4518,6 +4573,24 @@ fn typecheck(expected: *Type, have: *Type) TypecheckResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_unreachable(analyzer: *Analyzer, thread: *Thread) void {
|
||||||
|
assert(!analyzer.current_basic_block.is_terminated);
|
||||||
|
const ur = thread.unreachables.append(.{
|
||||||
|
.instruction = .{
|
||||||
|
.value = .{
|
||||||
|
.sema = .{
|
||||||
|
.id = .instruction,
|
||||||
|
.thread = thread.get_index(),
|
||||||
|
.resolved = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.id = .@"unreachable",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
_ = analyzer.current_basic_block.instructions.append(&ur.instruction);
|
||||||
|
analyzer.current_basic_block.is_terminated = true;
|
||||||
|
}
|
||||||
|
|
||||||
const JumpEmission = struct {
|
const JumpEmission = struct {
|
||||||
jump: *Jump,
|
jump: *Jump,
|
||||||
basic_block: *BasicBlock,
|
basic_block: *BasicBlock,
|
||||||
|
14
build.zig
14
build.zig
@ -599,10 +599,22 @@ pub fn build(b: *std.Build) !void {
|
|||||||
b.installArtifact(test_runner);
|
b.installArtifact(test_runner);
|
||||||
test_command.step.dependOn(b.getInstallStep());
|
test_command.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
const new_test = b.addExecutable(.{
|
||||||
|
.name = "new_test",
|
||||||
|
.target = native_target,
|
||||||
|
.root_source_file = b.path("build/new_test.zig"),
|
||||||
|
.optimize = .ReleaseSmall,
|
||||||
|
});
|
||||||
|
b.default_step.dependOn(&new_test.step);
|
||||||
|
|
||||||
|
const new_test_command = b.addRunArtifact(new_test);
|
||||||
|
new_test_command.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
if (b.args) |args| {
|
if (b.args) |args| {
|
||||||
run_command.addArgs(args);
|
run_command.addArgs(args);
|
||||||
debug_command.addArgs(args);
|
debug_command.addArgs(args);
|
||||||
test_command.addArgs(args);
|
test_command.addArgs(args);
|
||||||
|
new_test_command.addArgs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
const run_step = b.step("run", "Test the Nativity compiler");
|
const run_step = b.step("run", "Test the Nativity compiler");
|
||||||
@ -611,6 +623,8 @@ pub fn build(b: *std.Build) !void {
|
|||||||
debug_step.dependOn(&debug_command.step);
|
debug_step.dependOn(&debug_command.step);
|
||||||
const test_step = b.step("test", "Test the Nativity compiler");
|
const test_step = b.step("test", "Test the Nativity compiler");
|
||||||
test_step.dependOn(&test_command.step);
|
test_step.dependOn(&test_command.step);
|
||||||
|
const new_test_step = b.step("new_test", "Script to make a new test");
|
||||||
|
new_test_step.dependOn(&new_test_command.step);
|
||||||
|
|
||||||
const test_all = b.step("test_all", "Test all");
|
const test_all = b.step("test_all", "Test all");
|
||||||
test_all.dependOn(&test_command.step);
|
test_all.dependOn(&test_command.step);
|
||||||
|
17
build/new_test.zig
Normal file
17
build/new_test.zig
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
pub fn main () !void {
|
||||||
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
const allocator = arena.allocator();
|
||||||
|
const args = try std.process.argsAlloc(allocator);
|
||||||
|
if (args.len < 2) return;
|
||||||
|
|
||||||
|
const test_name = args[1];
|
||||||
|
try std.fs.cwd().makeDir(try std.mem.concat(allocator, u8, &.{"retest/standalone/", test_name}));
|
||||||
|
try std.fs.cwd().writeFile(.{
|
||||||
|
.sub_path = try std.mem.concat(allocator, u8, &.{"retest/standalone/", test_name, "/main.nat"}),
|
||||||
|
.data =
|
||||||
|
\\fn[cc(.c)] main[export]() s32 {
|
||||||
|
\\ return 0;
|
||||||
|
\\}
|
||||||
|
});
|
||||||
|
}
|
5
retest/standalone/assert/main.nat
Normal file
5
retest/standalone/assert/main.nat
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
fn[cc(.c)] main[export]() s32 {
|
||||||
|
>n: s32 = 1;
|
||||||
|
#assert(n);
|
||||||
|
return n - n;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user