Commit 097766bba3

mlugg <mlugg@mlugg.co.uk>
2024-10-17 11:19:14
compiler: implement `@FieldType`
Resolves: #21702
1 parent fffbb51
Changed files (7)
doc/langref.html.in
@@ -4879,6 +4879,13 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
       </p>
       {#header_close#}
 
+      {#header_open|@FieldType#}
+      <pre>{#syntax#}@FieldType(comptime Type: type, comptime field_name: []const u8) type{#endsyntax#}</pre>
+      <p>
+      Given a type and the name of one of its fields, returns the type of that field.
+      </p>
+      {#header_close#}
+
       {#header_open|@floatCast#}
       <pre>{#syntax#}@floatCast(value: anytype) anytype{#endsyntax#}</pre>
       <p>
lib/std/zig/AstGen.zig
@@ -9274,6 +9274,15 @@ fn builtinCall(
             });
             return rvalue(gz, ri, result, node);
         },
+        .FieldType => {
+            const ty_inst = try typeExpr(gz, scope, params[0]);
+            const name_inst = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]);
+            const result = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
+                .container_type = ty_inst,
+                .field_name = name_inst,
+            });
+            return rvalue(gz, ri, result, node);
+        },
 
         // zig fmt: off
         .as         => return as(       gz, scope, ri, node, params[0], params[1]),
lib/std/zig/AstRlAnnotate.zig
@@ -914,7 +914,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
         .work_item_id,
         .work_group_size,
         .work_group_id,
-        .field_parent_ptr,
         => {
             _ = try astrl.expr(args[0], block, ResultInfo.type_only);
             return false;
@@ -983,11 +982,17 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
         .has_decl,
         .has_field,
         .field,
+        .FieldType,
         => {
             _ = try astrl.expr(args[0], block, ResultInfo.type_only);
             _ = try astrl.expr(args[1], block, ResultInfo.type_only);
             return false;
         },
+        .field_parent_ptr => {
+            _ = try astrl.expr(args[0], block, ResultInfo.type_only);
+            _ = try astrl.expr(args[1], block, ResultInfo.none);
+            return false;
+        },
         .wasm_memory_grow => {
             _ = try astrl.expr(args[0], block, ResultInfo.type_only);
             _ = try astrl.expr(args[1], block, ResultInfo.type_only);
lib/std/zig/BuiltinFn.zig
@@ -50,6 +50,7 @@ pub const Tag = enum {
     @"extern",
     field,
     field_parent_ptr,
+    FieldType,
     float_cast,
     int_from_float,
     frame,
@@ -516,6 +517,13 @@ pub const list = list: {
                 .param_count = 2,
             },
         },
+        .{
+            "@FieldType",
+            .{
+                .tag = .FieldType,
+                .param_count = 2,
+            },
+        },
         .{
             "@floatCast",
             .{
test/behavior/struct.zig
@@ -2159,3 +2159,27 @@ test "matching captures causes struct equivalence" {
     comptime assert(@TypeOf(a) == @TypeOf(b));
     try expect(a.x == b.x);
 }
+
+test "struct @FieldType" {
+    const S = struct {
+        a: u32,
+        b: f64,
+        c: *@This(),
+    };
+
+    comptime assert(@FieldType(S, "a") == u32);
+    comptime assert(@FieldType(S, "b") == f64);
+    comptime assert(@FieldType(S, "c") == *S);
+}
+
+test "extern struct @FieldType" {
+    const S = extern struct {
+        a: u32,
+        b: f64,
+        c: *@This(),
+    };
+
+    comptime assert(@FieldType(S, "a") == u32);
+    comptime assert(@FieldType(S, "b") == f64);
+    comptime assert(@FieldType(S, "c") == *S);
+}
test/behavior/union.zig
@@ -2339,3 +2339,39 @@ test "signed enum tag with negative value" {
 
     try expect(e.a == i);
 }
+
+test "union @FieldType" {
+    const U = union {
+        a: u32,
+        b: f64,
+        c: *@This(),
+    };
+
+    comptime assert(@FieldType(U, "a") == u32);
+    comptime assert(@FieldType(U, "b") == f64);
+    comptime assert(@FieldType(U, "c") == *U);
+}
+
+test "tagged union @FieldType" {
+    const U = union(enum) {
+        a: u32,
+        b: f64,
+        c: *@This(),
+    };
+
+    comptime assert(@FieldType(U, "a") == u32);
+    comptime assert(@FieldType(U, "b") == f64);
+    comptime assert(@FieldType(U, "c") == *U);
+}
+
+test "extern union @FieldType" {
+    const U = extern union {
+        a: u32,
+        b: f64,
+        c: *@This(),
+    };
+
+    comptime assert(@FieldType(U, "a") == u32);
+    comptime assert(@FieldType(U, "b") == f64);
+    comptime assert(@FieldType(U, "c") == *U);
+}
test/cases/compile_errors/invalid_field_type_usage.zig
@@ -0,0 +1,13 @@
+export fn foo() void {
+    _ = @FieldType(u8, "a");
+}
+export fn bar() void {
+    const S = struct { a: u8 };
+    _ = @FieldType(S, "b");
+}
+
+// error
+//
+// :2:20: error: expected struct or union; found 'u8'
+// :6:23: error: no field named 'b' in struct 'tmp.bar.S'
+// :5:15: note: struct declared here