authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-31 02:20:12+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-01 17:34:07+01:00
log6e3e23a941c6c82550c41771a223afeec4accd47
tree6e1a7e51bf6696bfe79281cf6292d9fe3a7f5c3c
parent9e683f0f35e21c6dce127a46c9d8c3da2d23744d
signature Commit is signed but in an unrecognized format.

compiler: implement decl literals

Resolves: #9938

8 files changed, 149 insertions(+), 16 deletions(-)

lib/std/zig/AstGen.zig+40-14
...@@ -1028,7 +1028,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -1028,7 +1028,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
1028 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];1028 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
1029 return blockExpr(gz, scope, ri, node, statements, .normal);1029 return blockExpr(gz, scope, ri, node, statements, .normal);
1030 },1030 },
1031 .enum_literal => return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),1031 .enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| {
1032 const str_index = try astgen.identAsString(main_tokens[node]);
1033 const res = try gz.addPlNode(.decl_literal, node, Zir.Inst.Field{
1034 .lhs = res_ty,
1035 .field_name_start = str_index,
1036 });
1037 switch (ri.rl) {
1038 .discard, .none, .ref => unreachable, // no result type
1039 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us
1040 .ref_coerced_ty, .ptr, .inferred_ptr, .destructure => return rvalue(gz, ri, res, node),
1041 }
1042 } else return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
1032 .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),1043 .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
1033 // TODO restore this when implementing https://github.com/ziglang/zig/issues/60251044 // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
1034 // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),1045 // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),
...@@ -2752,6 +2763,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2752,6 +2763,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2752 .err_union_code_ptr,2763 .err_union_code_ptr,
2753 .ptr_type,2764 .ptr_type,
2754 .enum_literal,2765 .enum_literal,
2766 .decl_literal,
2767 .decl_literal_no_coerce,
2755 .merge_error_sets,2768 .merge_error_sets,
2756 .error_union_type,2769 .error_union_type,
2757 .bit_not,2770 .bit_not,
...@@ -5889,22 +5902,21 @@ fn tryExpr(...@@ -5889,22 +5902,21 @@ fn tryExpr(
5889 }5902 }
5890 const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };5903 const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
58915904
5892 const operand_ri: ResultInfo = .{5905 const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) {
5893 .rl = switch (ri.rl) {5906 .ref => .{ .ref, .try_ptr },
5894 .ref => .ref,5907 .ref_coerced_ty => |payload_ptr_ty| .{
5895 .ref_coerced_ty => |payload_ptr_ty| .{5908 .{ .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node) },
5896 .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node),5909 .try_ptr,
5897 },
5898 else => if (try ri.rl.resultType(parent_gz, node)) |payload_ty| .{
5899 // `coerced_ty` is OK due to the `rvalue` call below
5900 .coerced_ty = try parent_gz.addUnNode(.try_operand_ty, payload_ty, node),
5901 } else .none,
5902 },5910 },
5903 .ctx = .error_handling_expr,5911 else => if (try ri.rl.resultType(parent_gz, node)) |payload_ty| .{
5912 // `coerced_ty` is OK due to the `rvalue` call below
5913 .{ .coerced_ty = try parent_gz.addUnNode(.try_operand_ty, payload_ty, node) },
5914 .@"try",
5915 } else .{ .none, .@"try" },
5904 };5916 };
5917 const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr };
5905 // This could be a pointer or value depending on the `ri` parameter.5918 // This could be a pointer or value depending on the `ri` parameter.
5906 const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);5919 const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);
5907 const block_tag: Zir.Inst.Tag = if (operand_ri.rl == .ref) .try_ptr else .@"try";
5908 const try_inst = try parent_gz.makeBlockInst(block_tag, node);5920 const try_inst = try parent_gz.makeBlockInst(block_tag, node);
5909 try parent_gz.instructions.append(astgen.gpa, try_inst);5921 try parent_gz.instructions.append(astgen.gpa, try_inst);
59105922
...@@ -9916,7 +9928,7 @@ fn callExpr(...@@ -9916,7 +9928,7 @@ fn callExpr(
9916) InnerError!Zir.Inst.Ref {9928) InnerError!Zir.Inst.Ref {
9917 const astgen = gz.astgen;9929 const astgen = gz.astgen;
99189930
9919 const callee = try calleeExpr(gz, scope, call.ast.fn_expr);9931 const callee = try calleeExpr(gz, scope, ri.rl, call.ast.fn_expr);
9920 const modifier: std.builtin.CallModifier = blk: {9932 const modifier: std.builtin.CallModifier = blk: {
9921 if (gz.is_comptime) {9933 if (gz.is_comptime) {
9922 break :blk .compile_time;9934 break :blk .compile_time;
...@@ -10044,6 +10056,7 @@ const Callee = union(enum) {...@@ -10044,6 +10056,7 @@ const Callee = union(enum) {
10044fn calleeExpr(10056fn calleeExpr(
10045 gz: *GenZir,10057 gz: *GenZir,
10046 scope: *Scope,10058 scope: *Scope,
10059 call_rl: ResultInfo.Loc,
10047 node: Ast.Node.Index,10060 node: Ast.Node.Index,
10048) InnerError!Callee {10061) InnerError!Callee {
10049 const astgen = gz.astgen;10062 const astgen = gz.astgen;
...@@ -10070,6 +10083,19 @@ fn calleeExpr(...@@ -10070,6 +10083,19 @@ fn calleeExpr(
10070 .field_name_start = str_index,10083 .field_name_start = str_index,
10071 } };10084 } };
10072 },10085 },
10086 .enum_literal => if (try call_rl.resultType(gz, node)) |res_ty| {
10087 // Decl literal call syntax, e.g.
10088 // `const foo: T = .init();`
10089 // Look up `init` in `T`, but don't try and coerce it.
10090 const str_index = try astgen.identAsString(tree.nodes.items(.main_token)[node]);
10091 const callee = try gz.addPlNode(.decl_literal_no_coerce, node, Zir.Inst.Field{
10092 .lhs = res_ty,
10093 .field_name_start = str_index,
10094 });
10095 return .{ .direct = callee };
10096 } else {
10097 return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) };
10098 },
10073 else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },10099 else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },
10074 }10100 }
10075}10101}
lib/std/zig/Zir.zig+16
...@@ -651,6 +651,14 @@ pub const Inst = struct {...@@ -651,6 +651,14 @@ pub const Inst = struct {
651 err_union_code_ptr,651 err_union_code_ptr,
652 /// An enum literal. Uses the `str_tok` union field.652 /// An enum literal. Uses the `str_tok` union field.
653 enum_literal,653 enum_literal,
654 /// A decl literal. This is similar to `field`, but unwraps error unions and optionals,
655 /// and coerces the result to the given type.
656 /// Uses the `pl_node` union field. Payload is `Field`.
657 decl_literal,
658 /// The same as `decl_literal`, but the coercion is omitted. This is used for decl literal
659 /// function call syntax, i.e. `.foo()`.
660 /// Uses the `pl_node` union field. Payload is `Field`.
661 decl_literal_no_coerce,
654 /// A switch expression. Uses the `pl_node` union field.662 /// A switch expression. Uses the `pl_node` union field.
655 /// AST node is the switch, payload is `SwitchBlock`.663 /// AST node is the switch, payload is `SwitchBlock`.
656 switch_block,664 switch_block,
...@@ -1144,6 +1152,8 @@ pub const Inst = struct {...@@ -1144,6 +1152,8 @@ pub const Inst = struct {
1144 .err_union_code_ptr,1152 .err_union_code_ptr,
1145 .ptr_type,1153 .ptr_type,
1146 .enum_literal,1154 .enum_literal,
1155 .decl_literal,
1156 .decl_literal_no_coerce,
1147 .merge_error_sets,1157 .merge_error_sets,
1148 .error_union_type,1158 .error_union_type,
1149 .bit_not,1159 .bit_not,
...@@ -1442,6 +1452,8 @@ pub const Inst = struct {...@@ -1442,6 +1452,8 @@ pub const Inst = struct {
1442 .err_union_code_ptr,1452 .err_union_code_ptr,
1443 .ptr_type,1453 .ptr_type,
1444 .enum_literal,1454 .enum_literal,
1455 .decl_literal,
1456 .decl_literal_no_coerce,
1445 .merge_error_sets,1457 .merge_error_sets,
1446 .error_union_type,1458 .error_union_type,
1447 .bit_not,1459 .bit_not,
...@@ -1697,6 +1709,8 @@ pub const Inst = struct {...@@ -1697,6 +1709,8 @@ pub const Inst = struct {
1697 .err_union_code = .un_node,1709 .err_union_code = .un_node,
1698 .err_union_code_ptr = .un_node,1710 .err_union_code_ptr = .un_node,
1699 .enum_literal = .str_tok,1711 .enum_literal = .str_tok,
1712 .decl_literal = .pl_node,
1713 .decl_literal_no_coerce = .pl_node,
1700 .switch_block = .pl_node,1714 .switch_block = .pl_node,
1701 .switch_block_ref = .pl_node,1715 .switch_block_ref = .pl_node,
1702 .switch_block_err_union = .pl_node,1716 .switch_block_err_union = .pl_node,
...@@ -3842,6 +3856,8 @@ fn findDeclsInner(...@@ -3842,6 +3856,8 @@ fn findDeclsInner(
3842 .err_union_code,3856 .err_union_code,
3843 .err_union_code_ptr,3857 .err_union_code_ptr,
3844 .enum_literal,3858 .enum_literal,
3859 .decl_literal,
3860 .decl_literal_no_coerce,
3845 .validate_deref,3861 .validate_deref,
3846 .validate_destructure,3862 .validate_destructure,
3847 .field_type_ref,3863 .field_type_ref,
src/Sema.zig+50
...@@ -1072,6 +1072,8 @@ fn analyzeBodyInner(...@@ -1072,6 +1072,8 @@ fn analyzeBodyInner(
1072 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),1072 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
1073 .vector_elem_type => try sema.zirVectorElemType(block, inst),1073 .vector_elem_type => try sema.zirVectorElemType(block, inst),
1074 .enum_literal => try sema.zirEnumLiteral(block, inst),1074 .enum_literal => try sema.zirEnumLiteral(block, inst),
1075 .decl_literal => try sema.zirDeclLiteral(block, inst, true),
1076 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),
1075 .int_from_enum => try sema.zirIntFromEnum(block, inst),1077 .int_from_enum => try sema.zirIntFromEnum(block, inst),
1076 .enum_from_int => try sema.zirEnumFromInt(block, inst),1078 .enum_from_int => try sema.zirEnumFromInt(block, inst),
1077 .err_union_code => try sema.zirErrUnionCode(block, inst),1079 .err_union_code => try sema.zirErrUnionCode(block, inst),
...@@ -8874,6 +8876,54 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -8874,6 +8876,54 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
8874 })));8876 })));
8875}8877}
88768878
8879fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref {
8880 const tracy = trace(@src());
8881 defer tracy.end();
8882
8883 const pt = sema.pt;
8884 const zcu = pt.zcu;
8885 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
8886 const src = block.nodeOffset(inst_data.src_node);
8887 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
8888 const name = try zcu.intern_pool.getOrPutString(
8889 sema.gpa,
8890 pt.tid,
8891 sema.code.nullTerminatedString(extra.field_name_start),
8892 .no_embedded_nulls,
8893 );
8894 const orig_ty = sema.resolveType(block, src, extra.lhs) catch |err| switch (err) {
8895 error.GenericPoison => {
8896 // Treat this as a normal enum literal.
8897 return Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
8898 },
8899 else => |e| return e,
8900 };
8901
8902 var ty = orig_ty;
8903 while (true) switch (ty.zigTypeTag(zcu)) {
8904 .error_union => ty = ty.errorUnionPayload(zcu),
8905 .optional => ty = ty.optionalChild(zcu),
8906 .enum_literal, .error_set => {
8907 // Treat this as a normal enum literal.
8908 return Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
8909 },
8910 else => break,
8911 };
8912
8913 const result = try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src);
8914
8915 // Decl literals cannot lookup runtime `var`s.
8916 if (!try sema.isComptimeKnown(result)) {
8917 return sema.fail(block, src, "decl literal must be comptime-known", .{});
8918 }
8919
8920 if (do_coerce) {
8921 return sema.coerce(block, orig_ty, result, src);
8922 } else {
8923 return result;
8924 }
8925}
8926
8877fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8927fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8878 const pt = sema.pt;8928 const pt = sema.pt;
8879 const zcu = pt.zcu;8929 const zcu = pt.zcu;
src/print_zir.zig+2
...@@ -462,6 +462,8 @@ const Writer = struct {...@@ -462,6 +462,8 @@ const Writer = struct {
462462
463 .field_val,463 .field_val,
464 .field_ptr,464 .field_ptr,
465 .decl_literal,
466 .decl_literal_no_coerce,
465 => try self.writePlNodeField(stream, inst),467 => try self.writePlNodeField(stream, inst),
466468
467 .field_ptr_named,469 .field_ptr_named,
test/behavior.zig+1
...@@ -21,6 +21,7 @@ test {...@@ -21,6 +21,7 @@ test {
21 _ = @import("behavior/cast_int.zig");21 _ = @import("behavior/cast_int.zig");
22 _ = @import("behavior/comptime_memory.zig");22 _ = @import("behavior/comptime_memory.zig");
23 _ = @import("behavior/const_slice_child.zig");23 _ = @import("behavior/const_slice_child.zig");
24 _ = @import("behavior/decl_literals.zig");
24 _ = @import("behavior/decltest.zig");25 _ = @import("behavior/decltest.zig");
25 _ = @import("behavior/duplicated_test_names.zig");26 _ = @import("behavior/duplicated_test_names.zig");
26 _ = @import("behavior/defer.zig");27 _ = @import("behavior/defer.zig");
test/behavior/decl_literals.zig created+38
...@@ -0,0 +1,38 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const expect = std.testing.expect;
4
5test "decl literal" {
6 const S = struct {
7 x: u32,
8 const foo: @This() = .{ .x = 123 };
9 };
10
11 const val: S = .foo;
12 try expect(val.x == 123);
13}
14
15test "call decl literal" {
16 const S = struct {
17 x: u32,
18 fn init() @This() {
19 return .{ .x = 123 };
20 }
21 };
22
23 const val: S = .init();
24 try expect(val.x == 123);
25}
26
27test "call decl literal with error union" {
28 const S = struct {
29 x: u32,
30 fn init(err: bool) !@This() {
31 if (err) return error.Bad;
32 return .{ .x = 123 };
33 }
34 };
35
36 const val: S = try .init(false);
37 try expect(val.x == 123);
38}
test/cases/compile_errors/cast_enum_literal_to_enum_but_it_doesnt_match.zig+1-1
...@@ -11,5 +11,5 @@ export fn entry() void {...@@ -11,5 +11,5 @@ export fn entry() void {
11// backend=stage211// backend=stage2
12// target=native12// target=native
13//13//
14// :6:21: error: no field named 'c' in enum 'tmp.Foo'14// :6:21: error: enum 'tmp.Foo' has no member named 'c'
15// :1:13: note: enum declared here15// :1:13: note: enum declared here
test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig+1-1
...@@ -17,5 +17,5 @@ pub export fn entry() void {...@@ -17,5 +17,5 @@ pub export fn entry() void {
17// backend=stage217// backend=stage2
18// target=native18// target=native
19//19//
20// :7:28: error: no field named 'c' in enum 'meta.FieldEnum(tmp.MyStruct)'20// :7:28: error: enum 'meta.FieldEnum(tmp.MyStruct)' has no member named 'c'
21// :?:?: note: enum declared here21// :?:?: note: enum declared here