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
|
||||
c abi
|
||||
orelse
|
||||
size
|
||||
trailing zeroes
|
||||
@ -10,3 +8,5 @@ returns inside loops (else conditional)
|
||||
returns inside loops (non-conditional)
|
||||
function pointers
|
||||
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 {
|
||||
const identifier_start = parser.i;
|
||||
const is_string_literal_identifier = file[identifier_start] == '"';
|
||||
@ -1215,6 +1247,7 @@ const Parser = struct{
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
return compare;
|
||||
}
|
||||
|
||||
@ -1509,6 +1542,12 @@ const Keyword = enum{
|
||||
@"break",
|
||||
};
|
||||
|
||||
const Intrinsic = enum{
|
||||
assert,
|
||||
trap,
|
||||
@"unreachable",
|
||||
};
|
||||
|
||||
fn parse_keyword(identifier: []const u8) u32 {
|
||||
assert(identifier.len > 0);
|
||||
if (identifier[0] != '"') {
|
||||
@ -1730,6 +1769,7 @@ const Instruction = struct{
|
||||
ret,
|
||||
ret_void,
|
||||
store,
|
||||
@"unreachable",
|
||||
};
|
||||
|
||||
const id_to_instruction_map = std.EnumArray(Id, type).init(.{
|
||||
@ -1745,6 +1785,7 @@ const Instruction = struct{
|
||||
.ret = Return,
|
||||
.ret_void = void,
|
||||
.store = Store,
|
||||
.@"unreachable" = Unreachable,
|
||||
});
|
||||
|
||||
fn get_payload(instruction: *Instruction, comptime id: Id) *id_to_instruction_map.get(id) {
|
||||
@ -1846,6 +1887,10 @@ const Return = struct{
|
||||
value: *Value,
|
||||
};
|
||||
|
||||
const Unreachable = struct{
|
||||
instruction: Instruction,
|
||||
};
|
||||
|
||||
const Import = struct {
|
||||
global_declaration: GlobalDeclaration,
|
||||
hash: u32,
|
||||
@ -1891,6 +1936,7 @@ const Thread = struct{
|
||||
local_symbols: PinnedArray(LocalSymbol) = .{},
|
||||
argument_symbols: PinnedArray(ArgumentSymbol) = .{},
|
||||
global_variables: PinnedArray(GlobalVariable) = .{},
|
||||
unreachables: PinnedArray(Unreachable) = .{},
|
||||
analyzed_file_count: u32 = 0,
|
||||
assigned_file_count: u32 = 0,
|
||||
llvm: struct {
|
||||
@ -3231,6 +3277,10 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
|
||||
_ = llvm_phi_nodes.append(phi_node);
|
||||
break :block phi_node.toValue();
|
||||
},
|
||||
.@"unreachable" => block: {
|
||||
const ur = builder.createUnreachable();
|
||||
break :block ur.toValue();
|
||||
},
|
||||
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.parse_intrinsic(analyzer, thread, file);
|
||||
parser.skip_space(src);
|
||||
parser.expect_character(src, ';');
|
||||
},
|
||||
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 {
|
||||
jump: *Jump,
|
||||
basic_block: *BasicBlock,
|
||||
|
14
build.zig
14
build.zig
@ -599,10 +599,22 @@ pub fn build(b: *std.Build) !void {
|
||||
b.installArtifact(test_runner);
|
||||
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| {
|
||||
run_command.addArgs(args);
|
||||
debug_command.addArgs(args);
|
||||
test_command.addArgs(args);
|
||||
new_test_command.addArgs(args);
|
||||
}
|
||||
|
||||
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);
|
||||
const test_step = b.step("test", "Test the Nativity compiler");
|
||||
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");
|
||||
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