authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-13 10:04:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-13 10:04:46-07:00
logec4953504a07f3025d5f32344180dd9b6a4de8ae
treebbf9a1aed1c8a160caffe68821f71ffd5c1d6fec
parent6b2ce9d1e99902d429d9946bcc57e8cb02f4d224

stage2: implement safety checks at the zir_sema level


5 files changed, 91 insertions(+), 31 deletions(-)

src-self-hosted/Module.zig+67-5
...@@ -2219,11 +2219,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool {...@@ -2219,11 +2219,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool {
2219 };2219 };
2220}2220}
22212221
2222pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst {
2223 const b = try self.requireRuntimeBlock(scope, src);
2224 return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach);
2225}
2226
2227pub fn analyzeIsNull(2222pub fn analyzeIsNull(
2228 self: *Module,2223 self: *Module,
2229 scope: *Scope,2224 scope: *Scope,
...@@ -2902,3 +2897,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {...@@ -2902,3 +2897,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
2902 });2897 });
2903 }2898 }
2904}2899}
2900
2901pub const PanicId = enum {
2902 unreach,
2903 unwrap_null,
2904};
2905
2906pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
2907 const block_inst = try parent_block.arena.create(Inst.Block);
2908 block_inst.* = .{
2909 .base = .{
2910 .tag = Inst.Block.base_tag,
2911 .ty = Type.initTag(.void),
2912 .src = ok.src,
2913 },
2914 .body = .{
2915 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
2916 },
2917 };
2918
2919 const ok_body: ir.Body = .{
2920 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
2921 };
2922 const brvoid = try parent_block.arena.create(Inst.BrVoid);
2923 brvoid.* = .{
2924 .base = .{
2925 .tag = .brvoid,
2926 .ty = Type.initTag(.noreturn),
2927 .src = ok.src,
2928 },
2929 .block = block_inst,
2930 };
2931 ok_body.instructions[0] = &brvoid.base;
2932
2933 var fail_block: Scope.Block = .{
2934 .parent = parent_block,
2935 .func = parent_block.func,
2936 .decl = parent_block.decl,
2937 .instructions = .{},
2938 .arena = parent_block.arena,
2939 };
2940 defer fail_block.instructions.deinit(mod.gpa);
2941
2942 _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
2943
2944 const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
2945
2946 const condbr = try parent_block.arena.create(Inst.CondBr);
2947 condbr.* = .{
2948 .base = .{
2949 .tag = .condbr,
2950 .ty = Type.initTag(.noreturn),
2951 .src = ok.src,
2952 },
2953 .condition = ok,
2954 .then_body = ok_body,
2955 .else_body = fail_body,
2956 };
2957 block_inst.body.instructions[0] = &condbr.base;
2958
2959 try parent_block.instructions.append(mod.gpa, &block_inst.base);
2960}
2961
2962pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
2963 // TODO Once we have a panic function to call, call it here instead of breakpoint.
2964 _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
2965 return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
2966}
src-self-hosted/codegen.zig+2-3
...@@ -668,8 +668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -668,8 +668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
668 .store => return self.genStore(inst.castTag(.store).?),668 .store => return self.genStore(inst.castTag(.store).?),
669 .sub => return self.genSub(inst.castTag(.sub).?),669 .sub => return self.genSub(inst.castTag(.sub).?),
670 .unreach => return MCValue{ .unreach = {} },670 .unreach => return MCValue{ .unreach = {} },
671 .unwrap_optional_safe => return self.genUnwrapOptional(inst.castTag(.unwrap_optional_safe).?, true),671 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
672 .unwrap_optional_unsafe => return self.genUnwrapOptional(inst.castTag(.unwrap_optional_unsafe).?, false),
673 }672 }
674 }673 }
675674
...@@ -819,7 +818,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -819,7 +818,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
819 }818 }
820 }819 }
821820
822 fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp, safety_check: bool) !MCValue {821 fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
823 // No side effects, so if it's unreferenced, do nothing.822 // No side effects, so if it's unreferenced, do nothing.
824 if (inst.base.isUnused())823 if (inst.base.isUnused())
825 return MCValue.dead;824 return MCValue.dead;
src-self-hosted/ir.zig+2-4
...@@ -82,8 +82,7 @@ pub const Inst = struct {...@@ -82,8 +82,7 @@ pub const Inst = struct {
82 not,82 not,
83 floatcast,83 floatcast,
84 intcast,84 intcast,
85 unwrap_optional_safe,85 unwrap_optional,
86 unwrap_optional_unsafe,
8786
88 pub fn Type(tag: Tag) type {87 pub fn Type(tag: Tag) type {
89 return switch (tag) {88 return switch (tag) {
...@@ -104,8 +103,7 @@ pub const Inst = struct {...@@ -104,8 +103,7 @@ pub const Inst = struct {
104 .floatcast,103 .floatcast,
105 .intcast,104 .intcast,
106 .load,105 .load,
107 .unwrap_optional_safe,106 .unwrap_optional,
108 .unwrap_optional_unsafe,
109 => UnOp,107 => UnOp,
110108
111 .add,109 .add,
src-self-hosted/zir.zig+1-2
...@@ -1927,8 +1927,7 @@ const EmitZIR = struct {...@@ -1927,8 +1927,7 @@ const EmitZIR = struct {
1927 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),1927 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
1928 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),1928 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
1929 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),1929 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
1930 .unwrap_optional_safe => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional_safe).?, .unwrap_optional_safe),1930 .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe),
1931 .unwrap_optional_unsafe => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional_unsafe).?, .unwrap_optional_unsafe),
19321931
1933 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),1932 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
1934 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),1933 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
src-self-hosted/zir_sema.zig+19-17
...@@ -68,8 +68,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -68,8 +68,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
68 .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),68 .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),
69 .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),69 .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),
70 .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),70 .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),
71 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?),71 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true),
72 .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?),72 .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false),
73 .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),73 .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),
74 .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),74 .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),
75 .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),75 .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),
...@@ -313,7 +313,7 @@ fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!...@@ -313,7 +313,7 @@ fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
313 if (operand.value()) |val| {313 if (operand.value()) |val| {
314 const ref_payload = try scope.arena().create(Value.Payload.RefVal);314 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
315 ref_payload.* = .{ .val = val };315 ref_payload.* = .{ .val = val };
316 316
317 return mod.constInst(scope, inst.base.src, .{317 return mod.constInst(scope, inst.base.src, .{
318 .ty = ptr_type,318 .ty = ptr_type,
319 .val = Value.initPayload(&ref_payload.base),319 .val = Value.initPayload(&ref_payload.base),
...@@ -677,7 +677,7 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp...@@ -677,7 +677,7 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp
677 try mod.singleMutPtrType(scope, unwrap.base.src, child_type);677 try mod.singleMutPtrType(scope, unwrap.base.src, child_type);
678678
679 if (operand.value()) |val| {679 if (operand.value()) |val| {
680 if (val.tag() == .null_value) {680 if (val.isNull()) {
681 return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});681 return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});
682 }682 }
683 return mod.constInst(scope, unwrap.base.src, .{683 return mod.constInst(scope, unwrap.base.src, .{
...@@ -687,10 +687,11 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp...@@ -687,10 +687,11 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp
687 }687 }
688688
689 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);689 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
690 return if (safety_check)690 if (safety_check and mod.wantSafety(scope)) {
691 mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional_safe, operand)691 const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .isnonnull, operand);
692 else692 try mod.addSafetyCheck(b, is_non_null, .unwrap_null);
693 mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional_unsafe, operand);693 }
694 return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand);
694}695}
695696
696fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {697fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
...@@ -1167,18 +1168,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1167,18 +1168,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1167 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);1168 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
1168}1169}
11691170
1170fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {1171fn analyzeInstUnreachable(
1171 return mod.analyzeUnreach(scope, unreach.base.src);1172 mod: *Module,
1172}1173 scope: *Scope,
11731174 unreach: *zir.Inst.NoOp,
1174fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {1175 safety_check: bool,
1176) InnerError!*Inst {
1175 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);1177 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
1176 // TODO Add compile error for @optimizeFor occurring too late in a scope.1178 // TODO Add compile error for @optimizeFor occurring too late in a scope.
1177 if (mod.wantSafety(scope)) {1179 if (safety_check and mod.wantSafety(scope)) {
1178 // TODO Once we have a panic function to call, call it here instead of this.1180 return mod.safetyPanic(b, unreach.base.src, .unreach);
1179 _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);1181 } else {
1182 return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
1180 }1183 }
1181 return mod.analyzeUnreach(scope, unreach.base.src);
1182}1184}
11831185
1184fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {1186fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {