1const std = @import("std");
2const assert = std.debug.assert;
3const mem = std.mem;
4
5const Sema = @import("../Sema.zig");
6const Block = Sema.Block;
7const Type = @import("../Type.zig");
8const Value = @import("../Value.zig");
9const Zcu = @import("../Zcu.zig");
10const CompileError = Zcu.CompileError;
11const SemaError = Zcu.SemaError;
12const LazySrcLoc = Zcu.LazySrcLoc;
13const InternPool = @import("../InternPool.zig");
14const Alignment = InternPool.Alignment;
15const arith = @import("arith.zig");
16const trace = @import("../tracy.zig").trace;
17
18pub const LayoutResolveReason = enum {
19 variable,
20 constant,
21 parameter,
22 return_type,
23 field,
24 backing_enum,
25 init,
26 coerce,
27 ptr_access,
28 ptr_offset,
29 field_used,
30 field_queried,
31 size_of,
32 align_of,
33 type_info,
34 align_check,
35 bit_ptr_child,
36 @"export",
37 @"extern",
38 asm_out_type,
39 std_lang_type,
40
41 /// Written after string: "while resolving type 'T' "
42 /// e.g. "while resolving type 'MyStruct' for variable declared here"
43 pub fn msg(r: LayoutResolveReason) []const u8 {
44 return switch (r) {
45 // zig fmt: off
46 .variable => "for variable declared here",
47 .constant => "for constant declared here",
48 .parameter => "for function parameter declared here",
49 .return_type => "for function return type declared here",
50 .field => "for field declared here",
51 .backing_enum => "for backing enum type declared here",
52 .init => "for initialization performed here",
53 .coerce => "for coercion performed here",
54 .ptr_access => "for pointer access here",
55 .ptr_offset => "for pointer offset here",
56 .field_used => "for field usage here",
57 .field_queried => "for field query here",
58 .size_of => "for size query here",
59 .align_of => "for alignment query here",
60 .type_info => "for type information query here",
61 .align_check => "for alignment check here",
62 .bit_ptr_child => "for bit size check here",
63 .@"export" => "for export here",
64 .@"extern" => "for extern declaration here",
65 .asm_out_type => "for inline assembly output type declared here",
66 .std_lang_type => "from 'std.lang'",
67 // zig fmt: on
68 };
69 }
70};
71
72/// Ensures that `ty` has known layout, including alignment, size, and (where relevant) field offsets.
73/// `ty` may be any type; its layout is resolved *recursively* if necessary.
74/// Adds incremental dependencies tracking any required type resolution.
75pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc, reason: LayoutResolveReason) SemaError!void {
76 return ensureLayoutResolvedInner(sema, ty, ty, &.{
77 .src = src,
78 .type_layout_reason = reason,
79 });
80}
81fn ensureLayoutResolvedInner(sema: *Sema, ty: Type, orig_ty: Type, reason: *const Zcu.DependencyReason) SemaError!void {
82 const pt = sema.pt;
83 const zcu = pt.zcu;
84 const ip = &zcu.intern_pool;
85 switch (ip.indexToKey(ty.toIntern())) {
86 .int_type,
87 .ptr_type,
88 .anyframe_type,
89 .simple_type,
90 .opaque_type,
91 .error_set_type,
92 .inferred_error_set_type,
93 => {},
94
95 .spirv_type => if (ty.isSpirvRuntimeArray(zcu)) {
96 return ensureLayoutResolvedInner(sema, ty.childType(zcu), orig_ty, reason);
97 },
98
99 .func_type => |func_type| {
100 for (func_type.param_types.get(ip)) |param_ty| {
101 try ensureLayoutResolvedInner(sema, .fromInterned(param_ty), orig_ty, reason);
102 }
103 try ensureLayoutResolvedInner(sema, .fromInterned(func_type.return_type), orig_ty, reason);
104 },
105
106 .array_type => |arr| return ensureLayoutResolvedInner(sema, .fromInterned(arr.child), orig_ty, reason),
107 .vector_type => |vec| return ensureLayoutResolvedInner(sema, .fromInterned(vec.child), orig_ty, reason),
108 .opt_type => |child| return ensureLayoutResolvedInner(sema, .fromInterned(child), orig_ty, reason),
109 .error_union_type => |eu| return ensureLayoutResolvedInner(sema, .fromInterned(eu.payload_type), orig_ty, reason),
110 .tuple_type => |tuple| for (tuple.types.get(ip)) |field_ty| {
111 try ensureLayoutResolvedInner(sema, .fromInterned(field_ty), orig_ty, reason);
112 },
113 .struct_type, .union_type, .enum_type => {
114 try sema.declareDependency(.{ .type_layout = ty.toIntern() });
115 try sema.addReferenceEntry(null, reason.src, .wrap(.{ .type_layout = ty.toIntern() }));
116 if (zcu.analysis_in_progress.contains(.wrap(.{ .type_layout = ty.toIntern() }))) {
117 return sema.failWithDependencyLoop(.wrap(.{ .type_layout = ty.toIntern() }), reason);
118 }
119 pt.ensureTypeLayoutUpToDate(ty, reason) catch |err| switch (err) {
120 error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = .wrap(.{ .type_layout = ty.toIntern() }) }),
121 else => |e| return e,
122 };
123 },
124
125 // values, not types
126 .undef,
127 .simple_value,
128 .@"extern",
129 .func,
130 .int,
131 .err,
132 .error_union,
133 .enum_literal,
134 .enum_tag,
135 .float,
136 .ptr,
137 .slice,
138 .opt,
139 .aggregate,
140 .un,
141 .bitpack,
142 // memoization, not types
143 .memoized_call,
144 => unreachable,
145 }
146}
147
148/// Asserts that `ty` is a non-tuple `struct` type, and ensures that its fields' default values
149/// are resolved. Adds incremental dependencies tracking the required type resolution.
150///
151/// It is not necessary to call this function to query the values of comptime fields: those values
152/// are available from type *layout* resolution, see `ensureLayoutResolved`.
153///
154/// Asserts that the *layout* of `ty` has already been resolved---see `ensureLayoutResolved`.
155pub fn ensureStructDefaultsResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!void {
156 const pt = sema.pt;
157 const zcu = pt.zcu;
158 const ip = &zcu.intern_pool;
159
160 assert(ip.indexToKey(ty.toIntern()) == .struct_type);
161 ty.assertHasLayout(zcu);
162
163 try sema.declareDependency(.{ .struct_defaults = ty.toIntern() });
164 try sema.addReferenceEntry(null, src, .wrap(.{ .struct_defaults = ty.toIntern() }));
165
166 const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined };
167
168 if (zcu.analysis_in_progress.contains(.wrap(.{ .struct_defaults = ty.toIntern() }))) {
169 return sema.failWithDependencyLoop(.wrap(.{ .struct_defaults = ty.toIntern() }), &reason);
170 }
171
172 pt.ensureStructDefaultsUpToDate(ty, &reason) catch |err| switch (err) {
173 error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = .wrap(.{ .struct_defaults = ty.toIntern() }) }),
174 else => |e| return e,
175 };
176}
177
178/// Asserts that `struct_ty` is a non-packed non-tuple struct, and that `sema.owner` is that type.
179/// This function *does* register the `src_hash` dependency on the struct.
180pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
181 const pt = sema.pt;
182 const zcu = pt.zcu;
183 const comp = zcu.comp;
184 const io = comp.io;
185 const gpa = comp.gpa;
186 const ip = &zcu.intern_pool;
187
188 const tracy = trace(@src());
189 defer tracy.end();
190 tracy.addText(struct_ty.containerTypeName(ip).toSlice(ip));
191 tracy.addTextFmt("ip_index={d}", .{struct_ty.toIntern()});
192
193 assert(sema.owner.unwrap().type_layout == struct_ty.toIntern());
194
195 const struct_obj = ip.loadStructType(struct_ty.toIntern());
196 assert(struct_obj.want_layout);
197 const zir_index = struct_obj.zir_index.resolve(ip) orelse {
198 return sema.failTransitive(.{ .lost_tracking = struct_obj.zir_index });
199 };
200
201 var block: Block = .{
202 .parent = null,
203 .sema = sema,
204 .namespace = struct_obj.namespace,
205 .instructions = .empty,
206 .inlining = null,
207 .comptime_reason = undefined, // always set before using `block`
208 .src_base_inst = struct_obj.zir_index,
209 .type_name_ctx = struct_obj.name,
210 };
211 defer block.instructions.deinit(gpa);
212
213 // There may be old field names in here from a previous update.
214 struct_obj.field_name_map.get(ip).clearRetainingCapacity();
215
216 if (struct_obj.is_reified) {
217 // The field names are populated, but we haven't checked for duplicates (nor populated the map) yet.
218 for (0..struct_obj.field_names.len) |field_index| {
219 const name = struct_obj.field_names.get(ip)[field_index];
220 if (ip.addFieldName(struct_obj.field_names, struct_obj.field_name_map, name)) |prev_field_index| {
221 return sema.failWithOwnedErrorMsg(&block, msg: {
222 const src = block.builtinCallArgSrc(.zero, 2);
223 const msg = try sema.errMsg(src, "duplicate struct field '{f}' at index '{d}", .{ name.fmt(ip), field_index });
224 errdefer msg.destroy(gpa);
225 try sema.errNote(src, msg, "previous field at index '{d}'", .{prev_field_index});
226 break :msg msg;
227 });
228 }
229 }
230 } else {
231 // Declared structs do not yet have field information populated:
232 // * field names
233 // * field comptime-ness
234 // * field types
235 // * field aligns
236 // It's our job to populate these now.
237 try sema.declareDependency(.{ .src_hash = struct_obj.zir_index });
238
239 // Likewise, comptime bits may be set. We clear them all first because it avoids needing
240 // "unset bit with AND" logic below (instead we only need the "set bit with OR" case).
241 @memset(struct_obj.field_is_comptime_bits.getAll(ip), 0);
242
243 const zir_struct = sema.code.getStructDecl(zir_index);
244 var field_it = zir_struct.iterateFields();
245 var any_comptime_fields = false;
246 while (field_it.next()) |zir_field| {
247 {
248 const name_slice = sema.code.nullTerminatedString(zir_field.name);
249 const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls);
250 assert(ip.addFieldName(struct_obj.field_names, struct_obj.field_name_map, name) == null); // AstGen validated this for us
251 }
252
253 if (zir_field.is_comptime) {
254 const bit_bag_index = zir_field.idx / 32;
255 const mask = @as(u32, 1) << @intCast(zir_field.idx % 32);
256 struct_obj.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask;
257 any_comptime_fields = true;
258 }
259
260 {
261 const field_ty_src = block.src(.{ .container_field_type = zir_field.idx });
262 const field_ty: Type = field_ty: {
263 block.comptime_reason = .{ .reason = .{
264 .src = field_ty_src,
265 .r = .{ .simple = .struct_field_types },
266 } };
267 const type_ref = try sema.resolveInlineBody(&block, zir_field.type_body, zir_index);
268 break :field_ty try sema.analyzeAsType(&block, field_ty_src, .struct_field_types, type_ref);
269 };
270 struct_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern();
271 }
272
273 if (struct_obj.field_aligns.len == 0) {
274 assert(zir_field.align_body == null);
275 } else {
276 const field_align_src = block.src(.{ .container_field_align = zir_field.idx });
277 const field_align: Alignment = a: {
278 block.comptime_reason = .{ .reason = .{
279 .src = field_align_src,
280 .r = .{ .simple = .struct_field_attrs },
281 } };
282 const align_body = zir_field.align_body orelse break :a .none;
283 const align_ref = try sema.resolveInlineBody(&block, align_body, zir_index);
284 break :a try sema.analyzeAsAlign(&block, field_align_src, align_ref);
285 };
286 struct_obj.field_aligns.get(ip)[zir_field.idx] = field_align;
287 }
288 }
289
290 // We also resolve the default values of any `comptime` fields now. This is not necessary in
291 // the case of a reified struct because the the default values were already poulated and
292 // validated by `Sema.zirReifyStruct`.
293 if (any_comptime_fields) {
294 try resolveStructDefaultsInner(sema, &block, &struct_obj, .comptime_fields);
295 }
296 }
297
298 if (struct_obj.layout == .@"packed") {
299 return resolvePackedStructLayout(sema, &block, struct_ty, &struct_obj);
300 }
301
302 // Resolve the layout of all fields, and check their types are allowed.
303 const fields_len = struct_obj.field_types.len;
304 for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {
305 const field_ty: Type = .fromInterned(field_ty_ip);
306 assert(!field_ty.isGenericPoison());
307 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
308 const field_name_src = block.src(.{ .container_field_name = @intCast(field_index) });
309 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
310 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
311 return sema.failWithOwnedErrorMsg(&block, msg: {
312 const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)});
313 errdefer msg.destroy(gpa);
314 try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
315 try sema.addDeclaredHereNote(msg, field_ty);
316 break :msg msg;
317 });
318 }
319 if (field_ty.zigTypeTag(zcu) == .spirv) {
320 if (field_ty.isSpirvRuntimeArray(zcu)) {
321 if (struct_obj.layout != .@"extern") {
322 return sema.failWithOwnedErrorMsg(&block, msg: {
323 const msg = try sema.errMsg(struct_ty.srcLoc(zcu), "non-extern struct cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
324 errdefer msg.destroy(gpa);
325 try sema.errNote(field_name_src, msg, "while checking this field", .{});
326 break :msg msg;
327 });
328 }
329 if (field_index != fields_len - 1) {
330 return sema.failWithOwnedErrorMsg(&block, msg: {
331 const msg = try sema.errMsg(struct_ty.srcLoc(zcu), "struct field of type '{f}' must be the last field", .{field_ty.fmt(pt)});
332 errdefer msg.destroy(gpa);
333 try sema.errNote(field_name_src, msg, "while checking this field", .{});
334 break :msg msg;
335 });
336 }
337
338 const elem_ty: Type = field_ty.childType(zcu);
339 if (elem_ty.zigTypeTag(zcu) == .spirv) {
340 return sema.failWithOwnedErrorMsg(&block, msg: {
341 const msg = try sema.errMsg(field_ty_src, "cannot embed SPIR-V type '{f}' in struct", .{elem_ty.fmt(pt)});
342 errdefer msg.destroy(gpa);
343 try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
344 try sema.addDeclaredHereNote(msg, field_ty);
345 break :msg msg;
346 });
347 }
348 } else {
349 return sema.failWithOwnedErrorMsg(&block, msg: {
350 const msg = try sema.errMsg(field_ty_src, "cannot directly embed SPIR-V type '{f}' in struct", .{field_ty.fmt(pt)});
351 errdefer msg.destroy(gpa);
352 try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
353 try sema.addDeclaredHereNote(msg, field_ty);
354 break :msg msg;
355 });
356 }
357 }
358
359 if (struct_obj.layout == .@"extern" and !field_ty.validateExtern(.struct_field, zcu)) {
360 return sema.failWithOwnedErrorMsg(&block, msg: {
361 const msg = try sema.errMsg(field_ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
362 errdefer msg.destroy(gpa);
363 try sema.explainWhyTypeIsNotExtern(msg, field_ty_src, field_ty, .struct_field);
364 try sema.addDeclaredHereNote(msg, field_ty);
365 break :msg msg;
366 });
367 }
368 }
369
370 // Fields are okay. Now we need to resolve the struct's overall layout (size, field offsets, etc).
371
372 var any_comptime_fields = false;
373 var struct_align: Alignment = .@"1";
374 var has_no_possible_value = false;
375 var has_runtime_state = false;
376 var has_comptime_state = false;
377 // Unlike `struct_obj.field_aligns`, these are not `.none`.
378 const resolved_field_aligns = try sema.arena.alloc(Alignment, struct_obj.field_names.len);
379 for (resolved_field_aligns, 0..) |*align_out, field_idx| {
380 const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]);
381 const field_align: Alignment = a: {
382 if (struct_obj.field_aligns.len != 0) {
383 const a = struct_obj.field_aligns.get(ip)[field_idx];
384 if (a != .none) break :a a;
385 }
386 break :a field_ty.abiAlignment(zcu);
387 };
388 align_out.* = field_align;
389 if (struct_obj.field_is_comptime_bits.get(ip, field_idx)) {
390 assert(struct_obj.layout == .auto); // comptime fields not allowed in extern or packed structs
391 struct_obj.field_runtime_order.get(ip)[field_idx] = .omitted; // comptime fields are not in the runtime order
392 any_comptime_fields = true;
393 continue; // `comptime` fields do not contribute to the struct layout
394 }
395 struct_align = struct_align.maxStrict(field_align);
396 if (struct_obj.layout == .auto) {
397 struct_obj.field_runtime_order.get(ip)[field_idx] = @fromBackingInt(@intCast(field_idx));
398 }
399 switch (field_ty.classify(zcu)) {
400 .one_possible_value => {},
401 .no_possible_value => has_no_possible_value = true,
402 .runtime => has_runtime_state = true,
403 .fully_comptime => has_comptime_state = true,
404 .partially_comptime => {
405 has_runtime_state = true;
406 has_comptime_state = true;
407 },
408 }
409 }
410 const class: Type.Class = class: {
411 if (has_no_possible_value) break :class .no_possible_value;
412 if (has_comptime_state) {
413 break :class if (has_runtime_state) .partially_comptime else .fully_comptime;
414 } else {
415 break :class if (has_runtime_state) .runtime else .one_possible_value;
416 }
417 };
418
419 switch (struct_obj.layout) {
420 .auto => {},
421 .@"extern" => assert(class != .no_possible_value), // field types are all extern, so are not NPV
422 .@"packed" => unreachable,
423 }
424
425 if (struct_obj.layout == .auto) {
426 const runtime_order = struct_obj.field_runtime_order.get(ip);
427 // This logic does not reorder fields; it only moves the omitted ones to the end so that logic
428 // elsewhere does not need to special-case. TODO: support field reordering in all the backends!
429 if (!zcu.backendSupportsFeature(.field_reordering)) {
430 var i: usize = 0;
431 var off: usize = 0;
432 while (i + off < runtime_order.len) {
433 if (runtime_order[i + off] == .omitted) {
434 off += 1;
435 } else {
436 runtime_order[i] = runtime_order[i + off];
437 i += 1;
438 }
439 }
440 } else {
441 // Sort by descending alignment to minimize padding.
442 const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder;
443 const AlignSortCtx = struct {
444 aligns: []const Alignment,
445 fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool {
446 assert(a != .unresolved);
447 assert(b != .unresolved);
448 if (a == .omitted) return false;
449 if (b == .omitted) return true;
450 const a_align = ctx.aligns[@backingInt(a)];
451 const b_align = ctx.aligns[@backingInt(b)];
452 return a_align.compare(.gt, b_align);
453 }
454 };
455 mem.sortUnstable(
456 RuntimeOrder,
457 runtime_order,
458 @as(AlignSortCtx, .{ .aligns = resolved_field_aligns }),
459 AlignSortCtx.lessThan,
460 );
461 }
462 }
463
464 var runtime_order_it = struct_obj.iterateRuntimeOrder(ip);
465 var cur_offset: u64 = 0;
466 while (runtime_order_it.next()) |field_idx| {
467 const field_ty: Type = .fromInterned(struct_obj.field_types.get(ip)[field_idx]);
468 const offset = resolved_field_aligns[field_idx].forward(cur_offset);
469 struct_obj.field_offsets.get(ip)[field_idx] = @truncate(offset); // truncate because the overflow is handled below
470 // A SPIR-V `runtime_array` always trails the struct and
471 // contributes nothing to the struct's static size.
472 const field_size = if (field_ty.isSpirvRuntimeArray(zcu)) 0 else field_ty.abiSize(zcu);
473 cur_offset = offset + field_size;
474 }
475 const struct_size: u32 = switch (class) {
476 .no_possible_value => 0,
477 else => std.math.cast(u32, struct_align.forward(cur_offset)) orelse return sema.fail(
478 &block,
479 struct_ty.srcLoc(zcu),
480 "struct layout requires size {d}, this compiler implementation supports up to {d}",
481 .{ struct_align.forward(cur_offset), std.math.maxInt(u32) },
482 ),
483 };
484 ip.resolveStructLayout(
485 io,
486 struct_ty.toIntern(),
487 struct_size,
488 struct_align,
489 class,
490 );
491}
492
493/// Asserts that `struct_ty` is a packed struct, and that `sema.owner` is that type.
494/// This function *does* register the `src_hash` dependency on the struct.
495fn resolvePackedStructLayout(
496 sema: *Sema,
497 block: *Block,
498 struct_ty: Type,
499 struct_obj: *const InternPool.LoadedStructType,
500) CompileError!void {
501 const pt = sema.pt;
502 const zcu = pt.zcu;
503 const comp = zcu.comp;
504 const io = comp.io;
505 const gpa = comp.gpa;
506 const ip = &zcu.intern_pool;
507
508 // Resolve the layout of all fields, and check their types are allowed.
509 // Also count the number of bits while we're at it.
510 var field_bits: u64 = 0;
511 for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {
512 const field_ty: Type = .fromInterned(field_ty_ip);
513 assert(!field_ty.isGenericPoison());
514 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
515 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
516 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
517 return sema.failWithOwnedErrorMsg(block, msg: {
518 const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)});
519 errdefer msg.destroy(gpa);
520 try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
521 try sema.addDeclaredHereNote(msg, field_ty);
522 break :msg msg;
523 });
524 }
525 if (field_ty.unpackable(zcu)) |reason| return sema.failWithOwnedErrorMsg(block, msg: {
526 const msg = try sema.errMsg(field_ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
527 errdefer msg.destroy(gpa);
528 try sema.explainWhyTypeIsUnpackable(msg, field_ty_src, reason);
529 break :msg msg;
530 });
531 switch (field_ty.classify(zcu)) {
532 .one_possible_value, .runtime => {},
533 .no_possible_value => unreachable, // packable types are not NPV
534 .partially_comptime => unreachable, // packable types are not comptime-only
535 .fully_comptime => unreachable, // packable types are not comptime-only
536 }
537 field_bits += field_ty.bitSize(zcu);
538 }
539
540 const explicit_backing_int_ty: ?Type = if (struct_obj.is_reified) ty: {
541 break :ty switch (struct_obj.packed_backing_mode) {
542 .explicit => .fromInterned(struct_obj.packed_backing_int_type),
543 .auto => null,
544 };
545 } else ty: {
546 const zir_index = struct_obj.zir_index.resolve(ip).?;
547 const zir_struct = sema.code.getStructDecl(zir_index);
548 const backing_int_type_body = zir_struct.backing_int_type_body orelse {
549 break :ty null; // inferred backing type
550 };
551 // Explicitly specified, so evaluate the backing int type expression.
552 const backing_int_type_src = block.src(.container_arg);
553 block.comptime_reason = .{ .reason = .{
554 .src = backing_int_type_src,
555 .r = .{ .simple = .packed_struct_backing_int_type },
556 } };
557 const type_ref = try sema.resolveInlineBody(block, backing_int_type_body, zir_index);
558 break :ty try sema.analyzeAsType(block, backing_int_type_src, .packed_struct_backing_int_type, type_ref);
559 };
560
561 // Finally, either validate or infer the backing int type.
562 const backing_int_ty: Type = if (explicit_backing_int_ty) |backing_ty| ty: {
563 if (backing_ty.zigTypeTag(zcu) != .int) return sema.fail(
564 block,
565 block.src(.container_arg),
566 "expected backing integer type, found '{f}'",
567 .{backing_ty.fmt(pt)},
568 );
569 if (field_bits != backing_ty.intInfo(zcu).bits) return sema.failWithOwnedErrorMsg(block, msg: {
570 const src = struct_ty.srcLoc(zcu);
571 const msg = try sema.errMsg(src, "backing integer bit width does not match total bit width of fields", .{});
572 errdefer msg.destroy(gpa);
573 try sema.errNote(
574 block.src(.container_arg),
575 msg,
576 "backing integer '{f}' has bit width '{d}'",
577 .{ backing_ty.fmt(pt), backing_ty.bitSize(zcu) },
578 );
579 try sema.errNote(src, msg, "struct fields have total bit width '{d}'", .{field_bits});
580 break :msg msg;
581 });
582 break :ty backing_ty;
583 } else ty: {
584 // We need to generate the inferred tag.
585 const backing_int_bits = std.math.cast(u16, field_bits) orelse return sema.fail(
586 block,
587 struct_ty.srcLoc(zcu),
588 "packed struct bit width '{d}' exceeds maximum bit width of 65535",
589 .{field_bits},
590 );
591 break :ty try pt.intType(.unsigned, backing_int_bits);
592 };
593 ip.resolvePackedStructLayout(
594 io,
595 struct_ty.toIntern(),
596 backing_int_ty.toIntern(),
597 );
598}
599
600/// Asserts that `struct_ty` is a non-tuple struct, and that `sema.owner` is that type.
601///
602/// Also asserts that the layout of `struct_ty` has *already* been resolved (though it is okay for
603/// that resolution to have failed). This requirement exists to ensure better error messages in the
604/// event of a dependency loop.
605///
606/// This function *does* register the `src_hash` dependency on the struct.
607pub fn resolveStructDefaults(sema: *Sema, struct_ty: Type) CompileError!void {
608 const pt = sema.pt;
609 const zcu = pt.zcu;
610 const comp = zcu.comp;
611 const gpa = comp.gpa;
612 const ip = &zcu.intern_pool;
613
614 const tracy = trace(@src());
615 defer tracy.end();
616 tracy.addText(struct_ty.containerTypeName(ip).toSlice(ip));
617 tracy.addTextFmt("ip_index={d}", .{struct_ty.toIntern()});
618
619 assert(sema.owner.unwrap().struct_defaults == struct_ty.toIntern());
620
621 // We always depend on the layout of `struct_ty`. However, we don't actually need to resolve it
622 // now, because the caller has done so for us. Just mark the dependency so that the incremental
623 // compilation handling understands the dependency graph.
624 try sema.declareDependency(.{ .type_layout = struct_ty.toIntern() });
625 struct_ty.assertHasLayout(zcu);
626 const layout_unit: InternPool.AnalUnit = .wrap(.{ .type_layout = struct_ty.toIntern() });
627 if (zcu.failed_analysis.contains(layout_unit) or zcu.transitive_failed_analysis.contains(layout_unit)) {
628 return sema.failTransitive(.{ .failed_unit = layout_unit });
629 }
630
631 const struct_obj = ip.loadStructType(struct_ty.toIntern());
632 assert(struct_obj.want_layout);
633
634 if (struct_obj.is_reified) {
635 // `Sema.zirReifyStruct` has already populated the default field values *and* (by loading
636 // the default values from pointers) validated their types, so we have nothing to do.
637 return;
638 }
639
640 try sema.declareDependency(.{ .src_hash = struct_obj.zir_index });
641
642 if (struct_obj.field_defaults.len == 0) {
643 // The struct has no default field values, so the slice has been omitted.
644 return;
645 }
646
647 var block: Block = .{
648 .parent = null,
649 .sema = sema,
650 .namespace = struct_obj.namespace,
651 .instructions = .empty,
652 .inlining = null,
653 .comptime_reason = undefined, // always set before using `block`
654 .src_base_inst = struct_obj.zir_index,
655 .type_name_ctx = struct_obj.name,
656 };
657 defer block.instructions.deinit(gpa);
658
659 return resolveStructDefaultsInner(sema, &block, &struct_obj, .normal_fields);
660}
661
662/// Asserts that the struct is not reified, and that `struct_obj.field_defaults.len` is non-zero.
663fn resolveStructDefaultsInner(
664 sema: *Sema,
665 block: *Block,
666 struct_obj: *const InternPool.LoadedStructType,
667 mode: enum { comptime_fields, normal_fields },
668) CompileError!void {
669 const pt = sema.pt;
670 const zcu = pt.zcu;
671 const comp = zcu.comp;
672 const gpa = comp.gpa;
673 const ip = &zcu.intern_pool;
674
675 assert(struct_obj.field_defaults.len > 0);
676
677 // We'll need to map the struct decl instruction to provide result types
678 const zir_index = struct_obj.zir_index.resolve(ip) orelse {
679 return sema.failTransitive(.{ .lost_tracking = struct_obj.zir_index });
680 };
681 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index});
682
683 const field_types = struct_obj.field_types.get(ip);
684
685 const zir_struct = sema.code.getStructDecl(zir_index);
686 var field_it = zir_struct.iterateFields();
687 while (field_it.next()) |zir_field| {
688 switch (mode) {
689 .comptime_fields => if (!zir_field.is_comptime) continue,
690 .normal_fields => if (zir_field.is_comptime) continue,
691 }
692
693 const default_val_src = block.src(.{ .container_field_value = zir_field.idx });
694 block.comptime_reason = .{ .reason = .{
695 .src = default_val_src,
696 .r = .{ .simple = .struct_field_default_value },
697 } };
698 const default_body = zir_field.default_body orelse {
699 struct_obj.field_defaults.get(ip)[zir_field.idx] = .none;
700 continue;
701 };
702 const field_ty: Type = .fromInterned(field_types[zir_field.idx]);
703 const uncoerced = ref: {
704 // Provide the result type
705 sema.inst_map.putAssumeCapacity(zir_index, .fromIntern(field_ty.toIntern()));
706 defer assert(sema.inst_map.remove(zir_index));
707 break :ref try sema.resolveInlineBody(block, default_body, zir_index);
708 };
709 const coerced = try sema.coerce(block, field_ty, uncoerced, default_val_src);
710 const default_val = try sema.resolveConstValue(block, default_val_src, coerced, null);
711 if (default_val.canMutateComptimeVarState(zcu)) {
712 const field_name = struct_obj.field_names.get(ip)[zir_field.idx];
713 return sema.failWithContainsReferenceToComptimeVar(block, default_val_src, field_name, "field default value", default_val);
714 }
715 struct_obj.field_defaults.get(ip)[zir_field.idx] = default_val.toIntern();
716 }
717}
718
719/// This logic must be kept in sync with `Type.getUnionLayout`.
720pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
721 const pt = sema.pt;
722 const zcu = pt.zcu;
723 const comp = zcu.comp;
724 const io = comp.io;
725 const gpa = comp.gpa;
726 const ip = &zcu.intern_pool;
727
728 const tracy = trace(@src());
729 defer tracy.end();
730 tracy.addText(union_ty.containerTypeName(ip).toSlice(ip));
731 tracy.addTextFmt("ip_index={d}", .{union_ty.toIntern()});
732
733 assert(sema.owner.unwrap().type_layout == union_ty.toIntern());
734
735 const union_obj = ip.loadUnionType(union_ty.toIntern());
736 assert(union_obj.want_layout);
737 const zir_index = union_obj.zir_index.resolve(ip) orelse {
738 return sema.failTransitive(.{ .lost_tracking = union_obj.zir_index });
739 };
740
741 var block: Block = .{
742 .parent = null,
743 .sema = sema,
744 .namespace = union_obj.namespace,
745 .instructions = .empty,
746 .inlining = null,
747 .comptime_reason = undefined, // always set before using `block`
748 .src_base_inst = union_obj.zir_index,
749 .type_name_ctx = union_obj.name,
750 };
751 defer block.instructions.deinit(gpa);
752
753 const enum_tag_ty: Type = switch (union_obj.enum_tag_mode) {
754 .explicit => validated_tag_ty: {
755 // If the union is reified, its enum tag type is already populated. If the union is
756 // declared, we need to evaluate the enum tag type expression (the `E` in `union(E)`).
757 const tag_ty: Type = switch (union_obj.is_reified) {
758 true => .fromInterned(union_obj.enum_tag_type),
759 false => tag_ty: {
760 const zir_union = sema.code.getUnionDecl(zir_index);
761 assert(zir_union.kind == .tagged_explicit); // `Zcu.mapOldZirToNew` guarantees that the ZIR mapping preserves `kind`
762 const tag_type_body = zir_union.arg_type_body.?;
763 const tag_type_src = block.src(.container_arg);
764 block.comptime_reason = .{ .reason = .{
765 .src = tag_type_src,
766 .r = .{ .simple = .union_enum_tag_type },
767 } };
768 const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index);
769 break :tag_ty try sema.analyzeAsType(&block, tag_type_src, .union_enum_tag_type, type_ref);
770 },
771 };
772 // Because the type is explicitly specified, we need to validate it.
773 if (tag_ty.zigTypeTag(zcu) != .@"enum") return sema.fail(
774 &block,
775 block.src(.container_arg),
776 "expected enum tag type, found '{f}'",
777 .{tag_ty.fmt(pt)},
778 );
779 break :validated_tag_ty tag_ty;
780 },
781 // If no tag type was specified, we generate one keyed on this union type.
782 .auto => switch (try ip.getGeneratedEnumTagType(gpa, io, pt.tid, .{
783 .union_type = union_ty.toIntern(),
784 // The int tag for this enum is usually inferred---the exception is `union(enum(T))`.
785 .int_tag_mode = switch (union_obj.is_reified) {
786 true => .auto,
787 false => switch (sema.code.getUnionDecl(zir_index).kind) {
788 .tagged_enum_explicit => .explicit,
789 else => .auto,
790 },
791 },
792 .fields_len = @intCast(union_obj.field_types.len),
793 })) {
794 .existing => |tag_ty| .fromInterned(tag_ty),
795 .wip => |wip| tag_ty: {
796 errdefer wip.cancel(ip, pt.tid);
797 _ = wip.setName(ip, try ip.getOrPutStringFmt(
798 gpa,
799 io,
800 pt.tid,
801 "@typeInfo({f}).@\"union\".tag_type.?",
802 .{union_obj.name.fmt(ip)},
803 .no_embedded_nulls,
804 ), .none);
805 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
806 .parent = union_obj.namespace.toOptional(),
807 .owner_type = wip.index,
808 .file_scope = zcu.namespacePtr(union_obj.namespace).file_scope,
809 .generation = zcu.generation,
810 });
811 if (comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
812 break :tag_ty .fromInterned(wip.finish(ip, new_namespace_index));
813 },
814 },
815 };
816
817 try sema.ensureLayoutResolved(enum_tag_ty, block.src(.container_arg), .backing_enum);
818 const enum_obj = ip.loadEnumType(enum_tag_ty.toIntern());
819
820 if (union_obj.is_reified) {
821 // We have field names in `union_obj.reified_field_names`, but we haven't
822 // checked them against the backing type yet.
823 const union_field_names = union_obj.reified_field_names.get(ip);
824 match_fields: {
825 // We can efficiently *check* if the fields match...
826 if (union_field_names.len == enum_obj.field_names.len) {
827 for (union_field_names, enum_obj.field_names.get(ip)) |union_field_name, enum_field_name| {
828 if (!std.mem.eql(u8, union_field_name.toSlice(ip), enum_field_name.toSlice(ip))) break;
829 } else {
830 break :match_fields;
831 }
832 }
833 // ...but if they don't, reporting a nice error is a little more involved. If some field
834 // is present in the enum but not the union, or vice versa, we will report that instead
835 // of a generic "field order mismatch" error. Of course, this error is impossible for a
836 // generated tag type, because we populated that from the union ZIR!
837 assert(enum_obj.owner_union != union_ty.toIntern());
838 return failUnionFieldMismatch(sema, &block, union_field_names, enum_tag_ty, &enum_obj);
839 }
840 } else {
841 // Declared unions do not have field types or aligns populated yet.
842 // We also need to check the field names match the backing enum.
843 try sema.declareDependency(.{ .src_hash = union_obj.zir_index });
844 const zir_union = sema.code.getUnionDecl(zir_index);
845
846 // We'll first check the field names against the backing enum, and only analyze the types
847 // once we know the fields match one-to-one.
848 match_fields: {
849 // We can efficiently *check* if the fields match...
850 if (zir_union.field_names.len == enum_obj.field_names.len) {
851 for (zir_union.field_names, enum_obj.field_names.get(ip)) |union_field_name_zir, enum_field_name| {
852 const union_field_name_slice = sema.code.nullTerminatedString(union_field_name_zir);
853 if (!std.mem.eql(u8, union_field_name_slice, enum_field_name.toSlice(ip))) break;
854 } else {
855 break :match_fields;
856 }
857 }
858 // ...but if they don't, reporting a nice error is a little more involved. If some field
859 // is present in the enum but not the union, or vice versa, we will report that instead
860 // of a generic "field order mismatch" error. Of course, this error is impossible for a
861 // generated tag type, because we populated that from the union ZIR!
862 assert(enum_obj.owner_union != union_ty.toIntern());
863 const union_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, zir_union.field_names.len);
864 for (zir_union.field_names, union_field_names) |name_zir, *name| {
865 name.* = try ip.getOrPutString(gpa, io, pt.tid, sema.code.nullTerminatedString(name_zir), .no_embedded_nulls);
866 }
867 return failUnionFieldMismatch(sema, &block, union_field_names, enum_tag_ty, &enum_obj);
868 }
869
870 // Field names okay; populate types and aligns.
871 var field_it = zir_union.iterateFields();
872 while (field_it.next()) |zir_field| {
873 const field_ty_src = block.src(.{ .container_field_type = zir_field.idx });
874 const field_ty: Type = field_ty: {
875 block.comptime_reason = .{ .reason = .{
876 .src = field_ty_src,
877 .r = .{ .simple = .union_field_types },
878 } };
879 const type_body = zir_field.type_body orelse break :field_ty .void;
880 const type_ref = try sema.resolveInlineBody(&block, type_body, zir_index);
881 break :field_ty try sema.analyzeAsType(&block, field_ty_src, .union_field_types, type_ref);
882 };
883 union_obj.field_types.get(ip)[zir_field.idx] = field_ty.toIntern();
884
885 const field_align_src = block.src(.{ .container_field_align = zir_field.idx });
886 const explicit_field_align: Alignment = a: {
887 block.comptime_reason = .{ .reason = .{
888 .src = field_align_src,
889 .r = .{ .simple = .union_field_attrs },
890 } };
891 const align_body = zir_field.align_body orelse break :a .none;
892 const align_ref = try sema.resolveInlineBody(&block, align_body, zir_index);
893 break :a try sema.analyzeAsAlign(&block, field_align_src, align_ref);
894 };
895 if (union_obj.field_aligns.len != 0) {
896 union_obj.field_aligns.get(ip)[zir_field.idx] = explicit_field_align;
897 } else {
898 assert(explicit_field_align == .none);
899 }
900 }
901 }
902
903 if (union_obj.layout == .@"packed") {
904 return resolvePackedUnionLayout(sema, &block, union_ty, &union_obj, enum_tag_ty);
905 }
906
907 // Resolve the layout of all fields, and check their types are allowed.
908 for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {
909 const field_ty: Type = .fromInterned(field_ty_ip);
910 assert(!field_ty.isGenericPoison());
911 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
912 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
913 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
914 return sema.failWithOwnedErrorMsg(&block, msg: {
915 const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)});
916 errdefer msg.destroy(gpa);
917 try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
918 try sema.addDeclaredHereNote(msg, field_ty);
919 break :msg msg;
920 });
921 }
922 if (field_ty.zigTypeTag(zcu) == .spirv) {
923 return sema.failWithOwnedErrorMsg(&block, msg: {
924 const msg = try sema.errMsg(field_ty_src, "SPIR-V type '{f}' have unknown size and therefore cannot be directly embedded in unions", .{field_ty.fmt(pt)});
925 errdefer msg.destroy(gpa);
926 try sema.addDeclaredHereNote(msg, field_ty);
927 break :msg msg;
928 });
929 }
930 if (union_obj.layout == .@"extern" and !field_ty.validateExtern(.union_field, zcu)) {
931 return sema.failWithOwnedErrorMsg(&block, msg: {
932 const msg = try sema.errMsg(field_ty_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
933 errdefer msg.destroy(gpa);
934 try sema.explainWhyTypeIsNotExtern(msg, field_ty_src, field_ty, .union_field);
935 try sema.addDeclaredHereNote(msg, field_ty);
936 break :msg msg;
937 });
938 }
939 }
940
941 // Fields are okay. Now we need to resolve the union's overall layout (size, alignment, etc).
942 var payload_align: Alignment = .@"1";
943 var payload_size: u64 = 0;
944 var possible_tags: u32 = 0;
945 var payload_has_comptime_state = false;
946 for (0..union_obj.field_types.len) |field_idx| {
947 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
948 const field_align: Alignment = a: {
949 if (union_obj.field_aligns.len != 0) {
950 const a = union_obj.field_aligns.get(ip)[field_idx];
951 if (a != .none) break :a a;
952 }
953 break :a field_ty.abiAlignment(zcu);
954 };
955 payload_align = payload_align.maxStrict(field_align);
956 payload_size = @max(payload_size, field_ty.abiSize(zcu));
957
958 switch (field_ty.classify(zcu)) {
959 .no_possible_value => {}, // uninstantiable field has no effect
960 .one_possible_value, .runtime => {
961 possible_tags += 1;
962 },
963 .partially_comptime, .fully_comptime => {
964 possible_tags += 1;
965 payload_has_comptime_state = true;
966 },
967 }
968 }
969
970 // Uninstantiable `extern union`s don't make sense; disallow them.
971 if (possible_tags == 0 and union_obj.layout != .auto) {
972 // Field types are all extern, so not NPV; thus zero possible tags means no tags at all.
973 assert(union_obj.field_types.len == 0);
974 return sema.fail(&block, union_ty.srcLoc(zcu), "extern union has no fields", .{});
975 }
976
977 // We only need a runtime tag if there are multiple possible active fields *and* the union is
978 // not going to be comptime-only. Even if there are still runtime bits in the payload, the tag
979 // does not require runtime bits in a comptime-only union, because it is impossible to get a
980 // pointer to a union's tag.
981 const has_runtime_tag = switch (possible_tags) {
982 0, 1 => false,
983 else => union_obj.tag_usage != .none and !payload_has_comptime_state,
984 };
985
986 const class: Type.Class = class: {
987 if (possible_tags == 0) {
988 break :class .no_possible_value;
989 }
990 if (payload_has_comptime_state) {
991 break :class if (payload_size > 0) .partially_comptime else .fully_comptime;
992 }
993 const have_runtime_bits = has_runtime_tag or payload_size > 0;
994 break :class if (have_runtime_bits) .runtime else .one_possible_value;
995 };
996
997 const size: u64, const padding: u64, const alignment: Alignment = layout: {
998 if (!has_runtime_tag) {
999 break :layout .{ payload_align.forward(payload_size), 0, payload_align };
1000 }
1001 const tag_align = enum_tag_ty.abiAlignment(zcu);
1002 const tag_size = enum_tag_ty.abiSize(zcu);
1003 // The layout will either be (tag, payload, padding) or (payload, tag, padding) depending on
1004 // which has larger alignment. So the overall size is just the tag and payload sizes, added,
1005 // and padded to the larger alignment.
1006 const alignment = tag_align.maxStrict(payload_align);
1007 const unpadded_size = tag_size + payload_size;
1008 const size = alignment.forward(unpadded_size);
1009 break :layout .{ size, size - unpadded_size, alignment };
1010 };
1011
1012 if (class == .no_possible_value or class == .one_possible_value) {
1013 assert(size == 0);
1014 assert(padding == 0);
1015 }
1016
1017 const casted_size = std.math.cast(u32, size) orelse return sema.fail(
1018 &block,
1019 union_ty.srcLoc(zcu),
1020 "union layout requires size {d}, this compiler implementation supports up to {d}",
1021 .{ size, std.math.maxInt(u32) },
1022 );
1023 ip.resolveUnionLayout(
1024 io,
1025 union_ty.toIntern(),
1026 enum_tag_ty.toIntern(),
1027 class,
1028 has_runtime_tag,
1029 casted_size,
1030 @intCast(padding), // okay because padding is no greater than size
1031 alignment,
1032 );
1033}
1034fn failUnionFieldMismatch(sema: *Sema, block: *Block, union_field_names: []const InternPool.NullTerminatedString, enum_tag_ty: Type, enum_obj: *const InternPool.LoadedEnumType) CompileError {
1035 const pt = sema.pt;
1036 const zcu = pt.zcu;
1037 const comp = zcu.comp;
1038 const gpa = comp.gpa;
1039 const ip = &zcu.intern_pool;
1040 const enum_to_union_map = try sema.arena.alloc(?u32, enum_obj.field_names.len);
1041 @memset(enum_to_union_map, null);
1042 for (union_field_names, 0..) |field_name, union_field_index| {
1043 if (enum_obj.nameIndex(ip, field_name)) |enum_field_index| {
1044 enum_to_union_map[enum_field_index] = @intCast(union_field_index);
1045 continue;
1046 }
1047 const union_field_src = block.src(.{ .container_field_name = @intCast(union_field_index) });
1048 return sema.failWithOwnedErrorMsg(block, msg: {
1049 const msg = try sema.errMsg(union_field_src, "no field named '{f}' in enum '{f}'", .{ field_name.fmt(ip), enum_tag_ty.fmt(pt) });
1050 errdefer msg.destroy(gpa);
1051 try sema.addDeclaredHereNote(msg, enum_tag_ty);
1052 break :msg msg;
1053 });
1054 }
1055 for (enum_to_union_map, 0..) |union_field_index, enum_field_index| {
1056 if (union_field_index != null) continue;
1057 const field_name_ip = enum_obj.field_names.get(ip)[enum_field_index];
1058 const enum_field_src: LazySrcLoc = .{
1059 .base_node_inst = enum_tag_ty.typeDeclInstAllowGeneratedTag(zcu).?,
1060 .offset = .{ .container_field_name = @intCast(enum_field_index) },
1061 };
1062 return sema.failWithOwnedErrorMsg(block, msg: {
1063 const msg = try sema.errMsg(block.nodeOffset(.zero), "enum field '{f}' missing from union", .{field_name_ip.fmt(ip)});
1064 errdefer msg.destroy(gpa);
1065 try sema.errNote(enum_field_src, msg, "enum field here", .{});
1066 break :msg msg;
1067 });
1068 }
1069 // The only problem is the field ordering.
1070 for (enum_to_union_map, 0..) |union_field_index, enum_field_index| {
1071 if (union_field_index.? == enum_field_index) continue;
1072 const field_name = enum_obj.field_names.get(ip)[enum_field_index];
1073 const union_field_src = block.src(.{ .container_field_name = union_field_index.? });
1074 const enum_field_src: LazySrcLoc = .{
1075 .base_node_inst = enum_tag_ty.typeDeclInstAllowGeneratedTag(zcu).?,
1076 .offset = .{ .container_field_name = @intCast(enum_field_index) },
1077 };
1078 return sema.failWithOwnedErrorMsg(block, msg: {
1079 const msg = try sema.errMsg(block.nodeOffset(.zero), "union field order does not match tag enum field order", .{});
1080 errdefer msg.destroy(gpa);
1081 try sema.errNote(union_field_src, msg, "union field '{f}' is index {d}", .{ field_name.fmt(ip), union_field_index.? });
1082 try sema.errNote(enum_field_src, msg, "enum field '{f}' is index {d}", .{ field_name.fmt(ip), enum_field_index });
1083 break :msg msg;
1084 });
1085 }
1086 unreachable; // we already determined that *something* is wrong
1087}
1088fn resolvePackedUnionLayout(
1089 sema: *Sema,
1090 block: *Block,
1091 union_ty: Type,
1092 union_obj: *const InternPool.LoadedUnionType,
1093 enum_tag_ty: Type,
1094) CompileError!void {
1095 const pt = sema.pt;
1096 const zcu = pt.zcu;
1097 const comp = zcu.comp;
1098 const io = comp.io;
1099 const gpa = comp.gpa;
1100 const ip = &zcu.intern_pool;
1101
1102 // Uninstantiable `packed union`s don't make sense; disallow them.
1103 if (union_obj.field_types.len == 0) {
1104 return sema.fail(block, union_ty.srcLoc(zcu), "packed union has no fields", .{});
1105 }
1106
1107 // Resolve the layout of all fields, and check their types are allowed.
1108 for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {
1109 const field_ty: Type = .fromInterned(field_ty_ip);
1110 assert(!field_ty.isGenericPoison());
1111 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
1112 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
1113 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
1114 return sema.failWithOwnedErrorMsg(block, msg: {
1115 const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)});
1116 errdefer msg.destroy(gpa);
1117 try sema.errNote(field_ty_src, msg, "opaque types have unknown size", .{});
1118 try sema.addDeclaredHereNote(msg, field_ty);
1119 break :msg msg;
1120 });
1121 }
1122 if (field_ty.unpackable(zcu)) |reason| return sema.failWithOwnedErrorMsg(block, msg: {
1123 const msg = try sema.errMsg(field_ty_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
1124 errdefer msg.destroy(gpa);
1125 try sema.explainWhyTypeIsUnpackable(msg, field_ty_src, reason);
1126 break :msg msg;
1127 });
1128 assert(!field_ty.comptimeOnly(zcu)); // packable types are not comptime-only
1129 }
1130
1131 const explicit_backing_int_ty: ?Type = if (union_obj.is_reified) ty: {
1132 switch (union_obj.packed_backing_mode) {
1133 .explicit => break :ty .fromInterned(union_obj.packed_backing_int_type),
1134 .auto => break :ty null,
1135 }
1136 } else ty: {
1137 const zir_index = union_obj.zir_index.resolve(ip).?;
1138 const zir_union = sema.code.getUnionDecl(zir_index);
1139 const backing_int_type_body = zir_union.arg_type_body orelse {
1140 break :ty null; // inferred backing type
1141 };
1142 // Explicitly specified, so evaluate the backing int type expression.
1143 const backing_int_type_src = block.src(.container_arg);
1144 block.comptime_reason = .{ .reason = .{
1145 .src = backing_int_type_src,
1146 .r = .{ .simple = .packed_union_backing_int_type },
1147 } };
1148 const type_ref = try sema.resolveInlineBody(block, backing_int_type_body, zir_index);
1149 break :ty try sema.analyzeAsType(block, backing_int_type_src, .packed_union_backing_int_type, type_ref);
1150 };
1151
1152 // Finally, either validate or infer the backing int type.
1153 const backing_int_ty: Type = if (explicit_backing_int_ty) |backing_ty| ty: {
1154 if (backing_ty.zigTypeTag(zcu) != .int) return sema.fail(
1155 block,
1156 block.src(.container_arg),
1157 "expected backing integer type, found '{f}'",
1158 .{backing_ty.fmt(pt)},
1159 );
1160 const backing_int_bits = backing_ty.intInfo(zcu).bits;
1161 for (union_obj.field_types.get(ip), 0..) |field_type_ip, field_idx| {
1162 const field_type: Type = .fromInterned(field_type_ip);
1163 const field_bits = field_type.bitSize(zcu);
1164 if (field_bits != backing_int_bits) return sema.failWithOwnedErrorMsg(block, msg: {
1165 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_idx) });
1166 const msg = try sema.errMsg(field_ty_src, "field bit width does not match backing integer", .{});
1167 errdefer msg.destroy(gpa);
1168 try sema.errNote(field_ty_src, msg, "field type '{f}' has bit width '{d}'", .{ field_type.fmt(pt), field_bits });
1169 try sema.errNote(
1170 block.src(.container_arg),
1171 msg,
1172 "backing integer '{f}' has bit width '{d}'",
1173 .{ backing_ty.fmt(pt), backing_int_bits },
1174 );
1175 try sema.errNote(field_ty_src, msg, "all fields in a packed union must have the same bit width", .{});
1176 break :msg msg;
1177 });
1178 }
1179 break :ty backing_ty;
1180 } else ty: {
1181 const field_types = union_obj.field_types.get(ip);
1182 const first_field_type: Type = .fromInterned(field_types[0]);
1183 const first_field_bits = first_field_type.bitSize(zcu);
1184 for (field_types[1..], 1..) |field_type_ip, field_idx| {
1185 const field_type: Type = .fromInterned(field_type_ip);
1186 const field_bits = field_type.bitSize(zcu);
1187 if (field_bits != first_field_bits) return sema.failWithOwnedErrorMsg(block, msg: {
1188 const first_field_ty_src = block.src(.{ .container_field_type = 0 });
1189 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_idx) });
1190 const msg = try sema.errMsg(field_ty_src, "field bit width does not match earlier field", .{});
1191 errdefer msg.destroy(gpa);
1192 try sema.errNote(field_ty_src, msg, "field type '{f}' has bit width '{d}'", .{ field_type.fmt(pt), field_bits });
1193 try sema.errNote(first_field_ty_src, msg, "other field type '{f}' has bit width '{d}'", .{ first_field_type.fmt(pt), first_field_bits });
1194 try sema.errNote(field_ty_src, msg, "all fields in a packed union must have the same bit width", .{});
1195 break :msg msg;
1196 });
1197 }
1198 const backing_int_bits = std.math.cast(u16, first_field_bits) orelse return sema.fail(
1199 block,
1200 union_ty.srcLoc(zcu),
1201 "packed union bit width '{d}' exceeds maximum bit width of 65535",
1202 .{first_field_bits},
1203 );
1204 break :ty try pt.intType(.unsigned, backing_int_bits);
1205 };
1206 ip.resolvePackedUnionLayout(
1207 io,
1208 union_ty.toIntern(),
1209 enum_tag_ty.toIntern(),
1210 backing_int_ty.toIntern(),
1211 );
1212}
1213
1214pub fn resolveEnumLayout(sema: *Sema, enum_ty: Type) CompileError!void {
1215 const pt = sema.pt;
1216 const zcu = pt.zcu;
1217 const comp = zcu.comp;
1218 const io = comp.io;
1219 const gpa = comp.gpa;
1220 const ip = &zcu.intern_pool;
1221
1222 const tracy = trace(@src());
1223 defer tracy.end();
1224 tracy.addText(enum_ty.containerTypeName(ip).toSlice(ip));
1225 tracy.addTextFmt("ip_index={d}", .{enum_ty.toIntern()});
1226
1227 assert(sema.owner.unwrap().type_layout == enum_ty.toIntern());
1228
1229 const enum_obj = ip.loadEnumType(enum_ty.toIntern());
1230 assert(enum_obj.want_layout);
1231
1232 const maybe_parent_union_obj: ?InternPool.LoadedUnionType = un: {
1233 if (enum_obj.owner_union == .none) break :un null;
1234 break :un ip.loadUnionType(enum_obj.owner_union);
1235 };
1236
1237 const tracked_inst = enum_obj.zir_index.unwrap() orelse maybe_parent_union_obj.?.zir_index;
1238 const zir_index = tracked_inst.resolve(ip) orelse {
1239 return sema.failTransitive(.{ .lost_tracking = tracked_inst });
1240 };
1241
1242 var block: Block = .{
1243 .parent = null,
1244 .sema = sema,
1245 .namespace = enum_obj.namespace,
1246 .instructions = .empty,
1247 .inlining = null,
1248 .comptime_reason = undefined, // always set before using `block`
1249 .src_base_inst = tracked_inst,
1250 .type_name_ctx = enum_obj.name,
1251 };
1252 defer block.instructions.deinit(gpa);
1253
1254 // There may be old field names in the map from a previous update.
1255 enum_obj.field_name_map.get(ip).clearRetainingCapacity();
1256
1257 if (maybe_parent_union_obj) |*union_obj| {
1258 if (union_obj.is_reified) {
1259 // In the case of reification, the union stores the field names, just for us to copy.
1260 @memcpy(enum_obj.field_names.get(ip), union_obj.reified_field_names.get(ip));
1261 // The list of field names is now populated, but we haven't checked for duplicates yet,
1262 // nor have we populated the hash map.
1263 for (0..enum_obj.field_names.len) |field_index| {
1264 const name = enum_obj.field_names.get(ip)[field_index];
1265 if (ip.addFieldName(enum_obj.field_names, enum_obj.field_name_map, name)) |prev_field_index| {
1266 return sema.failWithOwnedErrorMsg(&block, msg: {
1267 const src = block.builtinCallArgSrc(.zero, 2);
1268 const msg = try sema.errMsg(src, "duplicate union field '{f}' at index '{d}", .{ name.fmt(ip), field_index });
1269 errdefer msg.destroy(gpa);
1270 try sema.errNote(src, msg, "previous field at index '{d}'", .{prev_field_index});
1271 break :msg msg;
1272 });
1273 }
1274 }
1275 } else {
1276 // Generated tag enums for declared unions do not yet have field names populated. It is
1277 // our job to populate them now.
1278 try sema.declareDependency(.{ .src_hash = union_obj.zir_index });
1279 const zir_union = sema.code.getUnionDecl(zir_index);
1280 for (zir_union.field_names) |zir_field_name| {
1281 const name_slice = sema.code.nullTerminatedString(zir_field_name);
1282 const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls);
1283 assert(ip.addFieldName(enum_obj.field_names, enum_obj.field_name_map, name) == null); // AstGen validated this for us
1284 }
1285 }
1286 } else {
1287 if (enum_obj.is_reified) {
1288 // The field names are populated, but we haven't checked for duplicates (nor populated the map) yet.
1289 for (0..enum_obj.field_names.len) |field_index| {
1290 const name = enum_obj.field_names.get(ip)[field_index];
1291 if (ip.addFieldName(enum_obj.field_names, enum_obj.field_name_map, name)) |prev_field_index| {
1292 return sema.failWithOwnedErrorMsg(&block, msg: {
1293 const src = block.builtinCallArgSrc(.zero, 2);
1294 const msg = try sema.errMsg(src, "duplicate enum field '{f}' at index '{d}'", .{ name.fmt(ip), field_index });
1295 errdefer msg.destroy(gpa);
1296 try sema.errNote(src, msg, "previous field at index '{d}'", .{prev_field_index});
1297 break :msg msg;
1298 });
1299 }
1300 }
1301 } else {
1302 // Declared enums do not yet have field names populated. It is our job to populate them now.
1303 try sema.declareDependency(.{ .src_hash = enum_obj.zir_index.unwrap().? });
1304 const zir_enum = sema.code.getEnumDecl(zir_index);
1305 for (zir_enum.field_names) |zir_field_name| {
1306 const name_slice = sema.code.nullTerminatedString(zir_field_name);
1307 const name = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls);
1308 assert(ip.addFieldName(enum_obj.field_names, enum_obj.field_name_map, name) == null); // AstGen validated this for us
1309 }
1310 }
1311 }
1312
1313 // Field names populated; now deal with the backing integer type. If explicitly provided,
1314 // validate it; otherwise, infer it.
1315
1316 const explicit_int_tag_ty: ?Type = if (enum_obj.is_reified) ty: {
1317 break :ty switch (enum_obj.int_tag_mode) {
1318 .explicit => .fromInterned(enum_obj.int_tag_type),
1319 .auto => null,
1320 };
1321 } else if (maybe_parent_union_obj) |*union_obj| ty: {
1322 if (union_obj.is_reified) {
1323 // Reification has no equivalent of 'union(enum(T))'.
1324 break :ty null;
1325 }
1326 const zir_union = sema.code.getUnionDecl(zir_index);
1327 if (zir_union.kind != .tagged_enum_explicit) {
1328 break :ty null; // int tag type will be inferred
1329 }
1330 // Explicitly specified, so evaluate the int tag type expression.
1331 const tag_type_body = zir_union.arg_type_body.?;
1332 const tag_type_src = block.src(.container_arg);
1333 block.comptime_reason = .{ .reason = .{
1334 .src = tag_type_src,
1335 .r = .{ .simple = .enum_int_tag_type },
1336 } };
1337 const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index);
1338 break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref);
1339 } else ty: {
1340 const zir_enum = sema.code.getEnumDecl(zir_index);
1341 const tag_type_body = zir_enum.tag_type_body orelse {
1342 break :ty null; // int tag type will be inferred
1343 };
1344 // Explicitly specified, so evaluate the int tag type expression.
1345 const tag_type_src = block.src(.container_arg);
1346 block.comptime_reason = .{ .reason = .{
1347 .src = tag_type_src,
1348 .r = .{ .simple = .enum_int_tag_type },
1349 } };
1350 const type_ref = try sema.resolveInlineBody(&block, tag_type_body, zir_index);
1351 break :ty try sema.analyzeAsType(&block, tag_type_src, .enum_int_tag_type, type_ref);
1352 };
1353 const empty_exhaustive = enum_obj.field_names.len == 0 and !enum_obj.nonexhaustive;
1354 const int_tag_ty: Type = if (explicit_int_tag_ty) |int_tag_ty| ty: {
1355 switch (int_tag_ty.zigTypeTag(zcu)) {
1356 .int => if (empty_exhaustive) return sema.fail(
1357 &block,
1358 block.src(.container_arg),
1359 "empty exhaustive enums must be backed by 'noreturn'",
1360 .{},
1361 ),
1362 .noreturn => if (!empty_exhaustive) return sema.fail(
1363 &block,
1364 block.src(.container_arg),
1365 "non-empty enums cannot be backed by 'noreturn'",
1366 .{},
1367 ),
1368 else => return sema.fail(
1369 &block,
1370 block.src(.container_arg),
1371 "expected integer tag type, found '{f}'",
1372 .{int_tag_ty.fmt(pt)},
1373 ),
1374 }
1375 break :ty int_tag_ty;
1376 } else ty: {
1377 if (empty_exhaustive) break :ty .noreturn;
1378 // Infer the int tag type from the field count
1379 const bits = Type.smallestUnsignedBits(enum_obj.field_names.len -| 1);
1380 break :ty try pt.intType(.unsigned, bits);
1381 };
1382
1383 ip.resolveEnumLayout(io, enum_ty.toIntern(), int_tag_ty.toIntern());
1384
1385 // Finally, deal with field values. For declared types we need to analyze the expressions, while
1386 // reified types already have them populated; but either way, we need to populate the hash map
1387 // (and validate the values along the way).
1388
1389 // We'll populate this map.
1390 const field_value_map = enum_obj.field_value_map.unwrap() orelse {
1391 // The enum is auto-numbered with an inferred tag type. We know that the tag type generated
1392 // earlier is sufficient for the number of fields, so we have nothing more to do.
1393 assert(enum_obj.int_tag_mode == .auto);
1394 return;
1395 };
1396
1397 // There may be old field values in here from a previous update.
1398 field_value_map.get(ip).clearRetainingCapacity();
1399
1400 // Map the enum (or union) decl instruction to provide the tag type as the result type
1401 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{zir_index});
1402 sema.inst_map.putAssumeCapacity(zir_index, .fromIntern(int_tag_ty.toIntern()));
1403 defer assert(sema.inst_map.remove(zir_index));
1404
1405 // First, populate any explicitly provided values. This is the part that actually depends on
1406 // the ZIR, and hence depends on whether this is a declared or generated enum. If any explicit
1407 // value is straight-up invalid, we'll emit an error here.
1408 if (maybe_parent_union_obj) |union_obj| {
1409 if (union_obj.is_reified) {
1410 // Generated tag type for reified union; values already populated.
1411 } else {
1412 // Generated tag type for declared union; evaluate the expressions given in the union declaration.
1413 const zir_union = sema.code.getUnionDecl(zir_index);
1414 var field_it = zir_union.iterateFields();
1415 while (field_it.next()) |zir_field| {
1416 const field_val_src = block.src(.{ .container_field_value = zir_field.idx });
1417 block.comptime_reason = .{ .reason = .{
1418 .src = field_val_src,
1419 .r = .{ .simple = .enum_field_values },
1420 } };
1421 const value_body = zir_field.value_body orelse {
1422 enum_obj.field_values.get(ip)[zir_field.idx] = .none;
1423 continue;
1424 };
1425 const uncoerced = try sema.resolveInlineBody(&block, value_body, zir_index);
1426 const coerced = try sema.coerce(&block, int_tag_ty, uncoerced, field_val_src);
1427 const val = try sema.resolveConstValue(&block, field_val_src, coerced, null);
1428 enum_obj.field_values.get(ip)[zir_field.idx] = val.toIntern();
1429 }
1430 }
1431 } else if (enum_obj.is_reified) {
1432 // Reified enum; values already populated.
1433 } else {
1434 // Declared enum; evaluate the expressions given in the enum declaration.
1435 const zir_enum = sema.code.getEnumDecl(zir_index);
1436 var field_it = zir_enum.iterateFields();
1437 while (field_it.next()) |zir_field| {
1438 const field_val_src = block.src(.{ .container_field_value = zir_field.idx });
1439 block.comptime_reason = .{ .reason = .{
1440 .src = field_val_src,
1441 .r = .{ .simple = .enum_field_values },
1442 } };
1443 const value_body = zir_field.value_body orelse {
1444 enum_obj.field_values.get(ip)[zir_field.idx] = .none;
1445 continue;
1446 };
1447 const uncoerced = try sema.resolveInlineBody(&block, value_body, zir_index);
1448 const coerced = try sema.coerce(&block, int_tag_ty, uncoerced, field_val_src);
1449 const val = try sema.resolveConstDefinedValue(&block, field_val_src, coerced, null);
1450 enum_obj.field_values.get(ip)[zir_field.idx] = val.toIntern();
1451 }
1452 }
1453
1454 // Explicit values are set. Now we'll go through the whole array and figure out the final
1455 // field values. This is also where we'll detect duplicates.
1456
1457 for (0..enum_obj.field_names.len) |field_idx| {
1458 const field_val_src = block.src(.{ .container_field_value = @intCast(field_idx) });
1459 // If the field value was not specified, compute the implicit value.
1460 const field_val = val: {
1461 const explicit_val = enum_obj.field_values.get(ip)[field_idx];
1462 if (explicit_val != .none) {
1463 assert(ip.typeOf(explicit_val) == int_tag_ty.toIntern());
1464 break :val explicit_val;
1465 }
1466 if (field_idx == 0) {
1467 // Implicit value is 0, which is valid for every integer type.
1468 const val = (try pt.intValue(int_tag_ty, 0)).toIntern();
1469 enum_obj.field_values.get(ip)[field_idx] = val;
1470 break :val val;
1471 }
1472 // Implicit non-initial value: take the previous field value and add one.
1473 const prev_field_val: Value = .fromInterned(enum_obj.field_values.get(ip)[field_idx - 1]);
1474 const result = try arith.incrementDefinedInt(sema, int_tag_ty, prev_field_val);
1475 if (result.overflow) return sema.fail(
1476 &block,
1477 field_val_src,
1478 "enum tag value '{f}' too large for type '{f}'",
1479 .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) },
1480 );
1481 const val = result.val.toIntern();
1482 enum_obj.field_values.get(ip)[field_idx] = val;
1483 break :val val;
1484 };
1485 if (ip.addFieldTagValue(enum_obj.field_values, field_value_map, field_val)) |prev_field_index| {
1486 return sema.failWithOwnedErrorMsg(&block, msg: {
1487 const prev_field_val_src = block.src(.{ .container_field_value = prev_field_index });
1488 const msg = try sema.errMsg(field_val_src, "enum tag value '{f}' for field '{f}' already taken", .{
1489 Value.fromInterned(field_val).fmtValueSema(pt, sema),
1490 enum_obj.field_names.get(ip)[field_idx].fmt(ip),
1491 });
1492 errdefer msg.destroy(gpa);
1493 try sema.errNote(prev_field_val_src, msg, "previous occurrence in field '{f}'", .{
1494 enum_obj.field_names.get(ip)[prev_field_index].fmt(ip),
1495 });
1496 break :msg msg;
1497 });
1498 }
1499 }
1500
1501 if (enum_obj.nonexhaustive) {
1502 const fields_len = enum_obj.field_names.len;
1503 if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) {
1504 return sema.fail(&block, block.nodeOffset(.zero), "non-exhaustive enum specifies every value", .{});
1505 }
1506 }
1507}