add syntax for comptime struct fields

This commit is contained in:
Andrew Kelley 2019-12-08 12:13:34 -05:00
parent 119ed128c0
commit fe8d65556d
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
6 changed files with 61 additions and 21 deletions

View File

@ -1006,6 +1006,7 @@ struct AstNodeStructField {
// populated if the "align(A)" is present
AstNode *align_expr;
Buf doc_comments;
Token *comptime_token;
};
struct AstNodeStringLiteral {

View File

@ -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"));

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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);
}