| author | |
| committer | |
| log | 4b9b9e725777a8f9e4ea9391beaeea34c834615f |
| tree | f1cbd788134cdbcde7f90dc2b9ada91dcd9159ba |
| parent | 232f8a291d2debd8a0fe9df2ce36d9035a15aefb |
Comment from this commit reproduced here:
LLVM does not allow us to change the type of globals. So we must
create a new global with the correct type, copy all its attributes,
and then update all references to point to the new global,
delete the original, and rename the new one to the old one's name.
This is necessary because LLVM does not support const bitcasting
a struct with padding bytes, which is needed to lower a const union value
to LLVM, when a field other than the most-aligned is active. Instead,
we must lower to an unnamed struct, and pointer cast at usage sites
of the global. Such an unnamed struct is the cause of the global type
mismatch, because we don't have the LLVM type until the *value* is created,
whereas the global needs to be created based on the type alone, because
lowering the value may reference the global as a pointer.8 files changed, 205 insertions(+), 129 deletions(-)
src/codegen/llvm.zig+70-19| ... | @@ -557,8 +557,8 @@ pub const DeclGen = struct { | ... | @@ -557,8 +557,8 @@ pub const DeclGen = struct { |
| 557 | return self.object.llvm_module; | 557 | return self.object.llvm_module; |
| 558 | } | 558 | } |
| 559 | 559 | ||
| 560 | fn genDecl(self: *DeclGen) !void { | 560 | fn genDecl(dg: *DeclGen) !void { |
| 561 | const decl = self.decl; | 561 | const decl = dg.decl; |
| 562 | assert(decl.has_tv); | 562 | assert(decl.has_tv); |
| 563 | 563 | ||
| 564 | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val }); | 564 | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val }); |
| ... | @@ -567,10 +567,10 @@ pub const DeclGen = struct { | ... | @@ -567,10 +567,10 @@ pub const DeclGen = struct { |
| 567 | _ = func_payload; | 567 | _ = func_payload; |
| 568 | @panic("TODO llvm backend genDecl function pointer"); | 568 | @panic("TODO llvm backend genDecl function pointer"); |
| 569 | } else if (decl.val.castTag(.extern_fn)) |extern_fn| { | 569 | } else if (decl.val.castTag(.extern_fn)) |extern_fn| { |
| 570 | _ = try self.resolveLlvmFunction(extern_fn.data); | 570 | _ = try dg.resolveLlvmFunction(extern_fn.data); |
| 571 | } else { | 571 | } else { |
| 572 | const target = self.module.getTarget(); | 572 | const target = dg.module.getTarget(); |
| 573 | const global = try self.resolveGlobalDecl(decl); | 573 | const global = try dg.resolveGlobalDecl(decl); |
| 574 | global.setAlignment(decl.getAlignment(target)); | 574 | global.setAlignment(decl.getAlignment(target)); |
| 575 | assert(decl.has_tv); | 575 | assert(decl.has_tv); |
| 576 | const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { | 576 | const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { |
| ... | @@ -581,8 +581,35 @@ pub const DeclGen = struct { | ... | @@ -581,8 +581,35 @@ pub const DeclGen = struct { |
| 581 | break :init_val decl.val; | 581 | break :init_val decl.val; |
| 582 | }; | 582 | }; |
| 583 | if (init_val.tag() != .unreachable_value) { | 583 | if (init_val.tag() != .unreachable_value) { |
| 584 | const llvm_init = try self.genTypedValue(.{ .ty = decl.ty, .val = init_val }); | 584 | const llvm_init = try dg.genTypedValue(.{ .ty = decl.ty, .val = init_val }); |
| 585 | global.setInitializer(llvm_init); | 585 | if (global.globalGetValueType() == llvm_init.typeOf()) { |
| 586 | global.setInitializer(llvm_init); | ||
| 587 | } else { | ||
| 588 | // LLVM does not allow us to change the type of globals. So we must | ||
| 589 | // create a new global with the correct type, copy all its attributes, | ||
| 590 | // and then update all references to point to the new global, | ||
| 591 | // delete the original, and rename the new one to the old one's name. | ||
| 592 | // This is necessary because LLVM does not support const bitcasting | ||
| 593 | // a struct with padding bytes, which is needed to lower a const union value | ||
| 594 | // to LLVM, when a field other than the most-aligned is active. Instead, | ||
| 595 | // we must lower to an unnamed struct, and pointer cast at usage sites | ||
| 596 | // of the global. Such an unnamed struct is the cause of the global type | ||
| 597 | // mismatch, because we don't have the LLVM type until the *value* is created, | ||
| 598 | // whereas the global needs to be created based on the type alone, because | ||
| 599 | // lowering the value may reference the global as a pointer. | ||
| 600 | const new_global = dg.object.llvm_module.addGlobalInAddressSpace( | ||
| 601 | llvm_init.typeOf(), | ||
| 602 | "", | ||
| 603 | dg.llvmAddressSpace(decl.@"addrspace"), | ||
| 604 | ); | ||
| 605 | new_global.setLinkage(global.getLinkage()); | ||
| 606 | new_global.setUnnamedAddr(global.getUnnamedAddress()); | ||
| 607 | new_global.setAlignment(global.getAlignment()); | ||
| 608 | new_global.setInitializer(llvm_init); | ||
| 609 | global.replaceAllUsesWith(new_global); | ||
| 610 | new_global.takeName(global); | ||
| 611 | global.deleteGlobal(); | ||
| 612 | } | ||
| 586 | } | 613 | } |
| 587 | } | 614 | } |
| 588 | } | 615 | } |
| ... | @@ -1456,9 +1483,15 @@ pub const DeclGen = struct { | ... | @@ -1456,9 +1483,15 @@ pub const DeclGen = struct { |
| 1456 | const layout = tv.ty.unionGetLayout(target); | 1483 | const layout = tv.ty.unionGetLayout(target); |
| 1457 | 1484 | ||
| 1458 | if (layout.payload_size == 0) { | 1485 | if (layout.payload_size == 0) { |
| 1459 | return genTypedValue(dg, .{ .ty = tv.ty.unionTagType().?, .val = tag_and_val.tag }); | 1486 | return genTypedValue(dg, .{ |
| 1487 | .ty = tv.ty.unionTagType().?, | ||
| 1488 | .val = tag_and_val.tag, | ||
| 1489 | }); | ||
| 1460 | } | 1490 | } |
| 1461 | const field_ty = tv.ty.unionFieldType(tag_and_val.tag); | 1491 | const union_obj = tv.ty.cast(Type.Payload.Union).?.data; |
| 1492 | const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag).?; | ||
| 1493 | assert(union_obj.haveFieldTypes()); | ||
| 1494 | const field_ty = union_obj.fields.values()[field_index].ty; | ||
| 1462 | const payload = p: { | 1495 | const payload = p: { |
| 1463 | if (!field_ty.hasCodeGenBits()) { | 1496 | if (!field_ty.hasCodeGenBits()) { |
| 1464 | const padding_len = @intCast(c_uint, layout.payload_size); | 1497 | const padding_len = @intCast(c_uint, layout.payload_size); |
| ... | @@ -1475,10 +1508,20 @@ pub const DeclGen = struct { | ... | @@ -1475,10 +1508,20 @@ pub const DeclGen = struct { |
| 1475 | }; | 1508 | }; |
| 1476 | break :p dg.context.constStruct(&fields, fields.len, .False); | 1509 | break :p dg.context.constStruct(&fields, fields.len, .False); |
| 1477 | }; | 1510 | }; |
| 1511 | |||
| 1512 | // In this case we must make an unnamed struct because LLVM does | ||
| 1513 | // not support bitcasting our payload struct to the true union payload type. | ||
| 1514 | // Instead we use an unnamed struct and every reference to the global | ||
| 1515 | // must pointer cast to the expected type before accessing the union. | ||
| 1516 | const need_unnamed = layout.most_aligned_field != field_index; | ||
| 1517 | |||
| 1478 | if (layout.tag_size == 0) { | 1518 | if (layout.tag_size == 0) { |
| 1479 | const llvm_payload_ty = llvm_union_ty.structGetTypeAtIndex(0); | 1519 | const fields: [1]*const llvm.Value = .{payload}; |
| 1480 | const fields: [1]*const llvm.Value = .{payload.constBitCast(llvm_payload_ty)}; | 1520 | if (need_unnamed) { |
| 1481 | return llvm_union_ty.constNamedStruct(&fields, fields.len); | 1521 | return dg.context.constStruct(&fields, fields.len, .False); |
| 1522 | } else { | ||
| 1523 | return llvm_union_ty.constNamedStruct(&fields, fields.len); | ||
| 1524 | } | ||
| 1482 | } | 1525 | } |
| 1483 | const llvm_tag_value = try genTypedValue(dg, .{ | 1526 | const llvm_tag_value = try genTypedValue(dg, .{ |
| 1484 | .ty = tv.ty.unionTagType().?, | 1527 | .ty = tv.ty.unionTagType().?, |
| ... | @@ -1486,13 +1529,15 @@ pub const DeclGen = struct { | ... | @@ -1486,13 +1529,15 @@ pub const DeclGen = struct { |
| 1486 | }); | 1529 | }); |
| 1487 | var fields: [2]*const llvm.Value = undefined; | 1530 | var fields: [2]*const llvm.Value = undefined; |
| 1488 | if (layout.tag_align >= layout.payload_align) { | 1531 | if (layout.tag_align >= layout.payload_align) { |
| 1489 | fields[0] = llvm_tag_value; | 1532 | fields = .{ llvm_tag_value, payload }; |
| 1490 | fields[1] = payload.constBitCast(llvm_union_ty.structGetTypeAtIndex(1)); | 1533 | } else { |
| 1534 | fields = .{ payload, llvm_tag_value }; | ||
| 1535 | } | ||
| 1536 | if (need_unnamed) { | ||
| 1537 | return dg.context.constStruct(&fields, fields.len, .False); | ||
| 1491 | } else { | 1538 | } else { |
| 1492 | fields[0] = payload.constBitCast(llvm_union_ty.structGetTypeAtIndex(0)); | 1539 | return llvm_union_ty.constNamedStruct(&fields, fields.len); |
| 1493 | fields[1] = llvm_tag_value; | ||
| 1494 | } | 1540 | } |
| 1495 | return llvm_union_ty.constNamedStruct(&fields, fields.len); | ||
| 1496 | }, | 1541 | }, |
| 1497 | .Vector => switch (tv.val.tag()) { | 1542 | .Vector => switch (tv.val.tag()) { |
| 1498 | .bytes => { | 1543 | .bytes => { |
| ... | @@ -1859,8 +1904,14 @@ pub const FuncGen = struct { | ... | @@ -1859,8 +1904,14 @@ pub const FuncGen = struct { |
| 1859 | global.setGlobalConstant(.True); | 1904 | global.setGlobalConstant(.True); |
| 1860 | global.setUnnamedAddr(.True); | 1905 | global.setUnnamedAddr(.True); |
| 1861 | global.setAlignment(ty.abiAlignment(target)); | 1906 | global.setAlignment(ty.abiAlignment(target)); |
| 1862 | gop.value_ptr.* = global; | 1907 | // Because of LLVM limitations for lowering certain types such as unions, |
| 1863 | return global; | 1908 | // the type of global constants might not match the type it is supposed to |
| 1909 | // be, and so we must bitcast the pointer at the usage sites. | ||
| 1910 | const wanted_llvm_ty = try self.dg.llvmType(ty); | ||
| 1911 | const wanted_llvm_ptr_ty = wanted_llvm_ty.pointerType(0); | ||
| 1912 | const casted_ptr = global.constBitCast(wanted_llvm_ptr_ty); | ||
| 1913 | gop.value_ptr.* = casted_ptr; | ||
| 1914 | return casted_ptr; | ||
| 1864 | } | 1915 | } |
| 1865 | 1916 | ||
| 1866 | fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void { | 1917 | fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void { |
src/codegen/llvm/bindings.zig+18| ... | @@ -184,6 +184,9 @@ pub const Value = opaque { | ... | @@ -184,6 +184,9 @@ pub const Value = opaque { |
| 184 | pub const setValueName2 = LLVMSetValueName2; | 184 | pub const setValueName2 = LLVMSetValueName2; |
| 185 | extern fn LLVMSetValueName2(Val: *const Value, Name: [*]const u8, NameLen: usize) void; | 185 | extern fn LLVMSetValueName2(Val: *const Value, Name: [*]const u8, NameLen: usize) void; |
| 186 | 186 | ||
| 187 | pub const takeName = ZigLLVMTakeName; | ||
| 188 | extern fn ZigLLVMTakeName(new_owner: *const Value, victim: *const Value) void; | ||
| 189 | |||
| 187 | pub const deleteFunction = LLVMDeleteFunction; | 190 | pub const deleteFunction = LLVMDeleteFunction; |
| 188 | extern fn LLVMDeleteFunction(Fn: *const Value) void; | 191 | extern fn LLVMDeleteFunction(Fn: *const Value) void; |
| 189 | 192 | ||
| ... | @@ -206,6 +209,21 @@ pub const Value = opaque { | ... | @@ -206,6 +209,21 @@ pub const Value = opaque { |
| 206 | return LLVMIsPoison(Val).toBool(); | 209 | return LLVMIsPoison(Val).toBool(); |
| 207 | } | 210 | } |
| 208 | extern fn LLVMIsPoison(Val: *const Value) Bool; | 211 | extern fn LLVMIsPoison(Val: *const Value) Bool; |
| 212 | |||
| 213 | pub const replaceAllUsesWith = LLVMReplaceAllUsesWith; | ||
| 214 | extern fn LLVMReplaceAllUsesWith(OldVal: *const Value, NewVal: *const Value) void; | ||
| 215 | |||
| 216 | pub const globalGetValueType = LLVMGlobalGetValueType; | ||
| 217 | extern fn LLVMGlobalGetValueType(Global: *const Value) *const Type; | ||
| 218 | |||
| 219 | pub const getLinkage = LLVMGetLinkage; | ||
| 220 | extern fn LLVMGetLinkage(Global: *const Value) Linkage; | ||
| 221 | |||
| 222 | pub const getUnnamedAddress = LLVMGetUnnamedAddress; | ||
| 223 | extern fn LLVMGetUnnamedAddress(Global: *const Value) Bool; | ||
| 224 | |||
| 225 | pub const getAlignment = LLVMGetAlignment; | ||
| 226 | extern fn LLVMGetAlignment(V: *const Value) c_uint; | ||
| 209 | }; | 227 | }; |
| 210 | 228 | ||
| 211 | pub const Type = opaque { | 229 | pub const Type = opaque { |
src/zig_llvm.cpp+4| ... | @@ -1319,6 +1319,10 @@ LLVMValueRef ZigLLVMBuildFPMulReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMVal | ... | @@ -1319,6 +1319,10 @@ LLVMValueRef ZigLLVMBuildFPMulReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMVal |
| 1319 | return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc), unwrap(Val))); | 1319 | return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc), unwrap(Val))); |
| 1320 | } | 1320 | } |
| 1321 | 1321 | ||
| 1322 | void ZigLLVMTakeName(LLVMValueRef new_owner, LLVMValueRef victim) { | ||
| 1323 | unwrap(new_owner)->takeName(unwrap(victim)); | ||
| 1324 | } | ||
| 1325 | |||
| 1322 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); | 1326 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); |
| 1323 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); | 1327 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); |
| 1324 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); | 1328 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); |
src/zig_llvm.h+13-11| ... | @@ -460,17 +460,19 @@ enum ZigLLVM_ObjectFormatType { | ... | @@ -460,17 +460,19 @@ enum ZigLLVM_ObjectFormatType { |
| 460 | ZigLLVM_XCOFF, | 460 | ZigLLVM_XCOFF, |
| 461 | }; | 461 | }; |
| 462 | 462 | ||
| 463 | LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val); | 463 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 464 | LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val); | 464 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 465 | LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val); | 465 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 466 | LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); | 466 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); |
| 467 | LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); | 467 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); |
| 468 | LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val); | 468 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 469 | LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val); | 469 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 470 | LLVMValueRef ZigLLVMBuildAddReduce(LLVMBuilderRef B, LLVMValueRef Val); | 470 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildAddReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 471 | LLVMValueRef ZigLLVMBuildMulReduce(LLVMBuilderRef B, LLVMValueRef Val); | 471 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMulReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 472 | LLVMValueRef ZigLLVMBuildFPAddReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Val); | 472 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildFPAddReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Val); |
| 473 | LLVMValueRef ZigLLVMBuildFPMulReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Val); | 473 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildFPMulReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Val); |
| 474 | |||
| 475 | ZIG_EXTERN_C void ZigLLVMTakeName(LLVMValueRef new_owner, LLVMValueRef victim); | ||
| 474 | 476 | ||
| 475 | #define ZigLLVM_DIFlags_Zero 0U | 477 | #define ZigLLVM_DIFlags_Zero 0U |
| 476 | #define ZigLLVM_DIFlags_Private 1U | 478 | #define ZigLLVM_DIFlags_Private 1U |
test/behavior/switch.zig+19| ... | @@ -294,3 +294,22 @@ test "switch on union with some prongs capturing" { | ... | @@ -294,3 +294,22 @@ test "switch on union with some prongs capturing" { |
| 294 | }; | 294 | }; |
| 295 | try expect(y == 11); | 295 | try expect(y == 11); |
| 296 | } | 296 | } |
| 297 | |||
| 298 | const Number = union(enum) { | ||
| 299 | One: u64, | ||
| 300 | Two: u8, | ||
| 301 | Three: f32, | ||
| 302 | }; | ||
| 303 | |||
| 304 | const number = Number{ .Three = 1.23 }; | ||
| 305 | |||
| 306 | fn returnsFalse() bool { | ||
| 307 | switch (number) { | ||
| 308 | Number.One => |x| return x > 1234, | ||
| 309 | Number.Two => |x| return x == 'a', | ||
| 310 | Number.Three => |x| return x > 12.34, | ||
| 311 | } | ||
| 312 | } | ||
| 313 | test "switch on const enum with var" { | ||
| 314 | try expect(!returnsFalse()); | ||
| 315 | } |
test/behavior/switch_stage1.zig-18| ... | @@ -3,24 +3,6 @@ const expect = std.testing.expect; | ... | @@ -3,24 +3,6 @@ const expect = std.testing.expect; |
| 3 | const expectError = std.testing.expectError; | 3 | const expectError = std.testing.expectError; |
| 4 | const expectEqual = std.testing.expectEqual; | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | 5 | ||
| 6 | const Number = union(enum) { | ||
| 7 | One: u64, | ||
| 8 | Two: u8, | ||
| 9 | Three: f32, | ||
| 10 | }; | ||
| 11 | |||
| 12 | const number = Number{ .Three = 1.23 }; | ||
| 13 | |||
| 14 | fn returnsFalse() bool { | ||
| 15 | switch (number) { | ||
| 16 | Number.One => |x| return x > 1234, | ||
| 17 | Number.Two => |x| return x == 'a', | ||
| 18 | Number.Three => |x| return x > 12.34, | ||
| 19 | } | ||
| 20 | } | ||
| 21 | test "switch on const enum with var" { | ||
| 22 | try expect(!returnsFalse()); | ||
| 23 | } | ||
| 24 | test "switch all prongs unreachable" { | 6 | test "switch all prongs unreachable" { |
| 25 | try testAllProngsUnreachable(); | 7 | try testAllProngsUnreachable(); |
| 26 | comptime try testAllProngsUnreachable(); | 8 | comptime try testAllProngsUnreachable(); |
test/behavior/union.zig+81| ... | @@ -71,3 +71,84 @@ test "0-sized extern union definition" { | ... | @@ -71,3 +71,84 @@ test "0-sized extern union definition" { |
| 71 | 71 | ||
| 72 | try expect(U.f == 1); | 72 | try expect(U.f == 1); |
| 73 | } | 73 | } |
| 74 | |||
| 75 | const Value = union(enum) { | ||
| 76 | Int: u64, | ||
| 77 | Array: [9]u8, | ||
| 78 | }; | ||
| 79 | |||
| 80 | const Agg = struct { | ||
| 81 | val1: Value, | ||
| 82 | val2: Value, | ||
| 83 | }; | ||
| 84 | |||
| 85 | const v1 = Value{ .Int = 1234 }; | ||
| 86 | const v2 = Value{ .Array = [_]u8{3} ** 9 }; | ||
| 87 | |||
| 88 | const err = @as(anyerror!Agg, Agg{ | ||
| 89 | .val1 = v1, | ||
| 90 | .val2 = v2, | ||
| 91 | }); | ||
| 92 | |||
| 93 | const array = [_]Value{ v1, v2, v1, v2 }; | ||
| 94 | |||
| 95 | test "unions embedded in aggregate types" { | ||
| 96 | switch (array[1]) { | ||
| 97 | Value.Array => |arr| try expect(arr[4] == 3), | ||
| 98 | else => unreachable, | ||
| 99 | } | ||
| 100 | switch ((err catch unreachable).val1) { | ||
| 101 | Value.Int => |x| try expect(x == 1234), | ||
| 102 | else => unreachable, | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | test "access a member of tagged union with conflicting enum tag name" { | ||
| 107 | const Bar = union(enum) { | ||
| 108 | A: A, | ||
| 109 | B: B, | ||
| 110 | |||
| 111 | const A = u8; | ||
| 112 | const B = void; | ||
| 113 | }; | ||
| 114 | |||
| 115 | comptime try expect(Bar.A == u8); | ||
| 116 | } | ||
| 117 | |||
| 118 | test "constant tagged union with payload" { | ||
| 119 | var empty = TaggedUnionWithPayload{ .Empty = {} }; | ||
| 120 | var full = TaggedUnionWithPayload{ .Full = 13 }; | ||
| 121 | shouldBeEmpty(empty); | ||
| 122 | shouldBeNotEmpty(full); | ||
| 123 | } | ||
| 124 | |||
| 125 | fn shouldBeEmpty(x: TaggedUnionWithPayload) void { | ||
| 126 | switch (x) { | ||
| 127 | TaggedUnionWithPayload.Empty => {}, | ||
| 128 | else => unreachable, | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | fn shouldBeNotEmpty(x: TaggedUnionWithPayload) void { | ||
| 133 | switch (x) { | ||
| 134 | TaggedUnionWithPayload.Empty => unreachable, | ||
| 135 | else => {}, | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | const TaggedUnionWithPayload = union(enum) { | ||
| 140 | Empty: void, | ||
| 141 | Full: i32, | ||
| 142 | }; | ||
| 143 | |||
| 144 | test "union alignment" { | ||
| 145 | comptime { | ||
| 146 | try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf([9]u8)); | ||
| 147 | try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf(u64)); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | const AlignTestTaggedUnion = union(enum) { | ||
| 152 | A: [9]u8, | ||
| 153 | B: u64, | ||
| 154 | }; |
test/behavior/union_stage1.zig-81| ... | @@ -3,37 +3,6 @@ const expect = std.testing.expect; | ... | @@ -3,37 +3,6 @@ const expect = std.testing.expect; |
| 3 | const expectEqual = std.testing.expectEqual; | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | const Tag = std.meta.Tag; | 4 | const Tag = std.meta.Tag; |
| 5 | 5 | ||
| 6 | const Value = union(enum) { | ||
| 7 | Int: u64, | ||
| 8 | Array: [9]u8, | ||
| 9 | }; | ||
| 10 | |||
| 11 | const Agg = struct { | ||
| 12 | val1: Value, | ||
| 13 | val2: Value, | ||
| 14 | }; | ||
| 15 | |||
| 16 | const v1 = Value{ .Int = 1234 }; | ||
| 17 | const v2 = Value{ .Array = [_]u8{3} ** 9 }; | ||
| 18 | |||
| 19 | const err = @as(anyerror!Agg, Agg{ | ||
| 20 | .val1 = v1, | ||
| 21 | .val2 = v2, | ||
| 22 | }); | ||
| 23 | |||
| 24 | const array = [_]Value{ v1, v2, v1, v2 }; | ||
| 25 | |||
| 26 | test "unions embedded in aggregate types" { | ||
| 27 | switch (array[1]) { | ||
| 28 | Value.Array => |arr| try expect(arr[4] == 3), | ||
| 29 | else => unreachable, | ||
| 30 | } | ||
| 31 | switch ((err catch unreachable).val1) { | ||
| 32 | Value.Int => |x| try expect(x == 1234), | ||
| 33 | else => unreachable, | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | const Letter = enum { A, B, C }; | 6 | const Letter = enum { A, B, C }; |
| 38 | const Payload = union(Letter) { | 7 | const Payload = union(Letter) { |
| 39 | A: i32, | 8 | A: i32, |
| ... | @@ -202,18 +171,6 @@ const PartialInstWithPayload = union(enum) { | ... | @@ -202,18 +171,6 @@ const PartialInstWithPayload = union(enum) { |
| 202 | Compiled: i32, | 171 | Compiled: i32, |
| 203 | }; | 172 | }; |
| 204 | 173 | ||
| 205 | test "access a member of tagged union with conflicting enum tag name" { | ||
| 206 | const Bar = union(enum) { | ||
| 207 | A: A, | ||
| 208 | B: B, | ||
| 209 | |||
| 210 | const A = u8; | ||
| 211 | const B = void; | ||
| 212 | }; | ||
| 213 | |||
| 214 | comptime try expect(Bar.A == u8); | ||
| 215 | } | ||
| 216 | |||
| 217 | test "tagged union initialization with runtime void" { | 174 | test "tagged union initialization with runtime void" { |
| 218 | try expect(testTaggedUnionInit({})); | 175 | try expect(testTaggedUnionInit({})); |
| 219 | } | 176 | } |
| ... | @@ -775,41 +732,3 @@ test "tagged union as return value" { | ... | @@ -775,41 +732,3 @@ test "tagged union as return value" { |
| 775 | fn returnAnInt(x: i32) TaggedFoo { | 732 | fn returnAnInt(x: i32) TaggedFoo { |
| 776 | return TaggedFoo{ .One = x }; | 733 | return TaggedFoo{ .One = x }; |
| 777 | } | 734 | } |
| 778 | |||
| 779 | test "constant tagged union with payload" { | ||
| 780 | var empty = TaggedUnionWithPayload{ .Empty = {} }; | ||
| 781 | var full = TaggedUnionWithPayload{ .Full = 13 }; | ||
| 782 | shouldBeEmpty(empty); | ||
| 783 | shouldBeNotEmpty(full); | ||
| 784 | } | ||
| 785 | |||
| 786 | fn shouldBeEmpty(x: TaggedUnionWithPayload) void { | ||
| 787 | switch (x) { | ||
| 788 | TaggedUnionWithPayload.Empty => {}, | ||
| 789 | else => unreachable, | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | fn shouldBeNotEmpty(x: TaggedUnionWithPayload) void { | ||
| 794 | switch (x) { | ||
| 795 | TaggedUnionWithPayload.Empty => unreachable, | ||
| 796 | else => {}, | ||
| 797 | } | ||
| 798 | } | ||
| 799 | |||
| 800 | const TaggedUnionWithPayload = union(enum) { | ||
| 801 | Empty: void, | ||
| 802 | Full: i32, | ||
| 803 | }; | ||
| 804 | |||
| 805 | test "union alignment" { | ||
| 806 | comptime { | ||
| 807 | try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf([9]u8)); | ||
| 808 | try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf(u64)); | ||
| 809 | } | ||
| 810 | } | ||
| 811 | |||
| 812 | const AlignTestTaggedUnion = union(enum) { | ||
| 813 | A: [9]u8, | ||
| 814 | B: u64, | ||
| 815 | }; |