aboutsummaryrefslogtreecommitdiff
path: root/src/Sema
diff options
context:
space:
mode:
authorMason Remaley <mason@anthropicstudios.com>2024-11-04 14:03:36 -0800
committermlugg <mlugg@mlugg.co.uk>2025-02-03 09:14:37 +0000
commit13c6eb0d71b253cc55a667e33dbdd4932f3710f1 (patch)
tree8c6eee3ffc63cb6b0ec8f6a4407af3a94a949c64 /src/Sema
parent953355ebeab881abff4a2c9315daa4fbb290d733 (diff)
downloadzig-13c6eb0d71b253cc55a667e33dbdd4932f3710f1.tar.gz
zig-13c6eb0d71b253cc55a667e33dbdd4932f3710f1.zip
compiler,std: implement ZON support
This commit allows using ZON (Zig Object Notation) in a few ways. * `@import` can be used to load ZON at comptime and convert it to a normal Zig value. In this case, `@import` must have a result type. * `std.zon.parse` can be used to parse ZON at runtime, akin to the parsing logic in `std.json`. * `std.zon.stringify` can be used to convert arbitrary data structures to ZON at runtime, again akin to `std.json`.
Diffstat (limited to 'src/Sema')
-rw-r--r--src/Sema/LowerZon.zig858
1 files changed, 858 insertions, 0 deletions
diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig
new file mode 100644
index 0000000000..2b2d16b90a
--- /dev/null
+++ b/src/Sema/LowerZon.zig
@@ -0,0 +1,858 @@
+const std = @import("std");
+const Zcu = @import("../Zcu.zig");
+const Sema = @import("../Sema.zig");
+const Air = @import("../Air.zig");
+const InternPool = @import("../InternPool.zig");
+const Type = @import("../Type.zig");
+const Value = @import("../Value.zig");
+const Zir = std.zig.Zir;
+const AstGen = std.zig.AstGen;
+const CompileError = Zcu.CompileError;
+const Ast = std.zig.Ast;
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const File = Zcu.File;
+const LazySrcLoc = Zcu.LazySrcLoc;
+const Ref = std.zig.Zir.Inst.Ref;
+const NullTerminatedString = InternPool.NullTerminatedString;
+const NumberLiteralError = std.zig.number_literal.Error;
+const NodeIndex = std.zig.Ast.Node.Index;
+const Zoir = std.zig.Zoir;
+
+const LowerZon = @This();
+
+sema: *Sema,
+file: *File,
+file_index: Zcu.File.Index,
+import_loc: LazySrcLoc,
+block: *Sema.Block,
+base_node_inst: InternPool.TrackedInst.Index,
+
+/// Lowers the given file as ZON.
+pub fn run(
+ sema: *Sema,
+ file: *File,
+ file_index: Zcu.File.Index,
+ res_ty: Type,
+ import_loc: LazySrcLoc,
+ block: *Sema.Block,
+) CompileError!InternPool.Index {
+ const pt = sema.pt;
+
+ _ = try file.getZoir(pt.zcu);
+
+ const tracked_inst = try pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{
+ .file = file_index,
+ .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file
+ });
+
+ var lower_zon: LowerZon = .{
+ .sema = sema,
+ .file = file,
+ .file_index = file_index,
+ .import_loc = import_loc,
+ .block = block,
+ .base_node_inst = tracked_inst,
+ };
+
+ try lower_zon.checkType(res_ty);
+
+ return lower_zon.lowerExpr(.root, res_ty);
+}
+
+/// Validate that `ty` is a valid ZON type. If not, emit a compile error.
+/// i.e. no nested optionals, no error sets, etc.
+fn checkType(self: *LowerZon, ty: Type) !void {
+ var visited: std.AutoHashMapUnmanaged(InternPool.Index, void) = .empty;
+ try self.checkTypeInner(ty, null, &visited);
+}
+
+fn checkTypeInner(
+ self: *LowerZon,
+ ty: Type,
+ parent_opt_ty: ?Type,
+ /// Visited structs and unions (not tuples). These are tracked because they are the only way in
+ /// which a type can be self-referential, so must be tracked to avoid loops. Tracking more types
+ /// consumes memory unnecessarily, and would be complicated by optionals.
+ /// Allocated into `self.sema.arena`.
+ visited: *std.AutoHashMapUnmanaged(InternPool.Index, void),
+) !void {
+ const sema = self.sema;
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+
+ switch (ty.zigTypeTag(zcu)) {
+ .bool,
+ .int,
+ .float,
+ .null,
+ .@"enum",
+ .comptime_float,
+ .comptime_int,
+ .enum_literal,
+ => {},
+
+ .noreturn,
+ .void,
+ .type,
+ .undefined,
+ .error_union,
+ .error_set,
+ .@"fn",
+ .frame,
+ .@"anyframe",
+ .@"opaque",
+ => return self.failUnsupportedResultType(ty, null),
+
+ .pointer => {
+ const ptr_info = ty.ptrInfo(zcu);
+ if (!ptr_info.flags.is_const) {
+ return self.failUnsupportedResultType(
+ ty,
+ "ZON does not allow mutable pointers",
+ );
+ }
+ switch (ptr_info.flags.size) {
+ .one => try self.checkTypeInner(
+ .fromInterned(ptr_info.child),
+ parent_opt_ty, // preserved
+ visited,
+ ),
+ .slice => try self.checkTypeInner(
+ .fromInterned(ptr_info.child),
+ null,
+ visited,
+ ),
+ .many => return self.failUnsupportedResultType(ty, "ZON does not allow many-pointers"),
+ .c => return self.failUnsupportedResultType(ty, "ZON does not allow C pointers"),
+ }
+ },
+ .optional => if (parent_opt_ty) |p| {
+ return self.failUnsupportedResultType(p, "ZON does not allow nested optionals");
+ } else try self.checkTypeInner(
+ ty.optionalChild(zcu),
+ ty,
+ visited,
+ ),
+ .array, .vector => {
+ try self.checkTypeInner(ty.childType(zcu), null, visited);
+ },
+ .@"struct" => if (ty.isTuple(zcu)) {
+ const tuple_info = ip.indexToKey(ty.toIntern()).tuple_type;
+ const field_types = tuple_info.types.get(ip);
+ for (field_types) |field_type| {
+ try self.checkTypeInner(.fromInterned(field_type), null, visited);
+ }
+ } else {
+ const gop = try visited.getOrPut(sema.arena, ty.toIntern());
+ if (gop.found_existing) return;
+ try ty.resolveFields(pt);
+ const struct_info = zcu.typeToStruct(ty).?;
+ for (struct_info.field_types.get(ip)) |field_type| {
+ try self.checkTypeInner(.fromInterned(field_type), null, visited);
+ }
+ },
+ .@"union" => {
+ const gop = try visited.getOrPut(sema.arena, ty.toIntern());
+ if (gop.found_existing) return;
+ try ty.resolveFields(pt);
+ const union_info = zcu.typeToUnion(ty).?;
+ for (union_info.field_types.get(ip)) |field_type| {
+ if (field_type != .void_type) {
+ try self.checkTypeInner(.fromInterned(field_type), null, visited);
+ }
+ }
+ },
+ }
+}
+
+fn nodeSrc(self: *LowerZon, node: Zoir.Node.Index) LazySrcLoc {
+ return .{
+ .base_node_inst = self.base_node_inst,
+ .offset = .{ .node_abs = node.getAstNode(self.file.zoir.?) },
+ };
+}
+
+fn failUnsupportedResultType(
+ self: *LowerZon,
+ ty: Type,
+ opt_note: ?[]const u8,
+) error{ AnalysisFail, OutOfMemory } {
+ @branchHint(.cold);
+ const sema = self.sema;
+ const gpa = sema.gpa;
+ const pt = sema.pt;
+ return sema.failWithOwnedErrorMsg(self.block, msg: {
+ const msg = try sema.errMsg(self.import_loc, "type '{}' is not available in ZON", .{ty.fmt(pt)});
+ errdefer msg.destroy(gpa);
+ if (opt_note) |n| try sema.errNote(self.import_loc, msg, "{s}", .{n});
+ break :msg msg;
+ });
+}
+
+fn fail(
+ self: *LowerZon,
+ node: Zoir.Node.Index,
+ comptime format: []const u8,
+ args: anytype,
+) error{ AnalysisFail, OutOfMemory } {
+ @branchHint(.cold);
+ const err_msg = try Zcu.ErrorMsg.create(self.sema.pt.zcu.gpa, self.nodeSrc(node), format, args);
+ try self.sema.pt.zcu.errNote(self.import_loc, err_msg, "imported here", .{});
+ return self.sema.failWithOwnedErrorMsg(self.block, err_msg);
+}
+
+fn lowerExpr(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {
+ const pt = self.sema.pt;
+ return self.lowerExprInner(node, res_ty) catch |err| switch (err) {
+ error.WrongType => return self.fail(
+ node,
+ "expected type '{}'",
+ .{res_ty.fmt(pt)},
+ ),
+ else => |e| return e,
+ };
+}
+
+fn lowerExprInner(
+ self: *LowerZon,
+ node: Zoir.Node.Index,
+ res_ty: Type,
+) (CompileError || error{WrongType})!InternPool.Index {
+ const pt = self.sema.pt;
+ switch (res_ty.zigTypeTag(pt.zcu)) {
+ .optional => return pt.intern(.{
+ .opt = .{
+ .ty = res_ty.toIntern(),
+ .val = if (node.get(self.file.zoir.?) == .null) b: {
+ break :b .none;
+ } else b: {
+ const child_type = res_ty.optionalChild(pt.zcu);
+ break :b try self.lowerExprInner(node, child_type);
+ },
+ },
+ }),
+ .pointer => {
+ const ptr_info = res_ty.ptrInfo(pt.zcu);
+ switch (ptr_info.flags.size) {
+ .one => return pt.intern(.{ .ptr = .{
+ .ty = res_ty.toIntern(),
+ .base_addr = .{
+ .uav = .{
+ .orig_ty = res_ty.toIntern(),
+ .val = try self.lowerExprInner(node, .fromInterned(ptr_info.child)),
+ },
+ },
+ .byte_offset = 0,
+ } }),
+ .slice => return self.lowerSlice(node, res_ty),
+ else => {
+ // Unsupported pointer type, checked in `lower`
+ unreachable;
+ },
+ }
+ },
+ .bool => return self.lowerBool(node),
+ .int, .comptime_int => return self.lowerInt(node, res_ty),
+ .float, .comptime_float => return self.lowerFloat(node, res_ty),
+ .null => return self.lowerNull(node),
+ .@"enum" => return self.lowerEnum(node, res_ty),
+ .enum_literal => return self.lowerEnumLiteral(node),
+ .array => return self.lowerArray(node, res_ty),
+ .@"struct" => return self.lowerStructOrTuple(node, res_ty),
+ .@"union" => return self.lowerUnion(node, res_ty),
+ .vector => return self.lowerVector(node, res_ty),
+
+ .type,
+ .noreturn,
+ .undefined,
+ .error_union,
+ .error_set,
+ .@"fn",
+ .@"opaque",
+ .frame,
+ .@"anyframe",
+ .void,
+ => return self.fail(node, "type '{}' not available in ZON", .{res_ty.fmt(pt)}),
+ }
+}
+
+fn lowerBool(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
+ return switch (node.get(self.file.zoir.?)) {
+ .true => .bool_true,
+ .false => .bool_false,
+ else => return error.WrongType,
+ };
+}
+
+fn lowerInt(
+ self: *LowerZon,
+ node: Zoir.Node.Index,
+ res_ty: Type,
+) !InternPool.Index {
+ @setFloatMode(.strict);
+ return switch (node.get(self.file.zoir.?)) {
+ .int_literal => |int| switch (int) {
+ .small => |val| {
+ const rhs: i32 = val;
+
+ // If our result is a fixed size integer, check that our value is not out of bounds
+ if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
+ const lhs_info = res_ty.intInfo(self.sema.pt.zcu);
+
+ // If lhs is unsigned and rhs is less than 0, we're out of bounds
+ if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail(
+ node,
+ "type '{}' cannot represent integer value '{}'",
+ .{ res_ty.fmt(self.sema.pt), rhs },
+ );
+
+ // If lhs has less than the 32 bits rhs can hold, we need to check the max and
+ // min values
+ if (std.math.cast(u5, lhs_info.bits)) |bits| {
+ const min_int: i32 = if (lhs_info.signedness == .unsigned or bits == 0) b: {
+ break :b 0;
+ } else b: {
+ break :b -(@as(i32, 1) << (bits - 1));
+ };
+ const max_int: i32 = if (bits == 0) b: {
+ break :b 0;
+ } else b: {
+ break :b (@as(i32, 1) << (bits - @intFromBool(lhs_info.signedness == .signed))) - 1;
+ };
+ if (rhs < min_int or rhs > max_int) {
+ return self.fail(
+ node,
+ "type '{}' cannot represent integer value '{}'",
+ .{ res_ty.fmt(self.sema.pt), rhs },
+ );
+ }
+ }
+ }
+
+ return self.sema.pt.intern(.{ .int = .{
+ .ty = res_ty.toIntern(),
+ .storage = .{ .i64 = rhs },
+ } });
+ },
+ .big => |val| {
+ if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
+ const int_info = res_ty.intInfo(self.sema.pt.zcu);
+ if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) {
+ return self.fail(
+ node,
+ "type '{}' cannot represent integer value '{}'",
+ .{ res_ty.fmt(self.sema.pt), val },
+ );
+ }
+ }
+
+ return self.sema.pt.intern(.{ .int = .{
+ .ty = res_ty.toIntern(),
+ .storage = .{ .big_int = val },
+ } });
+ },
+ },
+ .float_literal => |val| {
+ // Check for fractional components
+ if (@rem(val, 1) != 0) {
+ return self.fail(
+ node,
+ "fractional component prevents float value '{}' from coercion to type '{}'",
+ .{ val, res_ty.fmt(self.sema.pt) },
+ );
+ }
+
+ // Create a rational representation of the float
+ var rational = try std.math.big.Rational.init(self.sema.arena);
+ rational.setFloat(f128, val) catch |err| switch (err) {
+ error.NonFiniteFloat => unreachable,
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+
+ // The float is reduced in rational.setFloat, so we assert that denominator is equal to
+ // one
+ const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
+ assert(rational.q.toConst().eqlAbs(big_one));
+
+ // Check that the result is in range of the result type
+ const int_info = res_ty.intInfo(self.sema.pt.zcu);
+ if (!rational.p.fitsInTwosComp(int_info.signedness, int_info.bits)) {
+ return self.fail(
+ node,
+ "type '{}' cannot represent integer value '{}'",
+ .{ val, res_ty.fmt(self.sema.pt) },
+ );
+ }
+
+ return self.sema.pt.intern(.{
+ .int = .{
+ .ty = res_ty.toIntern(),
+ .storage = .{ .big_int = rational.p.toConst() },
+ },
+ });
+ },
+ .char_literal => |val| {
+ // If our result is a fixed size integer, check that our value is not out of bounds
+ if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
+ const dest_info = res_ty.intInfo(self.sema.pt.zcu);
+ const unsigned_bits = dest_info.bits - @intFromBool(dest_info.signedness == .signed);
+ if (unsigned_bits < 21) {
+ const out_of_range: u21 = @as(u21, 1) << @intCast(unsigned_bits);
+ if (val >= out_of_range) {
+ return self.fail(
+ node,
+ "type '{}' cannot represent integer value '{}'",
+ .{ res_ty.fmt(self.sema.pt), val },
+ );
+ }
+ }
+ }
+ return self.sema.pt.intern(.{
+ .int = .{
+ .ty = res_ty.toIntern(),
+ .storage = .{ .i64 = val },
+ },
+ });
+ },
+
+ else => return error.WrongType,
+ };
+}
+
+fn lowerFloat(
+ self: *LowerZon,
+ node: Zoir.Node.Index,
+ res_ty: Type,
+) !InternPool.Index {
+ @setFloatMode(.strict);
+ const value = switch (node.get(self.file.zoir.?)) {
+ .int_literal => |int| switch (int) {
+ .small => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))),
+ .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128)),
+ },
+ .float_literal => |val| try self.sema.pt.floatValue(res_ty, val),
+ .char_literal => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))),
+ .pos_inf => b: {
+ if (res_ty.toIntern() == .comptime_float_type) return self.fail(
+ node,
+ "expected type '{}'",
+ .{res_ty.fmt(self.sema.pt)},
+ );
+ break :b try self.sema.pt.floatValue(res_ty, std.math.inf(f128));
+ },
+ .neg_inf => b: {
+ if (res_ty.toIntern() == .comptime_float_type) return self.fail(
+ node,
+ "expected type '{}'",
+ .{res_ty.fmt(self.sema.pt)},
+ );
+ break :b try self.sema.pt.floatValue(res_ty, -std.math.inf(f128));
+ },
+ .nan => b: {
+ if (res_ty.toIntern() == .comptime_float_type) return self.fail(
+ node,
+ "expected type '{}'",
+ .{res_ty.fmt(self.sema.pt)},
+ );
+ break :b try self.sema.pt.floatValue(res_ty, std.math.nan(f128));
+ },
+ else => return error.WrongType,
+ };
+ return value.toIntern();
+}
+
+fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
+ switch (node.get(self.file.zoir.?)) {
+ .null => return .null_value,
+ else => return error.WrongType,
+ }
+}
+
+fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
+ const array_info = res_ty.arrayInfo(self.sema.pt.zcu);
+ const nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
+ .array_literal => |nodes| nodes,
+ .empty_literal => .{ .start = node, .len = 0 },
+ else => return error.WrongType,
+ };
+
+ if (nodes.len != array_info.len) {
+ return error.WrongType;
+ }
+
+ const elems = try self.sema.arena.alloc(
+ InternPool.Index,
+ nodes.len + @intFromBool(array_info.sentinel != null),
+ );
+
+ for (0..nodes.len) |i| {
+ elems[i] = try self.lowerExpr(nodes.at(@intCast(i)), array_info.elem_type);
+ }
+
+ if (array_info.sentinel) |sentinel| {
+ elems[elems.len - 1] = sentinel.toIntern();
+ }
+
+ return self.sema.pt.intern(.{ .aggregate = .{
+ .ty = res_ty.toIntern(),
+ .storage = .{ .elems = elems },
+ } });
+}
+
+fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
+ const ip = &self.sema.pt.zcu.intern_pool;
+ switch (node.get(self.file.zoir.?)) {
+ .enum_literal => |field_name| {
+ const field_name_interned = try ip.getOrPutString(
+ self.sema.gpa,
+ self.sema.pt.tid,
+ field_name.get(self.file.zoir.?),
+ .no_embedded_nulls,
+ );
+ const field_index = res_ty.enumFieldIndex(field_name_interned, self.sema.pt.zcu) orelse {
+ return self.fail(
+ node,
+ "enum {} has no member named '{}'",
+ .{
+ res_ty.fmt(self.sema.pt),
+ std.zig.fmtId(field_name.get(self.file.zoir.?)),
+ },
+ );
+ };
+
+ const value = try self.sema.pt.enumValueFieldIndex(res_ty, field_index);
+
+ return value.toIntern();
+ },
+ else => return error.WrongType,
+ }
+}
+
+fn lowerEnumLiteral(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
+ const ip = &self.sema.pt.zcu.intern_pool;
+ switch (node.get(self.file.zoir.?)) {
+ .enum_literal => |field_name| {
+ const field_name_interned = try ip.getOrPutString(
+ self.sema.gpa,
+ self.sema.pt.tid,
+ field_name.get(self.file.zoir.?),
+ .no_embedded_nulls,
+ );
+ return self.sema.pt.intern(.{ .enum_literal = field_name_interned });
+ },
+ else => return error.WrongType,
+ }
+}
+
+fn lowerStructOrTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
+ const ip = &self.sema.pt.zcu.intern_pool;
+ return switch (ip.indexToKey(res_ty.toIntern())) {
+ .tuple_type => self.lowerTuple(node, res_ty),
+ .struct_type => self.lowerStruct(node, res_ty),
+ else => unreachable,
+ };
+}
+
+fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
+ const ip = &self.sema.pt.zcu.intern_pool;
+
+ const tuple_info = ip.indexToKey(res_ty.toIntern()).tuple_type;
+
+ const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
+ .array_literal => |nodes| nodes,
+ .empty_literal => .{ .start = node, .len = 0 },
+ else => return error.WrongType,
+ };
+
+ const field_types = tuple_info.types.get(ip);
+ const elems = try self.sema.arena.alloc(InternPool.Index, field_types.len);
+
+ const field_comptime_vals = tuple_info.values.get(ip);
+ if (field_comptime_vals.len > 0) {
+ @memcpy(elems, field_comptime_vals);
+ } else {
+ @memset(elems, .none);
+ }
+
+ for (0..elem_nodes.len) |i| {
+ if (i >= elems.len) {
+ const elem_node = elem_nodes.at(@intCast(i));
+ return self.fail(
+ elem_node,
+ "index {} outside tuple of length {}",
+ .{
+ elems.len,
+ elem_nodes.at(@intCast(i)).getAstNode(self.file.zoir.?),
+ },
+ );
+ }
+
+ const val = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i]));
+
+ if (elems[i] != .none and val != elems[i]) {
+ const elem_node = elem_nodes.at(@intCast(i));
+ return self.fail(
+ elem_node,
+ "value stored in comptime field does not match the default value of the field",
+ .{},
+ );
+ }
+
+ elems[i] = val;
+ }
+
+ for (elems, 0..) |val, i| {
+ if (val == .none) {
+ return self.fail(node, "missing tuple field with index {}", .{i});
+ }
+ }
+
+ return self.sema.pt.intern(.{ .aggregate = .{
+ .ty = res_ty.toIntern(),
+ .storage = .{ .elems = elems },
+ } });
+}
+
+fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
+ const ip = &self.sema.pt.zcu.intern_pool;
+ const gpa = self.sema.gpa;
+
+ try res_ty.resolveFields(self.sema.pt);
+ try res_ty.resolveStructFieldInits(self.sema.pt);
+ const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?;
+
+ const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
+ .struct_literal => |fields| fields,
+ .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } },
+ else => return error.WrongType,
+ };
+
+ const field_values = try self.sema.arena.alloc(InternPool.Index, struct_info.field_names.len);
+
+ const field_defaults = struct_info.field_inits.get(ip);
+ if (field_defaults.len > 0) {
+ @memcpy(field_values, field_defaults);
+ } else {
+ @memset(field_values, .none);
+ }
+
+ for (0..fields.names.len) |i| {
+ const field_name = try ip.getOrPutString(
+ gpa,
+ self.sema.pt.tid,
+ fields.names[i].get(self.file.zoir.?),
+ .no_embedded_nulls,
+ );
+ const field_node = fields.vals.at(@intCast(i));
+
+ const name_index = struct_info.nameIndex(ip, field_name) orelse {
+ return self.fail(field_node, "unexpected field '{}'", .{field_name.fmt(ip)});
+ };
+
+ const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]);
+ field_values[name_index] = try self.lowerExpr(field_node, field_type);
+
+ if (struct_info.comptime_bits.getBit(ip, name_index)) {
+ const val = ip.indexToKey(field_values[name_index]);
+ const default = ip.indexToKey(field_defaults[name_index]);
+ if (!val.eql(default, ip)) {
+ return self.fail(
+ field_node,
+ "value stored in comptime field does not match the default value of the field",
+ .{},
+ );
+ }
+ }
+ }
+
+ const field_names = struct_info.field_names.get(ip);
+ for (field_values, field_names) |*value, name| {
+ if (value.* == .none) return self.fail(node, "missing field '{}'", .{name.fmt(ip)});
+ }
+
+ return self.sema.pt.intern(.{ .aggregate = .{
+ .ty = res_ty.toIntern(),
+ .storage = .{
+ .elems = field_values,
+ },
+ } });
+}
+
+fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
+ const ip = &self.sema.pt.zcu.intern_pool;
+ const gpa = self.sema.gpa;
+
+ const ptr_info = res_ty.ptrInfo(self.sema.pt.zcu);
+
+ assert(ptr_info.flags.size == .slice);
+
+ // String literals
+ const string_alignment = ptr_info.flags.alignment == .none or ptr_info.flags.alignment == .@"1";
+ const string_sentinel = ptr_info.sentinel == .none or ptr_info.sentinel == .zero_u8;
+ if (string_alignment and ptr_info.child == .u8_type and string_sentinel) {
+ switch (node.get(self.file.zoir.?)) {
+ .string_literal => |val| {
+ const ip_str = try ip.getOrPutString(gpa, self.sema.pt.tid, val, .maybe_embedded_nulls);
+ const str_ref = try self.sema.addStrLit(ip_str, val.len);
+ return (try self.sema.coerce(
+ self.block,
+ res_ty,
+ str_ref,
+ self.nodeSrc(node),
+ )).toInterned().?;
+ },
+ else => {},
+ }
+ }
+
+ // Slice literals
+ const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
+ .array_literal => |nodes| nodes,
+ .empty_literal => .{ .start = node, .len = 0 },
+ else => return error.WrongType,
+ };
+
+ const elems = try self.sema.arena.alloc(InternPool.Index, elem_nodes.len + @intFromBool(ptr_info.sentinel != .none));
+
+ for (elems, 0..) |*elem, i| {
+ elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child));
+ }
+
+ if (ptr_info.sentinel != .none) {
+ elems[elems.len - 1] = ptr_info.sentinel;
+ }
+
+ const array_ty = try self.sema.pt.intern(.{ .array_type = .{
+ .len = elems.len,
+ .sentinel = ptr_info.sentinel,
+ .child = ptr_info.child,
+ } });
+
+ const array = try self.sema.pt.intern(.{ .aggregate = .{
+ .ty = array_ty,
+ .storage = .{ .elems = elems },
+ } });
+
+ const many_item_ptr_type = try self.sema.pt.intern(.{ .ptr_type = .{
+ .child = ptr_info.child,
+ .sentinel = ptr_info.sentinel,
+ .flags = b: {
+ var flags = ptr_info.flags;
+ flags.size = .many;
+ break :b flags;
+ },
+ .packed_offset = ptr_info.packed_offset,
+ } });
+
+ const many_item_ptr = try self.sema.pt.intern(.{
+ .ptr = .{
+ .ty = many_item_ptr_type,
+ .base_addr = .{
+ .uav = .{
+ .orig_ty = (try self.sema.pt.singleConstPtrType(.fromInterned(array_ty))).toIntern(),
+ .val = array,
+ },
+ },
+ .byte_offset = 0,
+ },
+ });
+
+ const len = (try self.sema.pt.intValue(.usize, elems.len)).toIntern();
+
+ return self.sema.pt.intern(.{ .slice = .{
+ .ty = res_ty.toIntern(),
+ .ptr = many_item_ptr,
+ .len = len,
+ } });
+}
+
+fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
+ const ip = &self.sema.pt.zcu.intern_pool;
+ try res_ty.resolveFields(self.sema.pt);
+ const union_info = self.sema.pt.zcu.typeToUnion(res_ty).?;
+ const enum_tag_info = union_info.loadTagType(ip);
+
+ const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) {
+ .enum_literal => |name| b: {
+ const field_name = try ip.getOrPutString(
+ self.sema.gpa,
+ self.sema.pt.tid,
+ name.get(self.file.zoir.?),
+ .no_embedded_nulls,
+ );
+ break :b .{ field_name, null };
+ },
+ .struct_literal => b: {
+ const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
+ .struct_literal => |fields| fields,
+ else => return self.fail(node, "expected type '{}'", .{res_ty.fmt(self.sema.pt)}),
+ };
+ if (fields.names.len != 1) {
+ return error.WrongType;
+ }
+ const field_name = try ip.getOrPutString(
+ self.sema.gpa,
+ self.sema.pt.tid,
+ fields.names[0].get(self.file.zoir.?),
+ .no_embedded_nulls,
+ );
+ break :b .{ field_name, fields.vals.at(0) };
+ },
+ else => return error.WrongType,
+ };
+
+ const name_index = enum_tag_info.nameIndex(ip, field_name) orelse {
+ return error.WrongType;
+ };
+ const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_ty), name_index);
+ const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]);
+ const val = if (maybe_field_node) |field_node| b: {
+ if (field_type.toIntern() == .void_type) {
+ return self.fail(field_node, "expected type 'void'", .{});
+ }
+ break :b try self.lowerExpr(field_node, field_type);
+ } else b: {
+ if (field_type.toIntern() != .void_type) {
+ return error.WrongType;
+ }
+ break :b .void_value;
+ };
+ return ip.getUnion(self.sema.pt.zcu.gpa, self.sema.pt.tid, .{
+ .ty = res_ty.toIntern(),
+ .tag = tag.toIntern(),
+ .val = val,
+ });
+}
+
+fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
+ const ip = &self.sema.pt.zcu.intern_pool;
+
+ const vector_info = ip.indexToKey(res_ty.toIntern()).vector_type;
+
+ const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
+ .array_literal => |nodes| nodes,
+ .empty_literal => .{ .start = node, .len = 0 },
+ else => return error.WrongType,
+ };
+
+ const elems = try self.sema.arena.alloc(InternPool.Index, vector_info.len);
+
+ if (elem_nodes.len != vector_info.len) {
+ return self.fail(
+ node,
+ "expected {} vector elements; found {}",
+ .{ vector_info.len, elem_nodes.len },
+ );
+ }
+
+ for (elems, 0..) |*elem, i| {
+ elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child));
+ }
+
+ return self.sema.pt.intern(.{ .aggregate = .{
+ .ty = res_ty.toIntern(),
+ .storage = .{ .elems = elems },
+ } });
+}