From 66634c245f56b1f9869485376d5f6286b1a38110 Mon Sep 17 00:00:00 2001 From: Biom4st3r Date: Fri, 31 Oct 2025 11:22:32 -0500 Subject: [PATCH 1/4] Add support for Decl Literal in Zon --- lib/std/zon/parse.zig | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index a3d0b6925163..0b7a005e67cb 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -805,6 +805,16 @@ const Parser = struct { const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { .struct_literal => |nodes| nodes, .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } }, + .enum_literal => |lit| { + const decl_name = lit.get(self.zoir); + inline for (@typeInfo(T).@"struct".decls) |decl| { + if (@TypeOf(@field(T,decl.name)) != T) continue; + if (std.mem.eql(u8, decl_name, decl.name)) { + return @field(T, decl.name); + } + } + return self.failNodeFmt(node, "Declaration Literal `{s}` does not exist in {s}",.{decl_name, @typeName(T)}); + }, else => return error.WrongType, }; @@ -1541,7 +1551,12 @@ test "std.zon structs" { const Vec0 = struct {}; const Vec1 = struct { x: f32 }; const Vec2 = struct { x: f32, y: f32 }; - const Vec3 = struct { x: f32, y: f32, z: f32 }; + const Vec3 = struct { + pub const Zero: @This() = .{ .x = 0,.y = 0, .z = 0 }; + x: f32, + y: f32, + z: f32, + }; const zero = try fromSlice(Vec0, gpa, ".{}", null, .{}); try std.testing.expectEqual(Vec0{}, zero); @@ -1554,6 +1569,12 @@ test "std.zon structs" { const three = try fromSlice(Vec3, gpa, ".{.x = 1.2, .y = 3.4, .z = 5.6}", null, .{}); try std.testing.expectEqual(Vec3{ .x = 1.2, .y = 3.4, .z = 5.6 }, three); + + const decl_literal_Zero = try fromSlice(Vec3, gpa, ".Zero", null, .{}); + try std.testing.expectEqual(decl_literal_Zero, @as(Vec3, .Zero)); + + const decl_literal_One: ?Vec3 = fromSlice(Vec3, gpa, ".DeclarationThatDoesNotExist", null, .{}) catch null; + try std.testing.expectEqual(decl_literal_One, null); } // Deep free (structs and arrays) From c4b799018a7a6996a2a6dfa055e1be201672073f Mon Sep 17 00:00:00 2001 From: Biom4st3r Date: Wed, 5 Nov 2025 15:26:39 -0600 Subject: [PATCH 2/4] * Add Decl Literal support for zon enum and union --- lib/std/zon/parse.zig | 119 ++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 38 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 0b7a005e67cb..b824f253cdd1 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -665,8 +665,27 @@ const Parser = struct { } } + fn parseDeclLiteral(self: @This(), T: type, nodeidx: Zoir.Node.Index, node: ?Zoir.Node) !T { + const real_node = node orelse nodeidx.get(self.zoir); + _ = real_node == .enum_literal or return error.WrongType; + + const target_name = real_node.enum_literal.get(self.zoir); + const T_decls = switch (@typeInfo(T)) { + inline .@"struct", .@"union", .@"enum" => |e| e.decls, + // .@"opaque "=> |o| o.decls, + else => return error.WrongType, + }; + + inline for (T_decls) |decl| { + if (@TypeOf(@field(T, decl.name)) != T) continue; + if (std.mem.eql(u8, target_name, decl.name)) + return @field(T, decl.name); + } + return error.NotFound; + } fn parseEnumLiteral(self: @This(), T: type, node: Zoir.Node.Index) !T { - switch (node.get(self.zoir)) { + const repr = node.get(self.zoir); + switch (repr) { .enum_literal => |field_name| { // Create a comptime string map for the enum fields const enum_fields = @typeInfo(T).@"enum".fields; @@ -678,8 +697,14 @@ const Parser = struct { // Get the tag if it exists const field_name_str = field_name.get(self.zoir); - return enum_tags.get(field_name_str) orelse - self.failUnexpected(T, "enum literal", node, null, field_name_str); + if (enum_tags.get(field_name_str)) |tag| + return tag + else if (self.parseDeclLiteral(T, node, repr)) |t| + return t + else |err| switch (err) { + error.NotFound => return self.failUnexpected(T, "enum literal", node, null, field_name_str), + else => |e| return e, + } }, else => return error.WrongType, } @@ -805,15 +830,13 @@ const Parser = struct { const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { .struct_literal => |nodes| nodes, .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } }, - .enum_literal => |lit| { - const decl_name = lit.get(self.zoir); - inline for (@typeInfo(T).@"struct".decls) |decl| { - if (@TypeOf(@field(T,decl.name)) != T) continue; - if (std.mem.eql(u8, decl_name, decl.name)) { - return @field(T, decl.name); - } - } - return self.failNodeFmt(node, "Declaration Literal `{s}` does not exist in {s}",.{decl_name, @typeName(T)}); + .enum_literal => |_| { + return self.parseDeclLiteral(T, node, repr) catch |err| switch (err) { + error.NotFound => return self.failNode(node, "expected decl literal"), + else => |e| { + return e; + }, + }; }, else => return error.WrongType, }; @@ -963,32 +986,34 @@ const Parser = struct { }; // Parse the union - switch (node.get(self.zoir)) { + const repr = node.get(self.zoir); + switch (repr) { .enum_literal => |field_name| { - // The union must be tagged for an enum literal to coerce to it - if (@"union".tag_type == null) { - return error.WrongType; - } - + const field_name_str = field_name.get(self.zoir); // Get the index of the named field. We don't use `parseEnum` here as // the order of the enum and the order of the union might not match! - const field_index = b: { - const field_name_str = field_name.get(self.zoir); - break :b field_indices.get(field_name_str) orelse - return self.failUnexpected(T, "field", node, null, field_name_str); - }; - - // Initialize the union from the given field. - switch (field_index) { - inline 0...field_infos.len - 1 => |i| { - // Fail if the field is not void - if (field_infos[i].type != void) - return self.failNode(node, "expected union"); - - // Instantiate the union - return @unionInit(T, field_infos[i].name, {}); - }, - else => unreachable, // Can't be out of bounds + if (field_indices.get(field_name_str)) |field_index| { + // The union must be tagged for an enum literal to coerce to it + if (@"union".tag_type == null) { + return error.WrongType; + } + // Initialize the union from the given field. + switch (field_index) { + inline 0...field_infos.len - 1 => |i| { + // Fail if the field is not void + if (field_infos[i].type != void) + return self.failNode(node, "expected union"); + + // Instantiate the union + return @unionInit(T, field_infos[i].name, {}); + }, + else => unreachable, // Can't be out of bounds + } + } else if (self.parseDeclLiteral(T, node, repr)) |t| { + return t; + } else |err| switch (err) { + error.NotFound => return self.failUnexpected(T, "field", node, null, field_name_str), + else => |e| return e, } }, .struct_literal => |struct_fields| { @@ -1423,8 +1448,20 @@ test "std.zon unions" { // Unions { - const Tagged = union(enum) { x: f32, @"y y": bool, z, @"z z" }; - const Untagged = union { x: f32, @"y y": bool, z: void, @"z z": void }; + const Tagged = union(enum) { + pub const LiteralXVal: @This() = .{ .x = 1.1 }; + x: f32, + @"y y": bool, + z, + @"z z", + }; + const Untagged = union { + pub const LiteralXVal: @This() = .{ .x = 1.1 }; + x: f32, + @"y y": bool, + z: void, + @"z z": void, + }; const tagged_x = try fromSlice(Tagged, gpa, ".{.x = 1.5}", null, .{}); try std.testing.expectEqual(Tagged{ .x = 1.5 }, tagged_x); @@ -1434,11 +1471,15 @@ test "std.zon unions" { try std.testing.expectEqual(@as(Tagged, .z), tagged_z_shorthand); const tagged_zz_shorthand = try fromSlice(Tagged, gpa, ".@\"z z\"", null, .{}); try std.testing.expectEqual(@as(Tagged, .@"z z"), tagged_zz_shorthand); + const tagged_x_decllit = try fromSlice(Tagged, gpa, ".LiteralXVal", null, .{}); + try std.testing.expectEqual(@as(Tagged, .LiteralXVal), tagged_x_decllit); const untagged_x = try fromSlice(Untagged, gpa, ".{.x = 1.5}", null, .{}); try std.testing.expect(untagged_x.x == 1.5); const untagged_y = try fromSlice(Untagged, gpa, ".{.@\"y y\" = true}", null, .{}); try std.testing.expect(untagged_y.@"y y"); + const untagged_x_decllit = try fromSlice(Untagged, gpa, ".LiteralXVal", null, .{}); + try std.testing.expectEqual(@as(Untagged, .LiteralXVal).x, untagged_x_decllit.x); } // Deep free @@ -1552,7 +1593,7 @@ test "std.zon structs" { const Vec1 = struct { x: f32 }; const Vec2 = struct { x: f32, y: f32 }; const Vec3 = struct { - pub const Zero: @This() = .{ .x = 0,.y = 0, .z = 0 }; + pub const Zero: @This() = .{ .x = 0, .y = 0, .z = 0 }; x: f32, y: f32, z: f32, @@ -2356,6 +2397,7 @@ test "std.zon enum literals" { const gpa = std.testing.allocator; const Enum = enum { + pub const NotBar: @This() = .baz; foo, bar, baz, @@ -2370,6 +2412,7 @@ test "std.zon enum literals" { Enum.@"ab\nc", try fromSlice(Enum, gpa, ".@\"ab\\nc\"", null, .{}), ); + try std.testing.expectEqual(Enum.NotBar, try fromSlice(Enum, gpa, ".NotBar", null, .{})); // Bad tag { From c49a00c79f856e94a2b267f6b25c624e1407d1d3 Mon Sep 17 00:00:00 2001 From: Biom4st3r Date: Wed, 5 Nov 2025 16:42:10 -0600 Subject: [PATCH 3/4] * remove comment --- lib/std/zon/parse.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index b824f253cdd1..cb575705689b 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -672,7 +672,6 @@ const Parser = struct { const target_name = real_node.enum_literal.get(self.zoir); const T_decls = switch (@typeInfo(T)) { inline .@"struct", .@"union", .@"enum" => |e| e.decls, - // .@"opaque "=> |o| o.decls, else => return error.WrongType, }; From a5d2fd75192a75f2733868af9dba548c1e2d0234 Mon Sep 17 00:00:00 2001 From: Biom4st3r Date: Thu, 6 Nov 2025 17:38:17 -0600 Subject: [PATCH 4/4] * Added support for @import'ing Decl Literals --- src/Sema.zig | 2 +- src/Sema/LowerZon.zig | 33 ++++++++++++++++++++++++++++++ test/behavior/zon.zig | 30 +++++++++++++++++++++++++++ test/behavior/zon/DeclLiterals.zon | 5 +++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 test/behavior/zon/DeclLiterals.zon diff --git a/src/Sema.zig b/src/Sema.zig index 4016041d82b5..51bd50b615a7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -26793,7 +26793,7 @@ fn fieldPtrLoad( return analyzeLoad(sema, block, src, field_ptr, field_name_src); } -fn fieldVal( +pub fn fieldVal( sema: *Sema, block: *Block, src: LazySrcLoc, diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index eb1fba121a69..c8038f2e31d3 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -645,6 +645,9 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I field_name.get(self.file.zoir.?), .no_embedded_nulls, ); + + const enum_info = ip.loadEnumType(res_ty.ip_index); + if (try self.lowerDeclLiteral(node, res_ty, field_name, enum_info.namespace)) |val| return val; const field_index = res_ty.enumFieldIndex(field_name_interned, self.sema.pt.zcu) orelse { return self.fail( node, @@ -746,6 +749,29 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. return (try self.sema.pt.aggregateValue(res_ty, elems)).toIntern(); } +fn lowerDeclLiteral(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type, name: Zoir.NullTerminatedString, namespace: InternPool.NamespaceIndex) !?InternPool.Index { + const ip = &self.sema.pt.zcu.intern_pool; + + const air = Air.internedToRef(res_ty.toIntern()); + // Intern the incoming name + const decl_name = try ip.getOrPutString( + self.sema.gpa, + self.sema.pt.tid, + name.get(self.file.zoir.?), + .no_embedded_nulls, + ); + + for (ip.namespacePtr(namespace).pub_decls.keys()) |decl| { + const nav = ip.getNav(decl); + if (nav.name == decl_name) { + const src = self.nodeSrc(node); + const val = try self.sema.fieldVal(self.block, src, air, decl_name, src); + return val.toInterned().?; + } + } + return null; +} + 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; @@ -757,6 +783,10 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool 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 } }, + .enum_literal => |name| { + if (try self.lowerDeclLiteral(node, res_ty, name, struct_info.namespace)) |val| return val; + return error.WrongType; + }, else => return error.WrongType, }; @@ -905,6 +935,9 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. name.get(self.file.zoir.?), .no_embedded_nulls, ); + + if (try self.lowerDeclLiteral(node, res_ty, name, union_info.namespace)) |val| return val; + break :b .{ field_name, null }; }, .struct_literal => b: { diff --git a/test/behavior/zon.zig b/test/behavior/zon.zig index 8a43b4894e3e..18bdeb7877a1 100644 --- a/test/behavior/zon.zig +++ b/test/behavior/zon.zig @@ -571,3 +571,33 @@ test "build.zig.zon" { try expectEqual(.{ "build.zig", "build.zig.zon", "src" }, build.paths); } + +test "decl literal" { + const Struct = struct { + pub const ADeclLitStruct: @This() = .{.a = 5,.b = 0.6}; + a: u64, + b: f64, + }; + const Union = union(enum){ + pub const ADeclLitUnion: @This() = .{.a = 5,}; + a: u64, + b: f64, + }; + const Enum = enum { + pub const ADeclLitEnum: @This() = .B; + A, + B, + C, + }; + + const Lits = struct { + a: Struct, + b: Union, + c: Enum, + }; + + const Preset: Lits = @import("zon/DeclLiterals.zon"); + try expectEqual(Preset.a, Struct.ADeclLitStruct); + try expectEqual(Preset.b, Union.ADeclLitUnion); + try expectEqual(Preset.c, Enum.ADeclLitEnum); +} diff --git a/test/behavior/zon/DeclLiterals.zon b/test/behavior/zon/DeclLiterals.zon new file mode 100644 index 000000000000..3ec33ece781e --- /dev/null +++ b/test/behavior/zon/DeclLiterals.zon @@ -0,0 +1,5 @@ +.{ + .a = .ADeclLitStruct, + .b = .ADeclLitUnion, + .c = .ADeclLitEnum, +}