diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 66da42cadf..2e847519f3 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2219,11 +2219,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool { }; } -pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst { - const b = try self.requireRuntimeBlock(scope, src); - return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach); -} - pub fn analyzeIsNull( self: *Module, scope: *Scope, @@ -2902,3 +2897,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { }); } } + +pub const PanicId = enum { + unreach, + unwrap_null, +}; + +pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void { + const block_inst = try parent_block.arena.create(Inst.Block); + block_inst.* = .{ + .base = .{ + .tag = Inst.Block.base_tag, + .ty = Type.initTag(.void), + .src = ok.src, + }, + .body = .{ + .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr. + }, + }; + + const ok_body: ir.Body = .{ + .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid. + }; + const brvoid = try parent_block.arena.create(Inst.BrVoid); + brvoid.* = .{ + .base = .{ + .tag = .brvoid, + .ty = Type.initTag(.noreturn), + .src = ok.src, + }, + .block = block_inst, + }; + ok_body.instructions[0] = &brvoid.base; + + var fail_block: Scope.Block = .{ + .parent = parent_block, + .func = parent_block.func, + .decl = parent_block.decl, + .instructions = .{}, + .arena = parent_block.arena, + }; + defer fail_block.instructions.deinit(mod.gpa); + + _ = try mod.safetyPanic(&fail_block, ok.src, panic_id); + + const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) }; + + const condbr = try parent_block.arena.create(Inst.CondBr); + condbr.* = .{ + .base = .{ + .tag = .condbr, + .ty = Type.initTag(.noreturn), + .src = ok.src, + }, + .condition = ok, + .then_body = ok_body, + .else_body = fail_body, + }; + block_inst.body.instructions[0] = &condbr.base; + + try parent_block.instructions.append(mod.gpa, &block_inst.base); +} + +pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst { + // TODO Once we have a panic function to call, call it here instead of breakpoint. + _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint); + return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach); +} diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 37e775b4b3..d25fa65f47 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -668,8 +668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .store => return self.genStore(inst.castTag(.store).?), .sub => return self.genSub(inst.castTag(.sub).?), .unreach => return MCValue{ .unreach = {} }, - .unwrap_optional_safe => return self.genUnwrapOptional(inst.castTag(.unwrap_optional_safe).?, true), - .unwrap_optional_unsafe => return self.genUnwrapOptional(inst.castTag(.unwrap_optional_unsafe).?, false), + .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), } } @@ -819,7 +818,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } - fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp, safety_check: bool) !MCValue { + fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue { // No side effects, so if it's unreferenced, do nothing. if (inst.base.isUnused()) return MCValue.dead; diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 307dcfe62e..81f99f53ba 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -82,8 +82,7 @@ pub const Inst = struct { not, floatcast, intcast, - unwrap_optional_safe, - unwrap_optional_unsafe, + unwrap_optional, pub fn Type(tag: Tag) type { return switch (tag) { @@ -104,8 +103,7 @@ pub const Inst = struct { .floatcast, .intcast, .load, - .unwrap_optional_safe, - .unwrap_optional_unsafe, + .unwrap_optional, => UnOp, .add, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 10fb419440..2e638b0755 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -1927,8 +1927,7 @@ const EmitZIR = struct { .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull), .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref), - .unwrap_optional_safe => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional_safe).?, .unwrap_optional_safe), - .unwrap_optional_unsafe => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional_unsafe).?, .unwrap_optional_unsafe), + .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe), .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index b217752494..74e57b7735 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -68,8 +68,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?), .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?), .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?), - .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?), - .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?), + .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true), + .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false), .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?), .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?), .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?), @@ -313,7 +313,7 @@ fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError! if (operand.value()) |val| { const ref_payload = try scope.arena().create(Value.Payload.RefVal); ref_payload.* = .{ .val = val }; - + return mod.constInst(scope, inst.base.src, .{ .ty = ptr_type, .val = Value.initPayload(&ref_payload.base), @@ -677,7 +677,7 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp try mod.singleMutPtrType(scope, unwrap.base.src, child_type); if (operand.value()) |val| { - if (val.tag() == .null_value) { + if (val.isNull()) { return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{}); } return mod.constInst(scope, unwrap.base.src, .{ @@ -687,10 +687,11 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp } const b = try mod.requireRuntimeBlock(scope, unwrap.base.src); - return if (safety_check) - mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional_safe, operand) - else - mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional_unsafe, operand); + if (safety_check and mod.wantSafety(scope)) { + const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .isnonnull, operand); + try mod.addSafetyCheck(b, is_non_null, .unwrap_null); + } + return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand); } fn 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 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); } -fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { - return mod.analyzeUnreach(scope, unreach.base.src); -} - -fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { +fn analyzeInstUnreachable( + mod: *Module, + scope: *Scope, + unreach: *zir.Inst.NoOp, + safety_check: bool, +) InnerError!*Inst { const b = try mod.requireRuntimeBlock(scope, unreach.base.src); // TODO Add compile error for @optimizeFor occurring too late in a scope. - if (mod.wantSafety(scope)) { - // TODO Once we have a panic function to call, call it here instead of this. - _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint); + if (safety_check and mod.wantSafety(scope)) { + return mod.safetyPanic(b, unreach.base.src, .unreach); + } else { + return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach); } - return mod.analyzeUnreach(scope, unreach.base.src); } fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {