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