authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-16 16:32:49+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-23 15:40:11+03:00
logff7ec4efb5a6da565b92bc7b129d03680a4a72bd
tree14006dc9e492015837a97328ccb85ac77f3e27e3
parent55fe34100f8b516480cf530eb58d00ea8b665765

Sema: bad union field access safety


15 files changed, 194 insertions(+), 90 deletions(-)

src/AstGen.zig+2-1
...@@ -1729,7 +1729,7 @@ fn structInitExprRlPtrInner(...@@ -1729,7 +1729,7 @@ fn structInitExprRlPtrInner(
1729 for (struct_init.ast.fields) |field_init| {1729 for (struct_init.ast.fields) |field_init| {
1730 const name_token = tree.firstToken(field_init) - 2;1730 const name_token = tree.firstToken(field_init) - 2;
1731 const str_index = try astgen.identAsString(name_token);1731 const str_index = try astgen.identAsString(name_token);
1732 const field_ptr = try gz.addPlNode(.field_ptr, field_init, Zir.Inst.Field{1732 const field_ptr = try gz.addPlNode(.field_ptr_init, field_init, Zir.Inst.Field{
1733 .lhs = result_ptr,1733 .lhs = result_ptr,
1734 .field_name_start = str_index,1734 .field_name_start = str_index,
1735 });1735 });
...@@ -2287,6 +2287,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2287,6 +2287,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2287 .elem_ptr_imm,2287 .elem_ptr_imm,
2288 .elem_val_node,2288 .elem_val_node,
2289 .field_ptr,2289 .field_ptr,
2290 .field_ptr_init,
2290 .field_val,2291 .field_val,
2291 .field_call_bind,2292 .field_call_bind,
2292 .field_ptr_named,2293 .field_ptr_named,
src/Module.zig+1-1
...@@ -787,7 +787,7 @@ pub const Decl = struct {...@@ -787,7 +787,7 @@ pub const Decl = struct {
787 const opaque_obj = ty.cast(Type.Payload.Opaque).?.data;787 const opaque_obj = ty.cast(Type.Payload.Opaque).?.data;
788 return &opaque_obj.namespace;788 return &opaque_obj.namespace;
789 },789 },
790 .@"union", .union_tagged => {790 .@"union", .union_safety_tagged, .union_tagged => {
791 const union_obj = ty.cast(Type.Payload.Union).?.data;791 const union_obj = ty.cast(Type.Payload.Union).?.data;
792 return &union_obj.namespace;792 return &union_obj.namespace;
793 },793 },
src/Sema.zig+90-41
...@@ -739,7 +739,8 @@ fn analyzeBodyInner(...@@ -739,7 +739,8 @@ fn analyzeBodyInner(
739 .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, false),739 .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst, false),
740 .error_union_type => try sema.zirErrorUnionType(block, inst),740 .error_union_type => try sema.zirErrorUnionType(block, inst),
741 .error_value => try sema.zirErrorValue(block, inst),741 .error_value => try sema.zirErrorValue(block, inst),
742 .field_ptr => try sema.zirFieldPtr(block, inst),742 .field_ptr => try sema.zirFieldPtr(block, inst, false),
743 .field_ptr_init => try sema.zirFieldPtr(block, inst, true),
743 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),744 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
744 .field_val => try sema.zirFieldVal(block, inst),745 .field_val => try sema.zirFieldVal(block, inst),
745 .field_val_named => try sema.zirFieldValNamed(block, inst),746 .field_val_named => try sema.zirFieldValNamed(block, inst),
...@@ -1547,11 +1548,11 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)...@@ -1547,11 +1548,11 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
1547 const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty));1548 const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty));
15481549
1549 // st.instruction_addresses = &addrs;1550 // st.instruction_addresses = &addrs;
1550 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src);1551 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src, true);
1551 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);1552 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
15521553
1553 // st.index = 0;1554 // st.index = 0;
1554 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src);1555 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src, true);
1555 const zero = try sema.addConstant(Type.usize, Value.zero);1556 const zero = try sema.addConstant(Type.usize, Value.zero);
1556 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);1557 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);
15571558
...@@ -2614,7 +2615,14 @@ fn zirUnionDecl(...@@ -2614,7 +2615,14 @@ fn zirUnionDecl(
2614 const new_decl_arena_allocator = new_decl_arena.allocator();2615 const new_decl_arena_allocator = new_decl_arena.allocator();
26152616
2616 const union_obj = try new_decl_arena_allocator.create(Module.Union);2617 const union_obj = try new_decl_arena_allocator.create(Module.Union);
2617 const type_tag: Type.Tag = if (small.has_tag_type or small.auto_enum_tag) .union_tagged else .@"union";2618 const type_tag = if (small.has_tag_type or small.auto_enum_tag)
2619 Type.Tag.union_tagged
2620 else if (small.layout != .Auto)
2621 Type.Tag.@"union"
2622 else switch (block.sema.mod.optimizeMode()) {
2623 .Debug, .ReleaseSafe => Type.Tag.union_safety_tagged,
2624 .ReleaseFast, .ReleaseSmall => Type.Tag.@"union",
2625 };
2618 const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union);2626 const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union);
2619 union_payload.* = .{2627 union_payload.* = .{
2620 .base = .{ .tag = type_tag },2628 .base = .{ .tag = type_tag },
...@@ -7923,7 +7931,7 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -7923,7 +7931,7 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
7923 return sema.fieldVal(block, src, object, field_name, field_name_src);7931 return sema.fieldVal(block, src, object, field_name, field_name_src);
7924}7932}
79257933
7926fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7934fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index, initializing: bool) CompileError!Air.Inst.Ref {
7927 const tracy = trace(@src());7935 const tracy = trace(@src());
7928 defer tracy.end();7936 defer tracy.end();
79297937
...@@ -7933,7 +7941,7 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -7933,7 +7941,7 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
7933 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;7941 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
7934 const field_name = sema.code.nullTerminatedString(extra.field_name_start);7942 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
7935 const object_ptr = try sema.resolveInst(extra.lhs);7943 const object_ptr = try sema.resolveInst(extra.lhs);
7936 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);7944 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, initializing);
7937}7945}
79387946
7939fn zirFieldCallBind(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7947fn 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...@@ -7972,7 +7980,7 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
7972 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;7980 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
7973 const object_ptr = try sema.resolveInst(extra.lhs);7981 const object_ptr = try sema.resolveInst(extra.lhs);
7974 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known");7982 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known");
7975 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);7983 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
7976}7984}
79777985
7978fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {7986fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
...@@ -14536,7 +14544,7 @@ fn zirStructInit(...@@ -14536,7 +14544,7 @@ fn zirStructInit(
14536 .@"addrspace" = target_util.defaultAddressSpace(target, .local),14544 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
14537 });14545 });
14538 const alloc = try block.addTy(.alloc, alloc_ty);14546 const alloc = try block.addTy(.alloc, alloc_ty);
14539 const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty);14547 const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true);
14540 try sema.storePtr(block, src, field_ptr, init_inst);14548 try sema.storePtr(block, src, field_ptr, init_inst);
14541 const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val);14549 const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val);
14542 _ = try block.addBinOp(.set_union_tag, alloc, new_tag);14550 _ = 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...@@ -15604,13 +15612,21 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
15604 if (decls_val.sliceLen(mod) > 0) {15612 if (decls_val.sliceLen(mod) > 0) {
15605 return sema.fail(block, src, "reified unions must have no decls", .{});15613 return sema.fail(block, src, "reified unions must have no decls", .{});
15606 }15614 }
15615 const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout);
1560715616
15608 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);15617 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
15609 errdefer new_decl_arena.deinit();15618 errdefer new_decl_arena.deinit();
15610 const new_decl_arena_allocator = new_decl_arena.allocator();15619 const new_decl_arena_allocator = new_decl_arena.allocator();
1561115620
15612 const union_obj = try new_decl_arena_allocator.create(Module.Union);15621 const union_obj = try new_decl_arena_allocator.create(Module.Union);
15613 const type_tag: Type.Tag = if (!tag_type_val.isNull()) .union_tagged else .@"union";15622 const type_tag = if (!tag_type_val.isNull())
15623 Type.Tag.union_tagged
15624 else if (layout != .Auto)
15625 Type.Tag.@"union"
15626 else switch (block.sema.mod.optimizeMode()) {
15627 .Debug, .ReleaseSafe => Type.Tag.union_safety_tagged,
15628 .ReleaseFast, .ReleaseSmall => Type.Tag.@"union",
15629 };
15614 const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union);15630 const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union);
15615 union_payload.* = .{15631 union_payload.* = .{
15616 .base = .{ .tag = type_tag },15632 .base = .{ .tag = type_tag },
...@@ -15631,7 +15647,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -15631,7 +15647,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
15631 .fields = .{},15647 .fields = .{},
15632 .node_offset = src.node_offset.x,15648 .node_offset = src.node_offset.x,
15633 .zir_index = inst,15649 .zir_index = inst,
15634 .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout),15650 .layout = layout,
15635 .status = .have_field_types,15651 .status = .have_field_types,
15636 .namespace = .{15652 .namespace = .{
15637 .parent = block.namespace,15653 .parent = block.namespace,
...@@ -15641,11 +15657,15 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -15641,11 +15657,15 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
15641 };15657 };
1564215658
15643 // Tag type15659 // Tag type
15660 var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
15644 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));15661 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
15645 union_obj.tag_ty = if (tag_type_val.optionalValue()) |payload_val| blk: {15662 if (tag_type_val.optionalValue()) |payload_val| {
15646 var buffer: Value.ToTypeBuffer = undefined;15663 var buffer: Value.ToTypeBuffer = undefined;
15647 break :blk try payload_val.toType(&buffer).copy(new_decl_arena_allocator);15664 union_obj.tag_ty = try payload_val.toType(&buffer).copy(new_decl_arena_allocator);
15648 } else try sema.generateUnionTagTypeSimple(block, fields_len, null);15665 } else {
15666 union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, fields_len, null);
15667 enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields;
15668 }
1564915669
15650 // Fields15670 // Fields
15651 if (fields_len > 0) {15671 if (fields_len > 0) {
...@@ -15669,6 +15689,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -15669,6 +15689,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
15669 sema.mod,15689 sema.mod,
15670 );15690 );
1567115691
15692 if (enum_field_names) |set| {
15693 set.putAssumeCapacity(field_name, {});
15694 }
15695
15672 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);15696 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
15673 if (gop.found_existing) {15697 if (gop.found_existing) {
15674 // TODO: better source location15698 // TODO: better source location
...@@ -18898,6 +18922,8 @@ pub const PanicId = enum {...@@ -18898,6 +18922,8 @@ pub const PanicId = enum {
18898 divide_by_zero,18922 divide_by_zero,
18899 remainder_division_zero_negative,18923 remainder_division_zero_negative,
18900 exact_division_remainder,18924 exact_division_remainder,
18925 /// TODO make this call `std.builtin.panicInactiveUnionField`.
18926 inactive_union_field,
18901};18927};
1890218928
18903fn addSafetyCheck(18929fn addSafetyCheck(
...@@ -19120,6 +19146,7 @@ fn safetyPanic(...@@ -19120,6 +19146,7 @@ fn safetyPanic(
19120 .divide_by_zero => "division by zero",19146 .divide_by_zero => "division by zero",
19121 .remainder_division_zero_negative => "remainder division by zero or negative value",19147 .remainder_division_zero_negative => "remainder division by zero or negative value",
19122 .exact_division_remainder => "exact division produced remainder",19148 .exact_division_remainder => "exact division produced remainder",
19149 .inactive_union_field => "access of inactive union field",
19123 };19150 };
1912419151
19125 const msg_inst = msg_inst: {19152 const msg_inst = msg_inst: {
...@@ -19339,7 +19366,7 @@ fn fieldVal(...@@ -19339,7 +19366,7 @@ fn fieldVal(
19339 },19366 },
19340 .Union => if (is_pointer_to) {19367 .Union => if (is_pointer_to) {
19341 // Avoid loading the entire union by fetching a pointer and loading that19368 // Avoid loading the entire union by fetching a pointer and loading that
19342 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty);19369 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
19343 return sema.analyzeLoad(block, src, field_ptr, object_src);19370 return sema.analyzeLoad(block, src, field_ptr, object_src);
19344 } else {19371 } else {
19345 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);19372 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
...@@ -19356,6 +19383,7 @@ fn fieldPtr(...@@ -19356,6 +19383,7 @@ fn fieldPtr(
19356 object_ptr: Air.Inst.Ref,19383 object_ptr: Air.Inst.Ref,
19357 field_name: []const u8,19384 field_name: []const u8,
19358 field_name_src: LazySrcLoc,19385 field_name_src: LazySrcLoc,
19386 initializing: bool,
19359) CompileError!Air.Inst.Ref {19387) CompileError!Air.Inst.Ref {
19360 // When editing this function, note that there is corresponding logic to be edited19388 // When editing this function, note that there is corresponding logic to be edited
19361 // in `fieldVal`. This function takes a pointer and returns a pointer.19389 // in `fieldVal`. This function takes a pointer and returns a pointer.
...@@ -19547,7 +19575,7 @@ fn fieldPtr(...@@ -19547,7 +19575,7 @@ fn fieldPtr(
19547 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)19575 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
19548 else19576 else
19549 object_ptr;19577 object_ptr;
19550 return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);19578 return sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
19551 },19579 },
19552 else => {},19580 else => {},
19553 }19581 }
...@@ -19995,6 +20023,7 @@ fn unionFieldPtr(...@@ -19995,6 +20023,7 @@ fn unionFieldPtr(
19995 field_name: []const u8,20023 field_name: []const u8,
19996 field_name_src: LazySrcLoc,20024 field_name_src: LazySrcLoc,
19997 unresolved_union_ty: Type,20025 unresolved_union_ty: Type,
20026 initializing: bool,
19998) CompileError!Air.Inst.Ref {20027) CompileError!Air.Inst.Ref {
19999 const arena = sema.arena;20028 const arena = sema.arena;
20000 assert(unresolved_union_ty.zigTypeTag() == .Union);20029 assert(unresolved_union_ty.zigTypeTag() == .Union);
...@@ -20010,30 +20039,32 @@ fn unionFieldPtr(...@@ -20010,30 +20039,32 @@ fn unionFieldPtr(
20010 .@"addrspace" = union_ptr_ty.ptrAddressSpace(),20039 .@"addrspace" = union_ptr_ty.ptrAddressSpace(),
20011 });20040 });
2001220041
20013 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {20042 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
20014 switch (union_obj.layout) {20043 switch (union_obj.layout) {
20015 .Auto => {20044 .Auto => if (!initializing) {
20016 // TODO emit the access of inactive union field error commented out below.20045 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
20017 // In order to do that, we need to first solve the problem that AstGen20046 break :ct;
20018 // emits field_ptr instructions in order to initialize union values.20047 if (union_val.isUndef()) {
20019 // In such case we need to know that the field_ptr instruction (which is20048 return sema.failWithUseOfUndef(block, src);
20020 // calling this unionFieldPtr function) is *initializing* the union,20049 }
20021 // in which case we would skip this check, and in fact we would actually20050 const tag_and_val = union_val.castTag(.@"union").?.data;
20022 // set the union tag here and the payload to undefined.20051 var field_tag_buf: Value.Payload.U32 = .{
2002320052 .base = .{ .tag = .enum_field_index },
20024 //const tag_and_val = union_val.castTag(.@"union").?.data;20053 .data = field_index,
20025 //var field_tag_buf: Value.Payload.U32 = .{20054 };
20026 // .base = .{ .tag = .enum_field_index },20055 const field_tag = Value.initPayload(&field_tag_buf.base);
20027 // .data = field_index,20056 const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
20028 //};20057 if (!tag_matches) {
20029 //const field_tag = Value.initPayload(&field_tag_buf.base);20058 const msg = msg: {
20030 //const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod);20059 const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data;
20031 //if (!tag_matches) {20060 const active_field_name = union_obj.fields.keys()[active_index];
20032 // // TODO enhance this saying which one was active20061 const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name });
20033 // // and which one was accessed, and showing where the union was declared.20062 errdefer msg.destroy(sema.gpa);
20034 // return sema.fail(block, src, "access of inactive union field", .{});20063 try sema.addDeclaredHereNote(msg, union_ty);
20035 //}20064 break :msg msg;
20036 // TODO add runtime safety check for the active tag20065 };
20066 return sema.failWithOwnedErrorMsg(block, msg);
20067 }
20037 },20068 },
20038 .Packed, .Extern => {},20069 .Packed, .Extern => {},
20039 }20070 }
...@@ -20048,6 +20079,16 @@ fn unionFieldPtr(...@@ -20048,6 +20079,16 @@ fn unionFieldPtr(
20048 }20079 }
2004920080
20050 try sema.requireRuntimeBlock(block, src, null);20081 try sema.requireRuntimeBlock(block, src, null);
20082 if (!initializing and union_obj.layout == .Auto and block.wantSafety() and union_ty.unionTagTypeSafety() != null) {
20083 const enum_ty = union_ty.unionTagTypeHypothetical();
20084 const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
20085 const wanted_tag = try sema.addConstant(enum_ty, wanted_tag_val);
20086 // TODO would it be better if get_union_tag supported pointers to unions?
20087 const union_val = try block.addTyOp(.load, union_ty, union_ptr);
20088 const active_tag = try block.addTyOp(.get_union_tag, enum_ty, union_val);
20089 const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
20090 try sema.addSafetyCheck(block, ok, .inactive_union_field);
20091 }
20051 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);20092 return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
20052}20093}
2005320094
...@@ -20106,6 +20147,14 @@ fn unionFieldVal(...@@ -20106,6 +20147,14 @@ fn unionFieldVal(
20106 }20147 }
2010720148
20108 try sema.requireRuntimeBlock(block, src, null);20149 try sema.requireRuntimeBlock(block, src, null);
20150 if (union_obj.layout == .Auto and block.wantSafety() and union_ty.unionTagTypeSafety() != null) {
20151 const enum_ty = union_ty.unionTagTypeHypothetical();
20152 const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
20153 const wanted_tag = try sema.addConstant(enum_ty, wanted_tag_val);
20154 const active_tag = try block.addTyOp(.get_union_tag, enum_ty, union_byval);
20155 const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
20156 try sema.addSafetyCheck(block, ok, .inactive_union_field);
20157 }
20109 return block.addStructFieldVal(union_byval, field_index, field.ty);20158 return block.addStructFieldVal(union_byval, field_index, field.ty);
20110}20159}
2011120160
...@@ -25424,7 +25473,7 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)...@@ -25424,7 +25473,7 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
25424 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);25473 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
25425 return ty;25474 return ty;
25426 },25475 },
25427 .@"union", .union_tagged => {25476 .@"union", .union_safety_tagged, .union_tagged => {
25428 const union_obj = ty.cast(Type.Payload.Union).?.data;25477 const union_obj = ty.cast(Type.Payload.Union).?.data;
25429 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);25478 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
25430 return ty;25479 return ty;
...@@ -26449,7 +26498,7 @@ pub fn typeHasOnePossibleValue(...@@ -26449,7 +26498,7 @@ pub fn typeHasOnePossibleValue(
26449 return null;26498 return null;
26450 }26499 }
26451 },26500 },
26452 .@"union", .union_tagged => {26501 .@"union", .union_safety_tagged, .union_tagged => {
26453 const resolved_ty = try sema.resolveTypeFields(block, src, ty);26502 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
26454 const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;26503 const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
26455 const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse26504 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...@@ -27081,7 +27130,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
27081 }27130 }
27082 },27131 },
2708327132
27084 .@"union", .union_tagged => {27133 .@"union", .union_safety_tagged, .union_tagged => {
27085 const union_obj = ty.cast(Type.Payload.Union).?.data;27134 const union_obj = ty.cast(Type.Payload.Union).?.data;
27086 switch (union_obj.requires_comptime) {27135 switch (union_obj.requires_comptime) {
27087 .no, .wip => return false,27136 .no, .wip => return false,
src/Zir.zig+5
...@@ -410,6 +410,8 @@ pub const Inst = struct {...@@ -410,6 +410,8 @@ pub const Inst = struct {
410 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.410 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
411 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.411 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
412 field_ptr,412 field_ptr,
413 /// Same as `field_ptr` but used for struct init.
414 field_ptr_init,
413 /// Given a struct or object that contains virtual fields, returns the named field.415 /// Given a struct or object that contains virtual fields, returns the named field.
414 /// The field name is stored in string_bytes. Used by a.b syntax.416 /// The field name is stored in string_bytes. Used by a.b syntax.
415 /// This instruction also accepts a pointer.417 /// This instruction also accepts a pointer.
...@@ -1070,6 +1072,7 @@ pub const Inst = struct {...@@ -1070,6 +1072,7 @@ pub const Inst = struct {
1070 .@"export",1072 .@"export",
1071 .export_value,1073 .export_value,
1072 .field_ptr,1074 .field_ptr,
1075 .field_ptr_init,
1073 .field_val,1076 .field_val,
1074 .field_call_bind,1077 .field_call_bind,
1075 .field_ptr_named,1078 .field_ptr_named,
...@@ -1370,6 +1373,7 @@ pub const Inst = struct {...@@ -1370,6 +1373,7 @@ pub const Inst = struct {
1370 .elem_ptr_imm,1373 .elem_ptr_imm,
1371 .elem_val_node,1374 .elem_val_node,
1372 .field_ptr,1375 .field_ptr,
1376 .field_ptr_init,
1373 .field_val,1377 .field_val,
1374 .field_call_bind,1378 .field_call_bind,
1375 .field_ptr_named,1379 .field_ptr_named,
...@@ -1629,6 +1633,7 @@ pub const Inst = struct {...@@ -1629,6 +1633,7 @@ pub const Inst = struct {
1629 .@"export" = .pl_node,1633 .@"export" = .pl_node,
1630 .export_value = .pl_node,1634 .export_value = .pl_node,
1631 .field_ptr = .pl_node,1635 .field_ptr = .pl_node,
1636 .field_ptr_init = .pl_node,
1632 .field_val = .pl_node,1637 .field_val = .pl_node,
1633 .field_ptr_named = .pl_node,1638 .field_ptr_named = .pl_node,
1634 .field_val_named = .pl_node,1639 .field_val_named = .pl_node,
src/arch/wasm/abi.zig+2-2
...@@ -77,7 +77,7 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {...@@ -77,7 +77,7 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
77 .Union => {77 .Union => {
78 const layout = ty.unionGetLayout(target);78 const layout = ty.unionGetLayout(target);
79 if (layout.payload_size == 0 and layout.tag_size != 0) {79 if (layout.payload_size == 0 and layout.tag_size != 0) {
80 return classifyType(ty.unionTagType().?, target);80 return classifyType(ty.unionTagTypeSafety().?, target);
81 }81 }
82 if (ty.unionFields().count() > 1) return memory;82 if (ty.unionFields().count() > 1) return memory;
83 return classifyType(ty.unionFields().values()[0].ty, target);83 return classifyType(ty.unionFields().values()[0].ty, target);
...@@ -111,7 +111,7 @@ pub fn scalarType(ty: Type, target: std.Target) Type {...@@ -111,7 +111,7 @@ pub fn scalarType(ty: Type, target: std.Target) Type {
111 .Union => {111 .Union => {
112 const layout = ty.unionGetLayout(target);112 const layout = ty.unionGetLayout(target);
113 if (layout.payload_size == 0 and layout.tag_size != 0) {113 if (layout.payload_size == 0 and layout.tag_size != 0) {
114 return scalarType(ty.unionTagType().?, target);114 return scalarType(ty.unionTagTypeSafety().?, target);
115 }115 }
116 std.debug.assert(ty.unionFields().count() == 1);116 std.debug.assert(ty.unionFields().count() == 1);
117 return scalarType(ty.unionFields().values()[0].ty, target);117 return scalarType(ty.unionFields().values()[0].ty, target);
src/codegen/c.zig+9-9
...@@ -504,7 +504,7 @@ pub const DeclGen = struct {...@@ -504,7 +504,7 @@ pub const DeclGen = struct {
504 if (field_ty.hasRuntimeBitsIgnoreComptime()) {504 if (field_ty.hasRuntimeBitsIgnoreComptime()) {
505 try writer.writeAll("&(");505 try writer.writeAll("&(");
506 try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty);506 try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty);
507 if (field_ptr.container_ty.tag() == .union_tagged) {507 if (field_ptr.container_ty.tag() == .union_tagged or field_ptr.container_ty.tag() == .union_safety_tagged) {
508 try writer.print(")->payload.{ }", .{fmtIdent(field_name)});508 try writer.print(")->payload.{ }", .{fmtIdent(field_name)});
509 } else {509 } else {
510 try writer.print(")->{ }", .{fmtIdent(field_name)});510 try writer.print(")->{ }", .{fmtIdent(field_name)});
...@@ -842,7 +842,7 @@ pub const DeclGen = struct {...@@ -842,7 +842,7 @@ pub const DeclGen = struct {
842 try dg.renderTypecast(writer, ty);842 try dg.renderTypecast(writer, ty);
843 try writer.writeAll("){");843 try writer.writeAll("){");
844844
845 if (ty.unionTagType()) |tag_ty| {845 if (ty.unionTagTypeSafety()) |tag_ty| {
846 if (layout.tag_size != 0) {846 if (layout.tag_size != 0) {
847 try writer.writeAll(".tag = ");847 try writer.writeAll(".tag = ");
848 try dg.renderValue(writer, tag_ty, union_obj.tag, location);848 try dg.renderValue(writer, tag_ty, union_obj.tag, location);
...@@ -858,7 +858,7 @@ pub const DeclGen = struct {...@@ -858,7 +858,7 @@ pub const DeclGen = struct {
858 try writer.print(".{ } = ", .{fmtIdent(field_name)});858 try writer.print(".{ } = ", .{fmtIdent(field_name)});
859 try dg.renderValue(writer, field_ty, union_obj.val, location);859 try dg.renderValue(writer, field_ty, union_obj.val, location);
860 }860 }
861 if (ty.unionTagType()) |_| {861 if (ty.unionTagTypeSafety()) |_| {
862 try writer.writeAll("}");862 try writer.writeAll("}");
863 }863 }
864 try writer.writeAll("}");864 try writer.writeAll("}");
...@@ -1110,7 +1110,7 @@ pub const DeclGen = struct {...@@ -1110,7 +1110,7 @@ pub const DeclGen = struct {
1110 defer buffer.deinit();1110 defer buffer.deinit();
11111111
1112 try buffer.appendSlice("typedef ");1112 try buffer.appendSlice("typedef ");
1113 if (t.unionTagType()) |tag_ty| {1113 if (t.unionTagTypeSafety()) |tag_ty| {
1114 const name: CValue = .{ .bytes = "tag" };1114 const name: CValue = .{ .bytes = "tag" };
1115 try buffer.appendSlice("struct {\n ");1115 try buffer.appendSlice("struct {\n ");
1116 if (layout.tag_size != 0) {1116 if (layout.tag_size != 0) {
...@@ -1134,7 +1134,7 @@ pub const DeclGen = struct {...@@ -1134,7 +1134,7 @@ pub const DeclGen = struct {
1134 }1134 }
1135 try buffer.appendSlice("} ");1135 try buffer.appendSlice("} ");
11361136
1137 if (t.unionTagType()) |_| {1137 if (t.unionTagTypeSafety()) |_| {
1138 try buffer.appendSlice("payload;\n} ");1138 try buffer.appendSlice("payload;\n} ");
1139 }1139 }
11401140
...@@ -3368,7 +3368,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc...@@ -3368,7 +3368,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
3368 field_name = fields.keys()[index];3368 field_name = fields.keys()[index];
3369 field_val_ty = fields.values()[index].ty;3369 field_val_ty = fields.values()[index].ty;
3370 },3370 },
3371 .@"union", .union_tagged => {3371 .@"union", .union_safety_tagged, .union_tagged => {
3372 const fields = struct_ty.unionFields();3372 const fields = struct_ty.unionFields();
3373 field_name = fields.keys()[index];3373 field_name = fields.keys()[index];
3374 field_val_ty = fields.values()[index].ty;3374 field_val_ty = fields.values()[index].ty;
...@@ -3383,7 +3383,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc...@@ -3383,7 +3383,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
3383 },3383 },
3384 else => unreachable,3384 else => unreachable,
3385 }3385 }
3386 const payload = if (struct_ty.tag() == .union_tagged) "payload." else "";3386 const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else "";
33873387
3388 const inst_ty = f.air.typeOfIndex(inst);3388 const inst_ty = f.air.typeOfIndex(inst);
3389 const local = try f.allocLocal(inst_ty, .Const);3389 const local = try f.allocLocal(inst_ty, .Const);
...@@ -3415,7 +3415,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3415,7 +3415,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
3415 defer buf.deinit();3415 defer buf.deinit();
3416 const field_name = switch (struct_ty.tag()) {3416 const field_name = switch (struct_ty.tag()) {
3417 .@"struct" => struct_ty.structFields().keys()[extra.field_index],3417 .@"struct" => struct_ty.structFields().keys()[extra.field_index],
3418 .@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index],3418 .@"union", .union_safety_tagged, .union_tagged => struct_ty.unionFields().keys()[extra.field_index],
3419 .tuple, .anon_struct => blk: {3419 .tuple, .anon_struct => blk: {
3420 const tuple = struct_ty.tupleFields();3420 const tuple = struct_ty.tupleFields();
3421 if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;3421 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 {...@@ -3425,7 +3425,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
3425 },3425 },
3426 else => unreachable,3426 else => unreachable,
3427 };3427 };
3428 const payload = if (struct_ty.tag() == .union_tagged) "payload." else "";3428 const payload = if (struct_ty.tag() == .union_tagged or struct_ty.tag() == .union_safety_tagged) "payload." else "";
34293429
3430 const inst_ty = f.air.typeOfIndex(inst);3430 const inst_ty = f.air.typeOfIndex(inst);
3431 const local = try f.allocLocal(inst_ty, .Const);3431 const local = try f.allocLocal(inst_ty, .Const);
src/codegen/llvm.zig+2-2
...@@ -3404,7 +3404,7 @@ pub const DeclGen = struct {...@@ -3404,7 +3404,7 @@ pub const DeclGen = struct {
34043404
3405 if (layout.payload_size == 0) {3405 if (layout.payload_size == 0) {
3406 return lowerValue(dg, .{3406 return lowerValue(dg, .{
3407 .ty = tv.ty.unionTagType().?,3407 .ty = tv.ty.unionTagTypeSafety().?,
3408 .val = tag_and_val.tag,3408 .val = tag_and_val.tag,
3409 });3409 });
3410 }3410 }
...@@ -3446,7 +3446,7 @@ pub const DeclGen = struct {...@@ -3446,7 +3446,7 @@ pub const DeclGen = struct {
3446 }3446 }
3447 }3447 }
3448 const llvm_tag_value = try lowerValue(dg, .{3448 const llvm_tag_value = try lowerValue(dg, .{
3449 .ty = tv.ty.unionTagType().?,3449 .ty = tv.ty.unionTagTypeSafety().?,
3450 .val = tag_and_val.tag,3450 .val = tag_and_val.tag,
3451 });3451 });
3452 var fields: [3]*const llvm.Value = undefined;3452 var fields: [3]*const llvm.Value = undefined;
src/print_zir.zig+1
...@@ -390,6 +390,7 @@ const Writer = struct {...@@ -390,6 +390,7 @@ const Writer = struct {
390 .switch_block => try self.writeSwitchBlock(stream, inst),390 .switch_block => try self.writeSwitchBlock(stream, inst),
391391
392 .field_ptr,392 .field_ptr,
393 .field_ptr_init,
393 .field_val,394 .field_val,
394 .field_call_bind,395 .field_call_bind,
395 => try self.writePlNodeField(stream, inst),396 => try self.writePlNodeField(stream, inst),
src/type.zig+56-27
...@@ -149,6 +149,7 @@ pub const Type = extern union {...@@ -149,6 +149,7 @@ pub const Type = extern union {
149 => return .Enum,149 => return .Enum,
150150
151 .@"union",151 .@"union",
152 .union_safety_tagged,
152 .union_tagged,153 .union_tagged,
153 .type_info,154 .type_info,
154 => return .Union,155 => return .Union,
...@@ -902,7 +903,7 @@ pub const Type = extern union {...@@ -902,7 +903,7 @@ pub const Type = extern union {
902 .reduce_op,903 .reduce_op,
903 => unreachable, // needed to resolve the type before now904 => unreachable, // needed to resolve the type before now
904905
905 .@"union", .union_tagged => {906 .@"union", .union_safety_tagged, .union_tagged => {
906 const a_union_obj = a.cast(Payload.Union).?.data;907 const a_union_obj = a.cast(Payload.Union).?.data;
907 const b_union_obj = (b.cast(Payload.Union) orelse return false).data;908 const b_union_obj = (b.cast(Payload.Union) orelse return false).data;
908 return a_union_obj == b_union_obj;909 return a_union_obj == b_union_obj;
...@@ -1210,7 +1211,7 @@ pub const Type = extern union {...@@ -1210,7 +1211,7 @@ pub const Type = extern union {
1210 .reduce_op,1211 .reduce_op,
1211 => unreachable, // needed to resolve the type before now1212 => unreachable, // needed to resolve the type before now
12121213
1213 .@"union", .union_tagged => {1214 .@"union", .union_safety_tagged, .union_tagged => {
1214 const union_obj: *const Module.Union = ty.cast(Payload.Union).?.data;1215 const union_obj: *const Module.Union = ty.cast(Payload.Union).?.data;
1215 std.hash.autoHash(hasher, std.builtin.TypeId.Union);1216 std.hash.autoHash(hasher, std.builtin.TypeId.Union);
1216 std.hash.autoHash(hasher, union_obj);1217 std.hash.autoHash(hasher, union_obj);
...@@ -1479,7 +1480,7 @@ pub const Type = extern union {...@@ -1479,7 +1480,7 @@ pub const Type = extern union {
1479 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),1480 .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
1480 .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),1481 .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
1481 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),1482 .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
1482 .@"union", .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),1483 .@"union", .union_safety_tagged, .union_tagged => return self.copyPayloadShallow(allocator, Payload.Union),
1483 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),1484 .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
1484 .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered),1485 .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered),
1485 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),1486 .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
...@@ -1603,7 +1604,7 @@ pub const Type = extern union {...@@ -1603,7 +1604,7 @@ pub const Type = extern union {
1603 @tagName(t), struct_obj.owner_decl,1604 @tagName(t), struct_obj.owner_decl,
1604 });1605 });
1605 },1606 },
1606 .@"union", .union_tagged => {1607 .@"union", .union_safety_tagged, .union_tagged => {
1607 const union_obj = ty.cast(Payload.Union).?.data;1608 const union_obj = ty.cast(Payload.Union).?.data;
1608 return writer.print("({s} decl={d})", .{1609 return writer.print("({s} decl={d})", .{
1609 @tagName(t), union_obj.owner_decl,1610 @tagName(t), union_obj.owner_decl,
...@@ -1989,7 +1990,7 @@ pub const Type = extern union {...@@ -1989,7 +1990,7 @@ pub const Type = extern union {
1989 const decl = mod.declPtr(struct_obj.owner_decl);1990 const decl = mod.declPtr(struct_obj.owner_decl);
1990 try decl.renderFullyQualifiedName(mod, writer);1991 try decl.renderFullyQualifiedName(mod, writer);
1991 },1992 },
1992 .@"union", .union_tagged => {1993 .@"union", .union_safety_tagged, .union_tagged => {
1993 const union_obj = ty.cast(Payload.Union).?.data;1994 const union_obj = ty.cast(Payload.Union).?.data;
1994 const decl = mod.declPtr(union_obj.owner_decl);1995 const decl = mod.declPtr(union_obj.owner_decl);
1995 try decl.renderFullyQualifiedName(mod, writer);1996 try decl.renderFullyQualifiedName(mod, writer);
...@@ -2485,8 +2486,8 @@ pub const Type = extern union {...@@ -2485,8 +2486,8 @@ pub const Type = extern union {
2485 return false;2486 return false;
2486 }2487 }
2487 },2488 },
2488 .union_tagged => {2489 .union_safety_tagged, .union_tagged => {
2489 const union_obj = ty.castTag(.union_tagged).?.data;2490 const union_obj = ty.cast(Payload.Union).?.data;
2490 if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) {2491 if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) {
2491 return true;2492 return true;
2492 }2493 }
...@@ -2644,7 +2645,7 @@ pub const Type = extern union {...@@ -2644,7 +2645,7 @@ pub const Type = extern union {
26442645
2645 .optional => ty.isPtrLikeOptional(),2646 .optional => ty.isPtrLikeOptional(),
2646 .@"struct" => ty.castTag(.@"struct").?.data.layout != .Auto,2647 .@"struct" => ty.castTag(.@"struct").?.data.layout != .Auto,
2647 .@"union" => ty.castTag(.@"union").?.data.layout != .Auto,2648 .@"union", .union_safety_tagged => ty.cast(Payload.Union).?.data.layout != .Auto,
2648 .union_tagged => false,2649 .union_tagged => false,
2649 };2650 };
2650 }2651 }
...@@ -3050,11 +3051,10 @@ pub const Type = extern union {...@@ -3050,11 +3051,10 @@ pub const Type = extern union {
3050 },3051 },
3051 .@"union" => {3052 .@"union" => {
3052 const union_obj = ty.castTag(.@"union").?.data;3053 const union_obj = ty.castTag(.@"union").?.data;
3053 // TODO pass `true` for have_tag when unions have a safety tag
3054 return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, false);3054 return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, false);
3055 },3055 },
3056 .union_tagged => {3056 .union_safety_tagged, .union_tagged => {
3057 const union_obj = ty.castTag(.union_tagged).?.data;3057 const union_obj = ty.cast(Payload.Union).?.data;
3058 return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, true);3058 return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, true);
3059 },3059 },
30603060
...@@ -3232,11 +3232,10 @@ pub const Type = extern union {...@@ -3232,11 +3232,10 @@ pub const Type = extern union {
3232 },3232 },
3233 .@"union" => {3233 .@"union" => {
3234 const union_obj = ty.castTag(.@"union").?.data;3234 const union_obj = ty.castTag(.@"union").?.data;
3235 // TODO pass `true` for have_tag when unions have a safety tag
3236 return abiSizeAdvancedUnion(ty, target, strat, union_obj, false);3235 return abiSizeAdvancedUnion(ty, target, strat, union_obj, false);
3237 },3236 },
3238 .union_tagged => {3237 .union_safety_tagged, .union_tagged => {
3239 const union_obj = ty.castTag(.union_tagged).?.data;3238 const union_obj = ty.cast(Payload.Union).?.data;
3240 return abiSizeAdvancedUnion(ty, target, strat, union_obj, true);3239 return abiSizeAdvancedUnion(ty, target, strat, union_obj, true);
3241 },3240 },
32423241
...@@ -3526,7 +3525,7 @@ pub const Type = extern union {...@@ -3526,7 +3525,7 @@ pub const Type = extern union {
3526 return try bitSizeAdvanced(int_tag_ty, target, sema_kit);3525 return try bitSizeAdvanced(int_tag_ty, target, sema_kit);
3527 },3526 },
35283527
3529 .@"union", .union_tagged => {3528 .@"union", .union_safety_tagged, .union_tagged => {
3530 if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);3529 if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty);
3531 const union_obj = ty.cast(Payload.Union).?.data;3530 const union_obj = ty.cast(Payload.Union).?.data;
3532 assert(union_obj.haveFieldTypes());3531 assert(union_obj.haveFieldTypes());
...@@ -4194,6 +4193,33 @@ pub const Type = extern union {...@@ -4194,6 +4193,33 @@ pub const Type = extern union {
4194 };4193 };
4195 }4194 }
41964195
4196 /// Same as `unionTagType` but includes safety tag.
4197 /// Codegen should use this version.
4198 pub fn unionTagTypeSafety(ty: Type) ?Type {
4199 return switch (ty.tag()) {
4200 .union_safety_tagged, .union_tagged => {
4201 const union_obj = ty.cast(Payload.Union).?.data;
4202 assert(union_obj.haveFieldTypes());
4203 return union_obj.tag_ty;
4204 },
4205
4206 .atomic_order,
4207 .atomic_rmw_op,
4208 .calling_convention,
4209 .address_space,
4210 .float_mode,
4211 .reduce_op,
4212 .call_options,
4213 .prefetch_options,
4214 .export_options,
4215 .extern_options,
4216 .type_info,
4217 => unreachable, // needed to call resolveTypeFields first
4218
4219 else => null,
4220 };
4221 }
4222
4197 /// Asserts the type is a union; returns the tag type, even if the tag will4223 /// Asserts the type is a union; returns the tag type, even if the tag will
4198 /// not be stored at runtime.4224 /// not be stored at runtime.
4199 pub fn unionTagTypeHypothetical(ty: Type) Type {4225 pub fn unionTagTypeHypothetical(ty: Type) Type {
...@@ -4225,8 +4251,8 @@ pub const Type = extern union {...@@ -4225,8 +4251,8 @@ pub const Type = extern union {
4225 const union_obj = ty.castTag(.@"union").?.data;4251 const union_obj = ty.castTag(.@"union").?.data;
4226 return union_obj.getLayout(target, false);4252 return union_obj.getLayout(target, false);
4227 },4253 },
4228 .union_tagged => {4254 .union_safety_tagged, .union_tagged => {
4229 const union_obj = ty.castTag(.union_tagged).?.data;4255 const union_obj = ty.cast(Payload.Union).?.data;
4230 return union_obj.getLayout(target, true);4256 return union_obj.getLayout(target, true);
4231 },4257 },
4232 else => unreachable,4258 else => unreachable,
...@@ -4238,6 +4264,7 @@ pub const Type = extern union {...@@ -4238,6 +4264,7 @@ pub const Type = extern union {
4238 .tuple, .empty_struct_literal, .anon_struct => .Auto,4264 .tuple, .empty_struct_literal, .anon_struct => .Auto,
4239 .@"struct" => ty.castTag(.@"struct").?.data.layout,4265 .@"struct" => ty.castTag(.@"struct").?.data.layout,
4240 .@"union" => ty.castTag(.@"union").?.data.layout,4266 .@"union" => ty.castTag(.@"union").?.data.layout,
4267 .union_safety_tagged => ty.castTag(.union_safety_tagged).?.data.layout,
4241 .union_tagged => ty.castTag(.union_tagged).?.data.layout,4268 .union_tagged => ty.castTag(.union_tagged).?.data.layout,
4242 else => unreachable,4269 else => unreachable,
4243 };4270 };
...@@ -4936,7 +4963,7 @@ pub const Type = extern union {...@@ -4936,7 +4963,7 @@ pub const Type = extern union {
4936 return null;4963 return null;
4937 }4964 }
4938 },4965 },
4939 .@"union", .union_tagged => {4966 .@"union", .union_safety_tagged, .union_tagged => {
4940 const union_obj = ty.cast(Payload.Union).?.data;4967 const union_obj = ty.cast(Payload.Union).?.data;
4941 const tag_val = union_obj.tag_ty.onePossibleValue() orelse return null;4968 const tag_val = union_obj.tag_ty.onePossibleValue() orelse return null;
4942 const only_field = union_obj.fields.values()[0];4969 const only_field = union_obj.fields.values()[0];
...@@ -5114,7 +5141,7 @@ pub const Type = extern union {...@@ -5114,7 +5141,7 @@ pub const Type = extern union {
5114 }5141 }
5115 },5142 },
51165143
5117 .@"union", .union_tagged => {5144 .@"union", .union_safety_tagged, .union_tagged => {
5118 const union_obj = ty.cast(Type.Payload.Union).?.data;5145 const union_obj = ty.cast(Type.Payload.Union).?.data;
5119 switch (union_obj.requires_comptime) {5146 switch (union_obj.requires_comptime) {
5120 .wip, .unknown => unreachable, // This function asserts types already resolved.5147 .wip, .unknown => unreachable, // This function asserts types already resolved.
...@@ -5167,6 +5194,7 @@ pub const Type = extern union {...@@ -5167,6 +5194,7 @@ pub const Type = extern union {
5167 .empty_struct => self.castTag(.empty_struct).?.data,5194 .empty_struct => self.castTag(.empty_struct).?.data,
5168 .@"opaque" => &self.castTag(.@"opaque").?.data.namespace,5195 .@"opaque" => &self.castTag(.@"opaque").?.data.namespace,
5169 .@"union" => &self.castTag(.@"union").?.data.namespace,5196 .@"union" => &self.castTag(.@"union").?.data.namespace,
5197 .union_safety_tagged => &self.castTag(.union_safety_tagged).?.data.namespace,
5170 .union_tagged => &self.castTag(.union_tagged).?.data.namespace,5198 .union_tagged => &self.castTag(.union_tagged).?.data.namespace,
51715199
5172 else => null,5200 else => null,
...@@ -5439,7 +5467,7 @@ pub const Type = extern union {...@@ -5439,7 +5467,7 @@ pub const Type = extern union {
5439 const struct_obj = ty.castTag(.@"struct").?.data;5467 const struct_obj = ty.castTag(.@"struct").?.data;
5440 return struct_obj.fields.values()[index].ty;5468 return struct_obj.fields.values()[index].ty;
5441 },5469 },
5442 .@"union", .union_tagged => {5470 .@"union", .union_safety_tagged, .union_tagged => {
5443 const union_obj = ty.cast(Payload.Union).?.data;5471 const union_obj = ty.cast(Payload.Union).?.data;
5444 return union_obj.fields.values()[index].ty;5472 return union_obj.fields.values()[index].ty;
5445 },5473 },
...@@ -5456,7 +5484,7 @@ pub const Type = extern union {...@@ -5456,7 +5484,7 @@ pub const Type = extern union {
5456 assert(struct_obj.layout != .Packed);5484 assert(struct_obj.layout != .Packed);
5457 return struct_obj.fields.values()[index].normalAlignment(target);5485 return struct_obj.fields.values()[index].normalAlignment(target);
5458 },5486 },
5459 .@"union", .union_tagged => {5487 .@"union", .union_safety_tagged, .union_tagged => {
5460 const union_obj = ty.cast(Payload.Union).?.data;5488 const union_obj = ty.cast(Payload.Union).?.data;
5461 return union_obj.fields.values()[index].normalAlignment(target);5489 return union_obj.fields.values()[index].normalAlignment(target);
5462 },5490 },
...@@ -5619,8 +5647,8 @@ pub const Type = extern union {...@@ -5619,8 +5647,8 @@ pub const Type = extern union {
5619 },5647 },
56205648
5621 .@"union" => return 0,5649 .@"union" => return 0,
5622 .union_tagged => {5650 .union_safety_tagged, .union_tagged => {
5623 const union_obj = ty.castTag(.union_tagged).?.data;5651 const union_obj = ty.cast(Payload.Union).?.data;
5624 const layout = union_obj.getLayout(target, true);5652 const layout = union_obj.getLayout(target, true);
5625 if (layout.tag_align >= layout.payload_align) {5653 if (layout.tag_align >= layout.payload_align) {
5626 // {Tag, Payload}5654 // {Tag, Payload}
...@@ -5660,7 +5688,7 @@ pub const Type = extern union {...@@ -5660,7 +5688,7 @@ pub const Type = extern union {
5660 const error_set = ty.castTag(.error_set).?.data;5688 const error_set = ty.castTag(.error_set).?.data;
5661 return error_set.srcLoc(mod);5689 return error_set.srcLoc(mod);
5662 },5690 },
5663 .@"union", .union_tagged => {5691 .@"union", .union_safety_tagged, .union_tagged => {
5664 const union_obj = ty.cast(Payload.Union).?.data;5692 const union_obj = ty.cast(Payload.Union).?.data;
5665 return union_obj.srcLoc(mod);5693 return union_obj.srcLoc(mod);
5666 },5694 },
...@@ -5704,7 +5732,7 @@ pub const Type = extern union {...@@ -5704,7 +5732,7 @@ pub const Type = extern union {
5704 const error_set = ty.castTag(.error_set).?.data;5732 const error_set = ty.castTag(.error_set).?.data;
5705 return error_set.owner_decl;5733 return error_set.owner_decl;
5706 },5734 },
5707 .@"union", .union_tagged => {5735 .@"union", .union_safety_tagged, .union_tagged => {
5708 const union_obj = ty.cast(Payload.Union).?.data;5736 const union_obj = ty.cast(Payload.Union).?.data;
5709 return union_obj.owner_decl;5737 return union_obj.owner_decl;
5710 },5738 },
...@@ -5748,7 +5776,7 @@ pub const Type = extern union {...@@ -5748,7 +5776,7 @@ pub const Type = extern union {
5748 const error_set = ty.castTag(.error_set).?.data;5776 const error_set = ty.castTag(.error_set).?.data;
5749 return error_set.node_offset;5777 return error_set.node_offset;
5750 },5778 },
5751 .@"union", .union_tagged => {5779 .@"union", .union_safety_tagged, .union_tagged => {
5752 const union_obj = ty.cast(Payload.Union).?.data;5780 const union_obj = ty.cast(Payload.Union).?.data;
5753 return union_obj.node_offset;5781 return union_obj.node_offset;
5754 },5782 },
...@@ -5893,6 +5921,7 @@ pub const Type = extern union {...@@ -5893,6 +5921,7 @@ pub const Type = extern union {
5893 @"opaque",5921 @"opaque",
5894 @"struct",5922 @"struct",
5895 @"union",5923 @"union",
5924 union_safety_tagged,
5896 union_tagged,5925 union_tagged,
5897 enum_simple,5926 enum_simple,
5898 enum_numbered,5927 enum_numbered,
...@@ -6009,7 +6038,7 @@ pub const Type = extern union {...@@ -6009,7 +6038,7 @@ pub const Type = extern union {
6009 .error_set_single => Payload.Name,6038 .error_set_single => Payload.Name,
6010 .@"opaque" => Payload.Opaque,6039 .@"opaque" => Payload.Opaque,
6011 .@"struct" => Payload.Struct,6040 .@"struct" => Payload.Struct,
6012 .@"union", .union_tagged => Payload.Union,6041 .@"union", .union_safety_tagged, .union_tagged => Payload.Union,
6013 .enum_full, .enum_nonexhaustive => Payload.EnumFull,6042 .enum_full, .enum_nonexhaustive => Payload.EnumFull,
6014 .enum_simple => Payload.EnumSimple,6043 .enum_simple => Payload.EnumSimple,
6015 .enum_numbered => Payload.EnumNumbered,6044 .enum_numbered => Payload.EnumNumbered,
test/behavior/bugs/1381.zig+2
...@@ -12,8 +12,10 @@ const A = union(enum) {...@@ -12,8 +12,10 @@ const A = union(enum) {
12};12};
1313
14test "union that needs padding bytes inside an array" {14test "union that needs padding bytes inside an array" {
15 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;16 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;17 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
18 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1719
18 var as = [_]A{20 var as = [_]A{
19 A{ .B = B{ .D = 1 } },21 A{ .B = B{ .D = 1 } },
test/behavior/struct.zig+3
...@@ -998,6 +998,9 @@ test "tuple element initialized with fn call" {...@@ -998,6 +998,9 @@ test "tuple element initialized with fn call" {
998}998}
999999
1000test "struct with union field" {1000test "struct with union field" {
1001 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1002 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1003
1001 const Value = struct {1004 const Value = struct {
1002 ref: u32 = 2,1005 ref: u32 = 2,
1003 kind: union(enum) {1006 kind: union(enum) {
test/behavior/type.zig+1-1
...@@ -412,7 +412,7 @@ test "Type.Union" {...@@ -412,7 +412,7 @@ test "Type.Union" {
412412
413 const Untagged = @Type(.{413 const Untagged = @Type(.{
414 .Union = .{414 .Union = .{
415 .layout = .Auto,415 .layout = .Extern,
416 .tag_type = null,416 .tag_type = null,
417 .fields = &.{417 .fields = &.{
418 .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) },418 .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) },
test/behavior/union.zig+13
...@@ -37,6 +37,7 @@ test "init union with runtime value - floats" {...@@ -37,6 +37,7 @@ test "init union with runtime value - floats" {
3737
38test "basic unions" {38test "basic unions" {
39 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;39 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
4041
41 var foo = Foo{ .int = 1 };42 var foo = Foo{ .int = 1 };
42 try expect(foo.int == 1);43 try expect(foo.int == 1);
...@@ -430,9 +431,11 @@ const Foo1 = union(enum) {...@@ -430,9 +431,11 @@ const Foo1 = union(enum) {
430var glbl: Foo1 = undefined;431var glbl: Foo1 = undefined;
431432
432test "global union with single field is correctly initialized" {433test "global union with single field is correctly initialized" {
434 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
433 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;435 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
434 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;436 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
435 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;437 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
438 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
436439
437 glbl = Foo1{440 glbl = Foo1{
438 .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },441 .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },
...@@ -473,8 +476,11 @@ test "update the tag value for zero-sized unions" {...@@ -473,8 +476,11 @@ test "update the tag value for zero-sized unions" {
473}476}
474477
475test "union initializer generates padding only if needed" {478test "union initializer generates padding only if needed" {
479 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
476 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;480 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
481 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
477 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;482 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
483 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
478484
479 const U = union(enum) {485 const U = union(enum) {
480 A: u24,486 A: u24,
...@@ -747,9 +753,11 @@ fn Setter(attr: Attribute) type {...@@ -747,9 +753,11 @@ fn Setter(attr: Attribute) type {
747}753}
748754
749test "return union init with void payload" {755test "return union init with void payload" {
756 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
750 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;757 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
751 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;758 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
752 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;759 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
760 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
753761
754 const S = struct {762 const S = struct {
755 fn entry() !void {763 fn entry() !void {
...@@ -775,6 +783,7 @@ test "@unionInit stored to a const" {...@@ -775,6 +783,7 @@ test "@unionInit stored to a const" {
775 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO783 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
776 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO784 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
777 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO785 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
786 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
778787
779 const S = struct {788 const S = struct {
780 const U = union(enum) {789 const U = union(enum) {
...@@ -937,6 +946,7 @@ test "cast from anonymous struct to union" {...@@ -937,6 +946,7 @@ test "cast from anonymous struct to union" {
937 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO946 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
938 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO947 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
939 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO948 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
949 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
940950
941 const S = struct {951 const S = struct {
942 const U = union(enum) {952 const U = union(enum) {
...@@ -969,6 +979,7 @@ test "cast from pointer to anonymous struct to pointer to union" {...@@ -969,6 +979,7 @@ test "cast from pointer to anonymous struct to pointer to union" {
969 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO979 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
970 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO980 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
971 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO981 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
982 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
972983
973 const S = struct {984 const S = struct {
974 const U = union(enum) {985 const U = union(enum) {
...@@ -1104,6 +1115,8 @@ test "union enum type gets a separate scope" {...@@ -1104,6 +1115,8 @@ test "union enum type gets a separate scope" {
11041115
1105test "global variable struct contains union initialized to non-most-aligned field" {1116test "global variable struct contains union initialized to non-most-aligned field" {
1106 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1117 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1118 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1119 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11071120
1108 const T = struct {1121 const T = struct {
1109 const U = union(enum) {1122 const U = union(enum) {
test/cases/compile_errors/wrong_initializer_for_union_payload_of_type_type.zig+1-2
...@@ -13,5 +13,4 @@ export fn entry() void {...@@ -13,5 +13,4 @@ export fn entry() void {
13// backend=stage213// backend=stage2
14// target=native14// target=native
15//15//
16// :9:14: error: expected type 'type', found 'tmp.U'16// :9:8: error: use of undefined value here causes undefined behavior
17// :1:11: note: union declared here
test/cases/safety/bad union field access.zig +6-4
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "access of inactive union field")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
810
9const Foo = union {11const Foo = union {
...@@ -21,5 +23,5 @@ fn bar(f: *Foo) void {...@@ -21,5 +23,5 @@ fn bar(f: *Foo) void {
21 f.float = 12.34;23 f.float = 12.34;
22}24}
23// run25// run
24// backend=stage1
25// target=native
\ No newline at end of file
26// backend=llvm
27// target=native