From b185b0b8b8e26b200c2639428967f8ef8a8f68ff Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 23 Feb 2024 17:45:02 -0600 Subject: [PATCH] fix macos build --- bootstrap/Compilation.zig | 160 +++++++++++++++++++++++++++++++--- bootstrap/backend/llvm.zig | 11 ++- bootstrap/frontend/parser.zig | 52 +++++++++-- bootstrap/main.zig | 9 +- lib/std/builtin.nat | 17 +++- lib/std/os.nat | 9 ++ lib/std/os/macos.nat | 1 + lib/std/start.nat | 6 +- test/tests/main.nat | 2 + 9 files changed, 243 insertions(+), 24 deletions(-) create mode 100644 test/tests/main.nat diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index 5871140..2e3d94e 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -36,6 +36,13 @@ fn reportUnterminatedArgumentError(string: []const u8) noreturn { std.debug.panic("Unterminated argument: {s}", .{string}); } +const Error = struct{ + message: []const u8, + node: Node.Index, +}; + + + pub fn createContext(allocator: Allocator) !*const Context{ const context: *Context = try allocator.create(Context); @@ -69,25 +76,38 @@ pub fn compileBuildExecutable(context: *const Context, arguments: [][:0]u8) !voi .link_libc = @import("builtin").os.tag == .macos, .generate_debug_information = true, .name = "build", + .is_test = false, }, }; try unit.compile(context); + const argv: []const []const u8 = &.{ "nat/build", "-compiler_path", context.executable_absolute_path }; const result = try std.ChildProcess.run(.{ .allocator = context.allocator, - .argv = &.{ "nat/build", "-compiler_path", context.executable_absolute_path }, + .argv = argv, }); - switch (result.term) { - .Exited => |exit_code| { - if (exit_code != 0) @panic("Bad exit code"); - }, - .Signal => @panic("Signaled"), - .Stopped => @panic("Stopped"), - .Unknown => @panic("Unknown"), + + const success = switch (result.term) { + .Exited => |exit_code| exit_code == 0, + else => false, + }; + if (!success) { + std.debug.print("The following command terminated with failure ({s}): {s}\n", .{@tagName(result.term), argv}); + if (result.stdout.len > 0) { + std.debug.print("STDOUT:\n{s}\n", .{result.stdout}); + } + if (result.stderr.len > 0) { + std.debug.print("STDOUT:\n{s}\n", .{result.stderr}); + } + std.os.abort(); } } -pub fn buildExecutable(context: *const Context, arguments: [][:0]u8) !void { +const ExecutableOptions = struct{ + is_test: bool, +}; + +pub fn buildExecutable(context: *const Context, arguments: [][:0]u8, options: ExecutableOptions) !void { var maybe_executable_path: ?[]const u8 = null; var maybe_main_package_path: ?[]const u8 = null; var target_triplet: []const u8 = switch (@import("builtin").os.tag) { @@ -242,6 +262,7 @@ pub fn buildExecutable(context: *const Context, arguments: [][:0]u8) !void { }, .generate_debug_information = generate_debug_information, .name = executable_name, + .is_test = options.is_test, }, }; @@ -577,6 +598,7 @@ pub const Type = union(enum) { }; pub const Instruction = union(enum) { + add_overflow: AddOverflow, argument_declaration: *Debug.Declaration.Argument, branch: Branch, block: Debug.Block.Index, @@ -758,6 +780,12 @@ pub const Instruction = union(enum) { source: V, }; + const AddOverflow = struct{ + left: V, + right: V, + type: Type.Index, + }; + pub const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; @@ -1065,6 +1093,10 @@ pub const Debug = struct { lexed, parsed, }; + + pub fn getPath(file: *File, allocator: Allocator) ![]const u8 { + return try std.mem.concat(allocator, u8, &.{ file.package.directory.path, "/", file.relative_path}); + } }; }; @@ -1074,6 +1106,7 @@ pub const Mutability = enum(u1) { }; pub const IntrinsicId = enum { + add_overflow, assert, @"asm", //this is processed separately as it need special parsing cast, @@ -1085,6 +1118,7 @@ pub const IntrinsicId = enum { size, sign_extend, syscall, + trap, zero_extend, }; @@ -1561,11 +1595,86 @@ pub const Builder = struct { switch (argument_node.id) { .string_literal => { const error_message = try unit.fixupStringLiteral(context, argument_node.token); - std.debug.panic("Compile error: {s}", .{error_message}); + builder.reportCompileError(unit, context, .{ + .message = error_message, + .node = node_index, + }); }, else => |t| @panic(@tagName(t)), } }, + .add_overflow => { + assert(argument_node_list.len == 2); + const left = try builder.resolveRuntimeValue(unit, context, type_expect, argument_node_list[0], .right); + const right_type_expect = switch (type_expect) { + .none => Type.Expect { .type = left.type }, + else => type_expect, + }; + const right = try builder.resolveRuntimeValue(unit, context, right_type_expect, argument_node_list[1], .right); + + const add_overflow = try unit.instructions.append(context.allocator, .{ + .add_overflow = .{ + .left = left, + .right = right, + .type = left.type, + }, + }); + try builder.appendInstruction(unit, context, add_overflow); + + const result_type = try unit.getOptionalType(context, left.type); + + const extract_value = try unit.instructions.append(context.allocator, .{ + .extract_value = .{ + .expression = .{ + .value = .{ + .runtime = add_overflow, + }, + .type = result_type, + }, + .index = 1, + }, + }); + try builder.appendInstruction(unit, context, extract_value); + + const carry = try builder.newBasicBlock(unit, context); + const normal = try builder.newBasicBlock(unit, context); + + try builder.branch(unit, context, extract_value, carry, normal); + builder.current_basic_block = carry; + + try builder.buildRet(unit, context, .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = 1, + }, + }, + }, + .type = left.type, + }); + + builder.current_basic_block = normal; + + const result_extract_value = try unit.instructions.append(context.allocator, .{ + .extract_value = .{ + .expression = .{ + .value = .{ + .runtime = add_overflow, + }, + .type = result_type, + }, + .index = 0, + }, + }); + try builder.appendInstruction(unit, context, result_extract_value); + + return V{ + .value = .{ + .runtime = result_extract_value, + }, + .type = left.type, + }; + }, else => |t| @panic(@tagName(t)), } } @@ -3364,6 +3473,7 @@ pub const Builder = struct { fields: u32 = 0, declarations: u32 = 0, comptime_blocks: u32 = 0, + test_declarations: u32 = 0, } = .{}; for (container_nodes) |member_index| { @@ -3379,6 +3489,7 @@ pub const Builder = struct { .declaration => result.declarations += 1, .field => result.fields += 1, .comptime_block => result.comptime_blocks += 1, + .test_declaration => result.test_declarations += 1, } } @@ -3388,6 +3499,7 @@ pub const Builder = struct { var declaration_nodes = try ArrayList(Node.Index).initCapacity(context.allocator, count.declarations); var field_nodes = try ArrayList(Node.Index).initCapacity(context.allocator, count.fields); var comptime_block_nodes = try ArrayList(Node.Index).initCapacity(context.allocator, count.comptime_blocks); + var test_declarations = try ArrayList(Node.Index).initCapacity(context.allocator, count.test_declarations); for (container_nodes) |member_index| { const member_node = unit.getNode(member_index); @@ -3396,6 +3508,7 @@ pub const Builder = struct { .comptime_block => &comptime_block_nodes, .declaration => &declaration_nodes, .field => &field_nodes, + .test_declaration => &test_declarations, }; array_list.appendAssumeCapacity(member_index); } @@ -3587,6 +3700,10 @@ pub const Builder = struct { } } + if (unit.descriptor.is_test and count.test_declarations > 0) { + unreachable; + } + return type_index; } @@ -5109,6 +5226,11 @@ pub const Builder = struct { break :blk v; }, }, + .integer => { + const v = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.left, .left); + _ = v; + unreachable; + }, else => |t| @panic(@tagName(t)), }, .none => { @@ -7864,6 +7986,16 @@ pub const Builder = struct { try builder.appendInstruction(unit, context, ret); unit.basic_blocks.get(builder.current_basic_block).terminated = true; } + + fn reportCompileError(builder: *Builder, unit: *Unit, context: *const Context, err: Error) noreturn{ + const err_node = unit.getNode(err.node); + const file = unit.files.get(builder.current_file); + const token_debug_info = builder.getTokenDebugInfo(unit, err_node.token); + std.debug.print("{s}:{}:{}: \x1b[31merror:\x1b[0m {s}\n", .{file.getPath(context.allocator) catch unreachable, token_debug_info.line + 1, token_debug_info.column + 1, err.message}); + // std.debug.print("[COMPILATION {s}] ", .{if (compilation_success) "\x1b[32mOK\x1b[0m" else "\x1b[31mFAILED\x1b[0m"}); + // file.relative_path + std.os.abort(); + } }; pub const Enum = struct { @@ -8607,7 +8739,6 @@ pub const FixedKeyword = enum { @"var", void, noreturn, - function, @"while", bool, true, @@ -8628,6 +8759,7 @@ pub const FixedKeyword = enum { @"for", undefined, @"break", + @"test", }; pub const Descriptor = struct { @@ -8636,6 +8768,7 @@ pub const Descriptor = struct { target: std.Target, only_parse: bool, link_libc: bool, + is_test: bool, generate_debug_information: bool, name: []const u8, }; @@ -8649,6 +8782,7 @@ fn getContainerMemberType(member_id: Node.Id) MemberType { .enum_field, .container_field, => .field, + .test_declaration => .test_declaration, else => |t| @panic(@tagName(t)), }; } @@ -8657,6 +8791,7 @@ const MemberType = enum { declaration, field, comptime_block, + test_declaration, }; pub const Token = struct { @@ -8737,7 +8872,6 @@ pub const Token = struct { operator_compare_greater, operator_compare_greater_equal, // Fixed keywords - fixed_keyword_function, fixed_keyword_const, fixed_keyword_var, fixed_keyword_void, @@ -8763,7 +8897,7 @@ pub const Token = struct { fixed_keyword_for, fixed_keyword_undefined, fixed_keyword_break, - unused0, + fixed_keyword_test, unused1, unused2, unused3, diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index 968be85..798dec1 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -2605,7 +2605,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo try assembly_statements.appendSlice(context.allocator, "\n\t"); } - try constraints.appendSlice(context.allocator, ",~{dirflag},~{fpsr},~{flags}"); + // try constraints.appendSlice(context.allocator, ",~{dirflag},~{fpsr},~{flags}"); }, else => |t| @panic(@tagName(t)), } @@ -3090,6 +3090,15 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const intrinsic_call = try llvm.callIntrinsic("llvm.trap", parameter_types, parameter_values); try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, intrinsic_call); }, + .add_overflow => |add_overflow| { + const intrinsic_type = try llvm.getType(unit, context, add_overflow.type); + const parameter_types = [_]*LLVM.Type{intrinsic_type}; + const left = try llvm.emitRightValue(unit, context, add_overflow.left); + const right = try llvm.emitRightValue(unit, context, add_overflow.right); + const arguments = [_]*LLVM.Value{ left, right }; + const intrinsic_call = try llvm.callIntrinsic("llvm.sadd.with.overflow", ¶meter_types, &arguments); + try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, intrinsic_call); + }, else => |t| @panic(@tagName(t)), } } diff --git a/bootstrap/frontend/parser.zig b/bootstrap/frontend/parser.zig index cdae575..c6fabb4 100644 --- a/bootstrap/frontend/parser.zig +++ b/bootstrap/frontend/parser.zig @@ -184,6 +184,9 @@ pub const Node = struct { symbol_attribute_export, symbol_attributes, metadata, + test_declaration, + all_errors, + error_union, }; }; @@ -1372,7 +1375,6 @@ const Analyzer = struct { }); }, .operator_ampersand => try analyzer.pointerOrArrayTypeExpression(.single_pointer_type), - .operator_bang => unreachable, // error .operator_left_bracket => switch (analyzer.peekTokenAhead(1)) { .operator_ampersand => try analyzer.pointerOrArrayTypeExpression(.many_pointer_type), .operator_asterisk => @panic("Meant to use ampersand?"), @@ -1382,12 +1384,37 @@ const Analyzer = struct { } fn errorUnionExpression(analyzer: *Analyzer) !Node.Index { - const suffix_expression = try analyzer.suffixExpression(); + if (analyzer.peekToken() == .operator_bang) { + const error_union_token = try analyzer.expectToken(.operator_bang); + if (analyzer.peekToken() == .operator_asterisk) { + analyzer.consumeToken(); + // All errors + const all_errors_node = try analyzer.addNode(.{ + .id = .all_errors, + .token = error_union_token, + .left = .null, + .right = .null, + }); + const type_node = try analyzer.suffixExpression(); - return switch (analyzer.peekToken()) { - .operator_bang => unreachable, - else => suffix_expression, - }; + const error_union = try analyzer.addNode(.{ + .id = .error_union, + .token = error_union_token, + .left = all_errors_node, + .right = type_node, + }); + return error_union; + } else { + unreachable; + } + } else { + const suffix_expression = try analyzer.suffixExpression(); + + return switch (analyzer.peekToken()) { + .operator_bang => unreachable, + else => suffix_expression, + }; + } } fn suffixExpression(analyzer: *Analyzer) !Node.Index { @@ -1666,6 +1693,7 @@ const Analyzer = struct { } }, .fixed_keyword_const, .fixed_keyword_var => try analyzer.symbolDeclaration(), + .fixed_keyword_test => try analyzer.testDeclaration(), else => |t| @panic(@tagName(t)), }; @@ -1696,6 +1724,18 @@ const Analyzer = struct { }); } + fn testDeclaration(analyzer: *Analyzer) !Node.Index { + const test_token = try analyzer.expectToken(.fixed_keyword_test); + const name_node: Node.Index = if (analyzer.peekToken() == .string_literal) try analyzer.identifierNode() else .null; + const test_block = try analyzer.block(); + return try analyzer.addNode(.{ + .token = test_token, + .id = .test_declaration, + .left = test_block, + .right = name_node, + }); + } + fn primaryTypeExpression(analyzer: *Analyzer) anyerror!Node.Index { const token_i = analyzer.token_i; const token = analyzer.peekToken(); diff --git a/bootstrap/main.zig b/bootstrap/main.zig index e562f00..c63aaeb 100644 --- a/bootstrap/main.zig +++ b/bootstrap/main.zig @@ -42,11 +42,18 @@ pub fn main() !void { todo(); } else if (equal(u8, command, "exe")) { const context = try Compilation.createContext(allocator); - try Compilation.buildExecutable(context, command_arguments); + try Compilation.buildExecutable(context, command_arguments, .{ + .is_test = false, + }); } else if (equal(u8, command, "lib")) { todo(); } else if (equal(u8, command, "obj")) { todo(); + } else if (equal(u8, command, "test")) { + const context = try Compilation.createContext(allocator); + try Compilation.buildExecutable(context, command_arguments, .{ + .is_test = true, + }); } else { todo(); } diff --git a/lib/std/builtin.nat b/lib/std/builtin.nat index 5733d24..abfc005 100644 --- a/lib/std/builtin.nat +++ b/lib/std/builtin.nat @@ -12,10 +12,23 @@ const Cpu = enum{ const Abi = enum{ none, - gnu, - msvc, + gnu, msvc, }; const CallingConvention = enum{ system_v, }; + +const PanicReason = enum{ + integer_overflow, + null_unwrap, +}; + +const panic = fn (reason: PanicReason) noreturn{ + #trap(); +} + +const TestFunction = struct{ + name: []const u8, + function: &const fn () !*void, +}; diff --git a/lib/std/os.nat b/lib/std/os.nat index 9b28aa6..a26005f 100644 --- a/lib/std/os.nat +++ b/lib/std/os.nat @@ -415,6 +415,15 @@ const waitpid = fn(pid: Process.Id, flags: u32) ?u32 { } } }, + .macos => { + var status: s32 = undefined; + if (macos.waitpid(pid, status.&, #cast(flags)) != -1) { + const status_u: u32 = #cast(status); + return status_u; + } else { + return null; + } + }, else => #error("OS not supported"), } diff --git a/lib/std/os/macos.nat b/lib/std/os/macos.nat index 0556d16..78c3701 100644 --- a/lib/std/os/macos.nat +++ b/lib/std/os/macos.nat @@ -52,5 +52,6 @@ const mmap :: extern = fn (address: ?[&]const u8, length: usize, protection_flag const munmap :: extern = fn (address: [&]const u8, length: usize) s32; const execve :: extern = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) s32; const realpath :: extern = fn(path: [&:0]const u8, resolved_path: [&:0]u8) [&:0]u8; +const waitpid :: extern = fn(pid: ProcessId, status: &s32, flags: s32) s32; const _NSGetExecutablePath :: extern = fn (buffer: [&:0]u8, buffer_size: &u32) s32; diff --git a/lib/std/start.nat b/lib/std/start.nat index 53c1f43..2dc8834 100644 --- a/lib/std/start.nat +++ b/lib/std/start.nat @@ -35,6 +35,10 @@ const start :: export = fn (argc_argv_address: usize) noreturn { std.os.exit(exit_code = result); } -const main :: export = fn (argc: s32, argv: [&:null]?[&:null]u8, env: [&:null]?[&:null]u8) s32 { +const main :: export = fn (argc: s32, argv: [&]const [&:0]const u8, env: [&:null]const ?[&:null]const u8) s32 { + const argc_u: u32 = #cast(argc); + argument_count = argc_u; + argument_values = argv; + environment_values = env; return #import("main").main(); } diff --git a/test/tests/main.nat b/test/tests/main.nat new file mode 100644 index 0000000..071e8c6 --- /dev/null +++ b/test/tests/main.nat @@ -0,0 +1,2 @@ +test { +}