| author | |
| committer | |
| log | 6e3e23a941c6c82550c41771a223afeec4accd47 |
| tree | 6e1a7e51bf6696bfe79281cf6292d9fe3a7f5c3c |
| parent | 9e683f0f35e21c6dce127a46c9d8c3da2d23744d |
| signature |
Resolves: #99388 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 | 1028 | const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; |
| 1029 | 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 | 1043 | .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value), |
| 1033 | 1044 | // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025 |
| 1034 | 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 | 2763 | .err_union_code_ptr, |
| 2753 | 2764 | .ptr_type, |
| 2754 | 2765 | .enum_literal, |
| 2766 | .decl_literal, | |
| 2767 | .decl_literal_no_coerce, | |
| 2755 | 2768 | .merge_error_sets, |
| 2756 | 2769 | .error_union_type, |
| 2757 | 2770 | .bit_not, |
| ... | ... | @@ -5889,22 +5902,21 @@ fn tryExpr( |
| 5889 | 5902 | } |
| 5890 | 5903 | const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; |
| 5891 | 5904 | |
| 5892 | const operand_ri: ResultInfo = .{ | |
| 5893 | .rl = switch (ri.rl) { | |
| 5894 | .ref => .ref, | |
| 5895 | .ref_coerced_ty => |payload_ptr_ty| .{ | |
| 5896 | .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node), | |
| 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, | |
| 5905 | const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) { | |
| 5906 | .ref => .{ .ref, .try_ptr }, | |
| 5907 | .ref_coerced_ty => |payload_ptr_ty| .{ | |
| 5908 | .{ .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node) }, | |
| 5909 | .try_ptr, | |
| 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 | 5918 | // This could be a pointer or value depending on the `ri` parameter. |
| 5906 | 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 | 5920 | const try_inst = try parent_gz.makeBlockInst(block_tag, node); |
| 5909 | 5921 | try parent_gz.instructions.append(astgen.gpa, try_inst); |
| 5910 | 5922 | |
| ... | ... | @@ -9916,7 +9928,7 @@ fn callExpr( |
| 9916 | 9928 | ) InnerError!Zir.Inst.Ref { |
| 9917 | 9929 | const astgen = gz.astgen; |
| 9918 | 9930 | |
| 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 | 9932 | const modifier: std.builtin.CallModifier = blk: { |
| 9921 | 9933 | if (gz.is_comptime) { |
| 9922 | 9934 | break :blk .compile_time; |
| ... | ... | @@ -10044,6 +10056,7 @@ const Callee = union(enum) { |
| 10044 | 10056 | fn calleeExpr( |
| 10045 | 10057 | gz: *GenZir, |
| 10046 | 10058 | scope: *Scope, |
| 10059 | call_rl: ResultInfo.Loc, | |
| 10047 | 10060 | node: Ast.Node.Index, |
| 10048 | 10061 | ) InnerError!Callee { |
| 10049 | 10062 | const astgen = gz.astgen; |
| ... | ... | @@ -10070,6 +10083,19 @@ fn calleeExpr( |
| 10070 | 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 | 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 | 651 | err_union_code_ptr, |
| 652 | 652 | /// An enum literal. Uses the `str_tok` union field. |
| 653 | 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 | 662 | /// A switch expression. Uses the `pl_node` union field. |
| 655 | 663 | /// AST node is the switch, payload is `SwitchBlock`. |
| 656 | 664 | switch_block, |
| ... | ... | @@ -1144,6 +1152,8 @@ pub const Inst = struct { |
| 1144 | 1152 | .err_union_code_ptr, |
| 1145 | 1153 | .ptr_type, |
| 1146 | 1154 | .enum_literal, |
| 1155 | .decl_literal, | |
| 1156 | .decl_literal_no_coerce, | |
| 1147 | 1157 | .merge_error_sets, |
| 1148 | 1158 | .error_union_type, |
| 1149 | 1159 | .bit_not, |
| ... | ... | @@ -1442,6 +1452,8 @@ pub const Inst = struct { |
| 1442 | 1452 | .err_union_code_ptr, |
| 1443 | 1453 | .ptr_type, |
| 1444 | 1454 | .enum_literal, |
| 1455 | .decl_literal, | |
| 1456 | .decl_literal_no_coerce, | |
| 1445 | 1457 | .merge_error_sets, |
| 1446 | 1458 | .error_union_type, |
| 1447 | 1459 | .bit_not, |
| ... | ... | @@ -1697,6 +1709,8 @@ pub const Inst = struct { |
| 1697 | 1709 | .err_union_code = .un_node, |
| 1698 | 1710 | .err_union_code_ptr = .un_node, |
| 1699 | 1711 | .enum_literal = .str_tok, |
| 1712 | .decl_literal = .pl_node, | |
| 1713 | .decl_literal_no_coerce = .pl_node, | |
| 1700 | 1714 | .switch_block = .pl_node, |
| 1701 | 1715 | .switch_block_ref = .pl_node, |
| 1702 | 1716 | .switch_block_err_union = .pl_node, |
| ... | ... | @@ -3842,6 +3856,8 @@ fn findDeclsInner( |
| 3842 | 3856 | .err_union_code, |
| 3843 | 3857 | .err_union_code_ptr, |
| 3844 | 3858 | .enum_literal, |
| 3859 | .decl_literal, | |
| 3860 | .decl_literal_no_coerce, | |
| 3845 | 3861 | .validate_deref, |
| 3846 | 3862 | .validate_destructure, |
| 3847 | 3863 | .field_type_ref, |
src/Sema.zig+50| ... | ... | @@ -1072,6 +1072,8 @@ fn analyzeBodyInner( |
| 1072 | 1072 | .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), |
| 1073 | 1073 | .vector_elem_type => try sema.zirVectorElemType(block, inst), |
| 1074 | 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 | 1077 | .int_from_enum => try sema.zirIntFromEnum(block, inst), |
| 1076 | 1078 | .enum_from_int => try sema.zirEnumFromInt(block, inst), |
| 1077 | 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 | 8876 | }))); |
| 8875 | 8877 | } |
| 8876 | 8878 | |
| 8879 | fn 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 | ||
| 8877 | 8927 | fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 8878 | 8928 | const pt = sema.pt; |
| 8879 | 8929 | const zcu = pt.zcu; |
src/print_zir.zig+2| ... | ... | @@ -462,6 +462,8 @@ const Writer = struct { |
| 462 | 462 | |
| 463 | 463 | .field_val, |
| 464 | 464 | .field_ptr, |
| 465 | .decl_literal, | |
| 466 | .decl_literal_no_coerce, | |
| 465 | 467 | => try self.writePlNodeField(stream, inst), |
| 466 | 468 | |
| 467 | 469 | .field_ptr_named, |
test/behavior.zig+1| ... | ... | @@ -21,6 +21,7 @@ test { |
| 21 | 21 | _ = @import("behavior/cast_int.zig"); |
| 22 | 22 | _ = @import("behavior/comptime_memory.zig"); |
| 23 | 23 | _ = @import("behavior/const_slice_child.zig"); |
| 24 | _ = @import("behavior/decl_literals.zig"); | |
| 24 | 25 | _ = @import("behavior/decltest.zig"); |
| 25 | 26 | _ = @import("behavior/duplicated_test_names.zig"); |
| 26 | 27 | _ = @import("behavior/defer.zig"); |
test/behavior/decl_literals.zig created+38| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("std"); | |
| 3 | const expect = std.testing.expect; | |
| 4 | ||
| 5 | test "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 | ||
| 15 | test "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 | ||
| 27 | test "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 | 11 | // backend=stage2 |
| 12 | 12 | // 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 | 15 | // :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 | 17 | // backend=stage2 |
| 18 | 18 | // 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 | 21 | // :?:?: note: enum declared here |