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 {...@@ -964,7 +964,7 @@ pub const Union = struct {
964964
965 pub const Fields = std.StringArrayHashMapUnmanaged(Field);965 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 {
968 return s.owner_decl.getFullyQualifiedName(gpa);968 return s.owner_decl.getFullyQualifiedName(gpa);
969 }969 }
970970
...@@ -988,7 +988,7 @@ pub const Union = struct {...@@ -988,7 +988,7 @@ pub const Union = struct {
988 };988 };
989 }989 }
990990
991 pub fn onlyTagHasCodegenBits(u: Union) bool {991 pub fn hasAllZeroBitFieldTypes(u: Union) bool {
992 assert(u.haveFieldTypes());992 assert(u.haveFieldTypes());
993 for (u.fields.values()) |field| {993 for (u.fields.values()) |field| {
994 if (field.ty.hasCodeGenBits()) return false;994 if (field.ty.hasCodeGenBits()) return false;
...@@ -1038,13 +1038,32 @@ pub const Union = struct {...@@ -1038,13 +1038,32 @@ pub const Union = struct {
1038 }1038 }
10391039
1040 pub fn abiSize(u: Union, target: Target, have_tag: bool) u64 {1040 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);
1042 const is_packed = u.layout == .Packed;1058 const is_packed = u.layout == .Packed;
1043 if (is_packed) @panic("TODO packed unions");1059 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;
1045 var payload_size: u64 = 0;1064 var payload_size: u64 = 0;
1046 var payload_align: u32 = 0;1065 var payload_align: u32 = 0;
1047 for (u.fields.values()) |field| {1066 for (u.fields.values()) |field, i| {
1048 if (!field.ty.hasCodeGenBits()) continue;1067 if (!field.ty.hasCodeGenBits()) continue;
10491068
1050 const field_align = a: {1069 const field_align = a: {
...@@ -1054,12 +1073,28 @@ pub const Union = struct {...@@ -1054,12 +1073,28 @@ pub const Union = struct {
1054 break :a @intCast(u32, field.abi_align.toUnsignedInt());1073 break :a @intCast(u32, field.abi_align.toUnsignedInt());
1055 }1074 }
1056 };1075 };
1057 payload_size = @maximum(payload_size, field.ty.abiSize(target));1076 const field_size = field.ty.abiSize(target);
1058 payload_align = @maximum(payload_align, field_align);1077 if (field_size > payload_size) {
1059 }1078 payload_size = field_size;
1060 if (!have_tag) {1079 biggest_field = i;
1061 return std.mem.alignForwardGeneric(u64, payload_size, payload_align);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 }
1062 }1086 }
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 };
1063 // Put the tag before or after the payload depending on which one's1098 // Put the tag before or after the payload depending on which one's
1064 // alignment is greater.1099 // alignment is greater.
1065 const tag_size = u.tag_ty.abiSize(target);1100 const tag_size = u.tag_ty.abiSize(target);
...@@ -1078,7 +1113,17 @@ pub const Union = struct {...@@ -1078,7 +1113,17 @@ pub const Union = struct {
1078 size += tag_size;1113 size += tag_size;
1079 size = std.mem.alignForwardGeneric(u64, size, payload_align);1114 size = std.mem.alignForwardGeneric(u64, size, payload_align);
1080 }1115 }
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 };
1082 }1127 }
1083};1128};
10841129
src/Sema.zig+259-114
...@@ -1026,7 +1026,9 @@ fn resolveConstString(...@@ -1026,7 +1026,9 @@ fn resolveConstString(
10261026
1027pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {1027pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
1028 const air_inst = sema.resolveInst(zir_ref);1028 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;
1030}1032}
10311033
1032fn analyzeAsType(1034fn analyzeAsType(
...@@ -1284,10 +1286,10 @@ fn resolveInt(...@@ -1284,10 +1286,10 @@ fn resolveInt(
1284 block: *Block,1286 block: *Block,
1285 src: LazySrcLoc,1287 src: LazySrcLoc,
1286 zir_ref: Zir.Inst.Ref,1288 zir_ref: Zir.Inst.Ref,
1287 dest_type: Type,1289 dest_ty: Type,
1288) !u64 {1290) !u64 {
1289 const air_inst = sema.resolveInst(zir_ref);1291 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);
1291 const val = try sema.resolveConstValue(block, src, coerced);1293 const val = try sema.resolveConstValue(block, src, coerced);
12921294
1293 return val.toUnsignedInt();1295 return val.toUnsignedInt();
...@@ -2403,6 +2405,19 @@ fn failWithBadUnionFieldAccess(...@@ -2403,6 +2405,19 @@ fn failWithBadUnionFieldAccess(
2403 return sema.failWithOwnedErrorMsg(msg);2405 return sema.failWithOwnedErrorMsg(msg);
2404}2406}
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
2406fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {2421fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
2407 const tracy = trace(@src());2422 const tracy = trace(@src());
2408 defer tracy.end();2423 defer tracy.end();
...@@ -5059,9 +5074,9 @@ fn analyzeAs(...@@ -5059,9 +5074,9 @@ fn analyzeAs(
5059 zir_dest_type: Zir.Inst.Ref,5074 zir_dest_type: Zir.Inst.Ref,
5060 zir_operand: Zir.Inst.Ref,5075 zir_operand: Zir.Inst.Ref,
5061) CompileError!Air.Inst.Ref {5076) 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);
5063 const operand = sema.resolveInst(zir_operand);5078 const operand = sema.resolveInst(zir_operand);
5064 return sema.coerce(block, dest_type, operand, src);5079 return sema.coerce(block, dest_ty, operand, src);
5065}5080}
50665081
5067fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5082fn 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...@@ -5175,21 +5190,21 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
5175 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };5190 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
5176 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;5191 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);
5179 const operand = sema.resolveInst(extra.rhs);5194 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);
5182 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));5197 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
51835198
5184 if (try sema.isComptimeKnown(block, operand_src, operand)) {5199 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);
5186 } else if (dest_is_comptime_int) {5201 } else if (dest_is_comptime_int) {
5187 return sema.fail(block, src, "unable to cast runtime value to 'comptime_int'", .{});5202 return sema.fail(block, src, "unable to cast runtime value to 'comptime_int'", .{});
5188 }5203 }
51895204
5190 try sema.requireRuntimeBlock(block, operand_src);5205 try sema.requireRuntimeBlock(block, operand_src);
5191 // TODO insert safety check to make sure the value fits in the dest type5206 // 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);
5193}5208}
51945209
5195fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5210fn 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...@@ -5201,9 +5216,9 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
5201 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };5216 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
5202 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;5217 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);
5205 const operand = sema.resolveInst(extra.rhs);5220 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);
5207}5222}
52085223
5209fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5224fn 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...@@ -5216,17 +5231,17 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
5216 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };5231 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
5217 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;5232 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);
5220 const operand = sema.resolveInst(extra.rhs);5235 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()) {
5223 .ComptimeFloat => true,5238 .ComptimeFloat => true,
5224 .Float => false,5239 .Float => false,
5225 else => return sema.fail(5240 else => return sema.fail(
5226 block,5241 block,
5227 dest_ty_src,5242 dest_ty_src,
5228 "expected float type, found '{}'",5243 "expected float type, found '{}'",
5229 .{dest_type},5244 .{dest_ty},
5230 ),5245 ),
5231 };5246 };
52325247
...@@ -5242,19 +5257,19 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -5242,19 +5257,19 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
5242 }5257 }
52435258
5244 if (try sema.isComptimeKnown(block, operand_src, operand)) {5259 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);
5246 }5261 }
5247 if (dest_is_comptime_float) {5262 if (dest_is_comptime_float) {
5248 return sema.fail(block, src, "unable to cast runtime value to 'comptime_float'", .{});5263 return sema.fail(block, src, "unable to cast runtime value to 'comptime_float'", .{});
5249 }5264 }
5250 const target = sema.mod.getTarget();5265 const target = sema.mod.getTarget();
5251 const src_bits = operand_ty.floatBits(target);5266 const src_bits = operand_ty.floatBits(target);
5252 const dst_bits = dest_type.floatBits(target);5267 const dst_bits = dest_ty.floatBits(target);
5253 if (dst_bits >= src_bits) {5268 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);
5255 }5270 }
5256 try sema.requireRuntimeBlock(block, operand_src);5271 try sema.requireRuntimeBlock(block, operand_src);
5257 return block.addTyOp(.fptrunc, dest_type, operand);5272 return block.addTyOp(.fptrunc, dest_ty, operand);
5258}5273}
52595274
5260fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5275fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -11265,60 +11280,60 @@ fn elemPtrArray(...@@ -11265,60 +11280,60 @@ fn elemPtrArray(
11265fn coerce(11280fn coerce(
11266 sema: *Sema,11281 sema: *Sema,
11267 block: *Block,11282 block: *Block,
11268 dest_type_unresolved: Type,11283 dest_ty_unresolved: Type,
11269 inst: Air.Inst.Ref,11284 inst: Air.Inst.Ref,
11270 inst_src: LazySrcLoc,11285 inst_src: LazySrcLoc,
11271) CompileError!Air.Inst.Ref {11286) CompileError!Air.Inst.Ref {
11272 switch (dest_type_unresolved.tag()) {11287 switch (dest_ty_unresolved.tag()) {
11273 .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),11288 .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),
11274 .generic_poison => return inst,11289 .generic_poison => return inst,
11275 else => {},11290 else => {},
11276 }11291 }
11277 const dest_type_src = inst_src; // TODO better source location11292 const dest_ty_src = inst_src; // TODO better source location
11278 const dest_type = try sema.resolveTypeFields(block, dest_type_src, dest_type_unresolved);11293 const dest_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty_unresolved);
1127911294
11280 const inst_ty = sema.typeOf(inst);11295 const inst_ty = sema.typeOf(inst);
11281 // If the types are the same, we can return the operand.11296 // 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))
11283 return inst;11298 return inst;
1128411299
11285 const arena = sema.arena;11300 const arena = sema.arena;
11286 const target = sema.mod.getTarget();11301 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);
11289 if (in_memory_result == .ok) {11304 if (in_memory_result == .ok) {
11290 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {11305 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
11291 // Keep the comptime Value representation; take the new type.11306 // Keep the comptime Value representation; take the new type.
11292 return sema.addConstant(dest_type, val);11307 return sema.addConstant(dest_ty, val);
11293 }11308 }
11294 try sema.requireRuntimeBlock(block, inst_src);11309 try sema.requireRuntimeBlock(block, inst_src);
11295 return block.addTyOp(.bitcast, dest_type, inst);11310 return block.addTyOp(.bitcast, dest_ty, inst);
11296 }11311 }
1129711312
11298 // undefined to anything11313 // undefined to anything
11299 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {11314 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
11300 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {11315 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
11301 return sema.addConstant(dest_type, val);11316 return sema.addConstant(dest_ty, val);
11302 }11317 }
11303 }11318 }
11304 assert(inst_ty.zigTypeTag() != .Undefined);11319 assert(inst_ty.zigTypeTag() != .Undefined);
1130511320
11306 // comptime known number to other number11321 // 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|
11308 return some;11323 return some;
1130911324
11310 switch (dest_type.zigTypeTag()) {11325 switch (dest_ty.zigTypeTag()) {
11311 .Optional => {11326 .Optional => {
11312 // null to ?T11327 // null to ?T
11313 if (inst_ty.zigTypeTag() == .Null) {11328 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));
11315 }11330 }
1131611331
11317 // T to ?T11332 // T to ?T
11318 var buf: Type.Payload.ElemType = undefined;11333 var buf: Type.Payload.ElemType = undefined;
11319 const child_type = dest_type.optionalChild(&buf);11334 const child_type = dest_ty.optionalChild(&buf);
11320 const intermediate = try sema.coerce(block, child_type, inst, inst_src);11335 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);
11322 },11337 },
11323 .Pointer => {11338 .Pointer => {
11324 // Function body to function pointer.11339 // Function body to function pointer.
...@@ -11326,7 +11341,7 @@ fn coerce(...@@ -11326,7 +11341,7 @@ fn coerce(
11326 const fn_val = try sema.resolveConstValue(block, inst_src, inst);11341 const fn_val = try sema.resolveConstValue(block, inst_src, inst);
11327 const fn_decl = fn_val.castTag(.function).?.data.owner_decl;11342 const fn_decl = fn_val.castTag(.function).?.data.owner_decl;
11328 const inst_as_ptr = try sema.analyzeDeclRef(fn_decl);11343 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);
11330 }11345 }
1133111346
11332 // Coercions where the source is a single pointer to an array.11347 // Coercions where the source is a single pointer to an array.
...@@ -11335,38 +11350,38 @@ fn coerce(...@@ -11335,38 +11350,38 @@ fn coerce(
11335 const array_type = inst_ty.elemType();11350 const array_type = inst_ty.elemType();
11336 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;11351 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
11337 const array_elem_type = array_type.elemType();11352 const array_elem_type = array_type.elemType();
11338 const dest_is_mut = !dest_type.isConstPtr();11353 const dest_is_mut = !dest_ty.isConstPtr();
11339 if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr;11354 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;11355 if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :src_array_ptr;
11341 if (inst_ty.ptrAddressSpace() != dest_type.ptrAddressSpace()) 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();
11344 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {11359 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
11345 .ok => {},11360 .ok => {},
11346 .no_match => break :src_array_ptr,11361 .no_match => break :src_array_ptr,
11347 }11362 }
1134811363
11349 switch (dest_type.ptrSize()) {11364 switch (dest_ty.ptrSize()) {
11350 .Slice => {11365 .Slice => {
11351 // *[N]T to []T11366 // *[N]T to []T
11352 return sema.coerceArrayPtrToSlice(block, dest_type, inst, inst_src);11367 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
11353 },11368 },
11354 .C => {11369 .C => {
11355 // *[N]T to [*c]T11370 // *[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);
11357 },11372 },
11358 .Many => {11373 .Many => {
11359 // *[N]T to [*]T11374 // *[N]T to [*]T
11360 // *[N:s]T to [*:s]T11375 // *[N:s]T to [*:s]T
11361 // *[N:s]T to [*]T11376 // *[N:s]T to [*]T
11362 if (dest_type.sentinel()) |dst_sentinel| {11377 if (dest_ty.sentinel()) |dst_sentinel| {
11363 if (array_type.sentinel()) |src_sentinel| {11378 if (array_type.sentinel()) |src_sentinel| {
11364 if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {11379 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);
11366 }11381 }
11367 }11382 }
11368 } else {11383 } else {
11369 return sema.coerceArrayPtrToMany(block, dest_type, inst, inst_src);11384 return sema.coerceArrayPtrToMany(block, dest_ty, inst, inst_src);
11370 }11385 }
11371 },11386 },
11372 .One => {},11387 .One => {},
...@@ -11378,14 +11393,14 @@ fn coerce(...@@ -11378,14 +11393,14 @@ fn coerce(
11378 if (inst_ty.zigTypeTag() == .Int) {11393 if (inst_ty.zigTypeTag() == .Int) {
11379 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above11394 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);
11382 const src_info = inst_ty.intInfo(target);11397 const src_info = inst_ty.intInfo(target);
11383 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or11398 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
11384 // small enough unsigned ints can get casted to large enough signed ints11399 // small enough unsigned ints can get casted to large enough signed ints
11385 (dst_info.signedness == .signed and dst_info.bits > src_info.bits))11400 (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
11386 {11401 {
11387 try sema.requireRuntimeBlock(block, inst_src);11402 try sema.requireRuntimeBlock(block, inst_src);
11388 return block.addTyOp(.intcast, dest_type, inst);11403 return block.addTyOp(.intcast, dest_ty, inst);
11389 }11404 }
11390 }11405 }
11391 },11406 },
...@@ -11395,10 +11410,10 @@ fn coerce(...@@ -11395,10 +11410,10 @@ fn coerce(
11395 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above11410 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above
1139611411
11397 const src_bits = inst_ty.floatBits(target);11412 const src_bits = inst_ty.floatBits(target);
11398 const dst_bits = dest_type.floatBits(target);11413 const dst_bits = dest_ty.floatBits(target);
11399 if (dst_bits >= src_bits) {11414 if (dst_bits >= src_bits) {
11400 try sema.requireRuntimeBlock(block, inst_src);11415 try sema.requireRuntimeBlock(block, inst_src);
11401 return block.addTyOp(.fpext, dest_type, inst);11416 return block.addTyOp(.fpext, dest_ty, inst);
11402 }11417 }
11403 }11418 }
11404 },11419 },
...@@ -11407,7 +11422,7 @@ fn coerce(...@@ -11407,7 +11422,7 @@ fn coerce(
11407 // enum literal to enum11422 // enum literal to enum
11408 const val = try sema.resolveConstValue(block, inst_src, inst);11423 const val = try sema.resolveConstValue(block, inst_src, inst);
11409 const bytes = val.castTag(.enum_literal).?.data;11424 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);
11411 const field_index = resolved_dest_type.enumFieldIndex(bytes) orelse {11426 const field_index = resolved_dest_type.enumFieldIndex(bytes) orelse {
11412 const msg = msg: {11427 const msg = msg: {
11413 const msg = try sema.errMsg(11428 const msg = try sema.errMsg(
...@@ -11435,20 +11450,24 @@ fn coerce(...@@ -11435,20 +11450,24 @@ fn coerce(
11435 .Union => blk: {11450 .Union => blk: {
11436 // union to its own tag type11451 // union to its own tag type
11437 const union_tag_ty = inst_ty.unionTagType() orelse break :blk;11452 const union_tag_ty = inst_ty.unionTagType() orelse break :blk;
11438 if (union_tag_ty.eql(dest_type)) {11453 if (union_tag_ty.eql(dest_ty)) {
11439 return sema.unionToTag(block, dest_type, inst, inst_src);11454 return sema.unionToTag(block, dest_ty, inst, inst_src);
11440 }11455 }
11441 },11456 },
11442 else => {},11457 else => {},
11443 },11458 },
11444 .ErrorUnion => {11459 .ErrorUnion => {
11445 // T to E!T or E to E!T11460 // 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 => {},
11447 },11466 },
11448 else => {},11467 else => {},
11449 }11468 }
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 });
11452}11471}
1145311472
11454const InMemoryCoercionResult = enum {11473const InMemoryCoercionResult = enum {
...@@ -11467,14 +11486,14 @@ const InMemoryCoercionResult = enum {...@@ -11467,14 +11486,14 @@ const InMemoryCoercionResult = enum {
11467/// * sentinel-terminated pointers can coerce into `[*]`11486/// * sentinel-terminated pointers can coerce into `[*]`
11468/// TODO improve this function to report recursive compile errors like it does in stage1.11487/// TODO improve this function to report recursive compile errors like it does in stage1.
11469/// look at the function types_match_const_cast_only11488/// 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 {11489fn coerceInMemoryAllowed(dest_ty: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
11471 if (dest_type.eql(src_type))11490 if (dest_ty.eql(src_type))
11472 return .ok;11491 return .ok;
1147311492
11474 if (dest_type.zigTypeTag() == .Pointer and11493 if (dest_ty.zigTypeTag() == .Pointer and
11475 src_type.zigTypeTag() == .Pointer)11494 src_type.zigTypeTag() == .Pointer)
11476 {11495 {
11477 const dest_info = dest_type.ptrInfo().data;11496 const dest_info = dest_ty.ptrInfo().data;
11478 const src_info = src_type.ptrInfo().data;11497 const src_info = src_type.ptrInfo().data;
1147911498
11480 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);11499 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...@@ -11514,7 +11533,7 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, tar
11514 return .no_match;11533 return .no_match;
11515 }11534 }
1151611535
11517 if (dest_type.hasCodeGenBits() != src_type.hasCodeGenBits()) {11536 if (dest_ty.hasCodeGenBits() != src_type.hasCodeGenBits()) {
11518 return .no_match;11537 return .no_match;
11519 }11538 }
1152011539
...@@ -11532,7 +11551,7 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, tar...@@ -11532,7 +11551,7 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, tar
11532 !dest_info.pointee_type.eql(src_info.pointee_type))11551 !dest_info.pointee_type.eql(src_info.pointee_type))
11533 {11552 {
11534 const src_align = src_type.ptrAlignment(target);11553 const src_align = src_type.ptrAlignment(target);
11535 const dest_align = dest_type.ptrAlignment(target);11554 const dest_align = dest_ty.ptrAlignment(target);
1153611555
11537 if (dest_align > src_align) {11556 if (dest_align > src_align) {
11538 return .no_match;11557 return .no_match;
...@@ -11550,14 +11569,14 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, tar...@@ -11550,14 +11569,14 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, tar
11550fn coerceNum(11569fn coerceNum(
11551 sema: *Sema,11570 sema: *Sema,
11552 block: *Block,11571 block: *Block,
11553 dest_type: Type,11572 dest_ty: Type,
11554 inst: Air.Inst.Ref,11573 inst: Air.Inst.Ref,
11555 inst_src: LazySrcLoc,11574 inst_src: LazySrcLoc,
11556) CompileError!?Air.Inst.Ref {11575) CompileError!?Air.Inst.Ref {
11557 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse return null;11576 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse return null;
11558 const inst_ty = sema.typeOf(inst);11577 const inst_ty = sema.typeOf(inst);
11559 const src_zig_tag = inst_ty.zigTypeTag();11578 const src_zig_tag = inst_ty.zigTypeTag();
11560 const dst_zig_tag = dest_type.zigTypeTag();11579 const dst_zig_tag = dest_ty.zigTypeTag();
1156111580
11562 const target = sema.mod.getTarget();11581 const target = sema.mod.getTarget();
1156311582
...@@ -11565,37 +11584,37 @@ fn coerceNum(...@@ -11565,37 +11584,37 @@ fn coerceNum(
11565 .ComptimeInt, .Int => switch (src_zig_tag) {11584 .ComptimeInt, .Int => switch (src_zig_tag) {
11566 .Float, .ComptimeFloat => {11585 .Float, .ComptimeFloat => {
11567 if (val.floatHasFraction()) {11586 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 });
11569 }11588 }
11570 return sema.fail(block, inst_src, "TODO float to int", .{});11589 return sema.fail(block, inst_src, "TODO float to int", .{});
11571 },11590 },
11572 .Int, .ComptimeInt => {11591 .Int, .ComptimeInt => {
11573 if (!val.intFitsInType(dest_type, target)) {11592 if (!val.intFitsInType(dest_ty, target)) {
11574 return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_type, val });11593 return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val });
11575 }11594 }
11576 return try sema.addConstant(dest_type, val);11595 return try sema.addConstant(dest_ty, val);
11577 },11596 },
11578 else => {},11597 else => {},
11579 },11598 },
11580 .ComptimeFloat, .Float => switch (src_zig_tag) {11599 .ComptimeFloat, .Float => switch (src_zig_tag) {
11581 .ComptimeFloat => {11600 .ComptimeFloat => {
11582 const result_val = try val.floatCast(sema.arena, dest_type);11601 const result_val = try val.floatCast(sema.arena, dest_ty);
11583 return try sema.addConstant(dest_type, result_val);11602 return try sema.addConstant(dest_ty, result_val);
11584 },11603 },
11585 .Float => {11604 .Float => {
11586 const result_val = try val.floatCast(sema.arena, dest_type);11605 const result_val = try val.floatCast(sema.arena, dest_ty);
11587 if (!val.eql(result_val, dest_type)) {11606 if (!val.eql(result_val, dest_ty)) {
11588 return sema.fail(11607 return sema.fail(
11589 block,11608 block,
11590 inst_src,11609 inst_src,
11591 "type {} cannot represent float value {}",11610 "type {} cannot represent float value {}",
11592 .{ dest_type, val },11611 .{ dest_ty, val },
11593 );11612 );
11594 }11613 }
11595 return try sema.addConstant(dest_type, result_val);11614 return try sema.addConstant(dest_ty, result_val);
11596 },11615 },
11597 .Int, .ComptimeInt => {11616 .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);
11599 // TODO implement this compile error11618 // TODO implement this compile error
11600 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);11619 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
11601 //if (!int_again_val.eql(val, inst_ty)) {11620 //if (!int_again_val.eql(val, inst_ty)) {
...@@ -11603,10 +11622,10 @@ fn coerceNum(...@@ -11603,10 +11622,10 @@ fn coerceNum(
11603 // block,11622 // block,
11604 // inst_src,11623 // inst_src,
11605 // "type {} cannot represent integer value {}",11624 // "type {} cannot represent integer value {}",
11606 // .{ dest_type, val },11625 // .{ dest_ty, val },
11607 // );11626 // );
11608 //}11627 //}
11609 return try sema.addConstant(dest_type, result_val);11628 return try sema.addConstant(dest_ty, result_val);
11610 },11629 },
11611 else => {},11630 else => {},
11612 },11631 },
...@@ -11816,31 +11835,66 @@ fn beginComptimePtrMutation(...@@ -11816,31 +11835,66 @@ fn beginComptimePtrMutation(
11816 .field_ptr => {11835 .field_ptr => {
11817 const field_ptr = ptr_val.castTag(.field_ptr).?.data;11836 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
11818 var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr);11837 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);
11820 switch (parent.val.tag()) {11840 switch (parent.val.tag()) {
11821 .undef => {11841 .undef => {
11822 // A struct has been initialized to undefined at comptime and now we11842 // A struct or union has been initialized to undefined at comptime and now we
11823 // are for the first time setting a field. We must change the representation11843 // 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`.
11825 const arena = parent.beginArena(sema.gpa);11845 const arena = parent.beginArena(sema.gpa);
11826 defer parent.finishArena();11846 defer parent.finishArena();
1182711847
11828 const fields = try arena.alloc(Value, parent.ty.structFieldCount());11848 switch (parent.ty.zigTypeTag()) {
11829 mem.set(Value, fields, Value.undef);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{11855 return ComptimePtrMutationKit{
11834 .decl_ref_mut = parent.decl_ref_mut,11856 .decl_ref_mut = parent.decl_ref_mut,
11835 .val = &fields[field_ptr.field_index],11857 .val = &fields[field_index],
11836 .ty = field_ty,11858 .ty = field_ty,
11837 };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 }
11838 },11878 },
11839 .@"struct" => return ComptimePtrMutationKit{11879 .@"struct" => return ComptimePtrMutationKit{
11840 .decl_ref_mut = parent.decl_ref_mut,11880 .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],
11842 .ty = field_ty,11882 .ty = field_ty,
11843 },11883 },
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
11845 else => unreachable,11899 else => unreachable,
11846 }11900 }
...@@ -11855,7 +11909,7 @@ fn beginComptimePtrMutation(...@@ -11855,7 +11909,7 @@ fn beginComptimePtrMutation(
11855fn bitCast(11909fn bitCast(
11856 sema: *Sema,11910 sema: *Sema,
11857 block: *Block,11911 block: *Block,
11858 dest_type: Type,11912 dest_ty: Type,
11859 inst: Air.Inst.Ref,11913 inst: Air.Inst.Ref,
11860 inst_src: LazySrcLoc,11914 inst_src: LazySrcLoc,
11861) CompileError!Air.Inst.Ref {11915) CompileError!Air.Inst.Ref {
...@@ -11863,41 +11917,132 @@ fn bitCast(...@@ -11863,41 +11917,132 @@ fn bitCast(
11863 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {11917 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
11864 const target = sema.mod.getTarget();11918 const target = sema.mod.getTarget();
11865 const old_ty = sema.typeOf(inst);11919 const old_ty = sema.typeOf(inst);
11866 const result_val = try val.bitCast(old_ty, dest_type, target, sema.gpa, sema.arena);11920 const result_val = try val.bitCast(old_ty, dest_ty, target, sema.gpa, sema.arena);
11867 return sema.addConstant(dest_type, result_val);11921 return sema.addConstant(dest_ty, result_val);
11868 }11922 }
11869 try sema.requireRuntimeBlock(block, inst_src);11923 try sema.requireRuntimeBlock(block, inst_src);
11870 return block.addTyOp(.bitcast, dest_type, inst);11924 return block.addTyOp(.bitcast, dest_ty, inst);
11871}11925}
1187211926
11873fn coerceArrayPtrToSlice(11927fn coerceArrayPtrToSlice(
11874 sema: *Sema,11928 sema: *Sema,
11875 block: *Block,11929 block: *Block,
11876 dest_type: Type,11930 dest_ty: Type,
11877 inst: Air.Inst.Ref,11931 inst: Air.Inst.Ref,
11878 inst_src: LazySrcLoc,11932 inst_src: LazySrcLoc,
11879) CompileError!Air.Inst.Ref {11933) CompileError!Air.Inst.Ref {
11880 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {11934 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
11881 // The comptime Value representation is compatible with both types.11935 // The comptime Value representation is compatible with both types.
11882 return sema.addConstant(dest_type, val);11936 return sema.addConstant(dest_ty, val);
11883 }11937 }
11884 try sema.requireRuntimeBlock(block, inst_src);11938 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);
11886}11940}
1188711941
11888fn coerceArrayPtrToMany(11942fn coerceArrayPtrToMany(
11889 sema: *Sema,11943 sema: *Sema,
11890 block: *Block,11944 block: *Block,
11891 dest_type: Type,11945 dest_ty: Type,
11892 inst: Air.Inst.Ref,11946 inst: Air.Inst.Ref,
11893 inst_src: LazySrcLoc,11947 inst_src: LazySrcLoc,
11894) !Air.Inst.Ref {11948) !Air.Inst.Ref {
11895 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {11949 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
11896 // The comptime Value representation is compatible with both types.11950 // The comptime Value representation is compatible with both types.
11897 return sema.addConstant(dest_type, val);11951 return sema.addConstant(dest_ty, val);
11898 }11952 }
11899 try sema.requireRuntimeBlock(block, inst_src);11953 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);
11901}12046}
1190212047
11903fn analyzeDeclVal(12048fn analyzeDeclVal(
...@@ -12223,7 +12368,7 @@ fn cmpNumeric(...@@ -12223,7 +12368,7 @@ fn cmpNumeric(
12223 const target = sema.mod.getTarget();12368 const target = sema.mod.getTarget();
12224 if (lhs_is_float and rhs_is_float) {12369 if (lhs_is_float and rhs_is_float) {
12225 // Implicit cast the smaller one to the larger one.12370 // Implicit cast the smaller one to the larger one.
12226 const dest_type = x: {12371 const dest_ty = x: {
12227 if (lhs_ty_tag == .ComptimeFloat) {12372 if (lhs_ty_tag == .ComptimeFloat) {
12228 break :x rhs_ty;12373 break :x rhs_ty;
12229 } else if (rhs_ty_tag == .ComptimeFloat) {12374 } else if (rhs_ty_tag == .ComptimeFloat) {
...@@ -12235,8 +12380,8 @@ fn cmpNumeric(...@@ -12235,8 +12380,8 @@ fn cmpNumeric(
12235 break :x rhs_ty;12380 break :x rhs_ty;
12236 }12381 }
12237 };12382 };
12238 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs_src);12383 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
12239 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs_src);12384 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
12240 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);12385 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
12241 }12386 }
12242 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.12387 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
...@@ -12327,7 +12472,7 @@ fn cmpNumeric(...@@ -12327,7 +12472,7 @@ fn cmpNumeric(
12327 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);12472 rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed);
12328 }12473 }
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: {
12331 const max_bits = std.math.max(lhs_bits, rhs_bits);12476 const max_bits = std.math.max(lhs_bits, rhs_bits);
12332 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {12477 const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
12333 error.Overflow => return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}),12478 error.Overflow => return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}),
...@@ -12335,8 +12480,8 @@ fn cmpNumeric(...@@ -12335,8 +12480,8 @@ fn cmpNumeric(
12335 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;12480 const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
12336 break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);12481 break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);
12337 };12482 };
12338 const casted_lhs = try sema.coerce(block, dest_type, lhs, lhs_src);12483 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
12339 const casted_rhs = try sema.coerce(block, dest_type, rhs, rhs_src);12484 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
1234012485
12341 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);12486 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
12342}12487}
...@@ -12344,32 +12489,32 @@ fn cmpNumeric(...@@ -12344,32 +12489,32 @@ fn cmpNumeric(
12344fn wrapOptional(12489fn wrapOptional(
12345 sema: *Sema,12490 sema: *Sema,
12346 block: *Block,12491 block: *Block,
12347 dest_type: Type,12492 dest_ty: Type,
12348 inst: Air.Inst.Ref,12493 inst: Air.Inst.Ref,
12349 inst_src: LazySrcLoc,12494 inst_src: LazySrcLoc,
12350) !Air.Inst.Ref {12495) !Air.Inst.Ref {
12351 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {12496 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));
12353 }12498 }
1235412499
12355 try sema.requireRuntimeBlock(block, inst_src);12500 try sema.requireRuntimeBlock(block, inst_src);
12356 return block.addTyOp(.wrap_optional, dest_type, inst);12501 return block.addTyOp(.wrap_optional, dest_ty, inst);
12357}12502}
1235812503
12359fn wrapErrorUnion(12504fn wrapErrorUnion(
12360 sema: *Sema,12505 sema: *Sema,
12361 block: *Block,12506 block: *Block,
12362 dest_type: Type,12507 dest_ty: Type,
12363 inst: Air.Inst.Ref,12508 inst: Air.Inst.Ref,
12364 inst_src: LazySrcLoc,12509 inst_src: LazySrcLoc,
12365) !Air.Inst.Ref {12510) !Air.Inst.Ref {
12366 const inst_ty = sema.typeOf(inst);12511 const inst_ty = sema.typeOf(inst);
12367 const dest_err_set_ty = dest_type.errorUnionSet();12512 const dest_err_set_ty = dest_ty.errorUnionSet();
12368 const dest_payload_ty = dest_type.errorUnionPayload();12513 const dest_payload_ty = dest_ty.errorUnionPayload();
12369 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {12514 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
12370 if (inst_ty.zigTypeTag() != .ErrorSet) {12515 if (inst_ty.zigTypeTag() != .ErrorSet) {
12371 _ = try sema.coerce(block, dest_payload_ty, inst, inst_src);12516 _ = 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));
12373 }12518 }
12374 switch (dest_err_set_ty.tag()) {12519 switch (dest_err_set_ty.tag()) {
12375 .anyerror => {},12520 .anyerror => {},
...@@ -12417,7 +12562,7 @@ fn wrapErrorUnion(...@@ -12417,7 +12562,7 @@ fn wrapErrorUnion(
12417 },12562 },
12418 else => unreachable,12563 else => unreachable,
12419 }12564 }
12420 return sema.addConstant(dest_type, val);12565 return sema.addConstant(dest_ty, val);
12421 }12566 }
1242212567
12423 try sema.requireRuntimeBlock(block, inst_src);12568 try sema.requireRuntimeBlock(block, inst_src);
...@@ -12425,25 +12570,25 @@ fn wrapErrorUnion(...@@ -12425,25 +12570,25 @@ fn wrapErrorUnion(
12425 // we are coercing from E to E!T12570 // we are coercing from E to E!T
12426 if (inst_ty.zigTypeTag() == .ErrorSet) {12571 if (inst_ty.zigTypeTag() == .ErrorSet) {
12427 var coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);12572 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);
12429 } else {12574 } else {
12430 var coerced = try sema.coerce(block, dest_payload_ty, inst, inst_src);12575 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);
12432 }12577 }
12433}12578}
1243412579
12435fn unionToTag(12580fn unionToTag(
12436 sema: *Sema,12581 sema: *Sema,
12437 block: *Block,12582 block: *Block,
12438 dest_type: Type,12583 dest_ty: Type,
12439 un: Air.Inst.Ref,12584 un: Air.Inst.Ref,
12440 un_src: LazySrcLoc,12585 un_src: LazySrcLoc,
12441) !Air.Inst.Ref {12586) !Air.Inst.Ref {
12442 if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| {12587 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());
12444 }12589 }
12445 try sema.requireRuntimeBlock(block, un_src);12590 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);
12447}12592}
1244812593
12449fn resolvePeerTypes(12594fn resolvePeerTypes(
src/codegen/llvm.zig+181-58
...@@ -848,27 +848,79 @@ pub const DeclGen = struct {...@@ -848,27 +848,79 @@ pub const DeclGen = struct {
848 return llvm_struct_ty;848 return llvm_struct_ty;
849 },849 },
850 .Union => {850 .Union => {
851 const union_obj = t.castTag(.@"union").?.data;851 const gop = try dg.object.type_map.getOrPut(gpa, t);
852 assert(union_obj.haveFieldTypes());852 if (gop.found_existing) return gop.value_ptr.*;
853853
854 const enum_tag_ty = union_obj.tag_ty;854 // The Type memory is ephemeral; since we want to store a longer-lived
855 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);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()) {896 // Put the tag before or after the payload depending on which one's
858 return enum_tag_llvm_ty;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;
859 }908 }
909 // Untagged union
910 const layout = union_obj.getLayout(target, false);
860911
861 const target = dg.module.getTarget();912 const name = try union_obj.getFullyQualifiedName(gpa);
862 const most_aligned_field_index = union_obj.mostAlignedField(target);913 defer gpa.free(name);
863 const most_aligned_field = union_obj.fields.values()[most_aligned_field_index];914
864 // TODO handle when the most aligned field is different than the915 const llvm_union_ty = dg.context.structCreateNamed(name);
865 // biggest sized field.916 gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls
866917
867 const llvm_fields = [_]*const llvm.Type{918 const big_field = union_obj.fields.values()[layout.biggest_field];
868 try dg.llvmType(most_aligned_field.ty),919 const llvm_big_field_ty = try dg.llvmType(big_field.ty);
869 enum_tag_llvm_ty,920
870 };921 var llvm_fields: [1]*const llvm.Type = .{llvm_big_field_ty};
871 return dg.context.structType(&llvm_fields, llvm_fields.len, .False);922 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
923 return llvm_union_ty;
872 },924 },
873 .Fn => {925 .Fn => {
874 const fn_info = t.fnInfo();926 const fn_info = t.fnInfo();
...@@ -983,36 +1035,8 @@ pub const DeclGen = struct {...@@ -983,36 +1035,8 @@ pub const DeclGen = struct {
983 return int.constBitCast(llvm_ty);1035 return int.constBitCast(llvm_ty);
984 },1036 },
985 .Pointer => switch (tv.val.tag()) {1037 .Pointer => switch (tv.val.tag()) {
986 .decl_ref => {1038 .decl_ref_mut => return lowerDeclRefValue(self, tv, tv.val.castTag(.decl_ref_mut).?.data.decl),
987 if (tv.ty.isSlice()) {1039 .decl_ref => return lowerDeclRefValue(self, tv, tv.val.castTag(.decl_ref).?.data),
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 },
1016 .variable => {1040 .variable => {
1017 const decl = tv.val.castTag(.variable).?.data.owner_decl;1041 const decl = tv.val.castTag(.variable).?.data.owner_decl;
1018 decl.alive = true;1042 decl.alive = true;
...@@ -1192,6 +1216,49 @@ pub const DeclGen = struct {...@@ -1192,6 +1216,49 @@ pub const DeclGen = struct {
1192 @intCast(c_uint, llvm_fields.items.len),1216 @intCast(c_uint, llvm_fields.items.len),
1193 );1217 );
1194 },1218 },
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
1195 .ComptimeInt => unreachable,1262 .ComptimeInt => unreachable,
1196 .ComptimeFloat => unreachable,1263 .ComptimeFloat => unreachable,
1197 .Type => unreachable,1264 .Type => unreachable,
...@@ -1203,7 +1270,6 @@ pub const DeclGen = struct {...@@ -1203,7 +1270,6 @@ pub const DeclGen = struct {
1203 .BoundFn => unreachable,1270 .BoundFn => unreachable,
1204 .Opaque => unreachable,1271 .Opaque => unreachable,
12051272
1206 .Union,
1207 .Frame,1273 .Frame,
1208 .AnyFrame,1274 .AnyFrame,
1209 .Vector,1275 .Vector,
...@@ -1211,6 +1277,40 @@ pub const DeclGen = struct {...@@ -1211,6 +1277,40 @@ pub const DeclGen = struct {
1211 }1277 }
1212 }1278 }
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
1214 fn addAttr(dg: DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {1314 fn addAttr(dg: DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void {
1215 return dg.addAttrInt(val, index, name, 0);1315 return dg.addAttrInt(val, index, name, 0);
1216 }1316 }
...@@ -2917,25 +3017,45 @@ pub const FuncGen = struct {...@@ -2917,25 +3017,45 @@ pub const FuncGen = struct {
29173017
2918 fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3018 fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2919 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3019 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;
2920 const union_ptr = try self.resolveInst(bin_op.lhs);3024 const union_ptr = try self.resolveInst(bin_op.lhs);
2921 // TODO handle when onlyTagHasCodegenBits() == true
2922 const new_tag = try self.resolveInst(bin_op.rhs);3025 const new_tag = try self.resolveInst(bin_op.rhs);
2923 const tag_field_ptr = self.builder.buildStructGEP(union_ptr, 1, "");3026 if (layout.payload_size == 0) {
29243027 _ = 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, "");
2925 _ = self.builder.buildStore(new_tag, tag_field_ptr);3032 _ = self.builder.buildStore(new_tag, tag_field_ptr);
2926 return null;3033 return null;
2927 }3034 }
29283035
2929 fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3036 fn airGetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2930 if (self.liveness.isUnused(inst))3037 if (self.liveness.isUnused(inst)) return null;
2931 return null;
29323038
2933 const ty_op = self.air.instructions.items(.data)[inst].ty_op;3039 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2934 const un_ty = self.air.typeOf(ty_op.operand);3040 const un_ty = self.air.typeOf(ty_op.operand);
2935 const un = try self.resolveInst(ty_op.operand);3041 const target = self.dg.module.getTarget();
29363042 const layout = un_ty.unionGetLayout(target);
2937 _ = un_ty; // TODO handle when onlyTagHasCodegenBits() == true and other union forms3043 if (layout.tag_size == 0) return null;
2938 return self.builder.buildExtractValue(un, 1, "");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 }
2939 }3059 }
29403060
2941 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value {3061 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value {
...@@ -3004,7 +3124,10 @@ pub const FuncGen = struct {...@@ -3004,7 +3124,10 @@ pub const FuncGen = struct {
3004 if (!field.ty.hasCodeGenBits()) {3124 if (!field.ty.hasCodeGenBits()) {
3005 return null;3125 return null;
3006 }3126 }
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, "");
3008 return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, "");3131 return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, "");
3009 }3132 }
30103133
src/codegen/llvm/bindings.zig+3
...@@ -220,6 +220,9 @@ pub const Type = opaque {...@@ -220,6 +220,9 @@ pub const Type = opaque {
220 Packed: Bool,220 Packed: Bool,
221 ) void;221 ) void;
222222
223 pub const structGetTypeAtIndex = LLVMStructGetTypeAtIndex;
224 extern fn LLVMStructGetTypeAtIndex(StructTy: *const Type, i: c_uint) *const Type;
225
223 pub const getTypeKind = LLVMGetTypeKind;226 pub const getTypeKind = LLVMGetTypeKind;
224 extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind;227 extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind;
225};228};
src/type.zig+43-8
...@@ -1238,7 +1238,6 @@ pub const Type = extern union {...@@ -1238,7 +1238,6 @@ pub const Type = extern union {
1238 .fn_void_no_args,1238 .fn_void_no_args,
1239 .fn_naked_noreturn_no_args,1239 .fn_naked_noreturn_no_args,
1240 .fn_ccc_void_no_args,1240 .fn_ccc_void_no_args,
1241 .single_const_pointer_to_comptime_int,
1242 .const_slice_u8,1241 .const_slice_u8,
1243 .anyerror_void_error_union,1242 .anyerror_void_error_union,
1244 .empty_struct_literal,1243 .empty_struct_literal,
...@@ -1249,8 +1248,14 @@ pub const Type = extern union {...@@ -1249,8 +1248,14 @@ pub const Type = extern union {
1249 .error_set_inferred,1248 .error_set_inferred,
1250 .@"opaque",1249 .@"opaque",
1251 .generic_poison,1250 .generic_poison,
1251 .array_u8,
1252 .array_u8_sentinel_0,
1253 .int_signed,
1254 .int_unsigned,
1255 .enum_simple,
1252 => false,1256 => false,
12531257
1258 .single_const_pointer_to_comptime_int,
1254 .type,1259 .type,
1255 .comptime_int,1260 .comptime_int,
1256 .comptime_float,1261 .comptime_float,
...@@ -1263,8 +1268,6 @@ pub const Type = extern union {...@@ -1263,8 +1268,6 @@ pub const Type = extern union {
1263 .inferred_alloc_const => unreachable,1268 .inferred_alloc_const => unreachable,
1264 .bound_fn => unreachable,1269 .bound_fn => unreachable,
12651270
1266 .array_u8,
1267 .array_u8_sentinel_0,
1268 .array,1271 .array,
1269 .array_sentinel,1272 .array_sentinel,
1270 .vector,1273 .vector,
...@@ -1277,17 +1280,21 @@ pub const Type = extern union {...@@ -1277,17 +1280,21 @@ pub const Type = extern union {
1277 .c_mut_pointer,1280 .c_mut_pointer,
1278 .const_slice,1281 .const_slice,
1279 .mut_slice,1282 .mut_slice,
1280 .int_signed,1283 => return requiresComptime(childType(ty)),
1281 .int_unsigned,1284
1282 .optional,1285 .optional,
1283 .optional_single_mut_pointer,1286 .optional_single_mut_pointer,
1284 .optional_single_const_pointer,1287 .optional_single_const_pointer,
1288 => {
1289 var buf: Payload.ElemType = undefined;
1290 return requiresComptime(optionalChild(ty, &buf));
1291 },
1292
1285 .error_union,1293 .error_union,
1286 .anyframe_T,1294 .anyframe_T,
1287 .@"struct",1295 .@"struct",
1288 .@"union",1296 .@"union",
1289 .union_tagged,1297 .union_tagged,
1290 .enum_simple,
1291 .enum_numbered,1298 .enum_numbered,
1292 .enum_full,1299 .enum_full,
1293 .enum_nonexhaustive,1300 .enum_nonexhaustive,
...@@ -2568,6 +2575,24 @@ pub const Type = extern union {...@@ -2568,6 +2575,24 @@ pub const Type = extern union {
2568 return union_obj.fields.values()[index].ty;2575 return union_obj.fields.values()[index].ty;
2569 }2576 }
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
2571 /// Asserts that the type is an error union.2596 /// Asserts that the type is an error union.
2572 pub fn errorUnionPayload(self: Type) Type {2597 pub fn errorUnionPayload(self: Type) Type {
2573 return switch (self.tag()) {2598 return switch (self.tag()) {
...@@ -3361,17 +3386,26 @@ pub const Type = extern union {...@@ -3361,17 +3386,26 @@ pub const Type = extern union {
3361 }3386 }
3362 }3387 }
33633388
3389 /// Supports structs and unions.
3364 pub fn structFieldType(ty: Type, index: usize) Type {3390 pub fn structFieldType(ty: Type, index: usize) Type {
3365 switch (ty.tag()) {3391 switch (ty.tag()) {
3366 .@"struct" => {3392 .@"struct" => {
3367 const struct_obj = ty.castTag(.@"struct").?.data;3393 const struct_obj = ty.castTag(.@"struct").?.data;
3368 return struct_obj.fields.values()[index].ty;3394 return struct_obj.fields.values()[index].ty;
3369 },3395 },
3396 .@"union", .union_tagged => {
3397 const union_obj = ty.cast(Payload.Union).?.data;
3398 return union_obj.fields.values()[index].ty;
3399 },
3370 else => unreachable,3400 else => unreachable,
3371 }3401 }
3372 }3402 }
33733403
3374 pub fn declSrcLoc(ty: Type) Module.SrcLoc {3404 pub fn declSrcLoc(ty: Type) Module.SrcLoc {
3405 return declSrcLocOrNull(ty).?;
3406 }
3407
3408 pub fn declSrcLocOrNull(ty: Type) ?Module.SrcLoc {
3375 switch (ty.tag()) {3409 switch (ty.tag()) {
3376 .enum_full, .enum_nonexhaustive => {3410 .enum_full, .enum_nonexhaustive => {
3377 const enum_full = ty.cast(Payload.EnumFull).?.data;3411 const enum_full = ty.cast(Payload.EnumFull).?.data;
...@@ -3404,8 +3438,9 @@ pub const Type = extern union {...@@ -3404,8 +3438,9 @@ pub const Type = extern union {
3404 .export_options,3438 .export_options,
3405 .extern_options,3439 .extern_options,
3406 .type_info,3440 .type_info,
3407 => @panic("TODO resolve std.builtin types"),3441 => unreachable, // needed to call resolveTypeFields first
3408 else => unreachable,3442
3443 else => return null,
3409 }3444 }
3410 }3445 }
34113446
test/behavior/union.zig+39
...@@ -32,3 +32,42 @@ fn setFloat(foo: *Foo, x: f64) void {...@@ -32,3 +32,42 @@ fn setFloat(foo: *Foo, x: f64) void {
32fn setInt(foo: *Foo, x: i32) void {32fn setInt(foo: *Foo, x: i32) void {
33 foo.* = Foo{ .int = x };33 foo.* = Foo{ .int = x };
34}34}
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" {...@@ -34,33 +34,6 @@ test "unions embedded in aggregate types" {
34 }34 }
35}35}
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
64const Letter = enum { A, B, C };37const Letter = enum { A, B, C };
65const Payload = union(Letter) {38const Payload = union(Letter) {
66 A: i32,39 A: i32,
...@@ -131,19 +104,11 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {...@@ -131,19 +104,11 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
131 });104 });
132}105}
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
142const PackedPtrOrInt = packed union {107const PackedPtrOrInt = packed union {
143 ptr: *u8,108 ptr: *u8,
144 int: u64,109 int: u64,
145};110};
146test "extern union size" {111test "packed union size" {
147 comptime try expect(@sizeOf(PackedPtrOrInt) == 8);112 comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
148}113}
149114
...@@ -576,15 +541,6 @@ test "function call result coerces from tagged union to the tag" {...@@ -576,15 +541,6 @@ test "function call result coerces from tagged union to the tag" {
576 comptime try S.doTheTest();541 comptime try S.doTheTest();
577}542}
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
588test "union initializer generates padding only if needed" {544test "union initializer generates padding only if needed" {
589 const U = union(enum) {545 const U = union(enum) {
590 A: u24,546 A: u24,
...@@ -769,6 +725,7 @@ test "union enum type gets a separate scope" {...@@ -769,6 +725,7 @@ test "union enum type gets a separate scope" {
769725
770 try S.doTheTest();726 try S.doTheTest();
771}727}
728
772test "anytype union field: issue #9233" {729test "anytype union field: issue #9233" {
773 const Quux = union(enum) { bar: anytype };730 const Quux = union(enum) { bar: anytype };
774 _ = Quux;731 _ = Quux;
...@@ -845,7 +802,7 @@ const TaggedUnionWithPayload = union(enum) {...@@ -845,7 +802,7 @@ const TaggedUnionWithPayload = union(enum) {
845 Full: i32,802 Full: i32,
846};803};
847804
848test "enum alignment" {805test "union alignment" {
849 comptime {806 comptime {
850 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf([9]u8));807 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf([9]u8));
851 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf(u64));808 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf(u64));