authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-02 14:28:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-02 19:11:56-07:00
log50a530196ca4e91b387f9937475dd8891edb3f4f
treec90e57e1484a4c6fed1ae7922f761a9bdd6e7970
parent006e7f68056af62ae7713d7ef228841d11874735

stage2: fix handling compile error in inline fn call

* scopes properly inherit inlining information * compile errors of inline function calls are properly attached to the caller rather than the callee. - added a test case for this * --watch still opens a repl if compile errors happen.

4 files changed, 154 insertions(+), 95 deletions(-)

src/Module.zig+43-28
...@@ -759,33 +759,33 @@ pub const Scope = struct {...@@ -759,33 +759,33 @@ pub const Scope = struct {
759 instructions: ArrayListUnmanaged(*Inst),759 instructions: ArrayListUnmanaged(*Inst),
760 /// Points to the arena allocator of DeclAnalysis760 /// Points to the arena allocator of DeclAnalysis
761 arena: *Allocator,761 arena: *Allocator,
762 label: Label = Label.none,762 label: ?Label = null,
763 inlining: ?Inlining,
763 is_comptime: bool,764 is_comptime: bool,
764765
765 pub const Label = union(enum) {766 /// This `Block` maps a block ZIR instruction to the corresponding
766 none,767 /// TZIR instruction for break instruction analysis.
767 /// This `Block` maps a block ZIR instruction to the corresponding768 pub const Label = struct {
768 /// TZIR instruction for break instruction analysis.769 zir_block: *zir.Inst.Block,
769 breaking: struct {770 merges: Merges,
770 zir_block: *zir.Inst.Block,771 };
771 merges: Merges,
772 },
773 /// This `Block` indicates that an inline function call is happening
774 /// and return instructions should be analyzed as a break instruction
775 /// to this TZIR block instruction.
776 inlining: struct {
777 /// We use this to count from 0 so that arg instructions know
778 /// which parameter index they are, without having to store
779 /// a parameter index with each arg instruction.
780 param_index: usize,
781 casted_args: []*Inst,
782 merges: Merges,
783 },
784772
785 pub const Merges = struct {773 /// This `Block` indicates that an inline function call is happening
786 results: ArrayListUnmanaged(*Inst),774 /// and return instructions should be analyzed as a break instruction
787 block_inst: *Inst.Block,775 /// to this TZIR block instruction.
788 };776 pub const Inlining = struct {
777 caller: ?*Fn,
778 /// We use this to count from 0 so that arg instructions know
779 /// which parameter index they are, without having to store
780 /// a parameter index with each arg instruction.
781 param_index: usize,
782 casted_args: []*Inst,
783 merges: Merges,
784 };
785
786 pub const Merges = struct {
787 results: ArrayListUnmanaged(*Inst),
788 block_inst: *Inst.Block,
789 };789 };
790790
791 /// For debugging purposes.791 /// For debugging purposes.
...@@ -1093,6 +1093,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1093,6 +1093,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1093 .decl = decl,1093 .decl = decl,
1094 .instructions = .{},1094 .instructions = .{},
1095 .arena = &decl_arena.allocator,1095 .arena = &decl_arena.allocator,
1096 .inlining = null,
1096 .is_comptime = false,1097 .is_comptime = false,
1097 };1098 };
1098 defer block_scope.instructions.deinit(self.gpa);1099 defer block_scope.instructions.deinit(self.gpa);
...@@ -1281,6 +1282,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1281,6 +1282,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1281 .decl = decl,1282 .decl = decl,
1282 .instructions = .{},1283 .instructions = .{},
1283 .arena = &decl_arena.allocator,1284 .arena = &decl_arena.allocator,
1285 .inlining = null,
1284 .is_comptime = true,1286 .is_comptime = true,
1285 };1287 };
1286 defer block_scope.instructions.deinit(self.gpa);1288 defer block_scope.instructions.deinit(self.gpa);
...@@ -1346,6 +1348,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1346,6 +1348,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1346 .decl = decl,1348 .decl = decl,
1347 .instructions = .{},1349 .instructions = .{},
1348 .arena = &gen_scope_arena.allocator,1350 .arena = &gen_scope_arena.allocator,
1351 .inlining = null,
1349 .is_comptime = true,1352 .is_comptime = true,
1350 };1353 };
1351 defer inner_block.instructions.deinit(self.gpa);1354 defer inner_block.instructions.deinit(self.gpa);
...@@ -1466,6 +1469,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1466,6 +1469,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1466 .decl = decl,1469 .decl = decl,
1467 .instructions = .{},1470 .instructions = .{},
1468 .arena = &analysis_arena.allocator,1471 .arena = &analysis_arena.allocator,
1472 .inlining = null,
1469 .is_comptime = true,1473 .is_comptime = true,
1470 };1474 };
1471 defer block_scope.instructions.deinit(self.gpa);1475 defer block_scope.instructions.deinit(self.gpa);
...@@ -1843,6 +1847,7 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -1843,6 +1847,7 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1843 .decl = decl,1847 .decl = decl,
1844 .instructions = .{},1848 .instructions = .{},
1845 .arena = &arena.allocator,1849 .arena = &arena.allocator,
1850 .inlining = null,
1846 .is_comptime = false,1851 .is_comptime = false,
1847 };1852 };
1848 defer inner_block.instructions.deinit(self.gpa);1853 defer inner_block.instructions.deinit(self.gpa);
...@@ -3050,11 +3055,20 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com...@@ -3050,11 +3055,20 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com
3050 },3055 },
3051 .block => {3056 .block => {
3052 const block = scope.cast(Scope.Block).?;3057 const block = scope.cast(Scope.Block).?;
3053 if (block.func) |func| {3058 if (block.inlining) |*inlining| {
3054 func.state = .sema_failure;3059 if (inlining.caller) |func| {
3060 func.state = .sema_failure;
3061 } else {
3062 block.decl.analysis = .sema_failure;
3063 block.decl.generation = self.generation;
3064 }
3055 } else {3065 } else {
3056 block.decl.analysis = .sema_failure;3066 if (block.func) |func| {
3057 block.decl.generation = self.generation;3067 func.state = .sema_failure;
3068 } else {
3069 block.decl.analysis = .sema_failure;
3070 block.decl.generation = self.generation;
3071 }
3058 }3072 }
3059 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);3073 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
3060 },3074 },
...@@ -3414,6 +3428,7 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic...@@ -3414,6 +3428,7 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
3414 .decl = parent_block.decl,3428 .decl = parent_block.decl,
3415 .instructions = .{},3429 .instructions = .{},
3416 .arena = parent_block.arena,3430 .arena = parent_block.arena,
3431 .inlining = parent_block.inlining,
3417 .is_comptime = parent_block.is_comptime,3432 .is_comptime = parent_block.is_comptime,
3418 };3433 };
3419 defer fail_block.instructions.deinit(mod.gpa);3434 defer fail_block.instructions.deinit(mod.gpa);
src/main.zig+1-1
...@@ -1818,7 +1818,7 @@ fn buildOutputType(...@@ -1818,7 +1818,7 @@ fn buildOutputType(
1818 };1818 };
18191819
1820 updateModule(gpa, comp, zir_out_path, hook) catch |err| switch (err) {1820 updateModule(gpa, comp, zir_out_path, hook) catch |err| switch (err) {
1821 error.SemanticAnalyzeFail => process.exit(1),1821 error.SemanticAnalyzeFail => if (!watch) process.exit(1),
1822 else => |e| return e,1822 else => |e| return e,
1823 };1823 };
1824 try comp.makeBinFileExecutable();1824 try comp.makeBinFileExecutable();
src/zir_sema.zig+59-66
...@@ -576,13 +576,10 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In...@@ -576,13 +576,10 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In
576576
577fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {577fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
578 const b = try mod.requireFunctionBlock(scope, inst.base.src);578 const b = try mod.requireFunctionBlock(scope, inst.base.src);
579 switch (b.label) {579 if (b.inlining) |*inlining| {
580 .none, .breaking => {},580 const param_index = inlining.param_index;
581 .inlining => |*inlining| {581 inlining.param_index += 1;
582 const param_index = inlining.param_index;582 return inlining.casted_args[param_index];
583 inlining.param_index += 1;
584 return inlining.casted_args[param_index];
585 },
586 }583 }
587 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;584 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
588 const param_index = b.instructions.items.len;585 const param_index = b.instructions.items.len;
...@@ -620,6 +617,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError...@@ -620,6 +617,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
620 .decl = parent_block.decl,617 .decl = parent_block.decl,
621 .instructions = .{},618 .instructions = .{},
622 .arena = parent_block.arena,619 .arena = parent_block.arena,
620 .inlining = parent_block.inlining,
623 .is_comptime = parent_block.is_comptime,621 .is_comptime = parent_block.is_comptime,
624 };622 };
625 defer child_block.instructions.deinit(mod.gpa);623 defer child_block.instructions.deinit(mod.gpa);
...@@ -642,7 +640,8 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c...@@ -642,7 +640,8 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
642 .decl = parent_block.decl,640 .decl = parent_block.decl,
643 .instructions = .{},641 .instructions = .{},
644 .arena = parent_block.arena,642 .arena = parent_block.arena,
645 .label = .none,643 .label = null,
644 .inlining = parent_block.inlining,
646 .is_comptime = parent_block.is_comptime or is_comptime,645 .is_comptime = parent_block.is_comptime or is_comptime,
647 };646 };
648 defer child_block.instructions.deinit(mod.gpa);647 defer child_block.instructions.deinit(mod.gpa);
...@@ -680,18 +679,18 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt...@@ -680,18 +679,18 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
680 .decl = parent_block.decl,679 .decl = parent_block.decl,
681 .instructions = .{},680 .instructions = .{},
682 .arena = parent_block.arena,681 .arena = parent_block.arena,
683 .label = Scope.Block.Label{682 // TODO @as here is working around a stage1 miscompilation bug :(
684 .breaking = .{683 .label = @as(?Scope.Block.Label, Scope.Block.Label{
685 .zir_block = inst,684 .zir_block = inst,
686 .merges = .{685 .merges = .{
687 .results = .{},686 .results = .{},
688 .block_inst = block_inst,687 .block_inst = block_inst,
689 },
690 },688 },
691 },689 }),
690 .inlining = parent_block.inlining,
692 .is_comptime = is_comptime or parent_block.is_comptime,691 .is_comptime = is_comptime or parent_block.is_comptime,
693 };692 };
694 const merges = &child_block.label.breaking.merges;693 const merges = &child_block.label.?.merges;
695694
696 defer child_block.instructions.deinit(mod.gpa);695 defer child_block.instructions.deinit(mod.gpa);
697 defer merges.results.deinit(mod.gpa);696 defer merges.results.deinit(mod.gpa);
...@@ -705,7 +704,7 @@ fn analyzeBlockBody(...@@ -705,7 +704,7 @@ fn analyzeBlockBody(
705 mod: *Module,704 mod: *Module,
706 scope: *Scope,705 scope: *Scope,
707 child_block: *Scope.Block,706 child_block: *Scope.Block,
708 merges: *Scope.Block.Label.Merges,707 merges: *Scope.Block.Merges,
709) InnerError!*Inst {708) InnerError!*Inst {
710 const parent_block = scope.cast(Scope.Block).?;709 const parent_block = scope.cast(Scope.Block).?;
711710
...@@ -895,19 +894,20 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError...@@ -895,19 +894,20 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
895 .decl = scope.decl().?,894 .decl = scope.decl().?,
896 .instructions = .{},895 .instructions = .{},
897 .arena = scope.arena(),896 .arena = scope.arena(),
898 .label = Scope.Block.Label{897 .label = null,
899 .inlining = .{898 // TODO @as here is working around a stage1 miscompilation bug :(
900 .param_index = 0,899 .inlining = @as(?Scope.Block.Inlining, Scope.Block.Inlining{
901 .casted_args = casted_args,900 .caller = b.func,
902 .merges = .{901 .param_index = 0,
903 .results = .{},902 .casted_args = casted_args,
904 .block_inst = block_inst,903 .merges = .{
905 },904 .results = .{},
905 .block_inst = block_inst,
906 },906 },
907 },907 }),
908 .is_comptime = is_comptime_call,908 .is_comptime = is_comptime_call,
909 };909 };
910 const merges = &child_block.label.inlining.merges;910 const merges = &child_block.inlining.?.merges;
911911
912 defer child_block.instructions.deinit(mod.gpa);912 defer child_block.instructions.deinit(mod.gpa);
913 defer merges.results.deinit(mod.gpa);913 defer merges.results.deinit(mod.gpa);
...@@ -1416,6 +1416,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1416,6 +1416,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1416 .decl = parent_block.decl,1416 .decl = parent_block.decl,
1417 .instructions = .{},1417 .instructions = .{},
1418 .arena = parent_block.arena,1418 .arena = parent_block.arena,
1419 .inlining = parent_block.inlining,
1419 .is_comptime = parent_block.is_comptime,1420 .is_comptime = parent_block.is_comptime,
1420 };1421 };
1421 defer case_block.instructions.deinit(mod.gpa);1422 defer case_block.instructions.deinit(mod.gpa);
...@@ -1955,6 +1956,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1955,6 +1956,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1955 .decl = parent_block.decl,1956 .decl = parent_block.decl,
1956 .instructions = .{},1957 .instructions = .{},
1957 .arena = parent_block.arena,1958 .arena = parent_block.arena,
1959 .inlining = parent_block.inlining,
1958 .is_comptime = parent_block.is_comptime,1960 .is_comptime = parent_block.is_comptime,
1959 };1961 };
1960 defer true_block.instructions.deinit(mod.gpa);1962 defer true_block.instructions.deinit(mod.gpa);
...@@ -1966,6 +1968,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1966,6 +1968,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1966 .decl = parent_block.decl,1968 .decl = parent_block.decl,
1967 .instructions = .{},1969 .instructions = .{},
1968 .arena = parent_block.arena,1970 .arena = parent_block.arena,
1971 .inlining = parent_block.inlining,
1969 .is_comptime = parent_block.is_comptime,1972 .is_comptime = parent_block.is_comptime,
1970 };1973 };
1971 defer false_block.instructions.deinit(mod.gpa);1974 defer false_block.instructions.deinit(mod.gpa);
...@@ -1995,40 +1998,34 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!...@@ -1995,40 +1998,34 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
1995 const operand = try resolveInst(mod, scope, inst.positionals.operand);1998 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1996 const b = try mod.requireFunctionBlock(scope, inst.base.src);1999 const b = try mod.requireFunctionBlock(scope, inst.base.src);
19972000
1998 switch (b.label) {2001 if (b.inlining) |*inlining| {
1999 .inlining => |*inlining| {2002 // We are inlining a function call; rewrite the `ret` as a `break`.
2000 // We are inlining a function call; rewrite the `ret` as a `break`.2003 try inlining.merges.results.append(mod.gpa, operand);
2001 try inlining.merges.results.append(mod.gpa, operand);2004 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand);
2002 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand);
2003 },
2004 .none, .breaking => {
2005 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
2006 },
2007 }2005 }
2006
2007 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
2008}2008}
20092009
2010fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {2010fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
2011 const b = try mod.requireFunctionBlock(scope, inst.base.src);2011 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2012 switch (b.label) {2012 if (b.inlining) |*inlining| {
2013 .inlining => |*inlining| {2013 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.
2014 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.2014 const void_inst = try mod.constVoid(scope, inst.base.src);
2015 const void_inst = try mod.constVoid(scope, inst.base.src);2015 try inlining.merges.results.append(mod.gpa, void_inst);
2016 try inlining.merges.results.append(mod.gpa, void_inst);2016 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst);
2017 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst);2017 }
2018 },2018
2019 .none, .breaking => {2019 if (b.func) |func| {
2020 if (b.func) |func| {2020 // Need to emit a compile error if returning void is not allowed.
2021 // Need to emit a compile error if returning void is not allowed.2021 const void_inst = try mod.constVoid(scope, inst.base.src);
2022 const void_inst = try mod.constVoid(scope, inst.base.src);2022 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
2023 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;2023 const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
2024 const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);2024 if (casted_void.ty.zigTypeTag() != .Void) {
2025 if (casted_void.ty.zigTypeTag() != .Void) {2025 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
2026 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);2026 }
2027 }
2028 }
2029 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
2030 },
2031 }2027 }
2028 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
2032}2029}
20332030
2034fn floatOpAllowed(tag: zir.Inst.Tag) bool {2031fn floatOpAllowed(tag: zir.Inst.Tag) bool {
...@@ -2048,16 +2045,12 @@ fn analyzeBreak(...@@ -2048,16 +2045,12 @@ fn analyzeBreak(
2048) InnerError!*Inst {2045) InnerError!*Inst {
2049 var opt_block = scope.cast(Scope.Block);2046 var opt_block = scope.cast(Scope.Block);
2050 while (opt_block) |block| {2047 while (opt_block) |block| {
2051 switch (block.label) {2048 if (block.label) |*label| {
2052 .none => {},2049 if (label.zir_block == zir_block) {
2053 .breaking => |*label| {2050 try label.merges.results.append(mod.gpa, operand);
2054 if (label.zir_block == zir_block) {2051 const b = try mod.requireFunctionBlock(scope, src);
2055 try label.merges.results.append(mod.gpa, operand);2052 return mod.addBr(b, src, label.merges.block_inst, operand);
2056 const b = try mod.requireFunctionBlock(scope, src);2053 }
2057 return mod.addBr(b, src, label.merges.block_inst, operand);
2058 }
2059 },
2060 .inlining => unreachable, // Invalid `break` ZIR inside inline function call.
2061 }2054 }
2062 opt_block = block.parent;2055 opt_block = block.parent;
2063 } else unreachable;2056 } else unreachable;
test/stage2/test.zig+51
...@@ -1379,4 +1379,55 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1379,4 +1379,55 @@ pub fn addCases(ctx: *TestContext) !void {
1379 \\}1379 \\}
1380 , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"});1380 , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"});
1381 }1381 }
1382
1383 {
1384 var case = ctx.exe("compile error in inline fn call fixed", linux_x64);
1385 case.addError(
1386 \\export fn _start() noreturn {
1387 \\ var x: usize = 3;
1388 \\ const y = add(10, 2, x);
1389 \\ exit(y - 6);
1390 \\}
1391 \\
1392 \\inline fn add(a: usize, b: usize, c: usize) usize {
1393 \\ if (a == 10) @compileError("bad");
1394 \\ return a + b + c;
1395 \\}
1396 \\
1397 \\fn exit(code: usize) noreturn {
1398 \\ asm volatile ("syscall"
1399 \\ :
1400 \\ : [number] "{rax}" (231),
1401 \\ [arg1] "{rdi}" (code)
1402 \\ : "rcx", "r11", "memory"
1403 \\ );
1404 \\ unreachable;
1405 \\}
1406 , &[_][]const u8{":8:18: error: bad"});
1407
1408 case.addCompareOutput(
1409 \\export fn _start() noreturn {
1410 \\ var x: usize = 3;
1411 \\ const y = add(1, 2, x);
1412 \\ exit(y - 6);
1413 \\}
1414 \\
1415 \\inline fn add(a: usize, b: usize, c: usize) usize {
1416 \\ if (a == 10) @compileError("bad");
1417 \\ return a + b + c;
1418 \\}
1419 \\
1420 \\fn exit(code: usize) noreturn {
1421 \\ asm volatile ("syscall"
1422 \\ :
1423 \\ : [number] "{rax}" (231),
1424 \\ [arg1] "{rdi}" (code)
1425 \\ : "rcx", "r11", "memory"
1426 \\ );
1427 \\ unreachable;
1428 \\}
1429 ,
1430 "",
1431 );
1432 }
1382}1433}