mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
add syntax for comptime struct fields
This commit is contained in:
parent
119ed128c0
commit
fe8d65556d
@ -1006,6 +1006,7 @@ struct AstNodeStructField {
|
||||
// populated if the "align(A)" is present
|
||||
AstNode *align_expr;
|
||||
Buf doc_comments;
|
||||
Token *comptime_token;
|
||||
};
|
||||
|
||||
struct AstNodeStringLiteral {
|
||||
|
||||
@ -2736,6 +2736,16 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
|
||||
field_node = decl_node->data.container_decl.fields.at(i);
|
||||
type_struct_field->name = field_node->data.struct_field.name;
|
||||
type_struct_field->decl_node = field_node;
|
||||
if (field_node->data.struct_field.comptime_token != nullptr) {
|
||||
if (field_node->data.struct_field.value == nullptr) {
|
||||
add_token_error(g, field_node->owner,
|
||||
field_node->data.struct_field.comptime_token,
|
||||
buf_sprintf("comptime struct field missing initialization value"));
|
||||
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
type_struct_field->is_comptime = true;
|
||||
}
|
||||
|
||||
if (field_node->data.struct_field.type == nullptr) {
|
||||
add_node_error(g, field_node, buf_sprintf("struct field missing type"));
|
||||
|
||||
36
src/ir.cpp
36
src/ir.cpp
@ -19514,6 +19514,18 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) {
|
||||
if (field->init_val != nullptr) return;
|
||||
if (field->decl_node->type != NodeTypeStructField) return;
|
||||
AstNode *init_node = field->decl_node->data.struct_field.value;
|
||||
if (init_node == nullptr) return;
|
||||
// scope is not the scope of the struct init, it's the scope of the struct type decl
|
||||
Scope *analyze_scope = &get_container_scope(container_type)->base;
|
||||
// memoize it
|
||||
field->init_val = analyze_const_value(codegen, analyze_scope, init_node,
|
||||
field->type_entry, nullptr, UndefOk);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr,
|
||||
TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing)
|
||||
{
|
||||
@ -19523,6 +19535,7 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
|
||||
return ira->codegen->invalid_instruction;
|
||||
if (field->is_comptime) {
|
||||
IrInstruction *elem = ir_const(ira, source_instr, field_type);
|
||||
memoize_field_init_val(ira->codegen, struct_type, field);
|
||||
copy_const_val(elem->value, field->init_val);
|
||||
return ir_get_ref(ira, source_instr, elem, true, false);
|
||||
}
|
||||
@ -21556,25 +21569,12 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
|
||||
|
||||
// look for a default field value
|
||||
TypeStructField *field = container_type->data.structure.fields[i];
|
||||
memoize_field_init_val(ira->codegen, container_type, field);
|
||||
if (field->init_val == nullptr) {
|
||||
// it's not memoized. time to go analyze it
|
||||
AstNode *init_node;
|
||||
if (field->decl_node->type == NodeTypeStructField) {
|
||||
init_node = field->decl_node->data.struct_field.value;
|
||||
} else {
|
||||
init_node = nullptr;
|
||||
}
|
||||
if (init_node == nullptr) {
|
||||
ir_add_error_node(ira, instruction->source_node,
|
||||
buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name)));
|
||||
any_missing = true;
|
||||
continue;
|
||||
}
|
||||
// scope is not the scope of the struct init, it's the scope of the struct type decl
|
||||
Scope *analyze_scope = &get_container_scope(container_type)->base;
|
||||
// memoize it
|
||||
field->init_val = analyze_const_value(ira->codegen, analyze_scope, init_node,
|
||||
field->type_entry, nullptr, UndefOk);
|
||||
ir_add_error_node(ira, instruction->source_node,
|
||||
buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name)));
|
||||
any_missing = true;
|
||||
continue;
|
||||
}
|
||||
if (type_is_invalid(field->init_val->type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
@ -536,8 +536,8 @@ static void ast_parse_container_doc_comments(ParseContext *pc, Buf *buf) {
|
||||
// <- TestDecl ContainerMembers
|
||||
// / TopLevelComptime ContainerMembers
|
||||
// / KEYWORD_pub? TopLevelDecl ContainerMembers
|
||||
// / ContainerField COMMA ContainerMembers
|
||||
// / ContainerField
|
||||
// / KEYWORD_comptime? ContainerField COMMA ContainerMembers
|
||||
// / KEYWORD_comptime? ContainerField
|
||||
// /
|
||||
static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
|
||||
AstNodeContainerDecl res = {};
|
||||
@ -574,10 +574,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
|
||||
ast_error(pc, peek_token(pc), "expected function or variable declaration after pub");
|
||||
}
|
||||
|
||||
Token *comptime_token = eat_token_if(pc, TokenIdKeywordCompTime);
|
||||
|
||||
AstNode *container_field = ast_parse_container_field(pc);
|
||||
if (container_field != nullptr) {
|
||||
assert(container_field->type == NodeTypeStructField);
|
||||
container_field->data.struct_field.doc_comments = doc_comment_buf;
|
||||
container_field->data.struct_field.comptime_token = comptime_token;
|
||||
res.fields.append(container_field);
|
||||
if (eat_token_if(pc, TokenIdComma) != nullptr) {
|
||||
continue;
|
||||
@ -612,6 +615,13 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) {
|
||||
if (comptime == nullptr)
|
||||
return nullptr;
|
||||
|
||||
// 1 token lookahead because it could be a comptime struct field
|
||||
Token *lbrace = peek_token(pc);
|
||||
if (lbrace->id != TokenIdLBrace) {
|
||||
put_back_token(pc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *block = ast_expect(pc, ast_parse_block_expr);
|
||||
AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime);
|
||||
res->data.comptime_expr.expr = block;
|
||||
|
||||
@ -2,6 +2,15 @@ const tests = @import("tests.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.add("comptime struct field, no init value",
|
||||
\\const Foo = struct {
|
||||
\\ comptime b: i32,
|
||||
\\};
|
||||
\\export fn entry() void {
|
||||
\\ var f: Foo = undefined;
|
||||
\\}
|
||||
, "tmp.zig:2:5: error: comptime struct field missing initialization value");
|
||||
|
||||
cases.add(
|
||||
"bad usage of @call",
|
||||
\\export fn entry1() void {
|
||||
@ -32,7 +41,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
"tmp.zig:15:43: error: unable to evaluate constant expression",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
cases.add("exported async function",
|
||||
\\export async fn foo() void {}
|
||||
, "tmp.zig:1:1: error: exported function cannot be async");
|
||||
|
||||
|
||||
@ -789,3 +789,13 @@ test "struct with var field" {
|
||||
expect(pt.x == 1);
|
||||
expect(pt.y == 2);
|
||||
}
|
||||
|
||||
test "comptime struct field" {
|
||||
const T = struct {
|
||||
a: i32,
|
||||
comptime b: i32 = 1234,
|
||||
};
|
||||
|
||||
var foo: T = undefined;
|
||||
comptime expect(foo.b == 1234);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user