diff --git a/src/Sema.zig b/src/Sema.zig index 2abdf27998..d99ed3721e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1586,19 +1586,23 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE }), ); }, - .decl_ref_mut => { - const ptr_ty = try Type.ptr(sema.arena, .{ - .pointee_type = pointee_ty, - .@"addrspace" = addr_space, - }); - return sema.addConstant(ptr_ty, ptr_val); - }, else => {}, } } } - try sema.requireRuntimeBlock(block, src); + // We would like to rely on the mechanism below even for comptime values. + // However in the case that the pointer points to comptime-mutable value, + // we cannot do it. + if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| { + if (ptr_val.isComptimeMutablePtr()) { + const ptr_ty = try Type.ptr(sema.arena, .{ + .pointee_type = pointee_ty, + .@"addrspace" = addr_space, + }); + return sema.addConstant(ptr_ty, ptr_val); + } + } // Make a dummy store through the pointer to test the coercion. // 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 switch (air_tags[trash_inst]) { .bitcast => { if (Air.indexToRef(trash_inst) == dummy_operand) { - return block.addBitCast(ptr_ty, new_ptr); + return sema.bitCast(block, ptr_ty, new_ptr, src); } const ty_op = air_datas[trash_inst].ty_op; const operand_ty = sema.getTmpAir().typeOf(ty_op.operand); @@ -1646,28 +1650,16 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE .pointee_type = operand_ty, .@"addrspace" = addr_space, }); - new_ptr = try block.addBitCast(ptr_operand_ty, new_ptr); + new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src); }, .wrap_optional => { - const ty_op = air_datas[trash_inst].ty_op; - const payload_ty = sema.getTmpAir().typeOf(ty_op.operand); - const ptr_payload_ty = try Type.ptr(sema.arena, .{ - .pointee_type = payload_ty, - .@"addrspace" = addr_space, - }); - new_ptr = try block.addTyOp(.optional_payload_ptr_set, ptr_payload_ty, new_ptr); + new_ptr = try sema.analyzeOptionalPayloadPtr(block, src, new_ptr, false, true); }, .wrap_errunion_err => { return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{}); }, .wrap_errunion_payload => { - const ty_op = air_datas[trash_inst].ty_op; - const payload_ty = sema.getTmpAir().typeOf(ty_op.operand); - const ptr_payload_ty = try Type.ptr(sema.arena, .{ - .pointee_type = payload_ty, - .@"addrspace" = addr_space, - }); - new_ptr = try block.addTyOp(.errunion_payload_ptr_set, ptr_payload_ty, new_ptr); + new_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, new_ptr, false, true); }, else => { if (std.debug.runtime_safety) { @@ -2774,9 +2766,16 @@ fn validateUnionInit( const field_index = @intCast(u32, field_index_big); // Handle the possibility of the union value being comptime-known. - const union_ptr_inst = Air.refToIndex(sema.resolveInst(field_ptr_extra.lhs)).?; + const union_ptr_inst = Air.refToIndex(union_ptr).?; switch (sema.air_instructions.items(.tag)[union_ptr_inst]) { - .constant => return, // In this case the tag has already been set. No validation to do. + .constant => { + if (try sema.resolveDefinedValue(block, init_src, union_ptr)) |ptr_val| { + if (ptr_val.isComptimeMutablePtr()) { + // In this case the tag has already been set. No validation to do. + return; + } + } + }, .bitcast => { // TODO here we need to go back and see if we need to convert the union // to a comptime-known value. In such case, we must delete all the instructions @@ -2895,7 +2894,7 @@ fn validateStructInit( } var struct_is_comptime = true; - var first_block_index: usize = std.math.maxInt(u32); + var first_block_index = block.instructions.items.len; const air_tags = sema.air_instructions.items(.tag); const air_datas = sema.air_instructions.items(.data); @@ -2904,7 +2903,7 @@ fn validateStructInit( // ends up being comptime-known. const field_values = try sema.arena.alloc(Value, fields.len); - for (found_fields) |field_ptr, i| { + field: for (found_fields) |field_ptr, i| { const field = fields[i]; if (field_ptr != 0) { @@ -2919,40 +2918,57 @@ fn validateStructInit( const field_ptr_air_ref = sema.inst_map.get(field_ptr).?; const field_ptr_air_inst = Air.refToIndex(field_ptr_air_ref).?; - // Find the block index of the field_ptr so that we can look at the next - // instruction after it within the same block. + + //std.debug.print("validateStructInit (field_ptr_air_inst=%{d}):\n", .{ + // field_ptr_air_inst, + //}); + //for (block.instructions.items) |item| { + // std.debug.print(" %{d} = {s}\n", .{item, @tagName(air_tags[item])}); + //} + + // We expect to see something like this in the current block AIR: + // %a = field_ptr(...) + // store(%a, %b) + // If %b is a comptime operand, this field is comptime. + // + // However, in the case of a comptime-known pointer to a struct, the + // the field_ptr instruction is missing, so we have to pattern-match + // based only on the store instructions. + // `first_block_index` needs to point to the `field_ptr` if it exists; + // the `store` otherwise. + // + // It's also possible for there to be no store instruction, in the case + // of nested `coerce_result_ptr` instructions. If we see the `field_ptr` + // but we have not found a `store`, treat as a runtime-known field. + // Possible performance enhancement: save the `block_index` between iterations // of the for loop. - const next_air_inst = inst: { - var block_index = block.instructions.items.len - 1; - while (block.instructions.items[block_index] != field_ptr_air_inst) { - block_index -= 1; - } - first_block_index = @minimum(first_block_index, block_index); - break :inst block.instructions.items[block_index + 1]; - }; - - // If the next instructon is a store with a comptime operand, this field - // is comptime. - switch (air_tags[next_air_inst]) { - .store => { - const bin_op = air_datas[next_air_inst].bin_op; - if (bin_op.lhs != field_ptr_air_ref) { - struct_is_comptime = false; - continue; - } - if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| { - field_values[i] = val; - } else { - struct_is_comptime = false; - } - continue; - }, - else => { + var block_index = block.instructions.items.len - 1; + while (block_index > 0) : (block_index -= 1) { + const store_inst = block.instructions.items[block_index]; + if (store_inst == field_ptr_air_inst) { struct_is_comptime = false; - continue; - }, + continue :field; + } + if (air_tags[store_inst] != .store) continue; + const bin_op = air_datas[store_inst].bin_op; + if (bin_op.lhs != field_ptr_air_ref) continue; + if (block_index > 0 and + field_ptr_air_inst == block.instructions.items[block_index - 1]) + { + first_block_index = @minimum(first_block_index, block_index - 1); + } else { + first_block_index = @minimum(first_block_index, block_index); + } + if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| { + field_values[i] = val; + } else { + struct_is_comptime = false; + } + continue :field; } + struct_is_comptime = false; + continue :field; } const field_name = struct_obj.fields.keys()[i]; @@ -5355,21 +5371,28 @@ fn analyzeOptionalPayloadPtr( .@"addrspace" = optional_ptr_ty.ptrAddressSpace(), }); - if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| { + if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| { if (initializing) { + if (!ptr_val.isComptimeMutablePtr()) { + // If the pointer resulting from this function was stored at comptime, + // the optional non-null bit would be set that way. But in this case, + // we need to emit a runtime instruction to do it. + try sema.requireRuntimeBlock(block, src); + _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); + } return sema.addConstant( child_pointer, - try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val), + try Value.Tag.opt_payload_ptr.create(sema.arena, ptr_val), ); } - if (try sema.pointerDeref(block, src, pointer_val, optional_ptr_ty)) |val| { + if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| { if (val.isNull()) { return sema.fail(block, src, "unable to unwrap null", .{}); } // The same Value represents the pointer to the optional and the payload. return sema.addConstant( child_pointer, - try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val), + try Value.Tag.opt_payload_ptr.create(sema.arena, ptr_val), ); } } @@ -5379,10 +5402,11 @@ fn analyzeOptionalPayloadPtr( const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); try sema.addSafetyCheck(block, is_non_null, .unwrap_null); } - return block.addTyOp(if (initializing) + const air_tag: Air.Inst.Tag = if (initializing) .optional_payload_ptr_set else - .optional_payload_ptr, child_pointer, optional_ptr); + .optional_payload_ptr; + return block.addTyOp(air_tag, child_pointer, optional_ptr); } /// Value in, value out. @@ -5510,21 +5534,28 @@ fn analyzeErrUnionPayloadPtr( .@"addrspace" = operand_ty.ptrAddressSpace(), }); - if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { + if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| { if (initializing) { + if (!ptr_val.isComptimeMutablePtr()) { + // If the pointer resulting from this function was stored at comptime, + // the error union error code would be set that way. But in this case, + // we need to emit a runtime instruction to do it. + try sema.requireRuntimeBlock(block, src); + _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); + } return sema.addConstant( operand_pointer_ty, - try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val), + try Value.Tag.eu_payload_ptr.create(sema.arena, ptr_val), ); } - if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { + if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| { if (val.getError()) |name| { return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); } return sema.addConstant( operand_pointer_ty, - try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val), + try Value.Tag.eu_payload_ptr.create(sema.arena, ptr_val), ); } } @@ -5534,10 +5565,11 @@ fn analyzeErrUnionPayloadPtr( const is_non_err = try block.addUnOp(.is_err, operand); try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion); } - return block.addTyOp(if (initializing) + const air_tag: Air.Inst.Tag = if (initializing) .errunion_payload_ptr_set else - .unwrap_errunion_payload_ptr, operand_pointer_ty, operand); + .unwrap_errunion_payload_ptr; + return block.addTyOp(air_tag, operand_pointer_ty, operand); } /// Value in, value out @@ -15242,8 +15274,6 @@ fn storePtr2( } const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src); - if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) - return; const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand); @@ -15257,6 +15287,11 @@ fn storePtr2( } else break :rs ptr_src; } else ptr_src; + // We do this after the possible comptime store above, for the case of field_ptr stores + // to unions because we want the comptime tag to be set, even if the field type is void. + if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) + return; + // TODO handle if the element type requires comptime try sema.requireRuntimeBlock(block, runtime_src); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 23fa19d9cb..8beb4dd206 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1329,32 +1329,25 @@ pub const DeclGen = struct { const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False); return llvm_int.constIntToPtr(try dg.llvmType(tv.ty)); }, - .field_ptr => { - const field_ptr = tv.val.castTag(.field_ptr).?.data; - const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr); - const llvm_u32 = dg.context.intType(32); - const indices: [2]*const llvm.Value = .{ - llvm_u32.constInt(0, .False), - llvm_u32.constInt(field_ptr.field_index, .False), - }; - const uncasted = parent_ptr.constInBoundsGEP(&indices, indices.len); - return uncasted.constBitCast(try dg.llvmType(tv.ty)); + .field_ptr, .opt_payload_ptr, .eu_payload_ptr => { + const parent = try dg.lowerParentPtr(tv.val); + return parent.llvm_ptr.constBitCast(try dg.llvmType(tv.ty)); }, .elem_ptr => { const elem_ptr = tv.val.castTag(.elem_ptr).?.data; - const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr); + const parent = try dg.lowerParentPtr(elem_ptr.array_ptr); const llvm_usize = try dg.llvmType(Type.usize); - if (parent_ptr.typeOf().getElementType().getTypeKind() == .Array) { + if (parent.llvm_ptr.typeOf().getElementType().getTypeKind() == .Array) { const indices: [2]*const llvm.Value = .{ llvm_usize.constInt(0, .False), llvm_usize.constInt(elem_ptr.index, .False), }; - return parent_ptr.constInBoundsGEP(&indices, indices.len); + return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len); } else { const indices: [1]*const llvm.Value = .{ llvm_usize.constInt(elem_ptr.index, .False), }; - return parent_ptr.constInBoundsGEP(&indices, indices.len); + return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len); } }, .null_value, .zero => { @@ -1800,11 +1793,7 @@ pub const DeclGen = struct { llvm_ptr: *const llvm.Value, }; - fn lowerParentPtrDecl( - dg: *DeclGen, - ptr_val: Value, - decl: *Module.Decl, - ) Error!ParentPtr { + fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl) Error!ParentPtr { decl.markAlive(); var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, @@ -1818,42 +1807,134 @@ pub const DeclGen = struct { }; } - fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!*const llvm.Value { + fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!ParentPtr { switch (ptr_val.tag()) { .decl_ref_mut => { const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl; - return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr; + return dg.lowerParentPtrDecl(ptr_val, decl); }, .decl_ref => { const decl = ptr_val.castTag(.decl_ref).?.data; - return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr; + return dg.lowerParentPtrDecl(ptr_val, decl); }, .variable => { const decl = ptr_val.castTag(.variable).?.data.owner_decl; - return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr; + return dg.lowerParentPtrDecl(ptr_val, decl); }, .field_ptr => { const field_ptr = ptr_val.castTag(.field_ptr).?.data; - const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr); + const parent = try dg.lowerParentPtr(field_ptr.container_ptr); + const field_index = @intCast(u32, field_ptr.field_index); const llvm_u32 = dg.context.intType(32); - const indices: [2]*const llvm.Value = .{ - llvm_u32.constInt(0, .False), - llvm_u32.constInt(field_ptr.field_index, .False), - }; - return parent_ptr.constInBoundsGEP(&indices, indices.len); + const target = dg.module.getTarget(); + switch (parent.ty.zigTypeTag()) { + .Union => { + const fields = parent.ty.unionFields(); + const layout = parent.ty.unionGetLayout(target); + const field_ty = fields.values()[field_index].ty; + if (layout.payload_size == 0) { + // In this case a pointer to the union and a pointer to any + // (void) payload is the same. + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr, + .ty = field_ty, + }; + } + if (layout.tag_size == 0) { + const indices: [2]*const llvm.Value = .{ + llvm_u32.constInt(0, .False), + llvm_u32.constInt(0, .False), + }; + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), + .ty = field_ty, + }; + } + const llvm_pl_index = @boolToInt(layout.tag_align >= layout.payload_align); + const indices: [2]*const llvm.Value = .{ + llvm_u32.constInt(0, .False), + llvm_u32.constInt(llvm_pl_index, .False), + }; + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), + .ty = field_ty, + }; + }, + .Struct => { + var ty_buf: Type.Payload.Pointer = undefined; + const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?; + const indices: [2]*const llvm.Value = .{ + llvm_u32.constInt(0, .False), + llvm_u32.constInt(llvm_field_index, .False), + }; + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), + .ty = parent.ty.structFieldType(field_index), + }; + }, + else => unreachable, + } }, .elem_ptr => { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; - const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr); + const parent = try dg.lowerParentPtr(elem_ptr.array_ptr); const llvm_usize = try dg.llvmType(Type.usize); const indices: [2]*const llvm.Value = .{ llvm_usize.constInt(0, .False), llvm_usize.constInt(elem_ptr.index, .False), }; - return parent_ptr.constInBoundsGEP(&indices, indices.len); + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), + .ty = parent.ty.childType(), + }; + }, + .opt_payload_ptr => { + const opt_payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; + const parent = try dg.lowerParentPtr(opt_payload_ptr); + var buf: Type.Payload.ElemType = undefined; + const payload_ty = parent.ty.optionalChild(&buf); + if (!payload_ty.hasRuntimeBits() or parent.ty.isPtrLikeOptional()) { + // In this case, we represent pointer to optional the same as pointer + // to the payload. + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr, + .ty = payload_ty, + }; + } + + const llvm_u32 = dg.context.intType(32); + const indices: [2]*const llvm.Value = .{ + llvm_u32.constInt(0, .False), + llvm_u32.constInt(0, .False), + }; + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), + .ty = payload_ty, + }; + }, + .eu_payload_ptr => { + const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; + const parent = try dg.lowerParentPtr(eu_payload_ptr); + const payload_ty = parent.ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) { + // In this case, we represent pointer to error union the same as pointer + // to the payload. + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr, + .ty = payload_ty, + }; + } + + const llvm_u32 = dg.context.intType(32); + const indices: [2]*const llvm.Value = .{ + llvm_u32.constInt(0, .False), + llvm_u32.constInt(1, .False), + }; + return ParentPtr{ + .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len), + .ty = payload_ty, + }; }, - .opt_payload_ptr => return dg.todo("implement lowerParentPtr for optional payload", .{}), - .eu_payload_ptr => return dg.todo("implement lowerParentPtr for error union payload", .{}), else => unreachable, } } @@ -3142,7 +3223,9 @@ pub const FuncGen = struct { const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); _ = self.builder.buildStore(non_null_bit, non_null_ptr); } - // Then return the payload pointer. + // Then return the payload pointer (only if it's used). + if (self.liveness.isUnused(inst)) + return null; const indices: [2]*const llvm.Value = .{ index_type.constNull(), // dereference the pointer index_type.constNull(), // first field is the payload @@ -3236,7 +3319,9 @@ pub const FuncGen = struct { const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); _ = self.builder.buildStore(non_error_val, non_null_ptr); } - // Then return the payload pointer. + // Then return the payload pointer (only if it is used). + if (self.liveness.isUnused(inst)) + return null; const indices: [2]*const llvm.Value = .{ index_type.constNull(), // dereference the pointer 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 } /// Take into account 0 bit fields. Returns null if an llvm field could not be found. This only -/// happends if you want the field index of a zero sized field at the end of the struct. +/// happens if you want the field index of a zero sized field at the end of the struct. fn llvmFieldIndex( ty: Type, field_index: u32, diff --git a/test/behavior/bugs/3046.zig b/test/behavior/bugs/3046.zig index 60425373b8..71e937f244 100644 --- a/test/behavior/bugs/3046.zig +++ b/test/behavior/bugs/3046.zig @@ -12,6 +12,8 @@ fn couldFail() anyerror!i32 { var some_struct: SomeStruct = undefined; test "fixed" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO + some_struct = SomeStruct{ .field = couldFail() catch @as(i32, 0), }; diff --git a/test/behavior/for.zig b/test/behavior/for.zig index b4416caa28..2ec5a74d73 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -63,6 +63,8 @@ test "ignore lval with underscore (for loop)" { } test "basic for loop" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO + const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3; var buffer: [expected_result.len]u8 = undefined; diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 71f34d0096..33d3b6c268 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -72,6 +72,8 @@ test "optional with void type" { } test "address of unwrap optional" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 1cd5b05eb1..4bf7bcf67a 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -348,6 +348,9 @@ const Foo1 = union(enum) { var glbl: Foo1 = undefined; test "global union with single field is correctly initialized" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + glbl = Foo1{ .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 }, }; @@ -363,6 +366,7 @@ var glbl_array: [2]FooUnion = undefined; test "initialize global array of union" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; glbl_array[1] = FooUnion{ .U1 = 2 }; glbl_array[0] = FooUnion{ .U0 = 1 }; @@ -487,8 +491,7 @@ test "tagged union with all void fields but a meaningful tag" { } }; try S.doTheTest(); - // TODO enable the test at comptime too - //comptime try S.doTheTest(); + comptime try S.doTheTest(); } test "union(enum(u32)) with specified and unspecified tag values" {