| author | |
| committer | |
| log | 331cc810ded2058f2e2767b0485eb18d888a45e5 |
| tree | 9e5ba37e5b06eaccf5ff36976955a32cda5ddfc4 |
| parent | 90059a12e0ffe433132450f9f43221a198a22106 |
| parent | 16f9774d2d6f358c97637e35609dfe0fc14cb501 |
| signature |
stage2,x64: basic (un)tagged unions4 files changed, 217 insertions(+), 76 deletions(-)
src/arch/x86_64/CodeGen.zig+96-46| ... | @@ -2098,17 +2098,72 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2098,17 +2098,72 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2098 | 2098 | ||
| 2099 | fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | 2099 | fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| 2100 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2100 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2101 | _ = bin_op; | 2101 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2102 | return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); | 2102 | const union_ty = ptr_ty.childType(); |
| 2103 | const tag_ty = self.air.typeOf(bin_op.rhs); | ||
| 2104 | const layout = union_ty.unionGetLayout(self.target.*); | ||
| 2105 | |||
| 2106 | if (layout.tag_size == 0) { | ||
| 2107 | return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); | ||
| 2108 | } | ||
| 2109 | |||
| 2110 | const ptr = try self.resolveInst(bin_op.lhs); | ||
| 2111 | ptr.freezeIfRegister(&self.register_manager); | ||
| 2112 | defer ptr.unfreezeIfRegister(&self.register_manager); | ||
| 2113 | |||
| 2114 | const tag = try self.resolveInst(bin_op.rhs); | ||
| 2115 | tag.freezeIfRegister(&self.register_manager); | ||
| 2116 | defer tag.unfreezeIfRegister(&self.register_manager); | ||
| 2117 | |||
| 2118 | const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: { | ||
| 2119 | // TODO reusing the operand | ||
| 2120 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); | ||
| 2121 | try self.genBinMathOpMir(.add, ptr_ty, .{ .register = reg }, .{ .immediate = layout.payload_size }); | ||
| 2122 | break :blk MCValue{ .register = reg }; | ||
| 2123 | } else ptr; | ||
| 2124 | |||
| 2125 | try self.store(adjusted_ptr, tag, ptr_ty, tag_ty); | ||
| 2126 | |||
| 2127 | return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); | ||
| 2103 | } | 2128 | } |
| 2104 | 2129 | ||
| 2105 | fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | 2130 | fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| 2106 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2131 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2107 | const result: MCValue = if (self.liveness.isUnused(inst)) | 2132 | if (self.liveness.isUnused(inst)) { |
| 2108 | .dead | 2133 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| 2109 | else | 2134 | } |
| 2110 | return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); | 2135 | |
| 2111 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 2136 | const tag_ty = self.air.typeOfIndex(inst); |
| 2137 | const union_ty = self.air.typeOf(ty_op.operand); | ||
| 2138 | const layout = union_ty.unionGetLayout(self.target.*); | ||
| 2139 | |||
| 2140 | if (layout.tag_size == 0) { | ||
| 2141 | return self.finishAir(inst, .none, .{ ty_op.operand, .none, .none }); | ||
| 2142 | } | ||
| 2143 | |||
| 2144 | // TODO reusing the operand | ||
| 2145 | const operand = try self.resolveInst(ty_op.operand); | ||
| 2146 | operand.freezeIfRegister(&self.register_manager); | ||
| 2147 | defer operand.unfreezeIfRegister(&self.register_manager); | ||
| 2148 | |||
| 2149 | const tag_abi_size = tag_ty.abiSize(self.target.*); | ||
| 2150 | const offset: i32 = if (layout.tag_align < layout.payload_align) @intCast(i32, layout.payload_size) else 0; | ||
| 2151 | const dst_mcv: MCValue = blk: { | ||
| 2152 | switch (operand) { | ||
| 2153 | .stack_offset => |off| { | ||
| 2154 | if (tag_abi_size <= 8) { | ||
| 2155 | break :blk try self.copyToRegisterWithInstTracking(inst, tag_ty, .{ | ||
| 2156 | .stack_offset = off - offset, | ||
| 2157 | }); | ||
| 2158 | } | ||
| 2159 | |||
| 2160 | return self.fail("TODO implement get_union_tag for ABI larger than 8 bytes and operand {}", .{operand}); | ||
| 2161 | }, | ||
| 2162 | else => return self.fail("TODO implement get_union_tag for {}", .{operand}), | ||
| 2163 | } | ||
| 2164 | }; | ||
| 2165 | |||
| 2166 | return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); | ||
| 2112 | } | 2167 | } |
| 2113 | 2168 | ||
| 2114 | fn airClz(self: *Self, inst: Air.Inst.Index) !void { | 2169 | fn airClz(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2429,8 +2484,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2429,8 +2484,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2429 | }); | 2484 | }); |
| 2430 | }, | 2485 | }, |
| 2431 | .stack_offset => { | 2486 | .stack_offset => { |
| 2432 | const tmp_reg = try self.copyToTmpRegister(value_ty, value); | 2487 | if (abi_size <= 8) { |
| 2433 | return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); | 2488 | const tmp_reg = try self.copyToTmpRegister(value_ty, value); |
| 2489 | return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); | ||
| 2490 | } | ||
| 2491 | |||
| 2492 | try self.genInlineMemcpy(0, value_ty, value, .{ | ||
| 2493 | .source_stack_base = .rbp, | ||
| 2494 | .dest_stack_base = reg.to64(), | ||
| 2495 | }); | ||
| 2434 | }, | 2496 | }, |
| 2435 | else => |other| { | 2497 | else => |other| { |
| 2436 | return self.fail("TODO implement set pointee with {}", .{other}); | 2498 | return self.fail("TODO implement set pointee with {}", .{other}); |
| ... | @@ -3905,36 +3967,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u | ... | @@ -3905,36 +3967,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u |
| 3905 | .dead, .unreach => unreachable, | 3967 | .dead, .unreach => unreachable, |
| 3906 | .immediate => |imm| { | 3968 | .immediate => |imm| { |
| 3907 | _ = try self.addInst(.{ | 3969 | _ = try self.addInst(.{ |
| 3908 | .tag = .@"test", | 3970 | .tag = .xor, |
| 3909 | .ops = (Mir.Ops{ | 3971 | .ops = (Mir.Ops{ |
| 3910 | .reg1 = registerAlias(cond_reg, abi_size), | 3972 | .reg1 = registerAlias(cond_reg, abi_size), |
| 3911 | }).encode(), | 3973 | }).encode(), |
| 3912 | .data = .{ .imm = @intCast(u32, imm) }, | 3974 | .data = .{ .imm = @intCast(u32, imm) }, |
| 3913 | }); | 3975 | }); |
| 3914 | return self.addInst(.{ | ||
| 3915 | .tag = .cond_jmp_eq_ne, | ||
| 3916 | .ops = (Mir.Ops{ | ||
| 3917 | .flags = 0b00, | ||
| 3918 | }).encode(), | ||
| 3919 | .data = .{ .inst = undefined }, | ||
| 3920 | }); | ||
| 3921 | }, | 3976 | }, |
| 3922 | .register => |reg| { | 3977 | .register => |reg| { |
| 3923 | _ = try self.addInst(.{ | 3978 | _ = try self.addInst(.{ |
| 3924 | .tag = .@"test", | 3979 | .tag = .xor, |
| 3925 | .ops = (Mir.Ops{ | 3980 | .ops = (Mir.Ops{ |
| 3926 | .reg1 = registerAlias(cond_reg, abi_size), | 3981 | .reg1 = registerAlias(cond_reg, abi_size), |
| 3927 | .reg2 = registerAlias(reg, abi_size), | 3982 | .reg2 = registerAlias(reg, abi_size), |
| 3928 | }).encode(), | 3983 | }).encode(), |
| 3929 | .data = undefined, | 3984 | .data = undefined, |
| 3930 | }); | 3985 | }); |
| 3931 | return self.addInst(.{ | ||
| 3932 | .tag = .cond_jmp_eq_ne, | ||
| 3933 | .ops = (Mir.Ops{ | ||
| 3934 | .flags = 0b00, | ||
| 3935 | }).encode(), | ||
| 3936 | .data = .{ .inst = undefined }, | ||
| 3937 | }); | ||
| 3938 | }, | 3986 | }, |
| 3939 | .stack_offset => { | 3987 | .stack_offset => { |
| 3940 | if (abi_size <= 8) { | 3988 | if (abi_size <= 8) { |
| ... | @@ -3948,6 +3996,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u | ... | @@ -3948,6 +3996,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u |
| 3948 | return self.fail("TODO implement switch mir when case is {}", .{case}); | 3996 | return self.fail("TODO implement switch mir when case is {}", .{case}); |
| 3949 | }, | 3997 | }, |
| 3950 | } | 3998 | } |
| 3999 | |||
| 4000 | _ = try self.addInst(.{ | ||
| 4001 | .tag = .@"test", | ||
| 4002 | .ops = (Mir.Ops{ | ||
| 4003 | .reg1 = registerAlias(cond_reg, abi_size), | ||
| 4004 | .reg2 = registerAlias(cond_reg, abi_size), | ||
| 4005 | }).encode(), | ||
| 4006 | .data = undefined, | ||
| 4007 | }); | ||
| 4008 | return self.addInst(.{ | ||
| 4009 | .tag = .cond_jmp_eq_ne, | ||
| 4010 | .ops = (Mir.Ops{ | ||
| 4011 | .flags = 0b00, | ||
| 4012 | }).encode(), | ||
| 4013 | .data = .{ .inst = undefined }, | ||
| 4014 | }); | ||
| 3951 | }, | 4015 | }, |
| 3952 | .stack_offset => { | 4016 | .stack_offset => { |
| 3953 | try self.spillCompareFlagsIfOccupied(); | 4017 | try self.spillCompareFlagsIfOccupied(); |
| ... | @@ -5408,24 +5472,14 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { | ... | @@ -5408,24 +5472,14 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 5408 | } | 5472 | } |
| 5409 | 5473 | ||
| 5410 | switch (typed_value.ty.zigTypeTag()) { | 5474 | switch (typed_value.ty.zigTypeTag()) { |
| 5411 | .Array => { | ||
| 5412 | return self.lowerUnnamedConst(typed_value); | ||
| 5413 | }, | ||
| 5414 | .Pointer => switch (typed_value.ty.ptrSize()) { | 5475 | .Pointer => switch (typed_value.ty.ptrSize()) { |
| 5415 | .Slice => { | 5476 | .Slice => {}, |
| 5416 | return self.lowerUnnamedConst(typed_value); | ||
| 5417 | }, | ||
| 5418 | else => { | 5477 | else => { |
| 5419 | switch (typed_value.val.tag()) { | 5478 | switch (typed_value.val.tag()) { |
| 5420 | .int_u64 => { | 5479 | .int_u64 => { |
| 5421 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; | 5480 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; |
| 5422 | }, | 5481 | }, |
| 5423 | .slice => { | 5482 | else => {}, |
| 5424 | return self.lowerUnnamedConst(typed_value); | ||
| 5425 | }, | ||
| 5426 | else => { | ||
| 5427 | return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); | ||
| 5428 | }, | ||
| 5429 | } | 5483 | } |
| 5430 | }, | 5484 | }, |
| 5431 | }, | 5485 | }, |
| ... | @@ -5434,10 +5488,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { | ... | @@ -5434,10 +5488,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 5434 | if (info.bits <= ptr_bits and info.signedness == .signed) { | 5488 | if (info.bits <= ptr_bits and info.signedness == .signed) { |
| 5435 | return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) }; | 5489 | return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) }; |
| 5436 | } | 5490 | } |
| 5437 | if (info.bits > ptr_bits or info.signedness == .signed) { | 5491 | if (!(info.bits > ptr_bits or info.signedness == .signed)) { |
| 5438 | return self.fail("TODO const int bigger than ptr and signed int", .{}); | 5492 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; |
| 5439 | } | 5493 | } |
| 5440 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; | ||
| 5441 | }, | 5494 | }, |
| 5442 | .Bool => { | 5495 | .Bool => { |
| 5443 | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; | 5496 | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; |
| ... | @@ -5457,7 +5510,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { | ... | @@ -5457,7 +5510,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 5457 | } else if (typed_value.ty.abiSize(self.target.*) == 1) { | 5510 | } else if (typed_value.ty.abiSize(self.target.*) == 1) { |
| 5458 | return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; | 5511 | return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; |
| 5459 | } | 5512 | } |
| 5460 | return self.fail("TODO non pointer optionals", .{}); | ||
| 5461 | }, | 5513 | }, |
| 5462 | .Enum => { | 5514 | .Enum => { |
| 5463 | if (typed_value.val.castTag(.enum_field_index)) |field_index| { | 5515 | if (typed_value.val.castTag(.enum_field_index)) |field_index| { |
| ... | @@ -5504,13 +5556,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { | ... | @@ -5504,13 +5556,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 5504 | return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); | 5556 | return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); |
| 5505 | } | 5557 | } |
| 5506 | } | 5558 | } |
| 5507 | return self.lowerUnnamedConst(typed_value); | ||
| 5508 | }, | ||
| 5509 | .Struct => { | ||
| 5510 | return self.lowerUnnamedConst(typed_value); | ||
| 5511 | }, | 5559 | }, |
| 5512 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), | 5560 | else => {}, |
| 5513 | } | 5561 | } |
| 5562 | |||
| 5563 | return self.lowerUnnamedConst(typed_value); | ||
| 5514 | } | 5564 | } |
| 5515 | 5565 | ||
| 5516 | const CallMCValues = struct { | 5566 | const CallMCValues = struct { |
src/arch/x86_64/Emit.zig+6| ... | @@ -1859,6 +1859,9 @@ fn lowerToRmEnc( | ... | @@ -1859,6 +1859,9 @@ fn lowerToRmEnc( |
| 1859 | switch (reg_or_mem) { | 1859 | switch (reg_or_mem) { |
| 1860 | .register => |src_reg| { | 1860 | .register => |src_reg| { |
| 1861 | const encoder = try Encoder.init(code, 4); | 1861 | const encoder = try Encoder.init(code, 4); |
| 1862 | if (reg.size() == 16) { | ||
| 1863 | encoder.prefix16BitMode(); | ||
| 1864 | } | ||
| 1862 | encoder.rex(.{ | 1865 | encoder.rex(.{ |
| 1863 | .w = setRexWRegister(reg) or setRexWRegister(src_reg), | 1866 | .w = setRexWRegister(reg) or setRexWRegister(src_reg), |
| 1864 | .r = reg.isExtended(), | 1867 | .r = reg.isExtended(), |
| ... | @@ -1902,6 +1905,9 @@ fn lowerToMrEnc( | ... | @@ -1902,6 +1905,9 @@ fn lowerToMrEnc( |
| 1902 | switch (reg_or_mem) { | 1905 | switch (reg_or_mem) { |
| 1903 | .register => |dst_reg| { | 1906 | .register => |dst_reg| { |
| 1904 | const encoder = try Encoder.init(code, 3); | 1907 | const encoder = try Encoder.init(code, 3); |
| 1908 | if (dst_reg.size() == 16) { | ||
| 1909 | encoder.prefix16BitMode(); | ||
| 1910 | } | ||
| 1905 | encoder.rex(.{ | 1911 | encoder.rex(.{ |
| 1906 | .w = setRexWRegister(dst_reg) or setRexWRegister(reg), | 1912 | .w = setRexWRegister(dst_reg) or setRexWRegister(reg), |
| 1907 | .r = reg.isExtended(), | 1913 | .r = reg.isExtended(), |
src/codegen.zig+67-3| ... | @@ -466,10 +466,74 @@ pub fn generateSymbol( | ... | @@ -466,10 +466,74 @@ pub fn generateSymbol( |
| 466 | return Result{ .appended = {} }; | 466 | return Result{ .appended = {} }; |
| 467 | }, | 467 | }, |
| 468 | .Union => { | 468 | .Union => { |
| 469 | // TODO generateSymbol for unions | 469 | // TODO generate debug info for unions |
| 470 | const target = bin_file.options.target; | 470 | const target = bin_file.options.target; |
| 471 | const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); | 471 | const union_obj = typed_value.val.castTag(.@"union").?.data; |
| 472 | try code.writer().writeByteNTimes(0xaa, abi_size); | 472 | const layout = typed_value.ty.unionGetLayout(target); |
| 473 | |||
| 474 | if (layout.payload_size == 0) { | ||
| 475 | switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ | ||
| 476 | .ty = typed_value.ty.unionTagType().?, | ||
| 477 | .val = union_obj.tag, | ||
| 478 | }, code, debug_output)) { | ||
| 479 | .appended => {}, | ||
| 480 | .externally_managed => |external_slice| { | ||
| 481 | code.appendSliceAssumeCapacity(external_slice); | ||
| 482 | }, | ||
| 483 | .fail => |em| return Result{ .fail = em }, | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | // Check if we should store the tag first. | ||
| 488 | if (layout.tag_align >= layout.payload_align) { | ||
| 489 | switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ | ||
| 490 | .ty = typed_value.ty.unionTagType().?, | ||
| 491 | .val = union_obj.tag, | ||
| 492 | }, code, debug_output)) { | ||
| 493 | .appended => {}, | ||
| 494 | .externally_managed => |external_slice| { | ||
| 495 | code.appendSliceAssumeCapacity(external_slice); | ||
| 496 | }, | ||
| 497 | .fail => |em| return Result{ .fail = em }, | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data; | ||
| 502 | const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag).?; | ||
| 503 | assert(union_ty.haveFieldTypes()); | ||
| 504 | const field_ty = union_ty.fields.values()[field_index].ty; | ||
| 505 | if (!field_ty.hasRuntimeBits()) { | ||
| 506 | try code.writer().writeByteNTimes(0xaa, try math.cast(usize, layout.payload_size)); | ||
| 507 | } else { | ||
| 508 | switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ | ||
| 509 | .ty = field_ty, | ||
| 510 | .val = union_obj.val, | ||
| 511 | }, code, debug_output)) { | ||
| 512 | .appended => {}, | ||
| 513 | .externally_managed => |external_slice| { | ||
| 514 | code.appendSliceAssumeCapacity(external_slice); | ||
| 515 | }, | ||
| 516 | .fail => |em| return Result{ .fail = em }, | ||
| 517 | } | ||
| 518 | |||
| 519 | const padding = try math.cast(usize, layout.payload_size - field_ty.abiSize(target)); | ||
| 520 | if (padding > 0) { | ||
| 521 | try code.writer().writeByteNTimes(0, padding); | ||
| 522 | } | ||
| 523 | } | ||
| 524 | |||
| 525 | if (layout.tag_size > 0) { | ||
| 526 | switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ | ||
| 527 | .ty = union_ty.tag_ty, | ||
| 528 | .val = union_obj.tag, | ||
| 529 | }, code, debug_output)) { | ||
| 530 | .appended => {}, | ||
| 531 | .externally_managed => |external_slice| { | ||
| 532 | code.appendSliceAssumeCapacity(external_slice); | ||
| 533 | }, | ||
| 534 | .fail => |em| return Result{ .fail = em }, | ||
| 535 | } | ||
| 536 | } | ||
| 473 | 537 | ||
| 474 | return Result{ .appended = {} }; | 538 | return Result{ .appended = {} }; |
| 475 | }, | 539 | }, |
test/behavior/union.zig+48-27| ... | @@ -4,68 +4,100 @@ const expect = std.testing.expect; | ... | @@ -4,68 +4,100 @@ const expect = std.testing.expect; |
| 4 | const expectEqual = std.testing.expectEqual; | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | const Tag = std.meta.Tag; | 5 | const Tag = std.meta.Tag; |
| 6 | 6 | ||
| 7 | const Foo = union { | 7 | const FooWithFloats = union { |
| 8 | float: f64, | 8 | float: f64, |
| 9 | int: i32, | 9 | int: i32, |
| 10 | }; | 10 | }; |
| 11 | 11 | ||
| 12 | test "basic unions" { | 12 | test "basic unions with floats" { |
| 13 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 13 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 14 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 15 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 15 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 16 | 16 | ||
| 17 | var foo = Foo{ .int = 1 }; | 17 | var foo = FooWithFloats{ .int = 1 }; |
| 18 | try expect(foo.int == 1); | 18 | try expect(foo.int == 1); |
| 19 | foo = Foo{ .float = 12.34 }; | 19 | foo = FooWithFloats{ .float = 12.34 }; |
| 20 | try expect(foo.float == 12.34); | 20 | try expect(foo.float == 12.34); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | test "init union with runtime value" { | 23 | fn setFloat(foo: *FooWithFloats, x: f64) void { |
| 24 | foo.* = FooWithFloats{ .float = x }; | ||
| 25 | } | ||
| 26 | |||
| 27 | test "init union with runtime value - floats" { | ||
| 24 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 28 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 25 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 29 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 26 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 30 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 27 | 31 | ||
| 28 | var foo: Foo = undefined; | 32 | var foo: FooWithFloats = undefined; |
| 29 | 33 | ||
| 30 | setFloat(&foo, 12.34); | 34 | setFloat(&foo, 12.34); |
| 31 | try expect(foo.float == 12.34); | 35 | try expect(foo.float == 12.34); |
| 36 | } | ||
| 37 | |||
| 38 | test "basic unions" { | ||
| 39 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 40 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 41 | |||
| 42 | var foo = Foo{ .int = 1 }; | ||
| 43 | try expect(foo.int == 1); | ||
| 44 | foo = Foo{ .str = .{ .slice = "Hello!" } }; | ||
| 45 | try expect(std.mem.eql(u8, foo.str.slice, "Hello!")); | ||
| 46 | } | ||
| 47 | |||
| 48 | const Foo = union { | ||
| 49 | int: i32, | ||
| 50 | str: struct { | ||
| 51 | slice: []const u8, | ||
| 52 | }, | ||
| 53 | }; | ||
| 54 | |||
| 55 | test "init union with runtime value" { | ||
| 56 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 57 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 58 | |||
| 59 | var foo: Foo = undefined; | ||
| 32 | 60 | ||
| 33 | setInt(&foo, 42); | 61 | setInt(&foo, 42); |
| 34 | try expect(foo.int == 42); | 62 | try expect(foo.int == 42); |
| 35 | } | ||
| 36 | 63 | ||
| 37 | fn setFloat(foo: *Foo, x: f64) void { | 64 | setStr(&foo, "Hello!"); |
| 38 | foo.* = Foo{ .float = x }; | 65 | try expect(std.mem.eql(u8, foo.str.slice, "Hello!")); |
| 39 | } | 66 | } |
| 40 | 67 | ||
| 41 | fn setInt(foo: *Foo, x: i32) void { | 68 | fn setInt(foo: *Foo, x: i32) void { |
| 42 | foo.* = Foo{ .int = x }; | 69 | foo.* = Foo{ .int = x }; |
| 43 | } | 70 | } |
| 44 | 71 | ||
| 72 | fn setStr(foo: *Foo, slice: []const u8) void { | ||
| 73 | foo.* = Foo{ .str = .{ .slice = slice } }; | ||
| 74 | } | ||
| 75 | |||
| 45 | test "comptime union field access" { | 76 | test "comptime union field access" { |
| 46 | comptime { | 77 | comptime { |
| 47 | var foo = Foo{ .int = 0 }; | 78 | var foo = FooWithFloats{ .int = 0 }; |
| 48 | try expect(foo.int == 0); | 79 | try expect(foo.int == 0); |
| 49 | 80 | ||
| 50 | foo = Foo{ .float = 42.42 }; | 81 | foo = FooWithFloats{ .float = 12.34 }; |
| 51 | try expect(foo.float == 42.42); | 82 | try expect(foo.float == 12.34); |
| 52 | } | 83 | } |
| 53 | } | 84 | } |
| 54 | 85 | ||
| 55 | const FooExtern = extern union { | 86 | const FooExtern = extern union { |
| 56 | float: f64, | ||
| 57 | int: i32, | 87 | int: i32, |
| 88 | str: struct { | ||
| 89 | slice: []const u8, | ||
| 90 | }, | ||
| 58 | }; | 91 | }; |
| 59 | 92 | ||
| 60 | test "basic extern unions" { | 93 | test "basic extern unions" { |
| 61 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 62 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 94 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 63 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 95 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 64 | 96 | ||
| 65 | var foo = FooExtern{ .int = 1 }; | 97 | var foo = FooExtern{ .int = 1 }; |
| 66 | try expect(foo.int == 1); | 98 | try expect(foo.int == 1); |
| 67 | foo.float = 12.34; | 99 | foo.str.slice = "Well"; |
| 68 | try expect(foo.float == 12.34); | 100 | try expect(std.mem.eql(u8, foo.str.slice, "Well")); |
| 69 | } | 101 | } |
| 70 | 102 | ||
| 71 | const ExternPtrOrInt = extern union { | 103 | const ExternPtrOrInt = extern union { |
| ... | @@ -129,7 +161,6 @@ test "access a member of tagged union with conflicting enum tag name" { | ... | @@ -129,7 +161,6 @@ test "access a member of tagged union with conflicting enum tag name" { |
| 129 | } | 161 | } |
| 130 | 162 | ||
| 131 | test "constant tagged union with payload" { | 163 | test "constant tagged union with payload" { |
| 132 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 133 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 164 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 134 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 165 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 135 | 166 | ||
| ... | @@ -227,7 +258,6 @@ fn testComparison() !void { | ... | @@ -227,7 +258,6 @@ fn testComparison() !void { |
| 227 | } | 258 | } |
| 228 | 259 | ||
| 229 | test "comparison between union and enum literal" { | 260 | test "comparison between union and enum literal" { |
| 230 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 231 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 261 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 232 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 262 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 233 | 263 | ||
| ... | @@ -297,7 +327,6 @@ pub const PackThis = union(enum) { | ... | @@ -297,7 +327,6 @@ pub const PackThis = union(enum) { |
| 297 | }; | 327 | }; |
| 298 | 328 | ||
| 299 | test "constant packed union" { | 329 | test "constant packed union" { |
| 300 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 301 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 330 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 302 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 331 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 303 | 332 | ||
| ... | @@ -457,7 +486,6 @@ test "update the tag value for zero-sized unions" { | ... | @@ -457,7 +486,6 @@ test "update the tag value for zero-sized unions" { |
| 457 | } | 486 | } |
| 458 | 487 | ||
| 459 | test "union initializer generates padding only if needed" { | 488 | test "union initializer generates padding only if needed" { |
| 460 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 461 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 489 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 462 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 490 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 463 | 491 | ||
| ... | @@ -470,7 +498,6 @@ test "union initializer generates padding only if needed" { | ... | @@ -470,7 +498,6 @@ test "union initializer generates padding only if needed" { |
| 470 | } | 498 | } |
| 471 | 499 | ||
| 472 | test "runtime tag name with single field" { | 500 | test "runtime tag name with single field" { |
| 473 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 474 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 501 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 475 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 502 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 476 | 503 | ||
| ... | @@ -483,7 +510,6 @@ test "runtime tag name with single field" { | ... | @@ -483,7 +510,6 @@ test "runtime tag name with single field" { |
| 483 | } | 510 | } |
| 484 | 511 | ||
| 485 | test "method call on an empty union" { | 512 | test "method call on an empty union" { |
| 486 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 487 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 513 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 488 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 514 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 489 | 515 | ||
| ... | @@ -547,7 +573,6 @@ test "tagged union type" { | ... | @@ -547,7 +573,6 @@ test "tagged union type" { |
| 547 | } | 573 | } |
| 548 | 574 | ||
| 549 | test "tagged union as return value" { | 575 | test "tagged union as return value" { |
| 550 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 551 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 576 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 552 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 577 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 553 | 578 | ||
| ... | @@ -628,7 +653,6 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { | ... | @@ -628,7 +653,6 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { |
| 628 | } | 653 | } |
| 629 | 654 | ||
| 630 | test "switch on union with only 1 field" { | 655 | test "switch on union with only 1 field" { |
| 631 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 632 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 656 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 633 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 657 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 634 | 658 | ||
| ... | @@ -658,7 +682,6 @@ const PartialInstWithPayload = union(enum) { | ... | @@ -658,7 +682,6 @@ const PartialInstWithPayload = union(enum) { |
| 658 | }; | 682 | }; |
| 659 | 683 | ||
| 660 | test "union with only 1 field casted to its enum type which has enum value specified" { | 684 | test "union with only 1 field casted to its enum type which has enum value specified" { |
| 661 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 662 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 685 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 663 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 686 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 664 | 687 | ||
| ... | @@ -796,7 +819,6 @@ test "@unionInit stored to a const" { | ... | @@ -796,7 +819,6 @@ test "@unionInit stored to a const" { |
| 796 | } | 819 | } |
| 797 | 820 | ||
| 798 | test "@unionInit can modify a union type" { | 821 | test "@unionInit can modify a union type" { |
| 799 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 800 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 822 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 801 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 823 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 802 | 824 | ||
| ... | @@ -839,7 +861,6 @@ test "@unionInit can modify a pointer value" { | ... | @@ -839,7 +861,6 @@ test "@unionInit can modify a pointer value" { |
| 839 | } | 861 | } |
| 840 | 862 | ||
| 841 | test "union no tag with struct member" { | 863 | test "union no tag with struct member" { |
| 842 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 843 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 864 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 844 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 865 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 845 | 866 |