| author | |
| committer | |
| log | 6b2ce9d1e99902d429d9946bcc57e8cb02f4d224 |
| tree | 0b02c162eb1fef9555746cf074961063cb4af62b |
| parent | 4a40282391f0b92a83a6a8c269c27a32be92884a |
8 files changed, 57 insertions(+), 93 deletions(-)
src-self-hosted/Module.zig+1-23| ... | ... | @@ -2016,28 +2016,6 @@ pub fn addCall( |
| 2016 | 2016 | return &inst.base; |
| 2017 | 2017 | } |
| 2018 | 2018 | |
| 2019 | pub fn addUnwrapOptional( | |
| 2020 | self: *Module, | |
| 2021 | block: *Scope.Block, | |
| 2022 | src: usize, | |
| 2023 | ty: Type, | |
| 2024 | operand: *Inst, | |
| 2025 | safety_check: bool, | |
| 2026 | ) !*Inst { | |
| 2027 | const inst = try block.arena.create(Inst.UnwrapOptional); | |
| 2028 | inst.* = .{ | |
| 2029 | .base = .{ | |
| 2030 | .tag = .unwrap_optional, | |
| 2031 | .ty = ty, | |
| 2032 | .src = src, | |
| 2033 | }, | |
| 2034 | .operand = operand, | |
| 2035 | .safety_check = safety_check, | |
| 2036 | }; | |
| 2037 | try block.instructions.append(self.gpa, &inst.base); | |
| 2038 | return &inst.base; | |
| 2039 | } | |
| 2040 | ||
| 2041 | 2019 | pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { |
| 2042 | 2020 | const const_inst = try scope.arena().create(Inst.Constant); |
| 2043 | 2021 | const_inst.* = .{ |
| ... | ... | @@ -2500,7 +2478,7 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst |
| 2500 | 2478 | |
| 2501 | 2479 | // null to ?T |
| 2502 | 2480 | if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) { |
| 2503 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = inst.ty.onePossibleValue().? }); | |
| 2481 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | |
| 2504 | 2482 | } |
| 2505 | 2483 | |
| 2506 | 2484 | // T to ?T |
src-self-hosted/astgen.zig+1-1| ... | ... | @@ -311,7 +311,7 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si |
| 311 | 311 | const src = tree.token_locs[node.rtoken].start; |
| 312 | 312 | |
| 313 | 313 | const operand = try expr(mod, scope, .lvalue, node.lhs); |
| 314 | const unwrapped_ptr = try addZIRInst(mod, scope, src, zir.Inst.UnwrapOptional, .{ .operand = operand }, .{}); | |
| 314 | const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand); | |
| 315 | 315 | if (rl == .lvalue) return unwrapped_ptr; |
| 316 | 316 | |
| 317 | 317 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); |
src-self-hosted/codegen.zig+3-2| ... | ... | @@ -668,7 +668,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 668 | 668 | .store => return self.genStore(inst.castTag(.store).?), |
| 669 | 669 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 670 | 670 | .unreach => return MCValue{ .unreach = {} }, |
| 671 | .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), | |
| 671 | .unwrap_optional_safe => return self.genUnwrapOptional(inst.castTag(.unwrap_optional_safe).?, true), | |
| 672 | .unwrap_optional_unsafe => return self.genUnwrapOptional(inst.castTag(.unwrap_optional_unsafe).?, false), | |
| 672 | 673 | } |
| 673 | 674 | } |
| 674 | 675 | |
| ... | ... | @@ -818,7 +819,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 818 | 819 | } |
| 819 | 820 | } |
| 820 | 821 | |
| 821 | fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnwrapOptional) !MCValue { | |
| 822 | fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp, safety_check: bool) !MCValue { | |
| 822 | 823 | // No side effects, so if it's unreferenced, do nothing. |
| 823 | 824 | if (inst.base.isUnused()) |
| 824 | 825 | return MCValue.dead; |
src-self-hosted/ir.zig+4-23| ... | ... | @@ -82,7 +82,8 @@ pub const Inst = struct { |
| 82 | 82 | not, |
| 83 | 83 | floatcast, |
| 84 | 84 | intcast, |
| 85 | unwrap_optional, | |
| 85 | unwrap_optional_safe, | |
| 86 | unwrap_optional_unsafe, | |
| 86 | 87 | |
| 87 | 88 | pub fn Type(tag: Tag) type { |
| 88 | 89 | return switch (tag) { |
| ... | ... | @@ -103,6 +104,8 @@ pub const Inst = struct { |
| 103 | 104 | .floatcast, |
| 104 | 105 | .intcast, |
| 105 | 106 | .load, |
| 107 | .unwrap_optional_safe, | |
| 108 | .unwrap_optional_unsafe, | |
| 106 | 109 | => UnOp, |
| 107 | 110 | |
| 108 | 111 | .add, |
| ... | ... | @@ -125,7 +128,6 @@ pub const Inst = struct { |
| 125 | 128 | .condbr => CondBr, |
| 126 | 129 | .constant => Constant, |
| 127 | 130 | .loop => Loop, |
| 128 | .unwrap_optional => UnwrapOptional, | |
| 129 | 131 | }; |
| 130 | 132 | } |
| 131 | 133 | |
| ... | ... | @@ -421,27 +423,6 @@ pub const Inst = struct { |
| 421 | 423 | return null; |
| 422 | 424 | } |
| 423 | 425 | }; |
| 424 | ||
| 425 | pub const UnwrapOptional = struct { | |
| 426 | pub const base_tag = Tag.unwrap_optional; | |
| 427 | base: Inst, | |
| 428 | ||
| 429 | operand: *Inst, | |
| 430 | safety_check: bool, | |
| 431 | ||
| 432 | pub fn operandCount(self: *const UnwrapOptional) usize { | |
| 433 | return 1; | |
| 434 | } | |
| 435 | pub fn getOperand(self: *const UnwrapOptional, index: usize) ?*Inst { | |
| 436 | var i = index; | |
| 437 | ||
| 438 | if (i < 1) | |
| 439 | return self.operand; | |
| 440 | i -= 1; | |
| 441 | ||
| 442 | return null; | |
| 443 | } | |
| 444 | }; | |
| 445 | 426 | }; |
| 446 | 427 | |
| 447 | 428 | pub const Body = struct { |
src-self-hosted/type.zig+8-2| ... | ... | @@ -185,8 +185,6 @@ pub const Type = extern union { |
| 185 | 185 | return true; |
| 186 | 186 | }, |
| 187 | 187 | .Optional => { |
| 188 | if (a.tag() != b.tag()) | |
| 189 | return false; | |
| 190 | 188 | return a.elemType().eql(b.elemType()); |
| 191 | 189 | }, |
| 192 | 190 | .Float, |
| ... | ... | @@ -662,6 +660,10 @@ pub const Type = extern union { |
| 662 | 660 | .optional => { |
| 663 | 661 | const child_type = self.cast(Payload.Optional).?.child_type; |
| 664 | 662 | if (!child_type.hasCodeGenBits()) return 1; |
| 663 | ||
| 664 | if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) | |
| 665 | return @divExact(target.cpu.arch.ptrBitWidth(), 8); | |
| 666 | ||
| 665 | 667 | return child_type.abiAlignment(target); |
| 666 | 668 | }, |
| 667 | 669 | |
| ... | ... | @@ -750,6 +752,10 @@ pub const Type = extern union { |
| 750 | 752 | .optional => { |
| 751 | 753 | const child_type = self.cast(Payload.Optional).?.child_type; |
| 752 | 754 | if (!child_type.hasCodeGenBits()) return 1; |
| 755 | ||
| 756 | if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) | |
| 757 | return @divExact(target.cpu.arch.ptrBitWidth(), 8); | |
| 758 | ||
| 753 | 759 | // Optional types are represented as a struct with the child type as the first |
| 754 | 760 | // field and a boolean as the second. Since the child type's abi alignment is |
| 755 | 761 | // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal |
src-self-hosted/zir.zig+9-34| ... | ... | @@ -215,7 +215,9 @@ pub const Inst = struct { |
| 215 | 215 | /// Create an optional type '?T' |
| 216 | 216 | optional_type, |
| 217 | 217 | /// Unwraps an optional value 'lhs.?' |
| 218 | unwrap_optional, | |
| 218 | unwrap_optional_safe, | |
| 219 | /// Same as previous, but without safety checks. Used for orelse, if and while | |
| 220 | unwrap_optional_unsafe, | |
| 219 | 221 | |
| 220 | 222 | pub fn Type(tag: Tag) type { |
| 221 | 223 | return switch (tag) { |
| ... | ... | @@ -245,6 +247,8 @@ pub const Inst = struct { |
| 245 | 247 | .single_const_ptr_type, |
| 246 | 248 | .single_mut_ptr_type, |
| 247 | 249 | .optional_type, |
| 250 | .unwrap_optional_safe, | |
| 251 | .unwrap_optional_unsafe, | |
| 248 | 252 | => UnOp, |
| 249 | 253 | |
| 250 | 254 | .add, |
| ... | ... | @@ -303,7 +307,6 @@ pub const Inst = struct { |
| 303 | 307 | .fntype => FnType, |
| 304 | 308 | .elemptr => ElemPtr, |
| 305 | 309 | .condbr => CondBr, |
| 306 | .unwrap_optional => UnwrapOptional, | |
| 307 | 310 | }; |
| 308 | 311 | } |
| 309 | 312 | |
| ... | ... | @@ -379,7 +382,8 @@ pub const Inst = struct { |
| 379 | 382 | .typeof, |
| 380 | 383 | .xor, |
| 381 | 384 | .optional_type, |
| 382 | .unwrap_optional, | |
| 385 | .unwrap_optional_safe, | |
| 386 | .unwrap_optional_unsafe, | |
| 383 | 387 | => false, |
| 384 | 388 | |
| 385 | 389 | .@"break", |
| ... | ... | @@ -820,18 +824,6 @@ pub const Inst = struct { |
| 820 | 824 | }, |
| 821 | 825 | kw_args: struct {}, |
| 822 | 826 | }; |
| 823 | ||
| 824 | pub const UnwrapOptional = struct { | |
| 825 | pub const base_tag = Tag.unwrap_optional; | |
| 826 | base: Inst, | |
| 827 | ||
| 828 | positionals: struct { | |
| 829 | operand: *Inst, | |
| 830 | }, | |
| 831 | kw_args: struct { | |
| 832 | safety_check: bool = true, | |
| 833 | }, | |
| 834 | }; | |
| 835 | 827 | }; |
| 836 | 828 | |
| 837 | 829 | pub const ErrorMsg = struct { |
| ... | ... | @@ -1935,6 +1927,8 @@ const EmitZIR = struct { |
| 1935 | 1927 | .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull), |
| 1936 | 1928 | .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), |
| 1937 | 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), | |
| 1931 | .unwrap_optional_unsafe => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional_unsafe).?, .unwrap_optional_unsafe), | |
| 1938 | 1932 | |
| 1939 | 1933 | .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), |
| 1940 | 1934 | .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), |
| ... | ... | @@ -2157,25 +2151,6 @@ const EmitZIR = struct { |
| 2157 | 2151 | }; |
| 2158 | 2152 | break :blk &new_inst.base; |
| 2159 | 2153 | }, |
| 2160 | ||
| 2161 | .unwrap_optional => blk: { | |
| 2162 | const old_inst = inst.castTag(.unwrap_optional).?; | |
| 2163 | ||
| 2164 | const new_inst = try self.arena.allocator.create(Inst.UnwrapOptional); | |
| 2165 | new_inst.* = .{ | |
| 2166 | .base = .{ | |
| 2167 | .src = inst.src, | |
| 2168 | .tag = Inst.UnwrapOptional.base_tag, | |
| 2169 | }, | |
| 2170 | .positionals = .{ | |
| 2171 | .operand = try self.resolveInst(new_body, old_inst.operand), | |
| 2172 | }, | |
| 2173 | .kw_args = .{ | |
| 2174 | .safety_check = old_inst.safety_check, | |
| 2175 | }, | |
| 2176 | }; | |
| 2177 | break :blk &new_inst.base; | |
| 2178 | }, | |
| 2179 | 2154 | }; |
| 2180 | 2155 | try instructions.append(new_inst); |
| 2181 | 2156 | try inst_table.put(inst, new_inst); |
src-self-hosted/zir_sema.zig+7-3| ... | ... | @@ -107,7 +107,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 107 | 107 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), |
| 108 | 108 | .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), |
| 109 | 109 | .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?), |
| 110 | .unwrap_optional => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional).?), | |
| 110 | .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true), | |
| 111 | .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), | |
| 111 | 112 | } |
| 112 | 113 | } |
| 113 | 114 | |
| ... | ... | @@ -661,7 +662,7 @@ fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp |
| 661 | 662 | })); |
| 662 | 663 | } |
| 663 | 664 | |
| 664 | fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnwrapOptional) InnerError!*Inst { | |
| 665 | fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { | |
| 665 | 666 | const operand = try resolveInst(mod, scope, unwrap.positionals.operand); |
| 666 | 667 | assert(operand.ty.zigTypeTag() == .Pointer); |
| 667 | 668 | |
| ... | ... | @@ -686,7 +687,10 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.Unwr |
| 686 | 687 | } |
| 687 | 688 | |
| 688 | 689 | const b = try mod.requireRuntimeBlock(scope, unwrap.base.src); |
| 689 | return mod.addUnwrapOptional(b, unwrap.base.src, child_pointer, operand, unwrap.kw_args.safety_check); | |
| 690 | return if (safety_check) | |
| 691 | mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional_safe, operand) | |
| 692 | else | |
| 693 | mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional_unsafe, operand); | |
| 690 | 694 | } |
| 691 | 695 | |
| 692 | 696 | fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { |
test/stage2/compare_output.zig+24-5| ... | ... | @@ -31,11 +31,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 31 | 31 | \\export fn _start() noreturn { |
| 32 | 32 | \\ print(); |
| 33 | 33 | \\ |
| 34 | \\ const a: u32 = 2; | |
| 35 | \\ const b: ?u32 = a; | |
| 36 | \\ const c = b.?; | |
| 37 | \\ if (c != 2) unreachable; | |
| 38 | \\ | |
| 39 | 34 | \\ exit(); |
| 40 | 35 | \\} |
| 41 | 36 | \\ |
| ... | ... | @@ -446,5 +441,29 @@ pub fn addCases(ctx: *TestContext) !void { |
| 446 | 441 | , |
| 447 | 442 | "", |
| 448 | 443 | ); |
| 444 | ||
| 445 | // Optionals | |
| 446 | case.addCompareOutput( | |
| 447 | \\export fn _start() noreturn { | |
| 448 | \\ const a: u32 = 2; | |
| 449 | \\ const b: ?u32 = a; | |
| 450 | \\ const c = b.?; | |
| 451 | \\ if (c != 2) unreachable; | |
| 452 | \\ | |
| 453 | \\ exit(); | |
| 454 | \\} | |
| 455 | \\ | |
| 456 | \\fn exit() noreturn { | |
| 457 | \\ asm volatile ("syscall" | |
| 458 | \\ : | |
| 459 | \\ : [number] "{rax}" (231), | |
| 460 | \\ [arg1] "{rdi}" (0) | |
| 461 | \\ : "rcx", "r11", "memory" | |
| 462 | \\ ); | |
| 463 | \\ unreachable; | |
| 464 | \\} | |
| 465 | , | |
| 466 | "", | |
| 467 | ); | |
| 449 | 468 | } |
| 450 | 469 | } |