This commit is contained in:
biom4st3r 2025-11-25 15:23:30 +01:00 committed by GitHub
commit 66cfc8a128
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 160 additions and 29 deletions

View file

@ -665,8 +665,26 @@ 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,
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 +696,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,6 +829,14 @@ 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 => |_| {
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,
};
@ -953,32 +985,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);
};
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");
// 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
// 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| {
@ -1413,8 +1447,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);
@ -1424,11 +1470,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
@ -1541,7 +1591,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 +1609,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)
@ -2335,6 +2396,7 @@ test "std.zon enum literals" {
const gpa = std.testing.allocator;
const Enum = enum {
pub const NotBar: @This() = .baz;
foo,
bar,
baz,
@ -2349,6 +2411,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
{

View file

@ -26766,7 +26766,7 @@ fn fieldPtrLoad(
return analyzeLoad(sema, block, src, field_ptr, field_name_src);
}
fn fieldVal(
pub fn fieldVal(
sema: *Sema,
block: *Block,
src: LazySrcLoc,

View file

@ -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: {

View file

@ -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);
}

View file

@ -0,0 +1,5 @@
.{
.a = .ADeclLitStruct,
.b = .ADeclLitUnion,
.c = .ADeclLitEnum,
}