authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-21 22:50:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-21 23:49:38-07:00
log9dc98fbabbd5e91622d2459ec6265a8dae2405b8
treee6198957420b30ae672a581d0e25e09771fbbcfc
parent7f48bc3493fbfb4487285c4e532acd6fad8cefa5

Sema: fix comptime union initialization

The mechanism behind initializing a union's tag is a bit complicated, depending on whether the union is initialized at runtime, forced comptime, or implicit comptime. `coerce_result_ptr` now does not force a block to be a runtime context; instead of adding runtime instructions directly, it forwards analysis to the respective functions for initializing optionals and error unions. `validateUnionInit` now has logic to still emit a runtime `set_union_tag` instruction even if the union pointer is comptime-known, for the case of a pointer that is not comptime mutable, such as a variable or the result of `@intToPtr`. `validateStructInit` looks for a completely different pattern now; it now handles the possibility of the corresponding AIR instruction for the `field_ptr` to be missing or the corresponding `store` to be missing. See the new comment added to the function for more details. An equivalent change should probably be made to `validateArrayInit`. `analyzeOptionalPayloadPtr` and `analyzeErrUnionPayloadPtr` functions now emit a `optional_payload_ptr_set` or `errunion_payload_ptr_set` instruction respectively if `initializing` is true and the pointer value is not comptime-mutable. `storePtr2` now tries the comptime pointer store before checking if the element type has one possible value because the comptime pointer store can have side effects of setting a union tag, setting an optional payload non-null, or setting an error union to be non-error. The LLVM backend `lowerParentPtr` function is improved to take into account the differences in how the LLVM values are lowered depending on the Zig type. It now handles unions correctly as well as additionally handling optionals and error unions. In the LLVM backend, the instructions `optional_payload_ptr_set` and `errunion_payload_ptr_set` check liveness analysis and only do the side effects in the case the result of the instruction is unused. A few wasm and C backend test cases regressed, but they are due to TODOs in lowering of constants, so this is progress.

6 files changed, 238 insertions(+), 109 deletions(-)

src/Sema.zig+106-71
......@@ -1586,19 +1586,23 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
15861586 }),
15871587 );
15881588 },
1589 .decl_ref_mut => {
1590 const ptr_ty = try Type.ptr(sema.arena, .{
1591 .pointee_type = pointee_ty,
1592 .@"addrspace" = addr_space,
1593 });
1594 return sema.addConstant(ptr_ty, ptr_val);
1595 },
15961589 else => {},
15971590 }
15981591 }
15991592 }
16001593
1601 try sema.requireRuntimeBlock(block, src);
1594 // We would like to rely on the mechanism below even for comptime values.
1595 // However in the case that the pointer points to comptime-mutable value,
1596 // we cannot do it.
1597 if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| {
1598 if (ptr_val.isComptimeMutablePtr()) {
1599 const ptr_ty = try Type.ptr(sema.arena, .{
1600 .pointee_type = pointee_ty,
1601 .@"addrspace" = addr_space,
1602 });
1603 return sema.addConstant(ptr_ty, ptr_val);
1604 }
1605 }
16021606
16031607 // Make a dummy store through the pointer to test the coercion.
16041608 // We will then use the generated instructions to decide what
......@@ -1638,7 +1642,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
16381642 switch (air_tags[trash_inst]) {
16391643 .bitcast => {
16401644 if (Air.indexToRef(trash_inst) == dummy_operand) {
1641 return block.addBitCast(ptr_ty, new_ptr);
1645 return sema.bitCast(block, ptr_ty, new_ptr, src);
16421646 }
16431647 const ty_op = air_datas[trash_inst].ty_op;
16441648 const operand_ty = sema.getTmpAir().typeOf(ty_op.operand);
......@@ -1646,28 +1650,16 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
16461650 .pointee_type = operand_ty,
16471651 .@"addrspace" = addr_space,
16481652 });
1649 new_ptr = try block.addBitCast(ptr_operand_ty, new_ptr);
1653 new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src);
16501654 },
16511655 .wrap_optional => {
1652 const ty_op = air_datas[trash_inst].ty_op;
1653 const payload_ty = sema.getTmpAir().typeOf(ty_op.operand);
1654 const ptr_payload_ty = try Type.ptr(sema.arena, .{
1655 .pointee_type = payload_ty,
1656 .@"addrspace" = addr_space,
1657 });
1658 new_ptr = try block.addTyOp(.optional_payload_ptr_set, ptr_payload_ty, new_ptr);
1656 new_ptr = try sema.analyzeOptionalPayloadPtr(block, src, new_ptr, false, true);
16591657 },
16601658 .wrap_errunion_err => {
16611659 return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{});
16621660 },
16631661 .wrap_errunion_payload => {
1664 const ty_op = air_datas[trash_inst].ty_op;
1665 const payload_ty = sema.getTmpAir().typeOf(ty_op.operand);
1666 const ptr_payload_ty = try Type.ptr(sema.arena, .{
1667 .pointee_type = payload_ty,
1668 .@"addrspace" = addr_space,
1669 });
1670 new_ptr = try block.addTyOp(.errunion_payload_ptr_set, ptr_payload_ty, new_ptr);
1662 new_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, new_ptr, false, true);
16711663 },
16721664 else => {
16731665 if (std.debug.runtime_safety) {
......@@ -2774,9 +2766,16 @@ fn validateUnionInit(
27742766 const field_index = @intCast(u32, field_index_big);
27752767
27762768 // Handle the possibility of the union value being comptime-known.
2777 const union_ptr_inst = Air.refToIndex(sema.resolveInst(field_ptr_extra.lhs)).?;
2769 const union_ptr_inst = Air.refToIndex(union_ptr).?;
27782770 switch (sema.air_instructions.items(.tag)[union_ptr_inst]) {
2779 .constant => return, // In this case the tag has already been set. No validation to do.
2771 .constant => {
2772 if (try sema.resolveDefinedValue(block, init_src, union_ptr)) |ptr_val| {
2773 if (ptr_val.isComptimeMutablePtr()) {
2774 // In this case the tag has already been set. No validation to do.
2775 return;
2776 }
2777 }
2778 },
27802779 .bitcast => {
27812780 // TODO here we need to go back and see if we need to convert the union
27822781 // to a comptime-known value. In such case, we must delete all the instructions
......@@ -2895,7 +2894,7 @@ fn validateStructInit(
28952894 }
28962895
28972896 var struct_is_comptime = true;
2898 var first_block_index: usize = std.math.maxInt(u32);
2897 var first_block_index = block.instructions.items.len;
28992898
29002899 const air_tags = sema.air_instructions.items(.tag);
29012900 const air_datas = sema.air_instructions.items(.data);
......@@ -2904,7 +2903,7 @@ fn validateStructInit(
29042903 // ends up being comptime-known.
29052904 const field_values = try sema.arena.alloc(Value, fields.len);
29062905
2907 for (found_fields) |field_ptr, i| {
2906 field: for (found_fields) |field_ptr, i| {
29082907 const field = fields[i];
29092908
29102909 if (field_ptr != 0) {
......@@ -2919,40 +2918,57 @@ fn validateStructInit(
29192918
29202919 const field_ptr_air_ref = sema.inst_map.get(field_ptr).?;
29212920 const field_ptr_air_inst = Air.refToIndex(field_ptr_air_ref).?;
2922 // Find the block index of the field_ptr so that we can look at the next
2923 // instruction after it within the same block.
2921
2922 //std.debug.print("validateStructInit (field_ptr_air_inst=%{d}):\n", .{
2923 // field_ptr_air_inst,
2924 //});
2925 //for (block.instructions.items) |item| {
2926 // std.debug.print(" %{d} = {s}\n", .{item, @tagName(air_tags[item])});
2927 //}
2928
2929 // We expect to see something like this in the current block AIR:
2930 // %a = field_ptr(...)
2931 // store(%a, %b)
2932 // If %b is a comptime operand, this field is comptime.
2933 //
2934 // However, in the case of a comptime-known pointer to a struct, the
2935 // the field_ptr instruction is missing, so we have to pattern-match
2936 // based only on the store instructions.
2937 // `first_block_index` needs to point to the `field_ptr` if it exists;
2938 // the `store` otherwise.
2939 //
2940 // It's also possible for there to be no store instruction, in the case
2941 // of nested `coerce_result_ptr` instructions. If we see the `field_ptr`
2942 // but we have not found a `store`, treat as a runtime-known field.
2943
29242944 // Possible performance enhancement: save the `block_index` between iterations
29252945 // of the for loop.
2926 const next_air_inst = inst: {
2927 var block_index = block.instructions.items.len - 1;
2928 while (block.instructions.items[block_index] != field_ptr_air_inst) {
2929 block_index -= 1;
2946 var block_index = block.instructions.items.len - 1;
2947 while (block_index > 0) : (block_index -= 1) {
2948 const store_inst = block.instructions.items[block_index];
2949 if (store_inst == field_ptr_air_inst) {
2950 struct_is_comptime = false;
2951 continue :field;
29302952 }
2931 first_block_index = @minimum(first_block_index, block_index);
2932 break :inst block.instructions.items[block_index + 1];
2933 };
2934
2935 // If the next instructon is a store with a comptime operand, this field
2936 // is comptime.
2937 switch (air_tags[next_air_inst]) {
2938 .store => {
2939 const bin_op = air_datas[next_air_inst].bin_op;
2940 if (bin_op.lhs != field_ptr_air_ref) {
2941 struct_is_comptime = false;
2942 continue;
2943 }
2944 if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| {
2945 field_values[i] = val;
2946 } else {
2947 struct_is_comptime = false;
2948 }
2949 continue;
2950 },
2951 else => {
2953 if (air_tags[store_inst] != .store) continue;
2954 const bin_op = air_datas[store_inst].bin_op;
2955 if (bin_op.lhs != field_ptr_air_ref) continue;
2956 if (block_index > 0 and
2957 field_ptr_air_inst == block.instructions.items[block_index - 1])
2958 {
2959 first_block_index = @minimum(first_block_index, block_index - 1);
2960 } else {
2961 first_block_index = @minimum(first_block_index, block_index);
2962 }
2963 if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| {
2964 field_values[i] = val;
2965 } else {
29522966 struct_is_comptime = false;
2953 continue;
2954 },
2967 }
2968 continue :field;
29552969 }
2970 struct_is_comptime = false;
2971 continue :field;
29562972 }
29572973
29582974 const field_name = struct_obj.fields.keys()[i];
......@@ -5355,21 +5371,28 @@ fn analyzeOptionalPayloadPtr(
53555371 .@"addrspace" = optional_ptr_ty.ptrAddressSpace(),
53565372 });
53575373
5358 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
5374 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| {
53595375 if (initializing) {
5376 if (!ptr_val.isComptimeMutablePtr()) {
5377 // If the pointer resulting from this function was stored at comptime,
5378 // the optional non-null bit would be set that way. But in this case,
5379 // we need to emit a runtime instruction to do it.
5380 try sema.requireRuntimeBlock(block, src);
5381 _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
5382 }
53605383 return sema.addConstant(
53615384 child_pointer,
5362 try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val),
5385 try Value.Tag.opt_payload_ptr.create(sema.arena, ptr_val),
53635386 );
53645387 }
5365 if (try sema.pointerDeref(block, src, pointer_val, optional_ptr_ty)) |val| {
5388 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
53665389 if (val.isNull()) {
53675390 return sema.fail(block, src, "unable to unwrap null", .{});
53685391 }
53695392 // The same Value represents the pointer to the optional and the payload.
53705393 return sema.addConstant(
53715394 child_pointer,
5372 try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val),
5395 try Value.Tag.opt_payload_ptr.create(sema.arena, ptr_val),
53735396 );
53745397 }
53755398 }
......@@ -5379,10 +5402,11 @@ fn analyzeOptionalPayloadPtr(
53795402 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
53805403 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
53815404 }
5382 return block.addTyOp(if (initializing)
5405 const air_tag: Air.Inst.Tag = if (initializing)
53835406 .optional_payload_ptr_set
53845407 else
5385 .optional_payload_ptr, child_pointer, optional_ptr);
5408 .optional_payload_ptr;
5409 return block.addTyOp(air_tag, child_pointer, optional_ptr);
53865410}
53875411
53885412/// Value in, value out.
......@@ -5510,21 +5534,28 @@ fn analyzeErrUnionPayloadPtr(
55105534 .@"addrspace" = operand_ty.ptrAddressSpace(),
55115535 });
55125536
5513 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
5537 if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| {
55145538 if (initializing) {
5539 if (!ptr_val.isComptimeMutablePtr()) {
5540 // If the pointer resulting from this function was stored at comptime,
5541 // the error union error code would be set that way. But in this case,
5542 // we need to emit a runtime instruction to do it.
5543 try sema.requireRuntimeBlock(block, src);
5544 _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
5545 }
55155546 return sema.addConstant(
55165547 operand_pointer_ty,
5517 try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
5548 try Value.Tag.eu_payload_ptr.create(sema.arena, ptr_val),
55185549 );
55195550 }
5520 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
5551 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
55215552 if (val.getError()) |name| {
55225553 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
55235554 }
55245555
55255556 return sema.addConstant(
55265557 operand_pointer_ty,
5527 try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
5558 try Value.Tag.eu_payload_ptr.create(sema.arena, ptr_val),
55285559 );
55295560 }
55305561 }
......@@ -5534,10 +5565,11 @@ fn analyzeErrUnionPayloadPtr(
55345565 const is_non_err = try block.addUnOp(.is_err, operand);
55355566 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
55365567 }
5537 return block.addTyOp(if (initializing)
5568 const air_tag: Air.Inst.Tag = if (initializing)
55385569 .errunion_payload_ptr_set
55395570 else
5540 .unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
5571 .unwrap_errunion_payload_ptr;
5572 return block.addTyOp(air_tag, operand_pointer_ty, operand);
55415573}
55425574
55435575/// Value in, value out
......@@ -15242,8 +15274,6 @@ fn storePtr2(
1524215274 }
1524315275
1524415276 const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
15245 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
15246 return;
1524715277
1524815278 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
1524915279 const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);
......@@ -15257,6 +15287,11 @@ fn storePtr2(
1525715287 } else break :rs ptr_src;
1525815288 } else ptr_src;
1525915289
15290 // We do this after the possible comptime store above, for the case of field_ptr stores
15291 // to unions because we want the comptime tag to be set, even if the field type is void.
15292 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
15293 return;
15294
1526015295 // TODO handle if the element type requires comptime
1526115296
1526215297 try sema.requireRuntimeBlock(block, runtime_src);
src/codegen/llvm.zig+121-36
......@@ -1329,32 +1329,25 @@ pub const DeclGen = struct {
13291329 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
13301330 return llvm_int.constIntToPtr(try dg.llvmType(tv.ty));
13311331 },
1332 .field_ptr => {
1333 const field_ptr = tv.val.castTag(.field_ptr).?.data;
1334 const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr);
1335 const llvm_u32 = dg.context.intType(32);
1336 const indices: [2]*const llvm.Value = .{
1337 llvm_u32.constInt(0, .False),
1338 llvm_u32.constInt(field_ptr.field_index, .False),
1339 };
1340 const uncasted = parent_ptr.constInBoundsGEP(&indices, indices.len);
1341 return uncasted.constBitCast(try dg.llvmType(tv.ty));
1332 .field_ptr, .opt_payload_ptr, .eu_payload_ptr => {
1333 const parent = try dg.lowerParentPtr(tv.val);
1334 return parent.llvm_ptr.constBitCast(try dg.llvmType(tv.ty));
13421335 },
13431336 .elem_ptr => {
13441337 const elem_ptr = tv.val.castTag(.elem_ptr).?.data;
1345 const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr);
1338 const parent = try dg.lowerParentPtr(elem_ptr.array_ptr);
13461339 const llvm_usize = try dg.llvmType(Type.usize);
1347 if (parent_ptr.typeOf().getElementType().getTypeKind() == .Array) {
1340 if (parent.llvm_ptr.typeOf().getElementType().getTypeKind() == .Array) {
13481341 const indices: [2]*const llvm.Value = .{
13491342 llvm_usize.constInt(0, .False),
13501343 llvm_usize.constInt(elem_ptr.index, .False),
13511344 };
1352 return parent_ptr.constInBoundsGEP(&indices, indices.len);
1345 return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
13531346 } else {
13541347 const indices: [1]*const llvm.Value = .{
13551348 llvm_usize.constInt(elem_ptr.index, .False),
13561349 };
1357 return parent_ptr.constInBoundsGEP(&indices, indices.len);
1350 return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
13581351 }
13591352 },
13601353 .null_value, .zero => {
......@@ -1800,11 +1793,7 @@ pub const DeclGen = struct {
18001793 llvm_ptr: *const llvm.Value,
18011794 };
18021795
1803 fn lowerParentPtrDecl(
1804 dg: *DeclGen,
1805 ptr_val: Value,
1806 decl: *Module.Decl,
1807 ) Error!ParentPtr {
1796 fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl) Error!ParentPtr {
18081797 decl.markAlive();
18091798 var ptr_ty_payload: Type.Payload.ElemType = .{
18101799 .base = .{ .tag = .single_mut_pointer },
......@@ -1818,42 +1807,134 @@ pub const DeclGen = struct {
18181807 };
18191808 }
18201809
1821 fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!*const llvm.Value {
1810 fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!ParentPtr {
18221811 switch (ptr_val.tag()) {
18231812 .decl_ref_mut => {
18241813 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
1825 return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;
1814 return dg.lowerParentPtrDecl(ptr_val, decl);
18261815 },
18271816 .decl_ref => {
18281817 const decl = ptr_val.castTag(.decl_ref).?.data;
1829 return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;
1818 return dg.lowerParentPtrDecl(ptr_val, decl);
18301819 },
18311820 .variable => {
18321821 const decl = ptr_val.castTag(.variable).?.data.owner_decl;
1833 return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;
1822 return dg.lowerParentPtrDecl(ptr_val, decl);
18341823 },
18351824 .field_ptr => {
18361825 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
1837 const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr);
1826 const parent = try dg.lowerParentPtr(field_ptr.container_ptr);
1827 const field_index = @intCast(u32, field_ptr.field_index);
18381828 const llvm_u32 = dg.context.intType(32);
1839 const indices: [2]*const llvm.Value = .{
1840 llvm_u32.constInt(0, .False),
1841 llvm_u32.constInt(field_ptr.field_index, .False),
1842 };
1843 return parent_ptr.constInBoundsGEP(&indices, indices.len);
1829 const target = dg.module.getTarget();
1830 switch (parent.ty.zigTypeTag()) {
1831 .Union => {
1832 const fields = parent.ty.unionFields();
1833 const layout = parent.ty.unionGetLayout(target);
1834 const field_ty = fields.values()[field_index].ty;
1835 if (layout.payload_size == 0) {
1836 // In this case a pointer to the union and a pointer to any
1837 // (void) payload is the same.
1838 return ParentPtr{
1839 .llvm_ptr = parent.llvm_ptr,
1840 .ty = field_ty,
1841 };
1842 }
1843 if (layout.tag_size == 0) {
1844 const indices: [2]*const llvm.Value = .{
1845 llvm_u32.constInt(0, .False),
1846 llvm_u32.constInt(0, .False),
1847 };
1848 return ParentPtr{
1849 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1850 .ty = field_ty,
1851 };
1852 }
1853 const llvm_pl_index = @boolToInt(layout.tag_align >= layout.payload_align);
1854 const indices: [2]*const llvm.Value = .{
1855 llvm_u32.constInt(0, .False),
1856 llvm_u32.constInt(llvm_pl_index, .False),
1857 };
1858 return ParentPtr{
1859 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1860 .ty = field_ty,
1861 };
1862 },
1863 .Struct => {
1864 var ty_buf: Type.Payload.Pointer = undefined;
1865 const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?;
1866 const indices: [2]*const llvm.Value = .{
1867 llvm_u32.constInt(0, .False),
1868 llvm_u32.constInt(llvm_field_index, .False),
1869 };
1870 return ParentPtr{
1871 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1872 .ty = parent.ty.structFieldType(field_index),
1873 };
1874 },
1875 else => unreachable,
1876 }
18441877 },
18451878 .elem_ptr => {
18461879 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
1847 const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr);
1880 const parent = try dg.lowerParentPtr(elem_ptr.array_ptr);
18481881 const llvm_usize = try dg.llvmType(Type.usize);
18491882 const indices: [2]*const llvm.Value = .{
18501883 llvm_usize.constInt(0, .False),
18511884 llvm_usize.constInt(elem_ptr.index, .False),
18521885 };
1853 return parent_ptr.constInBoundsGEP(&indices, indices.len);
1886 return ParentPtr{
1887 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1888 .ty = parent.ty.childType(),
1889 };
1890 },
1891 .opt_payload_ptr => {
1892 const opt_payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
1893 const parent = try dg.lowerParentPtr(opt_payload_ptr);
1894 var buf: Type.Payload.ElemType = undefined;
1895 const payload_ty = parent.ty.optionalChild(&buf);
1896 if (!payload_ty.hasRuntimeBits() or parent.ty.isPtrLikeOptional()) {
1897 // In this case, we represent pointer to optional the same as pointer
1898 // to the payload.
1899 return ParentPtr{
1900 .llvm_ptr = parent.llvm_ptr,
1901 .ty = payload_ty,
1902 };
1903 }
1904
1905 const llvm_u32 = dg.context.intType(32);
1906 const indices: [2]*const llvm.Value = .{
1907 llvm_u32.constInt(0, .False),
1908 llvm_u32.constInt(0, .False),
1909 };
1910 return ParentPtr{
1911 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1912 .ty = payload_ty,
1913 };
1914 },
1915 .eu_payload_ptr => {
1916 const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
1917 const parent = try dg.lowerParentPtr(eu_payload_ptr);
1918 const payload_ty = parent.ty.errorUnionPayload();
1919 if (!payload_ty.hasRuntimeBits()) {
1920 // In this case, we represent pointer to error union the same as pointer
1921 // to the payload.
1922 return ParentPtr{
1923 .llvm_ptr = parent.llvm_ptr,
1924 .ty = payload_ty,
1925 };
1926 }
1927
1928 const llvm_u32 = dg.context.intType(32);
1929 const indices: [2]*const llvm.Value = .{
1930 llvm_u32.constInt(0, .False),
1931 llvm_u32.constInt(1, .False),
1932 };
1933 return ParentPtr{
1934 .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
1935 .ty = payload_ty,
1936 };
18541937 },
1855 .opt_payload_ptr => return dg.todo("implement lowerParentPtr for optional payload", .{}),
1856 .eu_payload_ptr => return dg.todo("implement lowerParentPtr for error union payload", .{}),
18571938 else => unreachable,
18581939 }
18591940 }
......@@ -3142,7 +3223,9 @@ pub const FuncGen = struct {
31423223 const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
31433224 _ = self.builder.buildStore(non_null_bit, non_null_ptr);
31443225 }
3145 // Then return the payload pointer.
3226 // Then return the payload pointer (only if it's used).
3227 if (self.liveness.isUnused(inst))
3228 return null;
31463229 const indices: [2]*const llvm.Value = .{
31473230 index_type.constNull(), // dereference the pointer
31483231 index_type.constNull(), // first field is the payload
......@@ -3236,7 +3319,9 @@ pub const FuncGen = struct {
32363319 const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
32373320 _ = self.builder.buildStore(non_error_val, non_null_ptr);
32383321 }
3239 // Then return the payload pointer.
3322 // Then return the payload pointer (only if it is used).
3323 if (self.liveness.isUnused(inst))
3324 return null;
32403325 const indices: [2]*const llvm.Value = .{
32413326 index_type.constNull(), // dereference the pointer
32423327 index_type.constInt(1, .False), // second field is the payload
......@@ -5257,7 +5342,7 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca
52575342}
52585343
52595344/// Take into account 0 bit fields. Returns null if an llvm field could not be found. This only
5260/// happends if you want the field index of a zero sized field at the end of the struct.
5345/// happens if you want the field index of a zero sized field at the end of the struct.
52615346fn llvmFieldIndex(
52625347 ty: Type,
52635348 field_index: u32,
test/behavior/bugs/3046.zig+2
......@@ -12,6 +12,8 @@ fn couldFail() anyerror!i32 {
1212var some_struct: SomeStruct = undefined;
1313
1414test "fixed" {
15 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
16
1517 some_struct = SomeStruct{
1618 .field = couldFail() catch @as(i32, 0),
1719 };
test/behavior/for.zig+2
......@@ -63,6 +63,8 @@ test "ignore lval with underscore (for loop)" {
6363}
6464
6565test "basic for loop" {
66 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
67
6668 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;
6769
6870 var buffer: [expected_result.len]u8 = undefined;
test/behavior/optional.zig+2
......@@ -72,6 +72,8 @@ test "optional with void type" {
7272}
7373
7474test "address of unwrap optional" {
75 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
7577 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7678 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7779 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
test/behavior/union.zig+5-2
......@@ -348,6 +348,9 @@ const Foo1 = union(enum) {
348348var glbl: Foo1 = undefined;
349349
350350test "global union with single field is correctly initialized" {
351 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
352 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
353
351354 glbl = Foo1{
352355 .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },
353356 };
......@@ -363,6 +366,7 @@ var glbl_array: [2]FooUnion = undefined;
363366
364367test "initialize global array of union" {
365368 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
369 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
366370
367371 glbl_array[1] = FooUnion{ .U1 = 2 };
368372 glbl_array[0] = FooUnion{ .U0 = 1 };
......@@ -487,8 +491,7 @@ test "tagged union with all void fields but a meaningful tag" {
487491 }
488492 };
489493 try S.doTheTest();
490 // TODO enable the test at comptime too
491 //comptime try S.doTheTest();
494 comptime try S.doTheTest();
492495}
493496
494497test "union(enum(u32)) with specified and unspecified tag values" {