Commit 6b2ce9d1e9

Vexu <git@vexu.eu>
2020-08-13 13:18:44
stage2: split unwrap_optional to safe and unsafe verions
1 parent 4a40282
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));
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;
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 {
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
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
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);
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 {
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;
+            \\}
+        ,
+            "",
+        );
     }
 }