diff --git a/src/astgen.zig b/src/astgen.zig index deb4eb6ae9..3db3f3654d 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -2324,6 +2324,15 @@ fn import(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!* return addZIRUnOp(mod, scope, src, .import, target); } +fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { + try ensureBuiltinParamCount(mod, scope, call, 1); + const tree = scope.tree(); + const src = tree.token_locs[call.builtin_token].start; + const params = call.params(); + const target = try expr(mod, scope, .none, params[0]); + return addZIRUnOp(mod, scope, src, .compileerror, target); +} + fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { const tree = scope.tree(); const arena = scope.arena(); @@ -2378,6 +2387,8 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint)); } else if (mem.eql(u8, builtin_name, "@import")) { return rlWrap(mod, scope, rl, try import(mod, scope, call)); + } else if (mem.eql(u8, builtin_name, "@compileError")) { + return compileError(mod, scope, call); } else if (mem.eql(u8, builtin_name, "@compileLog")) { return compileLog(mod, scope, call); } else { diff --git a/src/zir.zig b/src/zir.zig index 982c98ddc9..73530ec86d 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -300,6 +300,7 @@ pub const Inst = struct { => NoOp, .boolnot, + .compileerror, .deref, .@"return", .isnull, @@ -387,7 +388,6 @@ pub const Inst = struct { .declval => DeclVal, .declval_in_module => DeclValInModule, .coerce_result_block_ptr => CoerceResultBlockPtr, - .compileerror => CompileError, .compilelog => CompileLog, .loop => Loop, .@"const" => Const, @@ -701,16 +701,6 @@ pub const Inst = struct { kw_args: struct {}, }; - pub const CompileError = struct { - pub const base_tag = Tag.compileerror; - base: Inst, - - positionals: struct { - msg: []const u8, - }, - kw_args: struct {}, - }; - pub const CompileLog = struct { pub const base_tag = Tag.compilelog; base: Inst, @@ -1943,14 +1933,29 @@ const EmitZIR = struct { .sema_failure_retryable, .dependency_failure, => if (self.old_module.failed_decls.get(ir_decl)) |err_msg_list| { - const fail_inst = try self.arena.allocator.create(Inst.CompileError); + const fail_inst = try self.arena.allocator.create(Inst.UnOp); fail_inst.* = .{ .base = .{ .src = ir_decl.src(), - .tag = Inst.CompileError.base_tag, + .tag = .compileerror, }, .positionals = .{ - .msg = try self.arena.allocator.dupe(u8, err_msg_list.items[0].msg), + .operand = blk: { + const msg_str = try self.arena.allocator.dupe(u8, err_msg_list.items[0].msg); + + const str_inst = try self.arena.allocator.create(Inst.Str); + str_inst.* = .{ + .base = .{ + .src = ir_decl.src(), + .tag = Inst.Str.base_tag, + }, + .positionals = .{ + .bytes = msg_str, + }, + .kw_args = .{}, + }; + break :blk &str_inst.base; + }, }, .kw_args = .{}, }; @@ -2073,28 +2078,58 @@ const EmitZIR = struct { }, .sema_failure => { const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?.items[0]; - const fail_inst = try self.arena.allocator.create(Inst.CompileError); + const fail_inst = try self.arena.allocator.create(Inst.UnOp); fail_inst.* = .{ .base = .{ .src = src, - .tag = Inst.CompileError.base_tag, + .tag = .compileerror, }, .positionals = .{ - .msg = try self.arena.allocator.dupe(u8, err_msg.msg), + .operand = blk: { + const msg_str = try self.arena.allocator.dupe(u8, err_msg.msg); + + const str_inst = try self.arena.allocator.create(Inst.Str); + str_inst.* = .{ + .base = .{ + .src = src, + .tag = Inst.Str.base_tag, + }, + .positionals = .{ + .bytes = msg_str, + }, + .kw_args = .{}, + }; + break :blk &str_inst.base; + }, }, .kw_args = .{}, }; try instructions.append(&fail_inst.base); }, .dependency_failure => { - const fail_inst = try self.arena.allocator.create(Inst.CompileError); + const fail_inst = try self.arena.allocator.create(Inst.UnOp); fail_inst.* = .{ .base = .{ .src = src, - .tag = Inst.CompileError.base_tag, + .tag = .compileerror, }, .positionals = .{ - .msg = try self.arena.allocator.dupe(u8, "depends on another failed Decl"), + .operand = blk: { + const msg_str = try self.arena.allocator.dupe(u8, "depends on another failed Decl"); + + const str_inst = try self.arena.allocator.create(Inst.Str); + str_inst.* = .{ + .base = .{ + .src = src, + .tag = Inst.Str.base_tag, + }, + .positionals = .{ + .bytes = msg_str, + }, + .kw_args = .{}, + }; + break :blk &str_inst.base; + }, }, .kw_args = .{}, }; diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 16f2297ee6..8e6632ee84 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -486,8 +486,9 @@ fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) return mod.constVoid(scope, export_inst.base.src); } -fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst { - return mod.fail(scope, inst.base.src, "{}", .{inst.positionals.msg}); +fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const msg = try resolveConstString(mod, scope, inst.positionals.operand); + return mod.fail(scope, inst.base.src, "{}", .{msg}); } fn analyzeInstCompileLog(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileLog) InnerError!*Inst { diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 2bda86092e..4e57bbf9f7 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -1186,6 +1186,12 @@ pub fn addCases(ctx: *TestContext) !void { // "| true, 20, (runtime value), (function)" // TODO if this is here it invalidates the compile error checker. Need a way to check though. + ctx.compileError("compileError", linux_x64, + \\export fn _start() noreturn { + \\ @compileError("this is an error"); + \\ unreachable; + \\} + , &[_][]const u8{":2:3: error: this is an error"}); { var case = ctx.obj("variable shadowing", linux_x64); case.addError( diff --git a/test/stage2/zir.zig b/test/stage2/zir.zig index 82159e4f8e..da4038e792 100644 --- a/test/stage2/zir.zig +++ b/test/stage2/zir.zig @@ -150,18 +150,20 @@ pub fn addCases(ctx: *TestContext) !void { \\}) \\ \\@a = fn(@fnty, { - \\ %0 = call(@b, []) + \\ %0 = call(@c, []) \\ %1 = returnvoid() \\}) \\ - \\@b = fn(@fnty, { - \\ %9 = compileerror("message") + \\@b = str("message") + \\ + \\@c = fn(@fnty, { + \\ %9 = compileerror(@b) \\ %0 = call(@a, []) \\ %1 = returnvoid() \\}) , &[_][]const u8{ - ":18:21: error: message", + ":20:21: error: message", }, ); // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are @@ -179,20 +181,22 @@ pub fn addCases(ctx: *TestContext) !void { \\}) \\ \\@a = fn(@fnty, { - \\ %0 = call(@b, []) + \\ %0 = call(@c, []) \\ %1 = returnvoid() \\}) \\ - \\@b = fn(@fnty, { - \\ %9 = compileerror("message") + \\@b = str("message") + \\ + \\@c = fn(@fnty, { + \\ %9 = compileerror(@b) \\ %0 = call(@a, []) \\ %1 = returnvoid() \\}) , \\@void = primitive(void) \\@fnty = fntype([], @void, cc=C) - \\@9 = declref("9__anon_2") - \\@9__anon_2 = str("entry") + \\@9 = declref("9__anon_3") + \\@9__anon_3 = str("entry") \\@unnamed$4 = str("entry") \\@unnamed$5 = export(@unnamed$4, "entry") \\@11 = primitive(void_value)