diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index bbbcde9a71..66da42cadf 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2016,28 +2016,6 @@ pub fn addCall( return &inst.base; } -pub fn addUnwrapOptional( - self: *Module, - block: *Scope.Block, - src: usize, - ty: Type, - operand: *Inst, - safety_check: bool, -) !*Inst { - const inst = try block.arena.create(Inst.UnwrapOptional); - inst.* = .{ - .base = .{ - .tag = .unwrap_optional, - .ty = ty, - .src = src, - }, - .operand = operand, - .safety_check = safety_check, - }; - try block.instructions.append(self.gpa, &inst.base); - return &inst.base; -} - pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { const const_inst = try scope.arena().create(Inst.Constant); const_inst.* = .{ @@ -2500,7 +2478,7 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst // null to ?T if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) { - return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = inst.ty.onePossibleValue().? }); + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); } // T to ?T diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index ed3f8ab1b6..95c4e63c92 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -311,7 +311,7 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si const src = tree.token_locs[node.rtoken].start; const operand = try expr(mod, scope, .lvalue, node.lhs); - const unwrapped_ptr = try addZIRInst(mod, scope, src, zir.Inst.UnwrapOptional, .{ .operand = operand }, .{}); + const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand); if (rl == .lvalue) return unwrapped_ptr; return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 887126ba2b..37e775b4b3 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -668,7 +668,8 @@ 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 => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), + .unwrap_optional_safe => return self.genUnwrapOptional(inst.castTag(.unwrap_optional_safe).?, true), + .unwrap_optional_unsafe => return self.genUnwrapOptional(inst.castTag(.unwrap_optional_unsafe).?, false), } } @@ -818,7 +819,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } - fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnwrapOptional) !MCValue { + fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp, safety_check: bool) !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 93952a4214..307dcfe62e 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -82,7 +82,8 @@ pub const Inst = struct { not, floatcast, intcast, - unwrap_optional, + unwrap_optional_safe, + unwrap_optional_unsafe, pub fn Type(tag: Tag) type { return switch (tag) { @@ -103,6 +104,8 @@ pub const Inst = struct { .floatcast, .intcast, .load, + .unwrap_optional_safe, + .unwrap_optional_unsafe, => UnOp, .add, @@ -125,7 +128,6 @@ pub const Inst = struct { .condbr => CondBr, .constant => Constant, .loop => Loop, - .unwrap_optional => UnwrapOptional, }; } @@ -421,27 +423,6 @@ pub const Inst = struct { return null; } }; - - pub const UnwrapOptional = struct { - pub const base_tag = Tag.unwrap_optional; - base: Inst, - - operand: *Inst, - safety_check: bool, - - pub fn operandCount(self: *const UnwrapOptional) usize { - return 1; - } - pub fn getOperand(self: *const UnwrapOptional, index: usize) ?*Inst { - var i = index; - - if (i < 1) - return self.operand; - i -= 1; - - return null; - } - }; }; pub const Body = struct { diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index eadb91ffea..ef78b046e6 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -185,8 +185,6 @@ pub const Type = extern union { return true; }, .Optional => { - if (a.tag() != b.tag()) - return false; return a.elemType().eql(b.elemType()); }, .Float, @@ -662,6 +660,10 @@ pub const Type = extern union { .optional => { const child_type = self.cast(Payload.Optional).?.child_type; if (!child_type.hasCodeGenBits()) return 1; + + if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) + return @divExact(target.cpu.arch.ptrBitWidth(), 8); + return child_type.abiAlignment(target); }, @@ -750,6 +752,10 @@ pub const Type = extern union { .optional => { const child_type = self.cast(Payload.Optional).?.child_type; if (!child_type.hasCodeGenBits()) return 1; + + if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) + return @divExact(target.cpu.arch.ptrBitWidth(), 8); + // Optional types are represented as a struct with the child type as the first // field and a boolean as the second. Since the child type's abi alignment is // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 84e9731126..10fb419440 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -215,7 +215,9 @@ pub const Inst = struct { /// Create an optional type '?T' optional_type, /// Unwraps an optional value 'lhs.?' - unwrap_optional, + unwrap_optional_safe, + /// Same as previous, but without safety checks. Used for orelse, if and while + unwrap_optional_unsafe, pub fn Type(tag: Tag) type { return switch (tag) { @@ -245,6 +247,8 @@ pub const Inst = struct { .single_const_ptr_type, .single_mut_ptr_type, .optional_type, + .unwrap_optional_safe, + .unwrap_optional_unsafe, => UnOp, .add, @@ -303,7 +307,6 @@ pub const Inst = struct { .fntype => FnType, .elemptr => ElemPtr, .condbr => CondBr, - .unwrap_optional => UnwrapOptional, }; } @@ -379,7 +382,8 @@ pub const Inst = struct { .typeof, .xor, .optional_type, - .unwrap_optional, + .unwrap_optional_safe, + .unwrap_optional_unsafe, => false, .@"break", @@ -820,18 +824,6 @@ pub const Inst = struct { }, kw_args: struct {}, }; - - pub const UnwrapOptional = struct { - pub const base_tag = Tag.unwrap_optional; - base: Inst, - - positionals: struct { - operand: *Inst, - }, - kw_args: struct { - safety_check: bool = true, - }, - }; }; pub const ErrorMsg = struct { @@ -1935,6 +1927,8 @@ 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), .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), @@ -2157,25 +2151,6 @@ const EmitZIR = struct { }; break :blk &new_inst.base; }, - - .unwrap_optional => blk: { - const old_inst = inst.castTag(.unwrap_optional).?; - - const new_inst = try self.arena.allocator.create(Inst.UnwrapOptional); - new_inst.* = .{ - .base = .{ - .src = inst.src, - .tag = Inst.UnwrapOptional.base_tag, - }, - .positionals = .{ - .operand = try self.resolveInst(new_body, old_inst.operand), - }, - .kw_args = .{ - .safety_check = old_inst.safety_check, - }, - }; - break :blk &new_inst.base; - }, }; try instructions.append(new_inst); try inst_table.put(inst, new_inst); diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 39fbf9221a..b217752494 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -107,7 +107,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?), - .unwrap_optional => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional).?), + .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true), + .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), } } @@ -661,7 +662,7 @@ fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp })); } -fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnwrapOptional) InnerError!*Inst { +fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { const operand = try resolveInst(mod, scope, unwrap.positionals.operand); assert(operand.ty.zigTypeTag() == .Pointer); @@ -686,7 +687,10 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.Unwr } const b = try mod.requireRuntimeBlock(scope, unwrap.base.src); - return mod.addUnwrapOptional(b, unwrap.base.src, child_pointer, operand, unwrap.kw_args.safety_check); + 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); } fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index 477b5d92b1..29312a8f53 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -31,11 +31,6 @@ pub fn addCases(ctx: *TestContext) !void { \\export fn _start() noreturn { \\ print(); \\ - \\ const a: u32 = 2; - \\ const b: ?u32 = a; - \\ const c = b.?; - \\ if (c != 2) unreachable; - \\ \\ exit(); \\} \\ @@ -446,5 +441,29 @@ pub fn addCases(ctx: *TestContext) !void { , "", ); + + // Optionals + case.addCompareOutput( + \\export fn _start() noreturn { + \\ const a: u32 = 2; + \\ const b: ?u32 = a; + \\ const c = b.?; + \\ if (c != 2) unreachable; + \\ + \\ exit(); + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); } }