master
  1const std = @import("std");
  2const Zcu = @import("../Zcu.zig");
  3const Sema = @import("../Sema.zig");
  4const Air = @import("../Air.zig");
  5const InternPool = @import("../InternPool.zig");
  6const Type = @import("../Type.zig");
  7const Value = @import("../Value.zig");
  8const Zir = std.zig.Zir;
  9const AstGen = std.zig.AstGen;
 10const CompileError = Zcu.CompileError;
 11const Ast = std.zig.Ast;
 12const Allocator = std.mem.Allocator;
 13const assert = std.debug.assert;
 14const File = Zcu.File;
 15const LazySrcLoc = Zcu.LazySrcLoc;
 16const Ref = std.zig.Zir.Inst.Ref;
 17const NullTerminatedString = InternPool.NullTerminatedString;
 18const NumberLiteralError = std.zig.number_literal.Error;
 19const NodeIndex = std.zig.Ast.Node.Index;
 20const Zoir = std.zig.Zoir;
 21
 22const LowerZon = @This();
 23
 24sema: *Sema,
 25file: *File,
 26file_index: Zcu.File.Index,
 27import_loc: LazySrcLoc,
 28block: *Sema.Block,
 29base_node_inst: InternPool.TrackedInst.Index,
 30
 31/// Lowers the given file as ZON.
 32pub fn run(
 33    sema: *Sema,
 34    file: *File,
 35    file_index: Zcu.File.Index,
 36    res_ty_interned: InternPool.Index,
 37    import_loc: LazySrcLoc,
 38    block: *Sema.Block,
 39) CompileError!InternPool.Index {
 40    const pt = sema.pt;
 41
 42    const tracked_inst = try pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{
 43        .file = file_index,
 44        .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file
 45    });
 46
 47    var lower_zon: LowerZon = .{
 48        .sema = sema,
 49        .file = file,
 50        .file_index = file_index,
 51        .import_loc = import_loc,
 52        .block = block,
 53        .base_node_inst = tracked_inst,
 54    };
 55
 56    if (res_ty_interned == .none) {
 57        return lower_zon.lowerExprAnonResTy(.root);
 58    } else {
 59        const res_ty: Type = .fromInterned(res_ty_interned);
 60        try lower_zon.checkType(res_ty);
 61        return lower_zon.lowerExprKnownResTy(.root, res_ty);
 62    }
 63}
 64
 65fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!InternPool.Index {
 66    const gpa = self.sema.gpa;
 67    const pt = self.sema.pt;
 68    const ip = &pt.zcu.intern_pool;
 69    switch (node.get(self.file.zoir.?)) {
 70        .true => return .bool_true,
 71        .false => return .bool_false,
 72        .null => return .null_value,
 73        .pos_inf => return self.fail(node, "infinity requires a known result type", .{}),
 74        .neg_inf => return self.fail(node, "negative infinity requires a known result type", .{}),
 75        .nan => return self.fail(node, "NaN requires a known result type", .{}),
 76        .int_literal => |int| switch (int) {
 77            .small => |val| return pt.intern(.{ .int = .{
 78                .ty = .comptime_int_type,
 79                .storage = .{ .i64 = val },
 80            } }),
 81            .big => |val| return pt.intern(.{ .int = .{
 82                .ty = .comptime_int_type,
 83                .storage = .{ .big_int = val },
 84            } }),
 85        },
 86        .float_literal => |val| {
 87            const result = try pt.floatValue(.comptime_float, val);
 88            return result.toIntern();
 89        },
 90        .char_literal => |val| return pt.intern(.{ .int = .{
 91            .ty = .comptime_int_type,
 92            .storage = .{ .i64 = val },
 93        } }),
 94        .enum_literal => |val| return pt.intern(.{
 95            .enum_literal = try ip.getOrPutString(
 96                gpa,
 97                pt.tid,
 98                val.get(self.file.zoir.?),
 99                .no_embedded_nulls,
100            ),
101        }),
102        .string_literal => |val| {
103            const ip_str = try ip.getOrPutString(gpa, pt.tid, val, .maybe_embedded_nulls);
104            const result = try self.sema.addStrLit(ip_str, val.len);
105            return result.toInterned().?;
106        },
107        .empty_literal => return .empty_tuple,
108        .array_literal => |nodes| {
109            const types = try self.sema.arena.alloc(InternPool.Index, nodes.len);
110            const values = try self.sema.arena.alloc(InternPool.Index, nodes.len);
111            for (0..nodes.len) |i| {
112                values[i] = try self.lowerExprAnonResTy(nodes.at(@intCast(i)));
113                types[i] = Value.fromInterned(values[i]).typeOf(pt.zcu).toIntern();
114            }
115            const ty = try ip.getTupleType(
116                gpa,
117                pt.tid,
118                .{
119                    .types = types,
120                    .values = values,
121                },
122            );
123            return (try pt.aggregateValue(.fromInterned(ty), values)).toIntern();
124        },
125        .struct_literal => |init| {
126            const elems = try self.sema.arena.alloc(InternPool.Index, init.names.len);
127            for (0..init.names.len) |i| {
128                elems[i] = try self.lowerExprAnonResTy(init.vals.at(@intCast(i)));
129            }
130            const struct_ty = switch (try ip.getStructType(
131                gpa,
132                pt.tid,
133                .{
134                    .layout = .auto,
135                    .fields_len = @intCast(init.names.len),
136                    .known_non_opv = false,
137                    .requires_comptime = .no,
138                    .any_comptime_fields = true,
139                    .any_default_inits = true,
140                    .inits_resolved = true,
141                    .any_aligned_fields = false,
142                    .key = .{ .reified = .{
143                        .zir_index = self.base_node_inst,
144                        .type_hash = hash: {
145                            var hasher: std.hash.Wyhash = .init(0);
146                            hasher.update(std.mem.asBytes(&node));
147                            hasher.update(std.mem.sliceAsBytes(elems));
148                            hasher.update(std.mem.sliceAsBytes(init.names));
149                            break :hash hasher.final();
150                        },
151                    } },
152                },
153                false,
154            )) {
155                .wip => |wip| ty: {
156                    errdefer wip.cancel(ip, pt.tid);
157                    const type_name = try self.sema.createTypeName(
158                        self.block,
159                        .anon,
160                        "struct",
161                        self.base_node_inst.resolve(ip),
162                        wip.index,
163                    );
164                    wip.setName(ip, type_name.name, type_name.nav);
165
166                    const struct_type = ip.loadStructType(wip.index);
167
168                    for (init.names, 0..) |name, field_idx| {
169                        const name_interned = try ip.getOrPutString(
170                            gpa,
171                            pt.tid,
172                            name.get(self.file.zoir.?),
173                            .no_embedded_nulls,
174                        );
175                        assert(struct_type.addFieldName(ip, name_interned) == null);
176                        struct_type.setFieldComptime(ip, field_idx);
177                    }
178
179                    @memcpy(struct_type.field_inits.get(ip), elems);
180                    const types = struct_type.field_types.get(ip);
181                    for (0..init.names.len) |i| {
182                        types[i] = Value.fromInterned(elems[i]).typeOf(pt.zcu).toIntern();
183                    }
184
185                    const new_namespace_index = try pt.createNamespace(.{
186                        .parent = self.block.namespace.toOptional(),
187                        .owner_type = wip.index,
188                        .file_scope = self.block.getFileScopeIndex(pt.zcu),
189                        .generation = pt.zcu.generation,
190                    });
191                    try pt.zcu.comp.queueJob(.{ .resolve_type_fully = wip.index });
192                    codegen_type: {
193                        if (pt.zcu.comp.config.use_llvm) break :codegen_type;
194                        if (self.block.ownerModule().strip) break :codegen_type;
195                        pt.zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
196                        try pt.zcu.comp.queueJob(.{ .link_type = wip.index });
197                    }
198                    break :ty wip.finish(ip, new_namespace_index);
199                },
200                .existing => |ty| ty,
201            };
202            try self.sema.declareDependency(.{ .interned = struct_ty });
203            try self.sema.addTypeReferenceEntry(self.nodeSrc(node), struct_ty);
204
205            return (try pt.aggregateValue(.fromInterned(struct_ty), elems)).toIntern();
206        },
207    }
208}
209
210/// Validate that `ty` is a valid ZON type, or emit a compile error.
211///
212/// Rules out nested optionals, error sets, etc.
213fn checkType(self: *LowerZon, ty: Type) !void {
214    var visited: std.AutoHashMapUnmanaged(InternPool.Index, void) = .empty;
215    try self.checkTypeInner(ty, null, &visited);
216}
217
218fn checkTypeInner(
219    self: *LowerZon,
220    ty: Type,
221    parent_opt_ty: ?Type,
222    /// Visited structs and unions (not tuples). These are tracked because they are the only way in
223    /// which a type can be self-referential, so must be tracked to avoid loops. Tracking more types
224    /// consumes memory unnecessarily, and would be complicated by optionals.
225    /// Allocated into `self.sema.arena`.
226    visited: *std.AutoHashMapUnmanaged(InternPool.Index, void),
227) !void {
228    const sema = self.sema;
229    const pt = sema.pt;
230    const zcu = pt.zcu;
231    const ip = &zcu.intern_pool;
232
233    switch (ty.zigTypeTag(zcu)) {
234        .bool,
235        .int,
236        .float,
237        .null,
238        .@"enum",
239        .comptime_float,
240        .comptime_int,
241        .enum_literal,
242        => {},
243
244        .noreturn,
245        .void,
246        .type,
247        .undefined,
248        .error_union,
249        .error_set,
250        .@"fn",
251        .frame,
252        .@"anyframe",
253        .@"opaque",
254        => return self.failUnsupportedResultType(ty, null),
255
256        .pointer => {
257            const ptr_info = ty.ptrInfo(zcu);
258            if (!ptr_info.flags.is_const) {
259                return self.failUnsupportedResultType(
260                    ty,
261                    "ZON does not allow mutable pointers",
262                );
263            }
264            switch (ptr_info.flags.size) {
265                .one => try self.checkTypeInner(
266                    .fromInterned(ptr_info.child),
267                    parent_opt_ty, // preserved
268                    visited,
269                ),
270                .slice => try self.checkTypeInner(
271                    .fromInterned(ptr_info.child),
272                    null,
273                    visited,
274                ),
275                .many => return self.failUnsupportedResultType(ty, "ZON does not allow many-pointers"),
276                .c => return self.failUnsupportedResultType(ty, "ZON does not allow C pointers"),
277            }
278        },
279        .optional => if (parent_opt_ty) |p| {
280            return self.failUnsupportedResultType(p, "ZON does not allow nested optionals");
281        } else try self.checkTypeInner(
282            ty.optionalChild(zcu),
283            ty,
284            visited,
285        ),
286        .array, .vector => {
287            try self.checkTypeInner(ty.childType(zcu), null, visited);
288        },
289        .@"struct" => if (ty.isTuple(zcu)) {
290            const tuple_info = ip.indexToKey(ty.toIntern()).tuple_type;
291            const field_types = tuple_info.types.get(ip);
292            for (field_types) |field_type| {
293                try self.checkTypeInner(.fromInterned(field_type), null, visited);
294            }
295        } else {
296            const gop = try visited.getOrPut(sema.arena, ty.toIntern());
297            if (gop.found_existing) return;
298            try ty.resolveFields(pt);
299            const struct_info = zcu.typeToStruct(ty).?;
300            for (struct_info.field_types.get(ip)) |field_type| {
301                try self.checkTypeInner(.fromInterned(field_type), null, visited);
302            }
303        },
304        .@"union" => {
305            const gop = try visited.getOrPut(sema.arena, ty.toIntern());
306            if (gop.found_existing) return;
307            try ty.resolveFields(pt);
308            const union_info = zcu.typeToUnion(ty).?;
309            for (union_info.field_types.get(ip)) |field_type| {
310                if (field_type != .void_type) {
311                    try self.checkTypeInner(.fromInterned(field_type), null, visited);
312                }
313            }
314        },
315    }
316}
317
318fn nodeSrc(self: *LowerZon, node: Zoir.Node.Index) LazySrcLoc {
319    return .{
320        .base_node_inst = self.base_node_inst,
321        .offset = .{ .node_abs = node.getAstNode(self.file.zoir.?) },
322    };
323}
324
325fn failUnsupportedResultType(
326    self: *LowerZon,
327    ty: Type,
328    opt_note: ?[]const u8,
329) error{ AnalysisFail, OutOfMemory } {
330    @branchHint(.cold);
331    const sema = self.sema;
332    const gpa = sema.gpa;
333    const pt = sema.pt;
334    return sema.failWithOwnedErrorMsg(self.block, msg: {
335        const msg = try sema.errMsg(self.import_loc, "type '{f}' is not available in ZON", .{ty.fmt(pt)});
336        errdefer msg.destroy(gpa);
337        if (opt_note) |n| try sema.errNote(self.import_loc, msg, "{s}", .{n});
338        break :msg msg;
339    });
340}
341
342fn fail(
343    self: *LowerZon,
344    node: Zoir.Node.Index,
345    comptime format: []const u8,
346    args: anytype,
347) error{ AnalysisFail, OutOfMemory } {
348    @branchHint(.cold);
349    const err_msg = try Zcu.ErrorMsg.create(self.sema.pt.zcu.gpa, self.nodeSrc(node), format, args);
350    try self.sema.pt.zcu.errNote(self.import_loc, err_msg, "imported here", .{});
351    return self.sema.failWithOwnedErrorMsg(self.block, err_msg);
352}
353
354fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {
355    const pt = self.sema.pt;
356    return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) {
357        error.WrongType => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(pt)}),
358        else => |e| return e,
359    };
360}
361
362fn lowerExprKnownResTyInner(
363    self: *LowerZon,
364    node: Zoir.Node.Index,
365    res_ty: Type,
366) (CompileError || error{WrongType})!InternPool.Index {
367    const pt = self.sema.pt;
368    switch (res_ty.zigTypeTag(pt.zcu)) {
369        .optional => return pt.intern(.{
370            .opt = .{
371                .ty = res_ty.toIntern(),
372                .val = if (node.get(self.file.zoir.?) == .null) b: {
373                    break :b .none;
374                } else b: {
375                    const child_type = res_ty.optionalChild(pt.zcu);
376                    break :b try self.lowerExprKnownResTyInner(node, child_type);
377                },
378            },
379        }),
380        .pointer => {
381            const ptr_info = res_ty.ptrInfo(pt.zcu);
382            switch (ptr_info.flags.size) {
383                .one => return pt.intern(.{ .ptr = .{
384                    .ty = res_ty.toIntern(),
385                    .base_addr = .{
386                        .uav = .{
387                            .orig_ty = res_ty.toIntern(),
388                            .val = try self.lowerExprKnownResTyInner(node, .fromInterned(ptr_info.child)),
389                        },
390                    },
391                    .byte_offset = 0,
392                } }),
393                .slice => return self.lowerSlice(node, res_ty),
394                else => {
395                    // Unsupported pointer type, checked in `lower`
396                    unreachable;
397                },
398            }
399        },
400        .bool => return self.lowerBool(node),
401        .int, .comptime_int => return self.lowerInt(node, res_ty),
402        .float, .comptime_float => return self.lowerFloat(node, res_ty),
403        .null => return self.lowerNull(node),
404        .@"enum" => return self.lowerEnum(node, res_ty),
405        .enum_literal => return self.lowerEnumLiteral(node),
406        .array => return self.lowerArray(node, res_ty),
407        .@"struct" => return self.lowerStructOrTuple(node, res_ty),
408        .@"union" => return self.lowerUnion(node, res_ty),
409        .vector => return self.lowerVector(node, res_ty),
410
411        .type,
412        .noreturn,
413        .undefined,
414        .error_union,
415        .error_set,
416        .@"fn",
417        .@"opaque",
418        .frame,
419        .@"anyframe",
420        .void,
421        => return self.fail(node, "type '{f}' not available in ZON", .{res_ty.fmt(pt)}),
422    }
423}
424
425fn lowerBool(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
426    return switch (node.get(self.file.zoir.?)) {
427        .true => .bool_true,
428        .false => .bool_false,
429        else => return error.WrongType,
430    };
431}
432
433fn lowerInt(
434    self: *LowerZon,
435    node: Zoir.Node.Index,
436    res_ty: Type,
437) !InternPool.Index {
438    @setFloatMode(.strict);
439    return switch (node.get(self.file.zoir.?)) {
440        .int_literal => |int| switch (int) {
441            .small => |val| {
442                const rhs: i32 = val;
443
444                // If our result is a fixed size integer, check that our value is not out of bounds
445                if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
446                    const lhs_info = res_ty.intInfo(self.sema.pt.zcu);
447
448                    // If lhs is unsigned and rhs is less than 0, we're out of bounds
449                    if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail(
450                        node,
451                        "type '{f}' cannot represent integer value '{d}'",
452                        .{ res_ty.fmt(self.sema.pt), rhs },
453                    );
454
455                    // If lhs has less than the 32 bits rhs can hold, we need to check the max and
456                    // min values
457                    if (std.math.cast(u5, lhs_info.bits)) |bits| {
458                        const min_int: i32 = if (lhs_info.signedness == .unsigned or bits == 0) b: {
459                            break :b 0;
460                        } else b: {
461                            break :b -(@as(i32, 1) << (bits - 1));
462                        };
463                        const max_int: i32 = if (bits == 0) b: {
464                            break :b 0;
465                        } else b: {
466                            break :b (@as(i32, 1) << (bits - @intFromBool(lhs_info.signedness == .signed))) - 1;
467                        };
468                        if (rhs < min_int or rhs > max_int) {
469                            return self.fail(
470                                node,
471                                "type '{f}' cannot represent integer value '{d}'",
472                                .{ res_ty.fmt(self.sema.pt), rhs },
473                            );
474                        }
475                    }
476                }
477
478                return self.sema.pt.intern(.{ .int = .{
479                    .ty = res_ty.toIntern(),
480                    .storage = .{ .i64 = rhs },
481                } });
482            },
483            .big => |val| {
484                if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
485                    const int_info = res_ty.intInfo(self.sema.pt.zcu);
486                    if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) {
487                        return self.fail(
488                            node,
489                            "type '{f}' cannot represent integer value '{d}'",
490                            .{ res_ty.fmt(self.sema.pt), val },
491                        );
492                    }
493                }
494
495                return self.sema.pt.intern(.{ .int = .{
496                    .ty = res_ty.toIntern(),
497                    .storage = .{ .big_int = val },
498                } });
499            },
500        },
501        .float_literal => |val| {
502            var big_int: std.math.big.int.Mutable = .{
503                .limbs = try self.sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(val)),
504                .len = undefined,
505                .positive = undefined,
506            };
507            switch (big_int.setFloat(val, .trunc)) {
508                .inexact => return self.fail(
509                    node,
510                    "fractional component prevents float value '{d}' from coercion to type '{f}'",
511                    .{ val, res_ty.fmt(self.sema.pt) },
512                ),
513                .exact => {},
514            }
515
516            // Check that the result is in range of the result type
517            const int_info = res_ty.intInfo(self.sema.pt.zcu);
518            if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) {
519                return self.fail(
520                    node,
521                    "type '{f}' cannot represent integer value '{d}'",
522                    .{ res_ty.fmt(self.sema.pt), val },
523                );
524            }
525
526            return self.sema.pt.intern(.{
527                .int = .{
528                    .ty = res_ty.toIntern(),
529                    .storage = .{ .big_int = big_int.toConst() },
530                },
531            });
532        },
533        .char_literal => |val| {
534            // If our result is a fixed size integer, check that our value is not out of bounds
535            if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
536                const dest_info = res_ty.intInfo(self.sema.pt.zcu);
537                const unsigned_bits = dest_info.bits - @intFromBool(dest_info.signedness == .signed);
538                if (unsigned_bits < 21) {
539                    const out_of_range: u21 = @as(u21, 1) << @intCast(unsigned_bits);
540                    if (val >= out_of_range) {
541                        return self.fail(
542                            node,
543                            "type '{f}' cannot represent integer value '{d}'",
544                            .{ res_ty.fmt(self.sema.pt), val },
545                        );
546                    }
547                }
548            }
549            return self.sema.pt.intern(.{
550                .int = .{
551                    .ty = res_ty.toIntern(),
552                    .storage = .{ .i64 = val },
553                },
554            });
555        },
556
557        else => return error.WrongType,
558    };
559}
560
561fn lowerFloat(
562    self: *LowerZon,
563    node: Zoir.Node.Index,
564    res_ty: Type,
565) !InternPool.Index {
566    @setFloatMode(.strict);
567    const value = switch (node.get(self.file.zoir.?)) {
568        .int_literal => |int| switch (int) {
569            .small => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))),
570            .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128, .nearest_even)[0]),
571        },
572        .float_literal => |val| try self.sema.pt.floatValue(res_ty, val),
573        .char_literal => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))),
574        .pos_inf => b: {
575            if (res_ty.toIntern() == .comptime_float_type) return self.fail(
576                node,
577                "expected type '{f}'",
578                .{res_ty.fmt(self.sema.pt)},
579            );
580            break :b try self.sema.pt.floatValue(res_ty, std.math.inf(f128));
581        },
582        .neg_inf => b: {
583            if (res_ty.toIntern() == .comptime_float_type) return self.fail(
584                node,
585                "expected type '{f}'",
586                .{res_ty.fmt(self.sema.pt)},
587            );
588            break :b try self.sema.pt.floatValue(res_ty, -std.math.inf(f128));
589        },
590        .nan => b: {
591            if (res_ty.toIntern() == .comptime_float_type) return self.fail(
592                node,
593                "expected type '{f}'",
594                .{res_ty.fmt(self.sema.pt)},
595            );
596            break :b try self.sema.pt.floatValue(res_ty, std.math.nan(f128));
597        },
598        else => return error.WrongType,
599    };
600    return value.toIntern();
601}
602
603fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
604    switch (node.get(self.file.zoir.?)) {
605        .null => return .null_value,
606        else => return error.WrongType,
607    }
608}
609
610fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
611    const array_info = res_ty.arrayInfo(self.sema.pt.zcu);
612    const nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
613        .array_literal => |nodes| nodes,
614        .empty_literal => .{ .start = node, .len = 0 },
615        else => return error.WrongType,
616    };
617
618    if (nodes.len != array_info.len) {
619        return error.WrongType;
620    }
621
622    const elems = try self.sema.arena.alloc(
623        InternPool.Index,
624        nodes.len + @intFromBool(array_info.sentinel != null),
625    );
626
627    for (0..nodes.len) |i| {
628        elems[i] = try self.lowerExprKnownResTy(nodes.at(@intCast(i)), array_info.elem_type);
629    }
630
631    if (array_info.sentinel) |sentinel| {
632        elems[elems.len - 1] = sentinel.toIntern();
633    }
634
635    return (try self.sema.pt.aggregateValue(res_ty, elems)).toIntern();
636}
637
638fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
639    const ip = &self.sema.pt.zcu.intern_pool;
640    switch (node.get(self.file.zoir.?)) {
641        .enum_literal => |field_name| {
642            const field_name_interned = try ip.getOrPutString(
643                self.sema.gpa,
644                self.sema.pt.tid,
645                field_name.get(self.file.zoir.?),
646                .no_embedded_nulls,
647            );
648            const field_index = res_ty.enumFieldIndex(field_name_interned, self.sema.pt.zcu) orelse {
649                return self.fail(
650                    node,
651                    "enum {f} has no member named '{f}'",
652                    .{
653                        res_ty.fmt(self.sema.pt),
654                        std.zig.fmtId(field_name.get(self.file.zoir.?)),
655                    },
656                );
657            };
658
659            const value = try self.sema.pt.enumValueFieldIndex(res_ty, field_index);
660
661            return value.toIntern();
662        },
663        else => return error.WrongType,
664    }
665}
666
667fn lowerEnumLiteral(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
668    const ip = &self.sema.pt.zcu.intern_pool;
669    switch (node.get(self.file.zoir.?)) {
670        .enum_literal => |field_name| {
671            const field_name_interned = try ip.getOrPutString(
672                self.sema.gpa,
673                self.sema.pt.tid,
674                field_name.get(self.file.zoir.?),
675                .no_embedded_nulls,
676            );
677            return self.sema.pt.intern(.{ .enum_literal = field_name_interned });
678        },
679        else => return error.WrongType,
680    }
681}
682
683fn lowerStructOrTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
684    const ip = &self.sema.pt.zcu.intern_pool;
685    return switch (ip.indexToKey(res_ty.toIntern())) {
686        .tuple_type => self.lowerTuple(node, res_ty),
687        .struct_type => self.lowerStruct(node, res_ty),
688        else => unreachable,
689    };
690}
691
692fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
693    const ip = &self.sema.pt.zcu.intern_pool;
694
695    const tuple_info = ip.indexToKey(res_ty.toIntern()).tuple_type;
696
697    const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
698        .array_literal => |nodes| nodes,
699        .empty_literal => .{ .start = node, .len = 0 },
700        else => return error.WrongType,
701    };
702
703    const field_types = tuple_info.types.get(ip);
704    const elems = try self.sema.arena.alloc(InternPool.Index, field_types.len);
705
706    const field_comptime_vals = tuple_info.values.get(ip);
707    if (field_comptime_vals.len > 0) {
708        @memcpy(elems, field_comptime_vals);
709    } else {
710        @memset(elems, .none);
711    }
712
713    for (0..elem_nodes.len) |i| {
714        if (i >= elems.len) {
715            const elem_node = elem_nodes.at(@intCast(i));
716            return self.fail(
717                elem_node,
718                "index {} outside tuple of length {}",
719                .{
720                    elems.len,
721                    elem_nodes.at(@intCast(i)).getAstNode(self.file.zoir.?),
722                },
723            );
724        }
725
726        const val = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i]));
727
728        if (elems[i] != .none and val != elems[i]) {
729            const elem_node = elem_nodes.at(@intCast(i));
730            return self.fail(
731                elem_node,
732                "value stored in comptime field does not match the default value of the field",
733                .{},
734            );
735        }
736
737        elems[i] = val;
738    }
739
740    for (elems, 0..) |val, i| {
741        if (val == .none) {
742            return self.fail(node, "missing tuple field with index {}", .{i});
743        }
744    }
745
746    return (try self.sema.pt.aggregateValue(res_ty, elems)).toIntern();
747}
748
749fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
750    const ip = &self.sema.pt.zcu.intern_pool;
751    const gpa = self.sema.gpa;
752
753    try res_ty.resolveFields(self.sema.pt);
754    try res_ty.resolveStructFieldInits(self.sema.pt);
755    const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?;
756
757    const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
758        .struct_literal => |fields| fields,
759        .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } },
760        else => return error.WrongType,
761    };
762
763    const field_values = try self.sema.arena.alloc(InternPool.Index, struct_info.field_names.len);
764
765    const field_defaults = struct_info.field_inits.get(ip);
766    if (field_defaults.len > 0) {
767        @memcpy(field_values, field_defaults);
768    } else {
769        @memset(field_values, .none);
770    }
771
772    for (0..fields.names.len) |i| {
773        const field_name = try ip.getOrPutString(
774            gpa,
775            self.sema.pt.tid,
776            fields.names[i].get(self.file.zoir.?),
777            .no_embedded_nulls,
778        );
779        const field_node = fields.vals.at(@intCast(i));
780
781        const name_index = struct_info.nameIndex(ip, field_name) orelse {
782            return self.fail(field_node, "unexpected field '{f}'", .{field_name.fmt(ip)});
783        };
784
785        const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]);
786        field_values[name_index] = try self.lowerExprKnownResTy(field_node, field_type);
787
788        if (struct_info.comptime_bits.getBit(ip, name_index)) {
789            const val = ip.indexToKey(field_values[name_index]);
790            const default = ip.indexToKey(field_defaults[name_index]);
791            if (!val.eql(default, ip)) {
792                return self.fail(
793                    field_node,
794                    "value stored in comptime field does not match the default value of the field",
795                    .{},
796                );
797            }
798        }
799    }
800
801    const field_names = struct_info.field_names.get(ip);
802    for (field_values, field_names) |*value, name| {
803        if (value.* == .none) return self.fail(node, "missing field '{f}'", .{name.fmt(ip)});
804    }
805
806    return (try self.sema.pt.aggregateValue(res_ty, field_values)).toIntern();
807}
808
809fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
810    const ip = &self.sema.pt.zcu.intern_pool;
811    const gpa = self.sema.gpa;
812
813    const ptr_info = res_ty.ptrInfo(self.sema.pt.zcu);
814
815    assert(ptr_info.flags.size == .slice);
816
817    // String literals
818    const string_alignment = ptr_info.flags.alignment == .none or ptr_info.flags.alignment == .@"1";
819    const string_sentinel = ptr_info.sentinel == .none or ptr_info.sentinel == .zero_u8;
820    if (string_alignment and ptr_info.child == .u8_type and string_sentinel) {
821        switch (node.get(self.file.zoir.?)) {
822            .string_literal => |val| {
823                const ip_str = try ip.getOrPutString(gpa, self.sema.pt.tid, val, .maybe_embedded_nulls);
824                const str_ref = try self.sema.addStrLit(ip_str, val.len);
825                return (try self.sema.coerce(
826                    self.block,
827                    res_ty,
828                    str_ref,
829                    self.nodeSrc(node),
830                )).toInterned().?;
831            },
832            else => {},
833        }
834    }
835
836    // Slice literals
837    const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
838        .array_literal => |nodes| nodes,
839        .empty_literal => .{ .start = node, .len = 0 },
840        else => return error.WrongType,
841    };
842
843    const elems = try self.sema.arena.alloc(InternPool.Index, elem_nodes.len + @intFromBool(ptr_info.sentinel != .none));
844
845    for (elems, 0..) |*elem, i| {
846        elem.* = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child));
847    }
848
849    if (ptr_info.sentinel != .none) {
850        elems[elems.len - 1] = ptr_info.sentinel;
851    }
852
853    const array_ty = try self.sema.pt.arrayType(.{
854        .len = elems.len,
855        .sentinel = ptr_info.sentinel,
856        .child = ptr_info.child,
857    });
858
859    const array_val = try self.sema.pt.aggregateValue(array_ty, elems);
860
861    const many_item_ptr_type = try self.sema.pt.intern(.{ .ptr_type = .{
862        .child = ptr_info.child,
863        .sentinel = ptr_info.sentinel,
864        .flags = b: {
865            var flags = ptr_info.flags;
866            flags.size = .many;
867            break :b flags;
868        },
869        .packed_offset = ptr_info.packed_offset,
870    } });
871
872    const many_item_ptr = try self.sema.pt.intern(.{
873        .ptr = .{
874            .ty = many_item_ptr_type,
875            .base_addr = .{
876                .uav = .{
877                    .orig_ty = (try self.sema.pt.singleConstPtrType(array_ty)).toIntern(),
878                    .val = array_val.toIntern(),
879                },
880            },
881            .byte_offset = 0,
882        },
883    });
884
885    const len = (try self.sema.pt.intValue(.usize, elems.len)).toIntern();
886
887    return self.sema.pt.intern(.{ .slice = .{
888        .ty = res_ty.toIntern(),
889        .ptr = many_item_ptr,
890        .len = len,
891    } });
892}
893
894fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
895    const ip = &self.sema.pt.zcu.intern_pool;
896    try res_ty.resolveFields(self.sema.pt);
897    const union_info = self.sema.pt.zcu.typeToUnion(res_ty).?;
898    const enum_tag_info = union_info.loadTagType(ip);
899
900    const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) {
901        .enum_literal => |name| b: {
902            const field_name = try ip.getOrPutString(
903                self.sema.gpa,
904                self.sema.pt.tid,
905                name.get(self.file.zoir.?),
906                .no_embedded_nulls,
907            );
908            break :b .{ field_name, null };
909        },
910        .struct_literal => b: {
911            const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
912                .struct_literal => |fields| fields,
913                else => return self.fail(node, "expected type '{f}'", .{res_ty.fmt(self.sema.pt)}),
914            };
915            if (fields.names.len != 1) {
916                return error.WrongType;
917            }
918            const field_name = try ip.getOrPutString(
919                self.sema.gpa,
920                self.sema.pt.tid,
921                fields.names[0].get(self.file.zoir.?),
922                .no_embedded_nulls,
923            );
924            break :b .{ field_name, fields.vals.at(0) };
925        },
926        else => return error.WrongType,
927    };
928
929    const name_index = enum_tag_info.nameIndex(ip, field_name) orelse {
930        return error.WrongType;
931    };
932    const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_ty), name_index);
933    const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]);
934    const val = if (maybe_field_node) |field_node| b: {
935        if (field_type.toIntern() == .void_type) {
936            return self.fail(field_node, "expected type 'void'", .{});
937        }
938        break :b try self.lowerExprKnownResTy(field_node, field_type);
939    } else b: {
940        if (field_type.toIntern() != .void_type) {
941            return error.WrongType;
942        }
943        break :b .void_value;
944    };
945    return ip.getUnion(self.sema.pt.zcu.gpa, self.sema.pt.tid, .{
946        .ty = res_ty.toIntern(),
947        .tag = tag.toIntern(),
948        .val = val,
949    });
950}
951
952fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
953    const ip = &self.sema.pt.zcu.intern_pool;
954
955    const vector_info = ip.indexToKey(res_ty.toIntern()).vector_type;
956
957    const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
958        .array_literal => |nodes| nodes,
959        .empty_literal => .{ .start = node, .len = 0 },
960        else => return error.WrongType,
961    };
962
963    const elems = try self.sema.arena.alloc(InternPool.Index, vector_info.len);
964
965    if (elem_nodes.len != vector_info.len) {
966        return self.fail(
967            node,
968            "expected {} vector elements; found {}",
969            .{ vector_info.len, elem_nodes.len },
970        );
971    }
972
973    for (elems, 0..) |*elem, i| {
974        elem.* = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child));
975    }
976
977    return (try self.sema.pt.aggregateValue(res_ty, elems)).toIntern();
978}