diff --git a/doc/langref.html.in b/doc/langref.html.in index 225b98303b48..d8433c5b497b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4879,6 +4879,13 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val

{#header_close#} + {#header_open|@FieldType#} +
{#syntax#}@FieldType(comptime Type: type, comptime field_name: []const u8) type{#endsyntax#}
+

+ Given a type and the name of one of its fields, returns the type of that field. +

+ {#header_close#} + {#header_open|@floatCast#}
{#syntax#}@floatCast(value: anytype) anytype{#endsyntax#}

diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 7e11f8d44be9..16d2a9963204 100644 --- a/lib/std/zig/AstGen.zig +++ b/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]), diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig index 104cea1154b1..68d423ade741 100644 --- a/lib/std/zig/AstRlAnnotate.zig +++ b/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); diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig index 6b0d0cc0a2c9..236092fb132c 100644 --- a/lib/std/zig/BuiltinFn.zig +++ b/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", .{ diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 989da34ae7ea..cc373cd8b199 100644 --- a/test/behavior/struct.zig +++ b/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); +} diff --git a/test/behavior/union.zig b/test/behavior/union.zig index a87631c9e459..16ccfdd45124 100644 --- a/test/behavior/union.zig +++ b/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); +} diff --git a/test/cases/compile_errors/invalid_field_type_usage.zig b/test/cases/compile_errors/invalid_field_type_usage.zig new file mode 100644 index 000000000000..105e85d1f128 --- /dev/null +++ b/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