| 1 | const std = @import("std"); |
| 2 | const assert = std.debug.assert; |
| 3 | const mem = std.mem; |
| 4 | |
| 5 | const Sema = @import("../Sema.zig"); |
| 6 | const Block = Sema.Block; |
| 7 | const Type = @import("../Type.zig"); |
| 8 | const Value = @import("../Value.zig"); |
| 9 | const Zcu = @import("../Zcu.zig"); |
| 10 | const CompileError = Zcu.CompileError; |
| 11 | const SemaError = Zcu.SemaError; |
| 12 | const LazySrcLoc = Zcu.LazySrcLoc; |
| 13 | const InternPool = @import("../InternPool.zig"); |
| 14 | const Alignment = InternPool.Alignment; |
| 15 | const arith = @import("arith.zig"); |
| 16 | const trace = @import("../tracy.zig").trace; |
| 17 | |
| 18 | pub 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. |
| 75 | pub 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 | } |
| 81 | fn 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`. |
| 155 | pub 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. |
| 180 | pub 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. |
| 495 | fn 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. |
| 607 | pub 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. |
| 663 | fn 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`. |
| 720 | pub 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 | } |
| 1034 | fn 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 | } |
| 1088 | fn 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 | |
| 1214 | pub 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 | } |