Commit 9dc98fbabb
Changed files (6)
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,
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;
+ 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 :field;
}
- 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 => {
+ 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;
- },
+ }
+ 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);
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),
};
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;
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
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" {