Sema: bad union field access safety

This commit is contained in:
Veikka Tuominen 2022-07-16 16:32:49 +03:00
parent 55fe34100f
commit ff7ec4efb5
15 changed files with 194 additions and 90 deletions

View File

@ -1729,7 +1729,7 @@ fn structInitExprRlPtrInner(
for (struct_init.ast.fields) |field_init| { for (struct_init.ast.fields) |field_init| {
const name_token = tree.firstToken(field_init) - 2; const name_token = tree.firstToken(field_init) - 2;
const str_index = try astgen.identAsString(name_token); const str_index = try astgen.identAsString(name_token);
const field_ptr = try gz.addPlNode(.field_ptr, field_init, Zir.Inst.Field{ const field_ptr = try gz.addPlNode(.field_ptr_init, field_init, Zir.Inst.Field{
.lhs = result_ptr, .lhs = result_ptr,
.field_name_start = str_index, .field_name_start = str_index,
}); });
@ -2287,6 +2287,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.elem_ptr_imm, .elem_ptr_imm,
.elem_val_node, .elem_val_node,
.field_ptr, .field_ptr,
.field_ptr_init,
.field_val, .field_val,
.field_call_bind, .field_call_bind,
.field_ptr_named, .field_ptr_named,

View File

@ -787,7 +787,7 @@ pub const Decl = struct {
const opaque_obj = ty.cast(Type.Payload.Opaque).?.data; const opaque_obj = ty.cast(Type.Payload.Opaque).?.data;
return &opaque_obj.namespace; return &opaque_obj.namespace;
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Type.Payload.Union).?.data; const union_obj = ty.cast(Type.Payload.Union).?.data;
return &union_obj.namespace; return &union_obj.namespace;
}, },

View File

@ -739,7 +739,8 @@ fn analyzeBodyInner(
.err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, false), .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, false),
.error_union_type => try sema.zirErrorUnionType(block, inst), .error_union_type => try sema.zirErrorUnionType(block, inst),
.error_value => try sema.zirErrorValue(block, inst), .error_value => try sema.zirErrorValue(block, inst),
.field_ptr => try sema.zirFieldPtr(block, inst), .field_ptr => try sema.zirFieldPtr(block, inst, false),
.field_ptr_init => try sema.zirFieldPtr(block, inst, true),
.field_ptr_named => try sema.zirFieldPtrNamed(block, inst), .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
.field_val => try sema.zirFieldVal(block, inst), .field_val => try sema.zirFieldVal(block, inst),
.field_val_named => try sema.zirFieldValNamed(block, inst), .field_val_named => try sema.zirFieldValNamed(block, inst),
@ -1547,11 +1548,11 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty)); const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty));
// st.instruction_addresses = &addrs; // st.instruction_addresses = &addrs;
const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src); const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src, true);
try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
// st.index = 0; // st.index = 0;
const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src); const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src, true);
const zero = try sema.addConstant(Type.usize, Value.zero); const zero = try sema.addConstant(Type.usize, Value.zero);
try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store); try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);
@ -2614,7 +2615,14 @@ fn zirUnionDecl(
const new_decl_arena_allocator = new_decl_arena.allocator(); const new_decl_arena_allocator = new_decl_arena.allocator();
const union_obj = try new_decl_arena_allocator.create(Module.Union); const union_obj = try new_decl_arena_allocator.create(Module.Union);
const type_tag: Type.Tag = if (small.has_tag_type or small.auto_enum_tag) .union_tagged else .@"union"; const type_tag = if (small.has_tag_type or small.auto_enum_tag)
Type.Tag.union_tagged
else if (small.layout != .Auto)
Type.Tag.@"union"
else switch (block.sema.mod.optimizeMode()) {
.Debug, .ReleaseSafe => Type.Tag.union_safety_tagged,
.ReleaseFast, .ReleaseSmall => Type.Tag.@"union",
};
const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union); const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union);
union_payload.* = .{ union_payload.* = .{
.base = .{ .tag = type_tag }, .base = .{ .tag = type_tag },
@ -7923,7 +7931,7 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
return sema.fieldVal(block, src, object, field_name, field_name_src); return sema.fieldVal(block, src, object, field_name, field_name_src);
} }
fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index, initializing: bool) CompileError!Air.Inst.Ref {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
@ -7933,7 +7941,7 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(extra.field_name_start); const field_name = sema.code.nullTerminatedString(extra.field_name_start);
const object_ptr = try sema.resolveInst(extra.lhs); const object_ptr = try sema.resolveInst(extra.lhs);
return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, initializing);
} }
fn zirFieldCallBind(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { fn zirFieldCallBind(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@ -7972,7 +7980,7 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
const object_ptr = try sema.resolveInst(extra.lhs); const object_ptr = try sema.resolveInst(extra.lhs);
const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known"); const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known");
return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
} }
fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
@ -14536,7 +14544,7 @@ fn zirStructInit(
.@"addrspace" = target_util.defaultAddressSpace(target, .local), .@"addrspace" = target_util.defaultAddressSpace(target, .local),
}); });
const alloc = try block.addTy(.alloc, alloc_ty); const alloc = try block.addTy(.alloc, alloc_ty);
const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty); const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true);
try sema.storePtr(block, src, field_ptr, init_inst); try sema.storePtr(block, src, field_ptr, init_inst);
const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val); const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val);
_ = try block.addBinOp(.set_union_tag, alloc, new_tag); _ = try block.addBinOp(.set_union_tag, alloc, new_tag);
@ -15604,13 +15612,21 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
if (decls_val.sliceLen(mod) > 0) { if (decls_val.sliceLen(mod) > 0) {
return sema.fail(block, src, "reified unions must have no decls", .{}); return sema.fail(block, src, "reified unions must have no decls", .{});
} }
const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout);
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
errdefer new_decl_arena.deinit(); errdefer new_decl_arena.deinit();
const new_decl_arena_allocator = new_decl_arena.allocator(); const new_decl_arena_allocator = new_decl_arena.allocator();
const union_obj = try new_decl_arena_allocator.create(Module.Union); const union_obj = try new_decl_arena_allocator.create(Module.Union);
const type_tag: Type.Tag = if (!tag_type_val.isNull()) .union_tagged else .@"union"; const type_tag = if (!tag_type_val.isNull())
Type.Tag.union_tagged
else if (layout != .Auto)
Type.Tag.@"union"
else switch (block.sema.mod.optimizeMode()) {
.Debug, .ReleaseSafe => Type.Tag.union_safety_tagged,
.ReleaseFast, .ReleaseSmall => Type.Tag.@"union",
};
const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union); const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union);
union_payload.* = .{ union_payload.* = .{
.base = .{ .tag = type_tag }, .base = .{ .tag = type_tag },
@ -15631,7 +15647,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
.fields = .{}, .fields = .{},
.node_offset = src.node_offset.x, .node_offset = src.node_offset.x,
.zir_index = inst, .zir_index = inst,
.layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), .layout = layout,
.status = .have_field_types, .status = .have_field_types,
.namespace = .{ .namespace = .{
.parent = block.namespace, .parent = block.namespace,
@ -15641,11 +15657,15 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
}; };
// Tag type // Tag type
var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
union_obj.tag_ty = if (tag_type_val.optionalValue()) |payload_val| blk: { if (tag_type_val.optionalValue()) |payload_val| {
var buffer: Value.ToTypeBuffer = undefined; var buffer: Value.ToTypeBuffer = undefined;
break :blk try payload_val.toType(&buffer).copy(new_decl_arena_allocator); union_obj.tag_ty = try payload_val.toType(&buffer).copy(new_decl_arena_allocator);
} else try sema.generateUnionTagTypeSimple(block, fields_len, null); } else {
union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, fields_len, null);
enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields;
}
// Fields // Fields
if (fields_len > 0) { if (fields_len > 0) {
@ -15669,6 +15689,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
sema.mod, sema.mod,
); );
if (enum_field_names) |set| {
set.putAssumeCapacity(field_name, {});
}
const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
if (gop.found_existing) { if (gop.found_existing) {
// TODO: better source location // TODO: better source location
@ -18898,6 +18922,8 @@ pub const PanicId = enum {
divide_by_zero, divide_by_zero,
remainder_division_zero_negative, remainder_division_zero_negative,
exact_division_remainder, exact_division_remainder,
/// TODO make this call `std.builtin.panicInactiveUnionField`.
inactive_union_field,
}; };
fn addSafetyCheck( fn addSafetyCheck(
@ -19120,6 +19146,7 @@ fn safetyPanic(
.divide_by_zero => "division by zero", .divide_by_zero => "division by zero",
.remainder_division_zero_negative => "remainder division by zero or negative value", .remainder_division_zero_negative => "remainder division by zero or negative value",
.exact_division_remainder => "exact division produced remainder", .exact_division_remainder => "exact division produced remainder",
.inactive_union_field => "access of inactive union field",
}; };
const msg_inst = msg_inst: { const msg_inst = msg_inst: {
@ -19339,7 +19366,7 @@ fn fieldVal(
}, },
.Union => if (is_pointer_to) { .Union => if (is_pointer_to) {
// Avoid loading the entire union by fetching a pointer and loading that // Avoid loading the entire union by fetching a pointer and loading that
const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty); const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
return sema.analyzeLoad(block, src, field_ptr, object_src); return sema.analyzeLoad(block, src, field_ptr, object_src);
} else { } else {
return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty); return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
@ -19356,6 +19383,7 @@ fn fieldPtr(
object_ptr: Air.Inst.Ref, object_ptr: Air.Inst.Ref,
field_name: []const u8, field_name: []const u8,
field_name_src: LazySrcLoc, field_name_src: LazySrcLoc,
initializing: bool,
) CompileError!Air.Inst.Ref { ) CompileError!Air.Inst.Ref {
// When editing this function, note that there is corresponding logic to be edited // When editing this function, note that there is corresponding logic to be edited
// in `fieldVal`. This function takes a pointer and returns a pointer. // in `fieldVal`. This function takes a pointer and returns a pointer.
@ -19547,7 +19575,7 @@ fn fieldPtr(
try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
else else
object_ptr; object_ptr;
return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty); return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
}, },
else => {}, else => {},
} }
@ -19995,6 +20023,7 @@ fn unionFieldPtr(
field_name: []const u8, field_name: []const u8,
field_name_src: LazySrcLoc, field_name_src: LazySrcLoc,
unresolved_union_ty: Type, unresolved_union_ty: Type,
initializing: bool,
) CompileError!Air.Inst.Ref { ) CompileError!Air.Inst.Ref {
const arena = sema.arena; const arena = sema.arena;
assert(unresolved_union_ty.zigTypeTag() == .Union); assert(unresolved_union_ty.zigTypeTag() == .Union);
@ -20010,30 +20039,32 @@ fn unionFieldPtr(
.@"addrspace" = union_ptr_ty.ptrAddressSpace(), .@"addrspace" = union_ptr_ty.ptrAddressSpace(),
}); });
if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| { if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
switch (union_obj.layout) { switch (union_obj.layout) {
.Auto => { .Auto => if (!initializing) {
// TODO emit the access of inactive union field error commented out below. const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
// In order to do that, we need to first solve the problem that AstGen break :ct;
// emits field_ptr instructions in order to initialize union values. if (union_val.isUndef()) {
// In such case we need to know that the field_ptr instruction (which is return sema.failWithUseOfUndef(block, src);
// calling this unionFieldPtr function) is *initializing* the union, }
// in which case we would skip this check, and in fact we would actually const tag_and_val = union_val.castTag(.@"union").?.data;
// set the union tag here and the payload to undefined. var field_tag_buf: Value.Payload.U32 = .{
.base = .{ .tag = .enum_field_index },
//const tag_and_val = union_val.castTag(.@"union").?.data; .data = field_index,
//var field_tag_buf: Value.Payload.U32 = .{ };
// .base = .{ .tag = .enum_field_index }, const field_tag = Value.initPayload(&field_tag_buf.base);
// .data = field_index, const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
//}; if (!tag_matches) {
//const field_tag = Value.initPayload(&field_tag_buf.base); const msg = msg: {
//const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod); const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data;
//if (!tag_matches) { const active_field_name = union_obj.fields.keys()[active_index];
// // TODO enhance this saying which one was active const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name });
// // and which one was accessed, and showing where the union was declared. errdefer msg.destroy(sema.gpa);
// return sema.fail(block, src, "access of inactive union field", .{}); try sema.addDeclaredHereNote(msg, union_ty);
//} break :msg msg;
// TODO add runtime safety check for the active tag };
return sema.failWithOwnedErrorMsg(block, msg);
}
}, },
.Packed, .Extern => {}, .Packed, .Extern => {},
} }
@ -20048,6 +20079,16 @@ fn unionFieldPtr(
} }
try sema.requireRuntimeBlock(block, src, null); try sema.requireRuntimeBlock(block, src, null);
if (!initializing and union_obj.layout == .Auto and block.wantSafety() and union_ty.unionTagTypeSafety() != null) {
const enum_ty = union_ty.unionTagTypeHypothetical();
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
const wanted_tag = try sema.addConstant(enum_ty, wanted_tag_val);
// TODO would it be better if get_union_tag supported pointers to unions?
const union_val = try block.addTyOp(.load, union_ty, union_ptr);
const active_tag = try block.addTyOp(.get_union_tag, enum_ty, union_val);
const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
try sema.addSafetyCheck(block, ok, .inactive_union_field);
}
return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
} }
@ -20106,6 +20147,14 @@ fn unionFieldVal(
} }
try sema.requireRuntimeBlock(block, src, null); try sema.requireRuntimeBlock(block, src, null);
if (union_obj.layout == .Auto and block.wantSafety() and union_ty.unionTagTypeSafety() != null) {
const enum_ty = union_ty.unionTagTypeHypothetical();
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
const wanted_tag = try sema.addConstant(enum_ty, wanted_tag_val);
const active_tag = try block.addTyOp(.get_union_tag, enum_ty, union_byval);
const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
try sema.addSafetyCheck(block, ok, .inactive_union_field);
}
return block.addStructFieldVal(union_byval, field_index, field.ty); return block.addStructFieldVal(union_byval, field_index, field.ty);
} }
@ -25424,7 +25473,7 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj); try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
return ty; return ty;
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Type.Payload.Union).?.data; const union_obj = ty.cast(Type.Payload.Union).?.data;
try sema.resolveTypeFieldsUnion(block, src, ty, union_obj); try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
return ty; return ty;
@ -26449,7 +26498,7 @@ pub fn typeHasOnePossibleValue(
return null; return null;
} }
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const resolved_ty = try sema.resolveTypeFields(block, src, ty); const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
@ -27081,7 +27130,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
} }
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Type.Payload.Union).?.data; const union_obj = ty.cast(Type.Payload.Union).?.data;
switch (union_obj.requires_comptime) { switch (union_obj.requires_comptime) {
.no, .wip => return false, .no, .wip => return false,

View File

@ -410,6 +410,8 @@ pub const Inst = struct {
/// to the named field. The field name is stored in string_bytes. Used by a.b syntax. /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
/// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
field_ptr, field_ptr,
/// Same as `field_ptr` but used for struct init.
field_ptr_init,
/// Given a struct or object that contains virtual fields, returns the named field. /// Given a struct or object that contains virtual fields, returns the named field.
/// The field name is stored in string_bytes. Used by a.b syntax. /// The field name is stored in string_bytes. Used by a.b syntax.
/// This instruction also accepts a pointer. /// This instruction also accepts a pointer.
@ -1070,6 +1072,7 @@ pub const Inst = struct {
.@"export", .@"export",
.export_value, .export_value,
.field_ptr, .field_ptr,
.field_ptr_init,
.field_val, .field_val,
.field_call_bind, .field_call_bind,
.field_ptr_named, .field_ptr_named,
@ -1370,6 +1373,7 @@ pub const Inst = struct {
.elem_ptr_imm, .elem_ptr_imm,
.elem_val_node, .elem_val_node,
.field_ptr, .field_ptr,
.field_ptr_init,
.field_val, .field_val,
.field_call_bind, .field_call_bind,
.field_ptr_named, .field_ptr_named,
@ -1629,6 +1633,7 @@ pub const Inst = struct {
.@"export" = .pl_node, .@"export" = .pl_node,
.export_value = .pl_node, .export_value = .pl_node,
.field_ptr = .pl_node, .field_ptr = .pl_node,
.field_ptr_init = .pl_node,
.field_val = .pl_node, .field_val = .pl_node,
.field_ptr_named = .pl_node, .field_ptr_named = .pl_node,
.field_val_named = .pl_node, .field_val_named = .pl_node,

View File

@ -77,7 +77,7 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
.Union => { .Union => {
const layout = ty.unionGetLayout(target); const layout = ty.unionGetLayout(target);
if (layout.payload_size == 0 and layout.tag_size != 0) { if (layout.payload_size == 0 and layout.tag_size != 0) {
return classifyType(ty.unionTagType().?, target); return classifyType(ty.unionTagTypeSafety().?, target);
} }
if (ty.unionFields().count() > 1) return memory; if (ty.unionFields().count() > 1) return memory;
return classifyType(ty.unionFields().values()[0].ty, target); return classifyType(ty.unionFields().values()[0].ty, target);
@ -111,7 +111,7 @@ pub fn scalarType(ty: Type, target: std.Target) Type {
.Union => { .Union => {
const layout = ty.unionGetLayout(target); const layout = ty.unionGetLayout(target);
if (layout.payload_size == 0 and layout.tag_size != 0) { if (layout.payload_size == 0 and layout.tag_size != 0) {
return scalarType(ty.unionTagType().?, target); return scalarType(ty.unionTagTypeSafety().?, target);
} }
std.debug.assert(ty.unionFields().count() == 1); std.debug.assert(ty.unionFields().count() == 1);
return scalarType(ty.unionFields().values()[0].ty, target); return scalarType(ty.unionFields().values()[0].ty, target);

View File

@ -504,7 +504,7 @@ pub const DeclGen = struct {
if (field_ty.hasRuntimeBitsIgnoreComptime()) { if (field_ty.hasRuntimeBitsIgnoreComptime()) {
try writer.writeAll("&("); try writer.writeAll("&(");
try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty); try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty);
if (field_ptr.container_ty.tag() == .union_tagged) { if (field_ptr.container_ty.tag() == .union_tagged or field_ptr.container_ty.tag() == .union_safety_tagged) {
try writer.print(")->payload.{ }", .{fmtIdent(field_name)}); try writer.print(")->payload.{ }", .{fmtIdent(field_name)});
} else { } else {
try writer.print(")->{ }", .{fmtIdent(field_name)}); try writer.print(")->{ }", .{fmtIdent(field_name)});
@ -842,7 +842,7 @@ pub const DeclGen = struct {
try dg.renderTypecast(writer, ty); try dg.renderTypecast(writer, ty);
try writer.writeAll("){"); try writer.writeAll("){");
if (ty.unionTagType()) |tag_ty| { if (ty.unionTagTypeSafety()) |tag_ty| {
if (layout.tag_size != 0) { if (layout.tag_size != 0) {
try writer.writeAll(".tag = "); try writer.writeAll(".tag = ");
try dg.renderValue(writer, tag_ty, union_obj.tag, location); try dg.renderValue(writer, tag_ty, union_obj.tag, location);
@ -858,7 +858,7 @@ pub const DeclGen = struct {
try writer.print(".{ } = ", .{fmtIdent(field_name)}); try writer.print(".{ } = ", .{fmtIdent(field_name)});
try dg.renderValue(writer, field_ty, union_obj.val, location); try dg.renderValue(writer, field_ty, union_obj.val, location);
} }
if (ty.unionTagType()) |_| { if (ty.unionTagTypeSafety()) |_| {
try writer.writeAll("}"); try writer.writeAll("}");
} }
try writer.writeAll("}"); try writer.writeAll("}");
@ -1110,7 +1110,7 @@ pub const DeclGen = struct {
defer buffer.deinit(); defer buffer.deinit();
try buffer.appendSlice("typedef "); try buffer.appendSlice("typedef ");
if (t.unionTagType()) |tag_ty| { if (t.unionTagTypeSafety()) |tag_ty| {
const name: CValue = .{ .bytes = "tag" }; const name: CValue = .{ .bytes = "tag" };
try buffer.appendSlice("struct {\n "); try buffer.appendSlice("struct {\n ");
if (layout.tag_size != 0) { if (layout.tag_size != 0) {
@ -1134,7 +1134,7 @@ pub const DeclGen = struct {
} }
try buffer.appendSlice("} "); try buffer.appendSlice("} ");
if (t.unionTagType()) |_| { if (t.unionTagTypeSafety()) |_| {
try buffer.appendSlice("payload;\n} "); try buffer.appendSlice("payload;\n} ");
} }
@ -3368,7 +3368,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
field_name = fields.keys()[index]; field_name = fields.keys()[index];
field_val_ty = fields.values()[index].ty; field_val_ty = fields.values()[index].ty;
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const fields = struct_ty.unionFields(); const fields = struct_ty.unionFields();
field_name = fields.keys()[index]; field_name = fields.keys()[index];
field_val_ty = fields.values()[index].ty; field_val_ty = fields.values()[index].ty;
@ -3383,7 +3383,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
}, },
else => unreachable, else => unreachable,
} }
const payload = if (struct_ty.tag() == .union_tagged) "payload." else ""; const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else "";
const inst_ty = f.air.typeOfIndex(inst); const inst_ty = f.air.typeOfIndex(inst);
const local = try f.allocLocal(inst_ty, .Const); const local = try f.allocLocal(inst_ty, .Const);
@ -3415,7 +3415,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
defer buf.deinit(); defer buf.deinit();
const field_name = switch (struct_ty.tag()) { const field_name = switch (struct_ty.tag()) {
.@"struct" => struct_ty.structFields().keys()[extra.field_index], .@"struct" => struct_ty.structFields().keys()[extra.field_index],
.@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index], .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[extra.field_index],
.tuple, .anon_struct => blk: { .tuple, .anon_struct => blk: {
const tuple = struct_ty.tupleFields(); const tuple = struct_ty.tupleFields();
if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none; if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;
@ -3425,7 +3425,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
}, },
else => unreachable, else => unreachable,
}; };
const payload = if (struct_ty.tag() == .union_tagged) "payload." else ""; const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else "";
const inst_ty = f.air.typeOfIndex(inst); const inst_ty = f.air.typeOfIndex(inst);
const local = try f.allocLocal(inst_ty, .Const); const local = try f.allocLocal(inst_ty, .Const);

View File

@ -3404,7 +3404,7 @@ pub const DeclGen = struct {
if (layout.payload_size == 0) { if (layout.payload_size == 0) {
return lowerValue(dg, .{ return lowerValue(dg, .{
.ty = tv.ty.unionTagType().?, .ty = tv.ty.unionTagTypeSafety().?,
.val = tag_and_val.tag, .val = tag_and_val.tag,
}); });
} }
@ -3446,7 +3446,7 @@ pub const DeclGen = struct {
} }
} }
const llvm_tag_value = try lowerValue(dg, .{ const llvm_tag_value = try lowerValue(dg, .{
.ty = tv.ty.unionTagType().?, .ty = tv.ty.unionTagTypeSafety().?,
.val = tag_and_val.tag, .val = tag_and_val.tag,
}); });
var fields: [3]*const llvm.Value = undefined; var fields: [3]*const llvm.Value = undefined;

View File

@ -390,6 +390,7 @@ const Writer = struct {
.switch_block => try self.writeSwitchBlock(stream, inst), .switch_block => try self.writeSwitchBlock(stream, inst),
.field_ptr, .field_ptr,
.field_ptr_init,
.field_val, .field_val,
.field_call_bind, .field_call_bind,
=> try self.writePlNodeField(stream, inst), => try self.writePlNodeField(stream, inst),

View File

@ -149,6 +149,7 @@ pub const Type = extern union {
=> return .Enum, => return .Enum,
.@"union", .@"union",
.union_safety_tagged,
.union_tagged, .union_tagged,
.type_info, .type_info,
=> return .Union, => return .Union,
@ -902,7 +903,7 @@ pub const Type = extern union {
.reduce_op, .reduce_op,
=> unreachable, // needed to resolve the type before now => unreachable, // needed to resolve the type before now
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const a_union_obj = a.cast(Payload.Union).?.data; const a_union_obj = a.cast(Payload.Union).?.data;
const b_union_obj = (b.cast(Payload.Union) orelse return false).data; const b_union_obj = (b.cast(Payload.Union) orelse return false).data;
return a_union_obj == b_union_obj; return a_union_obj == b_union_obj;
@ -1210,7 +1211,7 @@ pub const Type = extern union {
.reduce_op, .reduce_op,
=> unreachable, // needed to resolve the type before now => unreachable, // needed to resolve the type before now
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj: *const Module.Union = ty.cast(Payload.Union).?.data; const union_obj: *const Module.Union = ty.cast(Payload.Union).?.data;
std.hash.autoHash(hasher, std.builtin.TypeId.Union); std.hash.autoHash(hasher, std.builtin.TypeId.Union);
std.hash.autoHash(hasher, union_obj); std.hash.autoHash(hasher, union_obj);
@ -1479,7 +1480,7 @@ pub const Type = extern union {
.error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
.empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
.@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
.@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union), .@"union", .union_safety_tagged, .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),
.enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple), .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
.enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered), .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered),
.enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull), .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
@ -1603,7 +1604,7 @@ pub const Type = extern union {
@tagName(t), struct_obj.owner_decl, @tagName(t), struct_obj.owner_decl,
}); });
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return writer.print("({s} decl={d})", .{ return writer.print("({s} decl={d})", .{
@tagName(t), union_obj.owner_decl, @tagName(t), union_obj.owner_decl,
@ -1989,7 +1990,7 @@ pub const Type = extern union {
const decl = mod.declPtr(struct_obj.owner_decl); const decl = mod.declPtr(struct_obj.owner_decl);
try decl.renderFullyQualifiedName(mod, writer); try decl.renderFullyQualifiedName(mod, writer);
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
const decl = mod.declPtr(union_obj.owner_decl); const decl = mod.declPtr(union_obj.owner_decl);
try decl.renderFullyQualifiedName(mod, writer); try decl.renderFullyQualifiedName(mod, writer);
@ -2485,8 +2486,8 @@ pub const Type = extern union {
return false; return false;
} }
}, },
.union_tagged => { .union_safety_tagged, .union_tagged => {
const union_obj = ty.castTag(.union_tagged).?.data; const union_obj = ty.cast(Payload.Union).?.data;
if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) { if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) {
return true; return true;
} }
@ -2644,7 +2645,7 @@ pub const Type = extern union {
.optional => ty.isPtrLikeOptional(), .optional => ty.isPtrLikeOptional(),
.@"struct" => ty.castTag(.@"struct").?.data.layout != .Auto, .@"struct" => ty.castTag(.@"struct").?.data.layout != .Auto,
.@"union" => ty.castTag(.@"union").?.data.layout != .Auto, .@"union", .union_safety_tagged => ty.cast(Payload.Union).?.data.layout != .Auto,
.union_tagged => false, .union_tagged => false,
}; };
} }
@ -3050,11 +3051,10 @@ pub const Type = extern union {
}, },
.@"union" => { .@"union" => {
const union_obj = ty.castTag(.@"union").?.data; const union_obj = ty.castTag(.@"union").?.data;
// TODO pass `true` for have_tag when unions have a safety tag
return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, false); return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, false);
}, },
.union_tagged => { .union_safety_tagged, .union_tagged => {
const union_obj = ty.castTag(.union_tagged).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, true); return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, true);
}, },
@ -3232,11 +3232,10 @@ pub const Type = extern union {
}, },
.@"union" => { .@"union" => {
const union_obj = ty.castTag(.@"union").?.data; const union_obj = ty.castTag(.@"union").?.data;
// TODO pass `true` for have_tag when unions have a safety tag
return abiSizeAdvancedUnion(ty, target, strat, union_obj, false); return abiSizeAdvancedUnion(ty, target, strat, union_obj, false);
}, },
.union_tagged => { .union_safety_tagged, .union_tagged => {
const union_obj = ty.castTag(.union_tagged).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return abiSizeAdvancedUnion(ty, target, strat, union_obj, true); return abiSizeAdvancedUnion(ty, target, strat, union_obj, true);
}, },
@ -3526,7 +3525,7 @@ pub const Type = extern union {
return try bitSizeAdvanced(int_tag_ty, target, sema_kit); return try bitSizeAdvanced(int_tag_ty, target, sema_kit);
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
assert(union_obj.haveFieldTypes()); assert(union_obj.haveFieldTypes());
@ -4194,6 +4193,33 @@ pub const Type = extern union {
}; };
} }
/// Same as `unionTagType` but includes safety tag.
/// Codegen should use this version.
pub fn unionTagTypeSafety(ty: Type) ?Type {
return switch (ty.tag()) {
.union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data;
assert(union_obj.haveFieldTypes());
return union_obj.tag_ty;
},
.atomic_order,
.atomic_rmw_op,
.calling_convention,
.address_space,
.float_mode,
.reduce_op,
.call_options,
.prefetch_options,
.export_options,
.extern_options,
.type_info,
=> unreachable, // needed to call resolveTypeFields first
else => null,
};
}
/// Asserts the type is a union; returns the tag type, even if the tag will /// Asserts the type is a union; returns the tag type, even if the tag will
/// not be stored at runtime. /// not be stored at runtime.
pub fn unionTagTypeHypothetical(ty: Type) Type { pub fn unionTagTypeHypothetical(ty: Type) Type {
@ -4225,8 +4251,8 @@ pub const Type = extern union {
const union_obj = ty.castTag(.@"union").?.data; const union_obj = ty.castTag(.@"union").?.data;
return union_obj.getLayout(target, false); return union_obj.getLayout(target, false);
}, },
.union_tagged => { .union_safety_tagged, .union_tagged => {
const union_obj = ty.castTag(.union_tagged).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return union_obj.getLayout(target, true); return union_obj.getLayout(target, true);
}, },
else => unreachable, else => unreachable,
@ -4238,6 +4264,7 @@ pub const Type = extern union {
.tuple, .empty_struct_literal, .anon_struct => .Auto, .tuple, .empty_struct_literal, .anon_struct => .Auto,
.@"struct" => ty.castTag(.@"struct").?.data.layout, .@"struct" => ty.castTag(.@"struct").?.data.layout,
.@"union" => ty.castTag(.@"union").?.data.layout, .@"union" => ty.castTag(.@"union").?.data.layout,
.union_safety_tagged => ty.castTag(.union_safety_tagged).?.data.layout,
.union_tagged => ty.castTag(.union_tagged).?.data.layout, .union_tagged => ty.castTag(.union_tagged).?.data.layout,
else => unreachable, else => unreachable,
}; };
@ -4936,7 +4963,7 @@ pub const Type = extern union {
return null; return null;
} }
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
const tag_val = union_obj.tag_ty.onePossibleValue() orelse return null; const tag_val = union_obj.tag_ty.onePossibleValue() orelse return null;
const only_field = union_obj.fields.values()[0]; const only_field = union_obj.fields.values()[0];
@ -5114,7 +5141,7 @@ pub const Type = extern union {
} }
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Type.Payload.Union).?.data; const union_obj = ty.cast(Type.Payload.Union).?.data;
switch (union_obj.requires_comptime) { switch (union_obj.requires_comptime) {
.wip, .unknown => unreachable, // This function asserts types already resolved. .wip, .unknown => unreachable, // This function asserts types already resolved.
@ -5167,6 +5194,7 @@ pub const Type = extern union {
.empty_struct => self.castTag(.empty_struct).?.data, .empty_struct => self.castTag(.empty_struct).?.data,
.@"opaque" => &self.castTag(.@"opaque").?.data.namespace, .@"opaque" => &self.castTag(.@"opaque").?.data.namespace,
.@"union" => &self.castTag(.@"union").?.data.namespace, .@"union" => &self.castTag(.@"union").?.data.namespace,
.union_safety_tagged => &self.castTag(.union_safety_tagged).?.data.namespace,
.union_tagged => &self.castTag(.union_tagged).?.data.namespace, .union_tagged => &self.castTag(.union_tagged).?.data.namespace,
else => null, else => null,
@ -5439,7 +5467,7 @@ pub const Type = extern union {
const struct_obj = ty.castTag(.@"struct").?.data; const struct_obj = ty.castTag(.@"struct").?.data;
return struct_obj.fields.values()[index].ty; return struct_obj.fields.values()[index].ty;
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return union_obj.fields.values()[index].ty; return union_obj.fields.values()[index].ty;
}, },
@ -5456,7 +5484,7 @@ pub const Type = extern union {
assert(struct_obj.layout != .Packed); assert(struct_obj.layout != .Packed);
return struct_obj.fields.values()[index].normalAlignment(target); return struct_obj.fields.values()[index].normalAlignment(target);
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return union_obj.fields.values()[index].normalAlignment(target); return union_obj.fields.values()[index].normalAlignment(target);
}, },
@ -5619,8 +5647,8 @@ pub const Type = extern union {
}, },
.@"union" => return 0, .@"union" => return 0,
.union_tagged => { .union_safety_tagged, .union_tagged => {
const union_obj = ty.castTag(.union_tagged).?.data; const union_obj = ty.cast(Payload.Union).?.data;
const layout = union_obj.getLayout(target, true); const layout = union_obj.getLayout(target, true);
if (layout.tag_align >= layout.payload_align) { if (layout.tag_align >= layout.payload_align) {
// {Tag, Payload} // {Tag, Payload}
@ -5660,7 +5688,7 @@ pub const Type = extern union {
const error_set = ty.castTag(.error_set).?.data; const error_set = ty.castTag(.error_set).?.data;
return error_set.srcLoc(mod); return error_set.srcLoc(mod);
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return union_obj.srcLoc(mod); return union_obj.srcLoc(mod);
}, },
@ -5704,7 +5732,7 @@ pub const Type = extern union {
const error_set = ty.castTag(.error_set).?.data; const error_set = ty.castTag(.error_set).?.data;
return error_set.owner_decl; return error_set.owner_decl;
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return union_obj.owner_decl; return union_obj.owner_decl;
}, },
@ -5748,7 +5776,7 @@ pub const Type = extern union {
const error_set = ty.castTag(.error_set).?.data; const error_set = ty.castTag(.error_set).?.data;
return error_set.node_offset; return error_set.node_offset;
}, },
.@"union", .union_tagged => { .@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data; const union_obj = ty.cast(Payload.Union).?.data;
return union_obj.node_offset; return union_obj.node_offset;
}, },
@ -5893,6 +5921,7 @@ pub const Type = extern union {
@"opaque", @"opaque",
@"struct", @"struct",
@"union", @"union",
union_safety_tagged,
union_tagged, union_tagged,
enum_simple, enum_simple,
enum_numbered, enum_numbered,
@ -6009,7 +6038,7 @@ pub const Type = extern union {
.error_set_single => Payload.Name, .error_set_single => Payload.Name,
.@"opaque" => Payload.Opaque, .@"opaque" => Payload.Opaque,
.@"struct" => Payload.Struct, .@"struct" => Payload.Struct,
.@"union", .union_tagged => Payload.Union, .@"union", .union_safety_tagged, .union_tagged => Payload.Union,
.enum_full, .enum_nonexhaustive => Payload.EnumFull, .enum_full, .enum_nonexhaustive => Payload.EnumFull,
.enum_simple => Payload.EnumSimple, .enum_simple => Payload.EnumSimple,
.enum_numbered => Payload.EnumNumbered, .enum_numbered => Payload.EnumNumbered,

View File

@ -12,8 +12,10 @@ const A = union(enum) {
}; };
test "union that needs padding bytes inside an array" { test "union that needs padding bytes inside an array" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
var as = [_]A{ var as = [_]A{
A{ .B = B{ .D = 1 } }, A{ .B = B{ .D = 1 } },

View File

@ -998,6 +998,9 @@ test "tuple element initialized with fn call" {
} }
test "struct with union field" { test "struct with union field" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const Value = struct { const Value = struct {
ref: u32 = 2, ref: u32 = 2,
kind: union(enum) { kind: union(enum) {

View File

@ -412,7 +412,7 @@ test "Type.Union" {
const Untagged = @Type(.{ const Untagged = @Type(.{
.Union = .{ .Union = .{
.layout = .Auto, .layout = .Extern,
.tag_type = null, .tag_type = null,
.fields = &.{ .fields = &.{
.{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) }, .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) },

View File

@ -37,6 +37,7 @@ test "init union with runtime value - floats" {
test "basic unions" { test "basic unions" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
var foo = Foo{ .int = 1 }; var foo = Foo{ .int = 1 };
try expect(foo.int == 1); try expect(foo.int == 1);
@ -430,9 +431,11 @@ const Foo1 = union(enum) {
var glbl: Foo1 = undefined; var glbl: Foo1 = undefined;
test "global union with single field is correctly initialized" { test "global union with single field is correctly initialized" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
glbl = Foo1{ glbl = Foo1{
.f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 }, .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },
@ -473,8 +476,11 @@ test "update the tag value for zero-sized unions" {
} }
test "union initializer generates padding only if needed" { test "union initializer generates padding only if needed" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const U = union(enum) { const U = union(enum) {
A: u24, A: u24,
@ -747,9 +753,11 @@ fn Setter(attr: Attribute) type {
} }
test "return union init with void payload" { test "return union init with void payload" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const S = struct { const S = struct {
fn entry() !void { fn entry() !void {
@ -775,6 +783,7 @@ test "@unionInit stored to a const" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const S = struct { const S = struct {
const U = union(enum) { const U = union(enum) {
@ -937,6 +946,7 @@ test "cast from anonymous struct to union" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const S = struct { const S = struct {
const U = union(enum) { const U = union(enum) {
@ -969,6 +979,7 @@ test "cast from pointer to anonymous struct to pointer to union" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const S = struct { const S = struct {
const U = union(enum) { const U = union(enum) {
@ -1104,6 +1115,8 @@ test "union enum type gets a separate scope" {
test "global variable struct contains union initialized to non-most-aligned field" { test "global variable struct contains union initialized to non-most-aligned field" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const T = struct { const T = struct {
const U = union(enum) { const U = union(enum) {

View File

@ -13,5 +13,4 @@ export fn entry() void {
// backend=stage2 // backend=stage2
// target=native // target=native
// //
// :9:14: error: expected type 'type', found 'tmp.U' // :9:8: error: use of undefined value here causes undefined behavior
// :1:11: note: union declared here

View File

@ -1,9 +1,11 @@
const std = @import("std"); const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
_ = message;
_ = stack_trace; _ = stack_trace;
std.process.exit(0); if (std.mem.eql(u8, message, "access of inactive union field")) {
std.process.exit(0);
}
std.process.exit(1);
} }
const Foo = union { const Foo = union {
@ -21,5 +23,5 @@ fn bar(f: *Foo) void {
f.float = 12.34; f.float = 12.34;
} }
// run // run
// backend=stage1 // backend=llvm
// target=native // target=native