authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-14 17:44:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-14 17:44:46-07:00
log8b882747813878a40b63572636a6e86a59a8581e
tree82bb715d9575a81c7b06d1ff205ee43585a47de1
parented5a5e22936e5d90b6c9d255b17076f0db45c040

stage2: improved union support

* `Module.Union.getFullyQualifiedName` returns a sentinel-terminated slice so that backends that need null-termination do not need an additional copy. * Module.Union: implement a `getLayout` function which returns information about ABI size and alignment so that the LLVM backend can properly lower union types into llvm types. * Sema: `resolveType` now returns `error.GenericPoison` rather than a Type with tag `generic_poison`. Callsites that want to allow that need to bypass this higher-level function. * Sema: implement coercion of enums and enum literals to unions. * Sema: fix comptime mutation of pointers to unions * LLVM backend: fully implement proper lowering of union types and values according to the union layout, and update the handling of AIR instructions that deal with unions to support union layouts. * LLVM backend: handle `decl_ref_mut` - Maybe this should be unreachable since comptime vars should be changed to be non-mutable when they go out of scope, but it's harmless for the LLVM backend to support lowering the value. * Type: fix `requiresComptime` for optionals, pointers, and some other types. This function is still wrong for structs, unions, and enums.

7 files changed, 583 insertions(+), 236 deletions(-)

src/Module.zig+55-10
......@@ -964,7 +964,7 @@ pub const Union = struct {
964964
965965 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
966966
967 pub fn getFullyQualifiedName(s: *Union, gpa: *Allocator) ![]u8 {
967 pub fn getFullyQualifiedName(s: *Union, gpa: *Allocator) ![:0]u8 {
968968 return s.owner_decl.getFullyQualifiedName(gpa);
969969 }
970970
......@@ -988,7 +988,7 @@ pub const Union = struct {
988988 };
989989 }
990990
991 pub fn onlyTagHasCodegenBits(u: Union) bool {
991 pub fn hasAllZeroBitFieldTypes(u: Union) bool {
992992 assert(u.haveFieldTypes());
993993 for (u.fields.values()) |field| {
994994 if (field.ty.hasCodeGenBits()) return false;
......@@ -1038,13 +1038,32 @@ pub const Union = struct {
10381038 }
10391039
10401040 pub fn abiSize(u: Union, target: Target, have_tag: bool) u64 {
1041 assert(u.haveFieldTypes());
1041 return u.getLayout(target, have_tag).abi_size;
1042 }
1043
1044 pub const Layout = struct {
1045 abi_size: u64,
1046 abi_align: u32,
1047 most_aligned_field: u32,
1048 most_aligned_field_size: u64,
1049 biggest_field: u32,
1050 payload_size: u64,
1051 payload_align: u32,
1052 tag_align: u32,
1053 tag_size: u64,
1054 };
1055
1056 pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {
1057 assert(u.status == .have_layout);
10421058 const is_packed = u.layout == .Packed;
10431059 if (is_packed) @panic("TODO packed unions");
10441060
1061 var most_aligned_field: usize = undefined;
1062 var most_aligned_field_size: u64 = undefined;
1063 var biggest_field: usize = undefined;
10451064 var payload_size: u64 = 0;
10461065 var payload_align: u32 = 0;
1047 for (u.fields.values()) |field| {
1066 for (u.fields.values()) |field, i| {
10481067 if (!field.ty.hasCodeGenBits()) continue;
10491068
10501069 const field_align = a: {
......@@ -1054,12 +1073,28 @@ pub const Union = struct {
10541073 break :a @intCast(u32, field.abi_align.toUnsignedInt());
10551074 }
10561075 };
1057 payload_size = @maximum(payload_size, field.ty.abiSize(target));
1058 payload_align = @maximum(payload_align, field_align);
1059 }
1060 if (!have_tag) {
1061 return std.mem.alignForwardGeneric(u64, payload_size, payload_align);
1076 const field_size = field.ty.abiSize(target);
1077 if (field_size > payload_size) {
1078 payload_size = field_size;
1079 biggest_field = i;
1080 }
1081 if (field_align > payload_align) {
1082 payload_align = field_align;
1083 most_aligned_field = i;
1084 most_aligned_field_size = field_size;
1085 }
10621086 }
1087 if (!have_tag) return .{
1088 .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align),
1089 .abi_align = payload_align,
1090 .most_aligned_field = @intCast(u32, most_aligned_field),
1091 .most_aligned_field_size = most_aligned_field_size,
1092 .biggest_field = @intCast(u32, biggest_field),
1093 .payload_size = payload_size,
1094 .payload_align = payload_align,
1095 .tag_align = 0,
1096 .tag_size = 0,
1097 };
10631098 // Put the tag before or after the payload depending on which one's
10641099 // alignment is greater.
10651100 const tag_size = u.tag_ty.abiSize(target);
......@@ -1078,7 +1113,17 @@ pub const Union = struct {
10781113 size += tag_size;
10791114 size = std.mem.alignForwardGeneric(u64, size, payload_align);
10801115 }
1081 return size;
1116 return .{
1117 .abi_size = size,
1118 .abi_align = @maximum(tag_align, payload_align),
1119 .most_aligned_field = @intCast(u32, most_aligned_field),
1120 .most_aligned_field_size = most_aligned_field_size,
1121 .biggest_field = @intCast(u32, biggest_field),
1122 .payload_size = payload_size,
1123 .payload_align = payload_align,
1124 .tag_align = tag_align,
1125 .tag_size = tag_size,
1126 };
10821127 }
10831128};
10841129
src/Sema.zig+259-114
......@@ -1026,7 +1026,9 @@ fn resolveConstString(
10261026
10271027pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
10281028 const air_inst = sema.resolveInst(zir_ref);
1029 return sema.analyzeAsType(block, src, air_inst);
1029 const ty = try sema.analyzeAsType(block, src, air_inst);
1030 if (ty.tag() == .generic_poison) return error.GenericPoison;
1031 return ty;
10301032}
10311033
10321034fn analyzeAsType(
......@@ -1284,10 +1286,10 @@ fn resolveInt(
12841286 block: *Block,
12851287 src: LazySrcLoc,
12861288 zir_ref: Zir.Inst.Ref,
1287 dest_type: Type,
1289 dest_ty: Type,
12881290) !u64 {
12891291 const air_inst = sema.resolveInst(zir_ref);
1290 const coerced = try sema.coerce(block, dest_type, air_inst, src);
1292 const coerced = try sema.coerce(block, dest_ty, air_inst, src);
12911293 const val = try sema.resolveConstValue(block, src, coerced);
12921294
12931295 return val.toUnsignedInt();
......@@ -2403,6 +2405,19 @@ fn failWithBadUnionFieldAccess(
24032405 return sema.failWithOwnedErrorMsg(msg);
24042406}
24052407
2408fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void {
2409 const src_loc = decl_ty.declSrcLocOrNull() orelse return;
2410 const category = switch (decl_ty.zigTypeTag()) {
2411 .Union => "union",
2412 .Struct => "struct",
2413 .Enum => "enum",
2414 .Opaque => "opaque",
2415 .ErrorSet => "error set",
2416 else => unreachable,
2417 };
2418 try sema.mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category});
2419}
2420
24062421fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
24072422 const tracy = trace(@src());
24082423 defer tracy.end();
......@@ -5059,9 +5074,9 @@ fn analyzeAs(
50595074 zir_dest_type: Zir.Inst.Ref,
50605075 zir_operand: Zir.Inst.Ref,
50615076) CompileError!Air.Inst.Ref {
5062 const dest_type = try sema.resolveType(block, src, zir_dest_type);
5077 const dest_ty = try sema.resolveType(block, src, zir_dest_type);
50635078 const operand = sema.resolveInst(zir_operand);
5064 return sema.coerce(block, dest_type, operand, src);
5079 return sema.coerce(block, dest_ty, operand, src);
50655080}
50665081
50675082fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -5175,21 +5190,21 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
51755190 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
51765191 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
51775192
5178 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
5193 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
51795194 const operand = sema.resolveInst(extra.rhs);
51805195
5181 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_type);
5196 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
51825197 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
51835198
51845199 if (try sema.isComptimeKnown(block, operand_src, operand)) {
5185 return sema.coerce(block, dest_type, operand, operand_src);
5200 return sema.coerce(block, dest_ty, operand, operand_src);
51865201 } else if (dest_is_comptime_int) {
51875202 return sema.fail(block, src, "unable to cast runtime value to 'comptime_int'", .{});
51885203 }
51895204
51905205 try sema.requireRuntimeBlock(block, operand_src);
51915206 // TODO insert safety check to make sure the value fits in the dest type
5192 return block.addTyOp(.intcast, dest_type, operand);
5207 return block.addTyOp(.intcast, dest_ty, operand);
51935208}
51945209
51955210fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -5201,9 +5216,9 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
52015216 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
52025217 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
52035218
5204 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
5219 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
52055220 const operand = sema.resolveInst(extra.rhs);
5206 return sema.bitCast(block, dest_type, operand, operand_src);
5221 return sema.bitCast(block, dest_ty, operand, operand_src);
52075222}
52085223
52095224fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -5216,17 +5231,17 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
52165231 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
52175232 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
52185233
5219 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
5234 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
52205235 const operand = sema.resolveInst(extra.rhs);
52215236
5222 const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
5237 const dest_is_comptime_float = switch (dest_ty.zigTypeTag()) {
52235238 .ComptimeFloat => true,
52245239 .Float => false,
52255240 else => return sema.fail(
52265241 block,
52275242 dest_ty_src,
52285243 "expected float type, found '{}'",
5229 .{dest_type},
5244 .{dest_ty},
52305245 ),
52315246 };
52325247
......@@ -5242,19 +5257,19 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
52425257 }
52435258
52445259 if (try sema.isComptimeKnown(block, operand_src, operand)) {
5245 return sema.coerce(block, dest_type, operand, operand_src);
5260 return sema.coerce(block, dest_ty, operand, operand_src);
52465261 }
52475262 if (dest_is_comptime_float) {
52485263 return sema.fail(block, src, "unable to cast runtime value to 'comptime_float'", .{});
52495264 }
52505265 const target = sema.mod.getTarget();
52515266 const src_bits = operand_ty.floatBits(target);
5252 const dst_bits = dest_type.floatBits(target);
5267 const dst_bits = dest_ty.floatBits(target);
52535268 if (dst_bits >= src_bits) {
5254 return sema.coerce(block, dest_type, operand, operand_src);
5269 return sema.coerce(block, dest_ty, operand, operand_src);
52555270 }
52565271 try sema.requireRuntimeBlock(block, operand_src);
5257 return block.addTyOp(.fptrunc, dest_type, operand);
5272 return block.addTyOp(.fptrunc, dest_ty, operand);
52585273}
52595274
52605275fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -11265,60 +11280,60 @@ fn elemPtrArray(
1126511280fn coerce(
1126611281 sema: *Sema,
1126711282 block: *Block,
11268 dest_type_unresolved: Type,
11283 dest_ty_unresolved: Type,
1126911284 inst: Air.Inst.Ref,
1127011285 inst_src: LazySrcLoc,
1127111286) CompileError!Air.Inst.Ref {
11272 switch (dest_type_unresolved.tag()) {
11287 switch (dest_ty_unresolved.tag()) {
1127311288 .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),
1127411289 .generic_poison => return inst,
1127511290 else => {},
1127611291 }
11277 const dest_type_src = inst_src; // TODO better source location
11278 const dest_type = try sema.resolveTypeFields(block, dest_type_src, dest_type_unresolved);
11292 const dest_ty_src = inst_src; // TODO better source location
11293 const dest_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty_unresolved);
1127911294
1128011295 const inst_ty = sema.typeOf(inst);
1128111296 // If the types are the same, we can return the operand.
11282 if (dest_type.eql(inst_ty))
11297 if (dest_ty.eql(inst_ty))
1128311298 return inst;
1128411299
1128511300 const arena = sema.arena;
1128611301 const target = sema.mod.getTarget();
1128711302
11288 const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty, false, target);
11303 const in_memory_result = coerceInMemoryAllowed(dest_ty, inst_ty, false, target);
1128911304 if (in_memory_result == .ok) {
1129011305 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
1129111306 // Keep the comptime Value representation; take the new type.
11292 return sema.addConstant(dest_type, val);
11307 return sema.addConstant(dest_ty, val);
1129311308 }
1129411309 try sema.requireRuntimeBlock(block, inst_src);
11295 return block.addTyOp(.bitcast, dest_type, inst);
11310 return block.addTyOp(.bitcast, dest_ty, inst);
1129611311 }
1129711312
1129811313 // undefined to anything
1129911314 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
1130011315 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
11301 return sema.addConstant(dest_type, val);
11316 return sema.addConstant(dest_ty, val);
1130211317 }
1130311318 }
1130411319 assert(inst_ty.zigTypeTag() != .Undefined);
1130511320
1130611321 // comptime known number to other number
11307 if (try sema.coerceNum(block, dest_type, inst, inst_src)) |some|
11322 if (try sema.coerceNum(block, dest_ty, inst, inst_src)) |some|
1130811323 return some;
1130911324
11310 switch (dest_type.zigTypeTag()) {
11325 switch (dest_ty.zigTypeTag()) {
1131111326 .Optional => {
1131211327 // null to ?T
1131311328 if (inst_ty.zigTypeTag() == .Null) {
11314 return sema.addConstant(dest_type, Value.initTag(.null_value));
11329 return sema.addConstant(dest_ty, Value.initTag(.null_value));
1131511330 }
1131611331
1131711332 // T to ?T
1131811333 var buf: Type.Payload.ElemType = undefined;
11319 const child_type = dest_type.optionalChild(&buf);
11334 const child_type = dest_ty.optionalChild(&buf);
1132011335 const intermediate = try sema.coerce(block, child_type, inst, inst_src);
11321 return sema.wrapOptional(block, dest_type, intermediate, inst_src);
11336 return sema.wrapOptional(block, dest_ty, intermediate, inst_src);
1132211337 },
1132311338 .Pointer => {
1132411339 // Function body to function pointer.
......@@ -11326,7 +11341,7 @@ fn coerce(
1132611341 const fn_val = try sema.resolveConstValue(block, inst_src, inst);
1132711342 const fn_decl = fn_val.castTag(.function).?.data.owner_decl;
1132811343 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl);
11329 return sema.coerce(block, dest_type, inst_as_ptr, inst_src);
11344 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
1133011345 }
1133111346
1133211347 // Coercions where the source is a single pointer to an array.
......@@ -11335,38 +11350,38 @@ fn coerce(
1133511350 const array_type = inst_ty.elemType();
1133611351 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
1133711352 const array_elem_type = array_type.elemType();
11338 const dest_is_mut = !dest_type.isConstPtr();
11353 const dest_is_mut = !dest_ty.isConstPtr();
1133911354 if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr;
11340 if (inst_ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
11341 if (inst_ty.ptrAddressSpace() != dest_type.ptrAddressSpace()) break :src_array_ptr;
11355 if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :src_array_ptr;
11356 if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :src_array_ptr;
1134211357
11343 const dst_elem_type = dest_type.elemType();
11358 const dst_elem_type = dest_ty.elemType();
1134411359 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
1134511360 .ok => {},
1134611361 .no_match => break :src_array_ptr,
1134711362 }
1134811363
11349 switch (dest_type.ptrSize()) {
11364 switch (dest_ty.ptrSize()) {
1135011365 .Slice => {
1135111366 // *[N]T to []T
11352 return sema.coerceArrayPtrToSlice(block, dest_type, inst, inst_src);
11367 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
1135311368 },
1135411369 .C => {
1135511370 // *[N]T to [*c]T
11356 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
11371 return sema.coerceArrayPtrToMany(block, dest_ty, inst, inst_src);
1135711372 },
1135811373 .Many => {
1135911374 // *[N]T to [*]T
1136011375 // *[N:s]T to [*:s]T
1136111376 // *[N:s]T to [*]T
11362 if (dest_type.sentinel()) |dst_sentinel| {
11377 if (dest_ty.sentinel()) |dst_sentinel| {
1136311378 if (array_type.sentinel()) |src_sentinel| {
1136411379 if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {
11365 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
11380 return sema.coerceArrayPtrToMany(block, dest_ty, inst, inst_src);
1136611381 }
1136711382 }
1136811383 } else {
11369 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);
11384 return sema.coerceArrayPtrToMany(block, dest_ty, inst, inst_src);
1137011385 }
1137111386 },
1137211387 .One => {},
......@@ -11378,14 +11393,14 @@ fn coerce(
1137811393 if (inst_ty.zigTypeTag() == .Int) {
1137911394 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above
1138011395
11381 const dst_info = dest_type.intInfo(target);
11396 const dst_info = dest_ty.intInfo(target);
1138211397 const src_info = inst_ty.intInfo(target);
1138311398 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
1138411399 // small enough unsigned ints can get casted to large enough signed ints
1138511400 (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
1138611401 {
1138711402 try sema.requireRuntimeBlock(block, inst_src);
11388 return block.addTyOp(.intcast, dest_type, inst);
11403 return block.addTyOp(.intcast, dest_ty, inst);
1138911404 }
1139011405 }
1139111406 },
......@@ -11395,10 +11410,10 @@ fn coerce(
1139511410 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above
1139611411
1139711412 const src_bits = inst_ty.floatBits(target);
11398 const dst_bits = dest_type.floatBits(target);
11413 const dst_bits = dest_ty.floatBits(target);
1139911414 if (dst_bits >= src_bits) {
1140011415 try sema.requireRuntimeBlock(block, inst_src);
11401 return block.addTyOp(.fpext, dest_type, inst);
11416 return block.addTyOp(.fpext, dest_ty, inst);
1140211417 }
1140311418 }
1140411419 },
......@@ -11407,7 +11422,7 @@ fn coerce(
1140711422 // enum literal to enum
1140811423 const val = try sema.resolveConstValue(block, inst_src, inst);
1140911424 const bytes = val.castTag(.enum_literal).?.data;
11410 const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type);
11425 const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_ty);
1141111426 const field_index = resolved_dest_type.enumFieldIndex(bytes) orelse {
1141211427 const msg = msg: {
1141311428 const msg = try sema.errMsg(
......@@ -11435,20 +11450,24 @@ fn coerce(
1143511450 .Union => blk: {
1143611451 // union to its own tag type
1143711452 const union_tag_ty = inst_ty.unionTagType() orelse break :blk;
11438 if (union_tag_ty.eql(dest_type)) {
11439 return sema.unionToTag(block, dest_type, inst, inst_src);
11453 if (union_tag_ty.eql(dest_ty)) {
11454 return sema.unionToTag(block, dest_ty, inst, inst_src);
1144011455 }
1144111456 },
1144211457 else => {},
1144311458 },
1144411459 .ErrorUnion => {
1144511460 // T to E!T or E to E!T
11446 return sema.wrapErrorUnion(block, dest_type, inst, inst_src);
11461 return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
11462 },
11463 .Union => switch (inst_ty.zigTypeTag()) {
11464 .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
11465 else => {},
1144711466 },
1144811467 else => {},
1144911468 }
1145011469
11451 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_type, inst_ty });
11470 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
1145211471}
1145311472
1145411473const InMemoryCoercionResult = enum {
......@@ -11467,14 +11486,14 @@ const InMemoryCoercionResult = enum {
1146711486/// * sentinel-terminated pointers can coerce into `[*]`
1146811487/// TODO improve this function to report recursive compile errors like it does in stage1.
1146911488/// look at the function types_match_const_cast_only
11470fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
11471 if (dest_type.eql(src_type))
11489fn coerceInMemoryAllowed(dest_ty: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
11490 if (dest_ty.eql(src_type))
1147211491 return .ok;
1147311492
11474 if (dest_type.zigTypeTag() == .Pointer and
11493 if (dest_ty.zigTypeTag() == .Pointer and
1147511494 src_type.zigTypeTag() == .Pointer)
1147611495 {
11477 const dest_info = dest_type.ptrInfo().data;
11496 const dest_info = dest_ty.ptrInfo().data;
1147811497 const src_info = src_type.ptrInfo().data;
1147911498
1148011499 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
......@@ -11514,7 +11533,7 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, tar
1151411533 return .no_match;
1151511534 }
1151611535
11517 if (dest_type.hasCodeGenBits() != src_type.hasCodeGenBits()) {
11536 if (dest_ty.hasCodeGenBits() != src_type.hasCodeGenBits()) {
1151811537 return .no_match;
1151911538 }
1152011539
......@@ -11532,7 +11551,7 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, tar
1153211551 !dest_info.pointee_type.eql(src_info.pointee_type))
1153311552 {
1153411553 const src_align = src_type.ptrAlignment(target);
11535 const dest_align = dest_type.ptrAlignment(target);
11554 const dest_align = dest_ty.ptrAlignment(target);
1153611555
1153711556 if (dest_align > src_align) {
1153811557 return .no_match;
......@@ -11550,14 +11569,14 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, tar
1155011569fn coerceNum(
1155111570 sema: *Sema,
1155211571 block: *Block,
11553 dest_type: Type,
11572 dest_ty: Type,
1155411573 inst: Air.Inst.Ref,
1155511574 inst_src: LazySrcLoc,
1155611575) CompileError!?Air.Inst.Ref {
1155711576 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse return null;
1155811577 const inst_ty = sema.typeOf(inst);
1155911578 const src_zig_tag = inst_ty.zigTypeTag();
11560 const dst_zig_tag = dest_type.zigTypeTag();
11579 const dst_zig_tag = dest_ty.zigTypeTag();
1156111580
1156211581 const target = sema.mod.getTarget();
1156311582
......@@ -11565,37 +11584,37 @@ fn coerceNum(
1156511584 .ComptimeInt, .Int => switch (src_zig_tag) {
1156611585 .Float, .ComptimeFloat => {
1156711586 if (val.floatHasFraction()) {
11568 return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_type });
11587 return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty });
1156911588 }
1157011589 return sema.fail(block, inst_src, "TODO float to int", .{});
1157111590 },
1157211591 .Int, .ComptimeInt => {
11573 if (!val.intFitsInType(dest_type, target)) {
11574 return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });
11592 if (!val.intFitsInType(dest_ty, target)) {
11593 return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val });
1157511594 }
11576 return try sema.addConstant(dest_type, val);
11595 return try sema.addConstant(dest_ty, val);
1157711596 },
1157811597 else => {},
1157911598 },
1158011599 .ComptimeFloat, .Float => switch (src_zig_tag) {
1158111600 .ComptimeFloat => {
11582 const result_val = try val.floatCast(sema.arena, dest_type);
11583 return try sema.addConstant(dest_type, result_val);
11601 const result_val = try val.floatCast(sema.arena, dest_ty);
11602 return try sema.addConstant(dest_ty, result_val);
1158411603 },
1158511604 .Float => {
11586 const result_val = try val.floatCast(sema.arena, dest_type);
11587 if (!val.eql(result_val, dest_type)) {
11605 const result_val = try val.floatCast(sema.arena, dest_ty);
11606 if (!val.eql(result_val, dest_ty)) {
1158811607 return sema.fail(
1158911608 block,
1159011609 inst_src,
1159111610 "type {} cannot represent float value {}",
11592 .{ dest_type, val },
11611 .{ dest_ty, val },
1159311612 );
1159411613 }
11595 return try sema.addConstant(dest_type, result_val);
11614 return try sema.addConstant(dest_ty, result_val);
1159611615 },
1159711616 .Int, .ComptimeInt => {
11598 const result_val = try val.intToFloat(sema.arena, dest_type, target);
11617 const result_val = try val.intToFloat(sema.arena, dest_ty, target);
1159911618 // TODO implement this compile error
1160011619 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
1160111620 //if (!int_again_val.eql(val, inst_ty)) {
......@@ -11603,10 +11622,10 @@ fn coerceNum(
1160311622 // block,
1160411623 // inst_src,
1160511624 // "type {} cannot represent integer value {}",
11606 // .{ dest_type, val },
11625 // .{ dest_ty, val },
1160711626 // );
1160811627 //}
11609 return try sema.addConstant(dest_type, result_val);
11628 return try sema.addConstant(dest_ty, result_val);
1161011629 },
1161111630 else => {},
1161211631 },
......@@ -11816,31 +11835,66 @@ fn beginComptimePtrMutation(
1181611835 .field_ptr => {
1181711836 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
1181811837 var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr);
11819 const field_ty = parent.ty.structFieldType(field_ptr.field_index);
11838 const field_index = @intCast(u32, field_ptr.field_index);
11839 const field_ty = parent.ty.structFieldType(field_index);
1182011840 switch (parent.val.tag()) {
1182111841 .undef => {
11822 // A struct has been initialized to undefined at comptime and now we
11842 // A struct or union has been initialized to undefined at comptime and now we
1182311843 // are for the first time setting a field. We must change the representation
11824 // of the struct from `undef` to `struct`.
11844 // of the struct/union from `undef` to `struct`/`union`.
1182511845 const arena = parent.beginArena(sema.gpa);
1182611846 defer parent.finishArena();
1182711847
11828 const fields = try arena.alloc(Value, parent.ty.structFieldCount());
11829 mem.set(Value, fields, Value.undef);
11848 switch (parent.ty.zigTypeTag()) {
11849 .Struct => {
11850 const fields = try arena.alloc(Value, parent.ty.structFieldCount());
11851 mem.set(Value, fields, Value.undef);
1183011852
11831 parent.val.* = try Value.Tag.@"struct".create(arena, fields);
11853 parent.val.* = try Value.Tag.@"struct".create(arena, fields);
1183211854
11833 return ComptimePtrMutationKit{
11834 .decl_ref_mut = parent.decl_ref_mut,
11835 .val = &fields[field_ptr.field_index],
11836 .ty = field_ty,
11837 };
11855 return ComptimePtrMutationKit{
11856 .decl_ref_mut = parent.decl_ref_mut,
11857 .val = &fields[field_index],
11858 .ty = field_ty,
11859 };
11860 },
11861 .Union => {
11862 const payload = try arena.create(Value.Payload.Union);
11863 payload.* = .{ .data = .{
11864 .tag = try Value.Tag.enum_field_index.create(arena, field_index),
11865 .val = Value.undef,
11866 } };
11867
11868 parent.val.* = Value.initPayload(&payload.base);
11869
11870 return ComptimePtrMutationKit{
11871 .decl_ref_mut = parent.decl_ref_mut,
11872 .val = &payload.data.val,
11873 .ty = field_ty,
11874 };
11875 },
11876 else => unreachable,
11877 }
1183811878 },
1183911879 .@"struct" => return ComptimePtrMutationKit{
1184011880 .decl_ref_mut = parent.decl_ref_mut,
11841 .val = &parent.val.castTag(.@"struct").?.data[field_ptr.field_index],
11881 .val = &parent.val.castTag(.@"struct").?.data[field_index],
1184211882 .ty = field_ty,
1184311883 },
11884 .@"union" => {
11885 // We need to set the active field of the union.
11886 const arena = parent.beginArena(sema.gpa);
11887 defer parent.finishArena();
11888
11889 const payload = &parent.val.castTag(.@"union").?.data;
11890 payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
11891
11892 return ComptimePtrMutationKit{
11893 .decl_ref_mut = parent.decl_ref_mut,
11894 .val = &payload.val,
11895 .ty = field_ty,
11896 };
11897 },
1184411898
1184511899 else => unreachable,
1184611900 }
......@@ -11855,7 +11909,7 @@ fn beginComptimePtrMutation(
1185511909fn bitCast(
1185611910 sema: *Sema,
1185711911 block: *Block,
11858 dest_type: Type,
11912 dest_ty: Type,
1185911913 inst: Air.Inst.Ref,
1186011914 inst_src: LazySrcLoc,
1186111915) CompileError!Air.Inst.Ref {
......@@ -11863,41 +11917,132 @@ fn bitCast(
1186311917 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
1186411918 const target = sema.mod.getTarget();
1186511919 const old_ty = sema.typeOf(inst);
11866 const result_val = try val.bitCast(old_ty, dest_type, target, sema.gpa, sema.arena);
11867 return sema.addConstant(dest_type, result_val);
11920 const result_val = try val.bitCast(old_ty, dest_ty, target, sema.gpa, sema.arena);
11921 return sema.addConstant(dest_ty, result_val);
1186811922 }
1186911923 try sema.requireRuntimeBlock(block, inst_src);
11870 return block.addTyOp(.bitcast, dest_type, inst);
11924 return block.addTyOp(.bitcast, dest_ty, inst);
1187111925}
1187211926
1187311927fn coerceArrayPtrToSlice(
1187411928 sema: *Sema,
1187511929 block: *Block,
11876 dest_type: Type,
11930 dest_ty: Type,
1187711931 inst: Air.Inst.Ref,
1187811932 inst_src: LazySrcLoc,
1187911933) CompileError!Air.Inst.Ref {
1188011934 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
1188111935 // The comptime Value representation is compatible with both types.
11882 return sema.addConstant(dest_type, val);
11936 return sema.addConstant(dest_ty, val);
1188311937 }
1188411938 try sema.requireRuntimeBlock(block, inst_src);
11885 return block.addTyOp(.array_to_slice, dest_type, inst);
11939 return block.addTyOp(.array_to_slice, dest_ty, inst);
1188611940}
1188711941
1188811942fn coerceArrayPtrToMany(
1188911943 sema: *Sema,
1189011944 block: *Block,
11891 dest_type: Type,
11945 dest_ty: Type,
1189211946 inst: Air.Inst.Ref,
1189311947 inst_src: LazySrcLoc,
1189411948) !Air.Inst.Ref {
1189511949 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
1189611950 // The comptime Value representation is compatible with both types.
11897 return sema.addConstant(dest_type, val);
11951 return sema.addConstant(dest_ty, val);
1189811952 }
1189911953 try sema.requireRuntimeBlock(block, inst_src);
11900 return sema.bitCast(block, dest_type, inst, inst_src);
11954 return sema.bitCast(block, dest_ty, inst, inst_src);
11955}
11956
11957fn coerceEnumToUnion(
11958 sema: *Sema,
11959 block: *Block,
11960 union_ty: Type,
11961 union_ty_src: LazySrcLoc,
11962 inst: Air.Inst.Ref,
11963 inst_src: LazySrcLoc,
11964) !Air.Inst.Ref {
11965 const inst_ty = sema.typeOf(inst);
11966
11967 const tag_ty = union_ty.unionTagType() orelse {
11968 const msg = msg: {
11969 const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
11970 union_ty, inst_ty,
11971 });
11972 errdefer msg.destroy(sema.gpa);
11973 try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{});
11974 try sema.addDeclaredHereNote(msg, union_ty);
11975 break :msg msg;
11976 };
11977 return sema.failWithOwnedErrorMsg(msg);
11978 };
11979
11980 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
11981 if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
11982 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
11983 const field_index = union_obj.tag_ty.enumTagFieldIndex(val) orelse {
11984 const msg = msg: {
11985 const msg = try sema.errMsg(block, inst_src, "union {} has no tag with value {}", .{
11986 union_ty, val,
11987 });
11988 errdefer msg.destroy(sema.gpa);
11989 try sema.addDeclaredHereNote(msg, union_ty);
11990 break :msg msg;
11991 };
11992 return sema.failWithOwnedErrorMsg(msg);
11993 };
11994 const field = union_obj.fields.values()[field_index];
11995 const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);
11996 const opv = (try sema.typeHasOnePossibleValue(block, inst_src, field_ty)) orelse {
11997 // TODO resolve the field names and include in the error message,
11998 // also instead of 'union declared here' make it 'field "foo" declared here'.
11999 const msg = msg: {
12000 const msg = try sema.errMsg(block, inst_src, "coercion to union {} must initialize {} field", .{
12001 union_ty, field_ty,
12002 });
12003 errdefer msg.destroy(sema.gpa);
12004 try sema.addDeclaredHereNote(msg, union_ty);
12005 break :msg msg;
12006 };
12007 return sema.failWithOwnedErrorMsg(msg);
12008 };
12009
12010 return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{
12011 .tag = val,
12012 .val = opv,
12013 }));
12014 }
12015
12016 try sema.requireRuntimeBlock(block, inst_src);
12017
12018 if (tag_ty.isNonexhaustiveEnum()) {
12019 const msg = msg: {
12020 const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} from non-exhaustive enum", .{
12021 union_ty,
12022 });
12023 errdefer msg.destroy(sema.gpa);
12024 try sema.addDeclaredHereNote(msg, tag_ty);
12025 break :msg msg;
12026 };
12027 return sema.failWithOwnedErrorMsg(msg);
12028 }
12029
12030 // If the union has all fields 0 bits, the union value is just the enum value.
12031 if (union_ty.unionHasAllZeroBitFieldTypes()) {
12032 return block.addTyOp(.bitcast, union_ty, enum_tag);
12033 }
12034
12035 // TODO resolve the field names and add a hint that says "field 'foo' has type 'bar'"
12036 // instead of the "union declared here" hint
12037 const msg = msg: {
12038 const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} which has non-void fields", .{
12039 union_ty,
12040 });
12041 errdefer msg.destroy(sema.gpa);
12042 try sema.addDeclaredHereNote(msg, union_ty);
12043 break :msg msg;
12044 };
12045 return sema.failWithOwnedErrorMsg(msg);
1190112046}
1190212047
1190312048fn analyzeDeclVal(
......@@ -12223,7 +12368,7 @@ fn cmpNumeric(
1222312368 const target = sema.mod.getTarget();
1222412369 if (lhs_is_float and rhs_is_float) {
1222512370 // Implicit cast the smaller one to the larger one.
12226 const dest_type = x: {
12371 const dest_ty = x: {
1222712372 if (lhs_ty_tag == .ComptimeFloat) {
1222812373 break :x rhs_ty;
1222912374 } else if (rhs_ty_tag == .ComptimeFloat) {
......@@ -12235,8 +12380,8 @@ fn cmpNumeric(
1223512380 break :x rhs_ty;
1223612381 }
1223712382 };
12238 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs_src);
12239 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs_src);
12383 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
12384 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
1224012385 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
1224112386 }
1224212387 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
......@@ -12327,7 +12472,7 @@ fn cmpNumeric(
1232712472 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
1232812473 }
1232912474
12330 const dest_type = if (dest_float_type) |ft| ft else blk: {
12475 const dest_ty = if (dest_float_type) |ft| ft else blk: {
1233112476 const max_bits = std.math.max(lhs_bits, rhs_bits);
1233212477 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
1233312478 error.Overflow => return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}),
......@@ -12335,8 +12480,8 @@ fn cmpNumeric(
1233512480 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
1233612481 break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);
1233712482 };
12338 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs_src);
12339 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs_src);
12483 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
12484 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
1234012485
1234112486 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
1234212487}
......@@ -12344,32 +12489,32 @@ fn cmpNumeric(
1234412489fn wrapOptional(
1234512490 sema: *Sema,
1234612491 block: *Block,
12347 dest_type: Type,
12492 dest_ty: Type,
1234812493 inst: Air.Inst.Ref,
1234912494 inst_src: LazySrcLoc,
1235012495) !Air.Inst.Ref {
1235112496 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
12352 return sema.addConstant(dest_type, try Value.Tag.opt_payload.create(sema.arena, val));
12497 return sema.addConstant(dest_ty, try Value.Tag.opt_payload.create(sema.arena, val));
1235312498 }
1235412499
1235512500 try sema.requireRuntimeBlock(block, inst_src);
12356 return block.addTyOp(.wrap_optional, dest_type, inst);
12501 return block.addTyOp(.wrap_optional, dest_ty, inst);
1235712502}
1235812503
1235912504fn wrapErrorUnion(
1236012505 sema: *Sema,
1236112506 block: *Block,
12362 dest_type: Type,
12507 dest_ty: Type,
1236312508 inst: Air.Inst.Ref,
1236412509 inst_src: LazySrcLoc,
1236512510) !Air.Inst.Ref {
1236612511 const inst_ty = sema.typeOf(inst);
12367 const dest_err_set_ty = dest_type.errorUnionSet();
12368 const dest_payload_ty = dest_type.errorUnionPayload();
12512 const dest_err_set_ty = dest_ty.errorUnionSet();
12513 const dest_payload_ty = dest_ty.errorUnionPayload();
1236912514 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
1237012515 if (inst_ty.zigTypeTag() != .ErrorSet) {
1237112516 _ = try sema.coerce(block, dest_payload_ty, inst, inst_src);
12372 return sema.addConstant(dest_type, try Value.Tag.eu_payload.create(sema.arena, val));
12517 return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
1237312518 }
1237412519 switch (dest_err_set_ty.tag()) {
1237512520 .anyerror => {},
......@@ -12417,7 +12562,7 @@ fn wrapErrorUnion(
1241712562 },
1241812563 else => unreachable,
1241912564 }
12420 return sema.addConstant(dest_type, val);
12565 return sema.addConstant(dest_ty, val);
1242112566 }
1242212567
1242312568 try sema.requireRuntimeBlock(block, inst_src);
......@@ -12425,25 +12570,25 @@ fn wrapErrorUnion(
1242512570 // we are coercing from E to E!T
1242612571 if (inst_ty.zigTypeTag() == .ErrorSet) {
1242712572 var coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
12428 return block.addTyOp(.wrap_errunion_err, dest_type, coerced);
12573 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
1242912574 } else {
1243012575 var coerced = try sema.coerce(block, dest_payload_ty, inst, inst_src);
12431 return block.addTyOp(.wrap_errunion_payload, dest_type, coerced);
12576 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
1243212577 }
1243312578}
1243412579
1243512580fn unionToTag(
1243612581 sema: *Sema,
1243712582 block: *Block,
12438 dest_type: Type,
12583 dest_ty: Type,
1243912584 un: Air.Inst.Ref,
1244012585 un_src: LazySrcLoc,
1244112586) !Air.Inst.Ref {
1244212587 if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| {
12443 return sema.addConstant(dest_type, un_val.unionTag());
12588 return sema.addConstant(dest_ty, un_val.unionTag());
1244412589 }
1244512590 try sema.requireRuntimeBlock(block, un_src);
12446 return block.addTyOp(.get_union_tag, dest_type, un);
12591 return block.addTyOp(.get_union_tag, dest_ty, un);
1244712592}
1244812593
1244912594fn resolvePeerTypes(
src/codegen/llvm.zig+181-58
......@@ -848,27 +848,79 @@ pub const DeclGen = struct {
848848 return llvm_struct_ty;
849849 },
850850 .Union => {
851 const union_obj = t.castTag(.@"union").?.data;
852 assert(union_obj.haveFieldTypes());
851 const gop = try dg.object.type_map.getOrPut(gpa, t);
852 if (gop.found_existing) return gop.value_ptr.*;
853853
854 const enum_tag_ty = union_obj.tag_ty;
855 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
854 // The Type memory is ephemeral; since we want to store a longer-lived
855 // reference, we need to copy it here.
856 gop.key_ptr.* = try t.copy(&dg.object.type_map_arena.allocator);
857
858 const union_obj = t.cast(Type.Payload.Union).?.data;
859 const target = dg.module.getTarget();
860 if (t.unionTagType()) |enum_tag_ty| {
861 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
862 const layout = union_obj.getLayout(target, true);
863
864 if (layout.payload_size == 0) {
865 gop.value_ptr.* = enum_tag_llvm_ty;
866 return enum_tag_llvm_ty;
867 }
868
869 const name = try union_obj.getFullyQualifiedName(gpa);
870 defer gpa.free(name);
871
872 const llvm_union_ty = dg.context.structCreateNamed(name);
873 gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls
874
875 const aligned_field = union_obj.fields.values()[layout.most_aligned_field];
876 const llvm_aligned_field_ty = try dg.llvmType(aligned_field.ty);
877
878 const llvm_payload_ty = t: {
879 if (layout.most_aligned_field_size == layout.payload_size) {
880 break :t llvm_aligned_field_ty;
881 }
882 const padding_len = @intCast(c_uint, layout.payload_size - layout.most_aligned_field_size);
883 const fields: [2]*const llvm.Type = .{
884 llvm_aligned_field_ty,
885 dg.context.intType(8).arrayType(padding_len),
886 };
887 break :t dg.context.structType(&fields, fields.len, .False);
888 };
889
890 if (layout.tag_size == 0) {
891 var llvm_fields: [1]*const llvm.Type = .{llvm_payload_ty};
892 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
893 return llvm_union_ty;
894 }
856895
857 if (union_obj.onlyTagHasCodegenBits()) {
858 return enum_tag_llvm_ty;
896 // Put the tag before or after the payload depending on which one's
897 // alignment is greater.
898 var llvm_fields: [2]*const llvm.Type = undefined;
899 if (layout.tag_align >= layout.payload_align) {
900 llvm_fields[0] = enum_tag_llvm_ty;
901 llvm_fields[1] = llvm_payload_ty;
902 } else {
903 llvm_fields[0] = llvm_payload_ty;
904 llvm_fields[1] = enum_tag_llvm_ty;
905 }
906 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
907 return llvm_union_ty;
859908 }
909 // Untagged union
910 const layout = union_obj.getLayout(target, false);
860911
861 const target = dg.module.getTarget();
862 const most_aligned_field_index = union_obj.mostAlignedField(target);
863 const most_aligned_field = union_obj.fields.values()[most_aligned_field_index];
864 // TODO handle when the most aligned field is different than the
865 // biggest sized field.
866
867 const llvm_fields = [_]*const llvm.Type{
868 try dg.llvmType(most_aligned_field.ty),
869 enum_tag_llvm_ty,
870 };
871 return dg.context.structType(&llvm_fields, llvm_fields.len, .False);
912 const name = try union_obj.getFullyQualifiedName(gpa);
913 defer gpa.free(name);
914
915 const llvm_union_ty = dg.context.structCreateNamed(name);
916 gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls
917
918 const big_field = union_obj.fields.values()[layout.biggest_field];
919 const llvm_big_field_ty = try dg.llvmType(big_field.ty);
920
921 var llvm_fields: [1]*const llvm.Type = .{llvm_big_field_ty};
922 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
923 return llvm_union_ty;
872924 },
873925 .Fn => {
874926 const fn_info = t.fnInfo();
......@@ -983,36 +1035,8 @@ pub const DeclGen = struct {
9831035 return int.constBitCast(llvm_ty);
9841036 },
9851037 .Pointer => switch (tv.val.tag()) {
986 .decl_ref => {
987 if (tv.ty.isSlice()) {
988 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
989 const ptr_ty = tv.ty.slicePtrFieldType(&buf);
990 var slice_len: Value.Payload.U64 = .{
991 .base = .{ .tag = .int_u64 },
992 .data = tv.val.sliceLen(),
993 };
994 const fields: [2]*const llvm.Value = .{
995 try self.genTypedValue(.{
996 .ty = ptr_ty,
997 .val = tv.val,
998 }),
999 try self.genTypedValue(.{
1000 .ty = Type.initTag(.usize),
1001 .val = Value.initPayload(&slice_len.base),
1002 }),
1003 };
1004 return self.context.constStruct(&fields, fields.len, .False);
1005 } else {
1006 const decl = tv.val.castTag(.decl_ref).?.data;
1007 decl.alive = true;
1008 const llvm_type = try self.llvmType(tv.ty);
1009 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
1010 try self.resolveLlvmFunction(decl)
1011 else
1012 try self.resolveGlobalDecl(decl);
1013 return llvm_val.constBitCast(llvm_type);
1014 }
1015 },
1038 .decl_ref_mut => return lowerDeclRefValue(self, tv, tv.val.castTag(.decl_ref_mut).?.data.decl),
1039 .decl_ref => return lowerDeclRefValue(self, tv, tv.val.castTag(.decl_ref).?.data),
10161040 .variable => {
10171041 const decl = tv.val.castTag(.variable).?.data.owner_decl;
10181042 decl.alive = true;
......@@ -1192,6 +1216,49 @@ pub const DeclGen = struct {
11921216 @intCast(c_uint, llvm_fields.items.len),
11931217 );
11941218 },
1219 .Union => {
1220 const llvm_union_ty = try self.llvmType(tv.ty);
1221 const tag_and_val = tv.val.castTag(.@"union").?.data;
1222
1223 const target = self.module.getTarget();
1224 const layout = tv.ty.unionGetLayout(target);
1225
1226 if (layout.payload_size == 0) {
1227 return genTypedValue(self, .{ .ty = tv.ty.unionTagType().?, .val = tag_and_val.tag });
1228 }
1229 const field_ty = tv.ty.unionFieldType(tag_and_val.tag);
1230 const payload = p: {
1231 const field = try genTypedValue(self, .{ .ty = field_ty, .val = tag_and_val.val });
1232 const field_size = field_ty.abiSize(target);
1233 if (field_size == layout.payload_size) {
1234 break :p field;
1235 }
1236 const padding_len = @intCast(c_uint, layout.payload_size - field_size);
1237 const fields: [2]*const llvm.Value = .{
1238 field, self.context.intType(8).arrayType(padding_len).getUndef(),
1239 };
1240 break :p self.context.constStruct(&fields, fields.len, .False);
1241 };
1242 if (layout.tag_size == 0) {
1243 const llvm_payload_ty = llvm_union_ty.structGetTypeAtIndex(0);
1244 const fields: [1]*const llvm.Value = .{payload.constBitCast(llvm_payload_ty)};
1245 return llvm_union_ty.constNamedStruct(&fields, fields.len);
1246 }
1247 const llvm_tag_value = try genTypedValue(self, .{
1248 .ty = tv.ty.unionTagType().?,
1249 .val = tag_and_val.tag,
1250 });
1251 var fields: [2]*const llvm.Value = undefined;
1252 if (layout.tag_align >= layout.payload_align) {
1253 fields[0] = llvm_tag_value;
1254 fields[1] = payload.constBitCast(llvm_union_ty.structGetTypeAtIndex(1));
1255 } else {
1256 fields[0] = payload.constBitCast(llvm_union_ty.structGetTypeAtIndex(0));
1257 fields[1] = llvm_tag_value;
1258 }
1259 return llvm_union_ty.constNamedStruct(&fields, fields.len);
1260 },
1261
11951262 .ComptimeInt => unreachable,
11961263 .ComptimeFloat => unreachable,
11971264 .Type => unreachable,
......@@ -1203,7 +1270,6 @@ pub const DeclGen = struct {
12031270 .BoundFn => unreachable,
12041271 .Opaque => unreachable,
12051272
1206 .Union,
12071273 .Frame,
12081274 .AnyFrame,
12091275 .Vector,
......@@ -1211,6 +1277,40 @@ pub const DeclGen = struct {
12111277 }
12121278 }
12131279
1280 fn lowerDeclRefValue(
1281 self: *DeclGen,
1282 tv: TypedValue,
1283 decl: *Module.Decl,
1284 ) Error!*const llvm.Value {
1285 if (tv.ty.isSlice()) {
1286 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1287 const ptr_ty = tv.ty.slicePtrFieldType(&buf);
1288 var slice_len: Value.Payload.U64 = .{
1289 .base = .{ .tag = .int_u64 },
1290 .data = tv.val.sliceLen(),
1291 };
1292 const fields: [2]*const llvm.Value = .{
1293 try self.genTypedValue(.{
1294 .ty = ptr_ty,
1295 .val = tv.val,
1296 }),
1297 try self.genTypedValue(.{
1298 .ty = Type.initTag(.usize),
1299 .val = Value.initPayload(&slice_len.base),
1300 }),
1301 };
1302 return self.context.constStruct(&fields, fields.len, .False);
1303 }
1304
1305 decl.alive = true;
1306 const llvm_type = try self.llvmType(tv.ty);
1307 const llvm_val = if (decl.ty.zigTypeTag() == .Fn)
1308 try self.resolveLlvmFunction(decl)
1309 else
1310 try self.resolveGlobalDecl(decl);
1311 return llvm_val.constBitCast(llvm_type);
1312 }
1313
12141314 fn addAttr(dg: DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
12151315 return dg.addAttrInt(val, index, name, 0);
12161316 }
......@@ -2917,25 +3017,45 @@ pub const FuncGen = struct {
29173017
29183018 fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
29193019 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3020 const un_ty = self.air.typeOf(bin_op.lhs).childType();
3021 const target = self.dg.module.getTarget();
3022 const layout = un_ty.unionGetLayout(target);
3023 if (layout.tag_size == 0) return null;
29203024 const union_ptr = try self.resolveInst(bin_op.lhs);
2921 // TODO handle when onlyTagHasCodegenBits() == true
29223025 const new_tag = try self.resolveInst(bin_op.rhs);
2923 const tag_field_ptr = self.builder.buildStructGEP(union_ptr, 1, "");
2924
3026 if (layout.payload_size == 0) {
3027 _ = self.builder.buildStore(new_tag, union_ptr);
3028 return null;
3029 }
3030 const tag_index = @boolToInt(layout.tag_align < layout.payload_align);
3031 const tag_field_ptr = self.builder.buildStructGEP(union_ptr, tag_index, "");
29253032 _ = self.builder.buildStore(new_tag, tag_field_ptr);
29263033 return null;
29273034 }
29283035
29293036 fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2930 if (self.liveness.isUnused(inst))
2931 return null;
3037 if (self.liveness.isUnused(inst)) return null;
29323038
29333039 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
29343040 const un_ty = self.air.typeOf(ty_op.operand);
2935 const un = try self.resolveInst(ty_op.operand);
2936
2937 _ = un_ty; // TODO handle when onlyTagHasCodegenBits() == true and other union forms
2938 return self.builder.buildExtractValue(un, 1, "");
3041 const target = self.dg.module.getTarget();
3042 const layout = un_ty.unionGetLayout(target);
3043 if (layout.tag_size == 0) return null;
3044 const union_handle = try self.resolveInst(ty_op.operand);
3045 if (isByRef(un_ty)) {
3046 if (layout.payload_size == 0) {
3047 return self.builder.buildLoad(union_handle, "");
3048 }
3049 const tag_index = @boolToInt(layout.tag_align < layout.payload_align);
3050 const tag_field_ptr = self.builder.buildStructGEP(union_handle, tag_index, "");
3051 return self.builder.buildLoad(tag_field_ptr, "");
3052 } else {
3053 if (layout.payload_size == 0) {
3054 return union_handle;
3055 }
3056 const tag_index = @boolToInt(layout.tag_align < layout.payload_align);
3057 return self.builder.buildExtractValue(union_handle, tag_index, "");
3058 }
29393059 }
29403060
29413061 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value {
......@@ -3004,7 +3124,10 @@ pub const FuncGen = struct {
30043124 if (!field.ty.hasCodeGenBits()) {
30053125 return null;
30063126 }
3007 const union_field_ptr = self.builder.buildStructGEP(union_ptr, 0, "");
3127 const target = self.dg.module.getTarget();
3128 const layout = union_ty.unionGetLayout(target);
3129 const payload_index = @boolToInt(layout.tag_align >= layout.payload_align);
3130 const union_field_ptr = self.builder.buildStructGEP(union_ptr, payload_index, "");
30083131 return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, "");
30093132 }
30103133
src/codegen/llvm/bindings.zig+3
......@@ -220,6 +220,9 @@ pub const Type = opaque {
220220 Packed: Bool,
221221 ) void;
222222
223 pub const structGetTypeAtIndex = LLVMStructGetTypeAtIndex;
224 extern fn LLVMStructGetTypeAtIndex(StructTy: *const Type, i: c_uint) *const Type;
225
223226 pub const getTypeKind = LLVMGetTypeKind;
224227 extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind;
225228};
src/type.zig+43-8
......@@ -1238,7 +1238,6 @@ pub const Type = extern union {
12381238 .fn_void_no_args,
12391239 .fn_naked_noreturn_no_args,
12401240 .fn_ccc_void_no_args,
1241 .single_const_pointer_to_comptime_int,
12421241 .const_slice_u8,
12431242 .anyerror_void_error_union,
12441243 .empty_struct_literal,
......@@ -1249,8 +1248,14 @@ pub const Type = extern union {
12491248 .error_set_inferred,
12501249 .@"opaque",
12511250 .generic_poison,
1251 .array_u8,
1252 .array_u8_sentinel_0,
1253 .int_signed,
1254 .int_unsigned,
1255 .enum_simple,
12521256 => false,
12531257
1258 .single_const_pointer_to_comptime_int,
12541259 .type,
12551260 .comptime_int,
12561261 .comptime_float,
......@@ -1263,8 +1268,6 @@ pub const Type = extern union {
12631268 .inferred_alloc_const => unreachable,
12641269 .bound_fn => unreachable,
12651270
1266 .array_u8,
1267 .array_u8_sentinel_0,
12681271 .array,
12691272 .array_sentinel,
12701273 .vector,
......@@ -1277,17 +1280,21 @@ pub const Type = extern union {
12771280 .c_mut_pointer,
12781281 .const_slice,
12791282 .mut_slice,
1280 .int_signed,
1281 .int_unsigned,
1283 => return requiresComptime(childType(ty)),
1284
12821285 .optional,
12831286 .optional_single_mut_pointer,
12841287 .optional_single_const_pointer,
1288 => {
1289 var buf: Payload.ElemType = undefined;
1290 return requiresComptime(optionalChild(ty, &buf));
1291 },
1292
12851293 .error_union,
12861294 .anyframe_T,
12871295 .@"struct",
12881296 .@"union",
12891297 .union_tagged,
1290 .enum_simple,
12911298 .enum_numbered,
12921299 .enum_full,
12931300 .enum_nonexhaustive,
......@@ -2568,6 +2575,24 @@ pub const Type = extern union {
25682575 return union_obj.fields.values()[index].ty;
25692576 }
25702577
2578 pub fn unionHasAllZeroBitFieldTypes(ty: Type) bool {
2579 return ty.cast(Payload.Union).?.data.hasAllZeroBitFieldTypes();
2580 }
2581
2582 pub fn unionGetLayout(ty: Type, target: Target) Module.Union.Layout {
2583 switch (ty.tag()) {
2584 .@"union" => {
2585 const union_obj = ty.castTag(.@"union").?.data;
2586 return union_obj.getLayout(target, false);
2587 },
2588 .union_tagged => {
2589 const union_obj = ty.castTag(.union_tagged).?.data;
2590 return union_obj.getLayout(target, true);
2591 },
2592 else => unreachable,
2593 }
2594 }
2595
25712596 /// Asserts that the type is an error union.
25722597 pub fn errorUnionPayload(self: Type) Type {
25732598 return switch (self.tag()) {
......@@ -3361,17 +3386,26 @@ pub const Type = extern union {
33613386 }
33623387 }
33633388
3389 /// Supports structs and unions.
33643390 pub fn structFieldType(ty: Type, index: usize) Type {
33653391 switch (ty.tag()) {
33663392 .@"struct" => {
33673393 const struct_obj = ty.castTag(.@"struct").?.data;
33683394 return struct_obj.fields.values()[index].ty;
33693395 },
3396 .@"union", .union_tagged => {
3397 const union_obj = ty.cast(Payload.Union).?.data;
3398 return union_obj.fields.values()[index].ty;
3399 },
33703400 else => unreachable,
33713401 }
33723402 }
33733403
33743404 pub fn declSrcLoc(ty: Type) Module.SrcLoc {
3405 return declSrcLocOrNull(ty).?;
3406 }
3407
3408 pub fn declSrcLocOrNull(ty: Type) ?Module.SrcLoc {
33753409 switch (ty.tag()) {
33763410 .enum_full, .enum_nonexhaustive => {
33773411 const enum_full = ty.cast(Payload.EnumFull).?.data;
......@@ -3404,8 +3438,9 @@ pub const Type = extern union {
34043438 .export_options,
34053439 .extern_options,
34063440 .type_info,
3407 => @panic("TODO resolve std.builtin types"),
3408 else => unreachable,
3441 => unreachable, // needed to call resolveTypeFields first
3442
3443 else => return null,
34093444 }
34103445 }
34113446
test/behavior/union.zig+39
......@@ -32,3 +32,42 @@ fn setFloat(foo: *Foo, x: f64) void {
3232fn setInt(foo: *Foo, x: i32) void {
3333 foo.* = Foo{ .int = x };
3434}
35
36test "comptime union field access" {
37 comptime {
38 var foo = Foo{ .int = 0 };
39 try expect(foo.int == 0);
40
41 foo = Foo{ .float = 42.42 };
42 try expect(foo.float == 42.42);
43 }
44}
45
46const FooExtern = extern union {
47 float: f64,
48 int: i32,
49};
50
51test "basic extern unions" {
52 var foo = FooExtern{ .int = 1 };
53 try expect(foo.int == 1);
54 foo.float = 12.34;
55 try expect(foo.float == 12.34);
56}
57
58const ExternPtrOrInt = extern union {
59 ptr: *u8,
60 int: u64,
61};
62test "extern union size" {
63 comptime try expect(@sizeOf(ExternPtrOrInt) == 8);
64}
65
66test "0-sized extern union definition" {
67 const U = extern union {
68 a: void,
69 const f = 1;
70 };
71
72 try expect(U.f == 1);
73}
test/behavior/union_stage1.zig+3-46
......@@ -34,33 +34,6 @@ test "unions embedded in aggregate types" {
3434 }
3535}
3636
37const Foo = union {
38 float: f64,
39 int: i32,
40};
41
42test "comptime union field access" {
43 comptime {
44 var foo = Foo{ .int = 0 };
45 try expect(foo.int == 0);
46
47 foo = Foo{ .float = 42.42 };
48 try expect(foo.float == 42.42);
49 }
50}
51
52const FooExtern = extern union {
53 float: f64,
54 int: i32,
55};
56
57test "basic extern unions" {
58 var foo = FooExtern{ .int = 1 };
59 try expect(foo.int == 1);
60 foo.float = 12.34;
61 try expect(foo.float == 12.34);
62}
63
6437const Letter = enum { A, B, C };
6538const Payload = union(Letter) {
6639 A: i32,
......@@ -131,19 +104,11 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
131104 });
132105}
133106
134const ExternPtrOrInt = extern union {
135 ptr: *u8,
136 int: u64,
137};
138test "extern union size" {
139 comptime try expect(@sizeOf(ExternPtrOrInt) == 8);
140}
141
142107const PackedPtrOrInt = packed union {
143108 ptr: *u8,
144109 int: u64,
145110};
146test "extern union size" {
111test "packed union size" {
147112 comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
148113}
149114
......@@ -576,15 +541,6 @@ test "function call result coerces from tagged union to the tag" {
576541 comptime try S.doTheTest();
577542}
578543
579test "0-sized extern union definition" {
580 const U = extern union {
581 a: void,
582 const f = 1;
583 };
584
585 try expect(U.f == 1);
586}
587
588544test "union initializer generates padding only if needed" {
589545 const U = union(enum) {
590546 A: u24,
......@@ -769,6 +725,7 @@ test "union enum type gets a separate scope" {
769725
770726 try S.doTheTest();
771727}
728
772729test "anytype union field: issue #9233" {
773730 const Quux = union(enum) { bar: anytype };
774731 _ = Quux;
......@@ -845,7 +802,7 @@ const TaggedUnionWithPayload = union(enum) {
845802 Full: i32,
846803};
847804
848test "enum alignment" {
805test "union alignment" {
849806 comptime {
850807 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf([9]u8));
851808 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf(u64));