diff --git a/src/bootstrap.zig b/src/bootstrap.zig index d4f6803..53df3a0 100644 --- a/src/bootstrap.zig +++ b/src/bootstrap.zig @@ -6124,6 +6124,47 @@ pub const Module = struct { } } + pub fn copy_statement(module: *Module, scope: *Scope, old_statement: *Statement) *Statement { + const new_statement = module.statements.add(); + new_statement.line = old_statement.line; + new_statement.column = old_statement.column; + + new_statement.bb = switch (old_statement.bb) { + .@"return" => |rv| if (rv) |v| .{ + .@"return" = module.clone_value(scope, v), + } else old_statement.bb, + .@"if" => |if_stmt| blk: { + const condition = module.clone_value(scope, if_stmt.condition); + const if_statement = module.copy_statement(scope, if_stmt.if_statement); + const else_statement = if (if_stmt.else_statement) |else_statement| module.copy_statement(scope, else_statement) else null; + break :blk .{ + .@"if" = .{ + .condition = condition, + .if_statement = if_statement, + .else_statement = else_statement, + }, + }; + }, + .block => |block| blk: { + const lexical_block = module.lexical_blocks.add(); + module.copy_block(scope, .{ + .source = block, + .destination = lexical_block, + }); + + break :blk .{ + .block = lexical_block, + }; + }, + .expression => |v| .{ + .expression = module.clone_value(scope, v), + }, + else => @trap(), + }; + + return new_statement; + } + const BlockCopy = struct { source: *LexicalBlock, destination: *LexicalBlock, @@ -6142,20 +6183,12 @@ pub const Module = struct { .parent = parent_scope, }, }; + const scope = &destination.scope; for (source.statements.get_slice()) |old_statement| { - const new_statement = module.statements.add(); - _ = destination.statements.append(new_statement); - new_statement.line = old_statement.line; - new_statement.column = old_statement.column; - - new_statement.bb = switch (old_statement.bb) { - .@"return" => |rv| if (rv) |v| .{ - .@"return" = module.clone_value(scope, v), - } else old_statement.bb, - else => @trap(), - }; + const statement = module.copy_statement(scope, old_statement); + _ = destination.statements.append(statement); } } @@ -6167,6 +6200,12 @@ pub const Module = struct { result.* = .{ .bb = switch (source.bb) { + .unary => |unary| .{ + .unary = .{ + .value = module.clone_value(scope, unary.value), + .id = unary.id, + }, + }, .binary => |binary| .{ .binary = .{ .left = module.clone_value(scope, binary.left), @@ -6203,6 +6242,7 @@ pub const Module = struct { else => @trap(), }, }, + .@"unreachable" => .@"unreachable", else => @trap(), }, .kind = source.kind, @@ -6489,9 +6529,10 @@ pub const Module = struct { } } - module.typecheck(analysis, integer_max_type); + const result_type = if (analysis.type) |et| et else integer_max_type; + module.typecheck(analysis, result_type); - break :blk integer_max_type; + break :blk result_type; }, .int_from_enum => |enum_value| blk: { module.analyze_value_type(enum_value, .{}); @@ -7728,7 +7769,7 @@ pub const Module = struct { max_type.resolve(module); const bit_count = max_type.bb.integer.bit_count; const max_value = if (bit_count == 64) ~@as(u64, 0) else (@as(u64, 1) << @intCast(bit_count - @intFromBool(max_type.bb.integer.signed))) - 1; - const constant_integer = max_type.llvm.abi.?.to_integer().get_constant(max_value, @intFromBool(false)); + const constant_integer = value_type.llvm.abi.?.to_integer().get_constant(max_value, @intFromBool(false)); break :blk constant_integer.to_value(); }, .int_from_enum => |enum_value| blk: { @@ -8193,11 +8234,15 @@ pub const Module = struct { _ = module.llvm.builder.create_branch(macro_entry_block); module.llvm.builder.position_at_end(macro_entry_block); - const macro_return_alloca = module.create_alloca(.{ + const valid_return_value = switch (macro_instantiation.return_type.bb) { + .void, .noreturn => false, + else => true, + }; + + macro_instantiation.return_alloca = if (valid_return_value) module.create_alloca(.{ .type = macro_instantiation.return_type, .name = "macro.return", - }); - macro_instantiation.return_alloca = macro_return_alloca; + }) else undefined; const macro_return_block = module.llvm.context.create_basic_block("macro.return_block", llvm_function); macro_instantiation.return_block = macro_return_block; @@ -8220,14 +8265,21 @@ pub const Module = struct { } module.analyze_block(macro_instantiation.block); + + if (module.llvm.builder.get_insert_block() != null) { + _ = module.llvm.builder.create_branch(macro_return_block); + } + module.llvm.builder.position_at_end(macro_return_block); - const load = module.create_load(.{ - .type = macro_instantiation.return_type, - .value = macro_return_alloca, - .type_kind = type_kind, - }); - + const load = switch (valid_return_value) { + true => module.create_load(.{ + .type = macro_instantiation.return_type, + .value = macro_instantiation.return_alloca, + .type_kind = type_kind, + }), + false => return, + }; break :blk load; }, else => @trap(), @@ -8546,7 +8598,7 @@ pub const Module = struct { if (module.llvm.builder.get_insert_block() != null) { _ = module.llvm.builder.create_branch(loop_continue_block); - } + } module.llvm.builder.position_at_end(loop_continue_block); _ = module.llvm.builder.create_branch(loop_entry_block); @@ -8645,7 +8697,6 @@ pub const Module = struct { const loop_exit_block = module.llvm.context.create_basic_block("foreach.exit", llvm_function); module.exit_block = loop_exit_block; - switch (for_loop.kind) { .slice => { const index_zero = index_type.llvm.abi.?.get_zero().to_value(); @@ -9565,6 +9616,7 @@ pub const Module = struct { @trap(); } }, + .void => ty, else => @trap(), }; diff --git a/src/compiler.bbb b/src/compiler.bbb index 9b3d0f7..c30f9a3 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -81,7 +81,7 @@ O = bits u32 [extern] write = fn [cc(c)] (fd: File, pointer: &u8, byte_count: u64) ssize; [extern] read = fn [cc(c)] (fd: File, pointer: &u8, byte_count: u64) ssize; -assert = fn (ok: u1) void +assert = macro (ok: u1) void { if (!ok) { @@ -660,7 +660,71 @@ is_space = fn (ch: u8) u1 return ch == ' ' or ch == '\n' or ch == '\t' or ch == '\r'; } -module_skip_space = fn (module: &Module) void +is_decimal = fn (ch: u8) u1 +{ + return ch >= '0' and ch <= '9'; +} + +is_octal = fn (ch: u8) u1 +{ + return ch >= '0' and ch <= '7'; +} + +is_binary = fn (ch: u8) u1 +{ + return ch == '0' or ch == '1'; +} + +is_hex_alpha_lower = fn (ch: u8) u1 +{ + return ch >= 'a' and ch <= 'f'; +} + +is_hex_alpha_upper = fn (ch: u8) u1 +{ + return ch >= 'A' and ch <= 'F'; +} + +is_hex_alpha = fn (ch: u8) u1 +{ + return is_hex_alpha_lower(ch) or is_hex_alpha_upper(ch); +} + +is_hex = fn (ch: u8) u1 +{ + return is_decimal(ch) or is_hex_alpha(ch); +} + +is_identifier_start = fn (ch: u8) u1 +{ + return (ch >= 'a' and ch >= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_'; +} + +is_identifier = fn (ch: u8) u1 +{ + return is_identifier_start(ch) or is_decimal(ch); +} + +report_error = fn () noreturn +{ + #trap(); +} + +get_line = fn (module: &Module) u32 +{ + >line = module.line_offset + 1; + assert(line <= #integer_max(u32)); + return #truncate(line); +} + +get_column = fn (module: &Module) u32 +{ + >column = module.offset - module.line_character_offset + 1; + assert(column <= #integer_max(u32)); + return #truncate(column); +} + +skip_space = fn (module: &Module) void { while (1) { @@ -701,11 +765,63 @@ module_skip_space = fn (module: &Module) void } } -module_parse = fn (module: &Module) void +consume_character_if_match = fn (module: &Module, expected_character: u8) u1 +{ + >is_ch: u1 = 0; + + if (module.offset < module.content.length) + { + >ch = module.content[module.offset]; + is_ch = expected_character == ch; + module.offset += #extend(is_ch); + } + + return is_ch; +} + +expect_character = fn (module: &Module, expected_character: u8) void +{ + if (!consume_character_if_match(module, expected_character)) + { + report_error(); + } +} + +parse_identifier = fn (module: &Module) []u8 +{ + >start = module.offset; + + if (is_identifier_start(module.content[start])) + { + module.offset += 1; + + while (module.offset < module.content.length) + { + if (is_identifier(module.content[module.offset])) + { + module.offset += 1; + } + else + { + break; + } + } + } + + if (module.offset - start == 0) + { + report_error(); + } + + >result = module.content[start..module.offset]; + return result; +} + +parse = fn (module: &Module) void { } -module_emit = fn (module: &Module) void +emit = fn (module: &Module) void { } @@ -794,8 +910,8 @@ compile = fn (arena: &Arena, options: CompileOptions) void .line_character_offset = 0, }; - module_parse(&module); - module_emit(&module); + parse(&module); + emit(&module); } compile_file = fn (arena: &Arena, compile_options: CompileFile) void diff --git a/src/main.zig b/src/main.zig index 1d70b63..1f23525 100644 --- a/src/main.zig +++ b/src/main.zig @@ -331,4 +331,5 @@ const names = &[_][]const u8{ "constant_global_reference", "generic_pointer_macro", "break_continue", + "noreturn_macro", }; diff --git a/tests/noreturn_macro.bbb b/tests/noreturn_macro.bbb new file mode 100644 index 0000000..0197244 --- /dev/null +++ b/tests/noreturn_macro.bbb @@ -0,0 +1,27 @@ +assert = macro (ok: u1) void +{ + if (!ok) + { + unreachable; + } +} + +align_forward = fn (value: u64, alignment: u64) u64 +{ + assert(alignment != 0); + >mask = alignment - 1; + >result = (value + mask) & ~mask; + return result; +} + +[export] main = fn [cc(c)] () s32 +{ + >result = align_forward(1, 64); + + if (result != 64) + { + #trap(); + } + + return 0; +}