Skip to content

Commit 6e3e23a

Browse files
committed
compiler: implement decl literals
Resolves: #9938
1 parent 9e683f0 commit 6e3e23a

8 files changed

+149
-16
lines changed

lib/std/zig/AstGen.zig

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
10281028
const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
10291029
return blockExpr(gz, scope, ri, node, statements, .normal);
10301030
},
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),
10321043
.error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
10331044
// TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
10341045
// .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
27522763
.err_union_code_ptr,
27532764
.ptr_type,
27542765
.enum_literal,
2766+
.decl_literal,
2767+
.decl_literal_no_coerce,
27552768
.merge_error_sets,
27562769
.error_union_type,
27572770
.bit_not,
@@ -5889,22 +5902,21 @@ fn tryExpr(
58895902
}
58905903
const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
58915904

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,
59025910
},
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" },
59045916
};
5917+
const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr };
59055918
// This could be a pointer or value depending on the `ri` parameter.
59065919
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";
59085920
const try_inst = try parent_gz.makeBlockInst(block_tag, node);
59095921
try parent_gz.instructions.append(astgen.gpa, try_inst);
59105922

@@ -9916,7 +9928,7 @@ fn callExpr(
99169928
) InnerError!Zir.Inst.Ref {
99179929
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);
99209932
const modifier: std.builtin.CallModifier = blk: {
99219933
if (gz.is_comptime) {
99229934
break :blk .compile_time;
@@ -10044,6 +10056,7 @@ const Callee = union(enum) {
1004410056
fn calleeExpr(
1004510057
gz: *GenZir,
1004610058
scope: *Scope,
10059+
call_rl: ResultInfo.Loc,
1004710060
node: Ast.Node.Index,
1004810061
) InnerError!Callee {
1004910062
const astgen = gz.astgen;
@@ -10070,6 +10083,19 @@ fn calleeExpr(
1007010083
.field_name_start = str_index,
1007110084
} };
1007210085
},
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+
},
1007310099
else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },
1007410100
}
1007510101
}

lib/std/zig/Zir.zig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,14 @@ pub const Inst = struct {
651651
err_union_code_ptr,
652652
/// An enum literal. Uses the `str_tok` union field.
653653
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,
654662
/// A switch expression. Uses the `pl_node` union field.
655663
/// AST node is the switch, payload is `SwitchBlock`.
656664
switch_block,
@@ -1144,6 +1152,8 @@ pub const Inst = struct {
11441152
.err_union_code_ptr,
11451153
.ptr_type,
11461154
.enum_literal,
1155+
.decl_literal,
1156+
.decl_literal_no_coerce,
11471157
.merge_error_sets,
11481158
.error_union_type,
11491159
.bit_not,
@@ -1442,6 +1452,8 @@ pub const Inst = struct {
14421452
.err_union_code_ptr,
14431453
.ptr_type,
14441454
.enum_literal,
1455+
.decl_literal,
1456+
.decl_literal_no_coerce,
14451457
.merge_error_sets,
14461458
.error_union_type,
14471459
.bit_not,
@@ -1697,6 +1709,8 @@ pub const Inst = struct {
16971709
.err_union_code = .un_node,
16981710
.err_union_code_ptr = .un_node,
16991711
.enum_literal = .str_tok,
1712+
.decl_literal = .pl_node,
1713+
.decl_literal_no_coerce = .pl_node,
17001714
.switch_block = .pl_node,
17011715
.switch_block_ref = .pl_node,
17021716
.switch_block_err_union = .pl_node,
@@ -3842,6 +3856,8 @@ fn findDeclsInner(
38423856
.err_union_code,
38433857
.err_union_code_ptr,
38443858
.enum_literal,
3859+
.decl_literal,
3860+
.decl_literal_no_coerce,
38453861
.validate_deref,
38463862
.validate_destructure,
38473863
.field_type_ref,

src/Sema.zig

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,8 @@ fn analyzeBodyInner(
10721072
.indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
10731073
.vector_elem_type => try sema.zirVectorElemType(block, inst),
10741074
.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),
10751077
.int_from_enum => try sema.zirIntFromEnum(block, inst),
10761078
.enum_from_int => try sema.zirEnumFromInt(block, inst),
10771079
.err_union_code => try sema.zirErrUnionCode(block, inst),
@@ -8874,6 +8876,54 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
88748876
})));
88758877
}
88768878

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+
88778927
fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
88788928
const pt = sema.pt;
88798929
const zcu = pt.zcu;

src/print_zir.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@ const Writer = struct {
462462

463463
.field_val,
464464
.field_ptr,
465+
.decl_literal,
466+
.decl_literal_no_coerce,
465467
=> try self.writePlNodeField(stream, inst),
466468

467469
.field_ptr_named,

test/behavior.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ test {
2121
_ = @import("behavior/cast_int.zig");
2222
_ = @import("behavior/comptime_memory.zig");
2323
_ = @import("behavior/const_slice_child.zig");
24+
_ = @import("behavior/decl_literals.zig");
2425
_ = @import("behavior/decltest.zig");
2526
_ = @import("behavior/duplicated_test_names.zig");
2627
_ = @import("behavior/defer.zig");

test/behavior/decl_literals.zig

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ export fn entry() void {
1111
// backend=stage2
1212
// target=native
1313
//
14-
// :6:21: error: no field named 'c' in enum 'tmp.Foo'
14+
// :6:21: error: enum 'tmp.Foo' has no member named 'c'
1515
// :1:13: note: enum declared here

test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ pub export fn entry() void {
1717
// backend=stage2
1818
// target=native
1919
//
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'
2121
// :?:?: note: enum declared here

0 commit comments

Comments
 (0)