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 {
759759 instructions: ArrayListUnmanaged(*Inst),
760760 /// Points to the arena allocator of DeclAnalysis
761761 arena: *Allocator,
762 label: Label = Label.none,
762 label: ?Label = null,
763 inlining: ?Inlining,
763764 is_comptime: bool,
764765
765 pub const Label = union(enum) {
766 none,
767 /// This `Block` maps a block ZIR instruction to the corresponding
768 /// TZIR instruction for break instruction analysis.
769 breaking: struct {
770 zir_block: *zir.Inst.Block,
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 },
766 /// This `Block` maps a block ZIR instruction to the corresponding
767 /// TZIR instruction for break instruction analysis.
768 pub const Label = struct {
769 zir_block: *zir.Inst.Block,
770 merges: Merges,
771 };
784772
785 pub const Merges = struct {
786 results: ArrayListUnmanaged(*Inst),
787 block_inst: *Inst.Block,
788 };
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 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,
789789 };
790790
791791 /// For debugging purposes.
......@@ -1093,6 +1093,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
10931093 .decl = decl,
10941094 .instructions = .{},
10951095 .arena = &decl_arena.allocator,
1096 .inlining = null,
10961097 .is_comptime = false,
10971098 };
10981099 defer block_scope.instructions.deinit(self.gpa);
......@@ -1281,6 +1282,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12811282 .decl = decl,
12821283 .instructions = .{},
12831284 .arena = &decl_arena.allocator,
1285 .inlining = null,
12841286 .is_comptime = true,
12851287 };
12861288 defer block_scope.instructions.deinit(self.gpa);
......@@ -1346,6 +1348,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13461348 .decl = decl,
13471349 .instructions = .{},
13481350 .arena = &gen_scope_arena.allocator,
1351 .inlining = null,
13491352 .is_comptime = true,
13501353 };
13511354 defer inner_block.instructions.deinit(self.gpa);
......@@ -1466,6 +1469,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
14661469 .decl = decl,
14671470 .instructions = .{},
14681471 .arena = &analysis_arena.allocator,
1472 .inlining = null,
14691473 .is_comptime = true,
14701474 };
14711475 defer block_scope.instructions.deinit(self.gpa);
......@@ -1843,6 +1847,7 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
18431847 .decl = decl,
18441848 .instructions = .{},
18451849 .arena = &arena.allocator,
1850 .inlining = null,
18461851 .is_comptime = false,
18471852 };
18481853 defer inner_block.instructions.deinit(self.gpa);
......@@ -3050,11 +3055,20 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com
30503055 },
30513056 .block => {
30523057 const block = scope.cast(Scope.Block).?;
3053 if (block.func) |func| {
3054 func.state = .sema_failure;
3058 if (block.inlining) |*inlining| {
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 }
30553065 } else {
3056 block.decl.analysis = .sema_failure;
3057 block.decl.generation = self.generation;
3066 if (block.func) |func| {
3067 func.state = .sema_failure;
3068 } else {
3069 block.decl.analysis = .sema_failure;
3070 block.decl.generation = self.generation;
3071 }
30583072 }
30593073 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
30603074 },
......@@ -3414,6 +3428,7 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
34143428 .decl = parent_block.decl,
34153429 .instructions = .{},
34163430 .arena = parent_block.arena,
3431 .inlining = parent_block.inlining,
34173432 .is_comptime = parent_block.is_comptime,
34183433 };
34193434 defer fail_block.instructions.deinit(mod.gpa);
src/main.zig+1-1
......@@ -1818,7 +1818,7 @@ fn buildOutputType(
18181818 };
18191819
18201820 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),
18221822 else => |e| return e,
18231823 };
18241824 try comp.makeBinFileExecutable();
src/zir_sema.zig+59-66
......@@ -576,13 +576,10 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In
576576
577577fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
578578 const b = try mod.requireFunctionBlock(scope, inst.base.src);
579 switch (b.label) {
580 .none, .breaking => {},
581 .inlining => |*inlining| {
582 const param_index = inlining.param_index;
583 inlining.param_index += 1;
584 return inlining.casted_args[param_index];
585 },
579 if (b.inlining) |*inlining| {
580 const param_index = inlining.param_index;
581 inlining.param_index += 1;
582 return inlining.casted_args[param_index];
586583 }
587584 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
588585 const param_index = b.instructions.items.len;
......@@ -620,6 +617,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
620617 .decl = parent_block.decl,
621618 .instructions = .{},
622619 .arena = parent_block.arena,
620 .inlining = parent_block.inlining,
623621 .is_comptime = parent_block.is_comptime,
624622 };
625623 defer child_block.instructions.deinit(mod.gpa);
......@@ -642,7 +640,8 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
642640 .decl = parent_block.decl,
643641 .instructions = .{},
644642 .arena = parent_block.arena,
645 .label = .none,
643 .label = null,
644 .inlining = parent_block.inlining,
646645 .is_comptime = parent_block.is_comptime or is_comptime,
647646 };
648647 defer child_block.instructions.deinit(mod.gpa);
......@@ -680,18 +679,18 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt
680679 .decl = parent_block.decl,
681680 .instructions = .{},
682681 .arena = parent_block.arena,
683 .label = Scope.Block.Label{
684 .breaking = .{
685 .zir_block = inst,
686 .merges = .{
687 .results = .{},
688 .block_inst = block_inst,
689 },
682 // TODO @as here is working around a stage1 miscompilation bug :(
683 .label = @as(?Scope.Block.Label, Scope.Block.Label{
684 .zir_block = inst,
685 .merges = .{
686 .results = .{},
687 .block_inst = block_inst,
690688 },
691 },
689 }),
690 .inlining = parent_block.inlining,
692691 .is_comptime = is_comptime or parent_block.is_comptime,
693692 };
694 const merges = &child_block.label.breaking.merges;
693 const merges = &child_block.label.?.merges;
695694
696695 defer child_block.instructions.deinit(mod.gpa);
697696 defer merges.results.deinit(mod.gpa);
......@@ -705,7 +704,7 @@ fn analyzeBlockBody(
705704 mod: *Module,
706705 scope: *Scope,
707706 child_block: *Scope.Block,
708 merges: *Scope.Block.Label.Merges,
707 merges: *Scope.Block.Merges,
709708) InnerError!*Inst {
710709 const parent_block = scope.cast(Scope.Block).?;
711710
......@@ -895,19 +894,20 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError
895894 .decl = scope.decl().?,
896895 .instructions = .{},
897896 .arena = scope.arena(),
898 .label = Scope.Block.Label{
899 .inlining = .{
900 .param_index = 0,
901 .casted_args = casted_args,
902 .merges = .{
903 .results = .{},
904 .block_inst = block_inst,
905 },
897 .label = null,
898 // TODO @as here is working around a stage1 miscompilation bug :(
899 .inlining = @as(?Scope.Block.Inlining, Scope.Block.Inlining{
900 .caller = b.func,
901 .param_index = 0,
902 .casted_args = casted_args,
903 .merges = .{
904 .results = .{},
905 .block_inst = block_inst,
906906 },
907 },
907 }),
908908 .is_comptime = is_comptime_call,
909909 };
910 const merges = &child_block.label.inlining.merges;
910 const merges = &child_block.inlining.?.merges;
911911
912912 defer child_block.instructions.deinit(mod.gpa);
913913 defer merges.results.deinit(mod.gpa);
......@@ -1416,6 +1416,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
14161416 .decl = parent_block.decl,
14171417 .instructions = .{},
14181418 .arena = parent_block.arena,
1419 .inlining = parent_block.inlining,
14191420 .is_comptime = parent_block.is_comptime,
14201421 };
14211422 defer case_block.instructions.deinit(mod.gpa);
......@@ -1955,6 +1956,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
19551956 .decl = parent_block.decl,
19561957 .instructions = .{},
19571958 .arena = parent_block.arena,
1959 .inlining = parent_block.inlining,
19581960 .is_comptime = parent_block.is_comptime,
19591961 };
19601962 defer true_block.instructions.deinit(mod.gpa);
......@@ -1966,6 +1968,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
19661968 .decl = parent_block.decl,
19671969 .instructions = .{},
19681970 .arena = parent_block.arena,
1971 .inlining = parent_block.inlining,
19691972 .is_comptime = parent_block.is_comptime,
19701973 };
19711974 defer false_block.instructions.deinit(mod.gpa);
......@@ -1995,40 +1998,34 @@ fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
19951998 const operand = try resolveInst(mod, scope, inst.positionals.operand);
19961999 const b = try mod.requireFunctionBlock(scope, inst.base.src);
19972000
1998 switch (b.label) {
1999 .inlining => |*inlining| {
2000 // We are inlining a function call; rewrite the `ret` as a `break`.
2001 try inlining.merges.results.append(mod.gpa, 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 },
2001 if (b.inlining) |*inlining| {
2002 // We are inlining a function call; rewrite the `ret` as a `break`.
2003 try inlining.merges.results.append(mod.gpa, operand);
2004 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand);
20072005 }
2006
2007 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
20082008}
20092009
20102010fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
20112011 const b = try mod.requireFunctionBlock(scope, inst.base.src);
2012 switch (b.label) {
2013 .inlining => |*inlining| {
2014 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.
2015 const void_inst = try mod.constVoid(scope, inst.base.src);
2016 try inlining.merges.results.append(mod.gpa, void_inst);
2017 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst);
2018 },
2019 .none, .breaking => {
2020 if (b.func) |func| {
2021 // Need to emit a compile error if returning void is not allowed.
2022 const void_inst = try mod.constVoid(scope, inst.base.src);
2023 const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
2024 const casted_void = try mod.coerce(scope, fn_ty.fnReturnType(), void_inst);
2025 if (casted_void.ty.zigTypeTag() != .Void) {
2026 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
2027 }
2028 }
2029 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
2030 },
2012 if (b.inlining) |*inlining| {
2013 // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`.
2014 const void_inst = try mod.constVoid(scope, inst.base.src);
2015 try inlining.merges.results.append(mod.gpa, void_inst);
2016 return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst);
2017 }
2018
2019 if (b.func) |func| {
2020 // 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 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 if (casted_void.ty.zigTypeTag() != .Void) {
2025 return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, casted_void);
2026 }
20312027 }
2028 return mod.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
20322029}
20332030
20342031fn floatOpAllowed(tag: zir.Inst.Tag) bool {
......@@ -2048,16 +2045,12 @@ fn analyzeBreak(
20482045) InnerError!*Inst {
20492046 var opt_block = scope.cast(Scope.Block);
20502047 while (opt_block) |block| {
2051 switch (block.label) {
2052 .none => {},
2053 .breaking => |*label| {
2054 if (label.zir_block == zir_block) {
2055 try label.merges.results.append(mod.gpa, operand);
2056 const b = try mod.requireFunctionBlock(scope, src);
2057 return mod.addBr(b, src, label.merges.block_inst, operand);
2058 }
2059 },
2060 .inlining => unreachable, // Invalid `break` ZIR inside inline function call.
2048 if (block.label) |*label| {
2049 if (label.zir_block == zir_block) {
2050 try label.merges.results.append(mod.gpa, operand);
2051 const b = try mod.requireFunctionBlock(scope, src);
2052 return mod.addBr(b, src, label.merges.block_inst, operand);
2053 }
20612054 }
20622055 opt_block = block.parent;
20632056 } else unreachable;
test/stage2/test.zig+51
......@@ -1379,4 +1379,55 @@ pub fn addCases(ctx: *TestContext) !void {
13791379 \\}
13801380 , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"});
13811381 }
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 }
13821433}