| author | |
| committer | |
| log | fe8d65556dc49bb0da7ee621597c3b24f0e879f6 |
| tree | 18af2a2e0e21b1dbec889564f9e49b7992ed20b2 |
| parent | 119ed128c05e95a4779a00cd87d3e2d5b01f9f17 |
| signature | Commit is signed but in an unrecognized format. |
6 files changed, 61 insertions(+), 21 deletions(-)
src/all_types.hpp+1| ... | ... | @@ -1006,6 +1006,7 @@ struct AstNodeStructField { |
| 1006 | 1006 | // populated if the "align(A)" is present |
| 1007 | 1007 | AstNode *align_expr; |
| 1008 | 1008 | Buf doc_comments; |
| 1009 | Token *comptime_token; | |
| 1009 | 1010 | }; |
| 1010 | 1011 | |
| 1011 | 1012 | struct AstNodeStringLiteral { |
src/analyze.cpp+10| ... | ... | @@ -2736,6 +2736,16 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { |
| 2736 | 2736 | field_node = decl_node->data.container_decl.fields.at(i); |
| 2737 | 2737 | type_struct_field->name = field_node->data.struct_field.name; |
| 2738 | 2738 | type_struct_field->decl_node = field_node; |
| 2739 | if (field_node->data.struct_field.comptime_token != nullptr) { | |
| 2740 | if (field_node->data.struct_field.value == nullptr) { | |
| 2741 | add_token_error(g, field_node->owner, | |
| 2742 | field_node->data.struct_field.comptime_token, | |
| 2743 | buf_sprintf("comptime struct field missing initialization value")); | |
| 2744 | struct_type->data.structure.resolve_status = ResolveStatusInvalid; | |
| 2745 | return ErrorSemanticAnalyzeFail; | |
| 2746 | } | |
| 2747 | type_struct_field->is_comptime = true; | |
| 2748 | } | |
| 2739 | 2749 | |
| 2740 | 2750 | if (field_node->data.struct_field.type == nullptr) { |
| 2741 | 2751 | add_node_error(g, field_node, buf_sprintf("struct field missing type")); |
src/ir.cpp+18-18| ... | ... | @@ -19514,6 +19514,18 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, |
| 19514 | 19514 | return ira->codegen->invalid_instruction; |
| 19515 | 19515 | } |
| 19516 | 19516 | |
| 19517 | static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) { | |
| 19518 | if (field->init_val != nullptr) return; | |
| 19519 | if (field->decl_node->type != NodeTypeStructField) return; | |
| 19520 | AstNode *init_node = field->decl_node->data.struct_field.value; | |
| 19521 | if (init_node == nullptr) return; | |
| 19522 | // scope is not the scope of the struct init, it's the scope of the struct type decl | |
| 19523 | Scope *analyze_scope = &get_container_scope(container_type)->base; | |
| 19524 | // memoize it | |
| 19525 | field->init_val = analyze_const_value(codegen, analyze_scope, init_node, | |
| 19526 | field->type_entry, nullptr, UndefOk); | |
| 19527 | } | |
| 19528 | ||
| 19517 | 19529 | static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr, |
| 19518 | 19530 | TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing) |
| 19519 | 19531 | { |
| ... | ... | @@ -19523,6 +19535,7 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction |
| 19523 | 19535 | return ira->codegen->invalid_instruction; |
| 19524 | 19536 | if (field->is_comptime) { |
| 19525 | 19537 | IrInstruction *elem = ir_const(ira, source_instr, field_type); |
| 19538 | memoize_field_init_val(ira->codegen, struct_type, field); | |
| 19526 | 19539 | copy_const_val(elem->value, field->init_val); |
| 19527 | 19540 | return ir_get_ref(ira, source_instr, elem, true, false); |
| 19528 | 19541 | } |
| ... | ... | @@ -21556,25 +21569,12 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc |
| 21556 | 21569 | |
| 21557 | 21570 | // look for a default field value |
| 21558 | 21571 | TypeStructField *field = container_type->data.structure.fields[i]; |
| 21572 | memoize_field_init_val(ira->codegen, container_type, field); | |
| 21559 | 21573 | if (field->init_val == nullptr) { |
| 21560 | // it's not memoized. time to go analyze it | |
| 21561 | AstNode *init_node; | |
| 21562 | if (field->decl_node->type == NodeTypeStructField) { | |
| 21563 | init_node = field->decl_node->data.struct_field.value; | |
| 21564 | } else { | |
| 21565 | init_node = nullptr; | |
| 21566 | } | |
| 21567 | if (init_node == nullptr) { | |
| 21568 | ir_add_error_node(ira, instruction->source_node, | |
| 21569 | buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name))); | |
| 21570 | any_missing = true; | |
| 21571 | continue; | |
| 21572 | } | |
| 21573 | // scope is not the scope of the struct init, it's the scope of the struct type decl | |
| 21574 | Scope *analyze_scope = &get_container_scope(container_type)->base; | |
| 21575 | // memoize it | |
| 21576 | field->init_val = analyze_const_value(ira->codegen, analyze_scope, init_node, | |
| 21577 | field->type_entry, nullptr, UndefOk); | |
| 21574 | ir_add_error_node(ira, instruction->source_node, | |
| 21575 | buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name))); | |
| 21576 | any_missing = true; | |
| 21577 | continue; | |
| 21578 | 21578 | } |
| 21579 | 21579 | if (type_is_invalid(field->init_val->type)) |
| 21580 | 21580 | return ira->codegen->invalid_instruction; |
src/parser.cpp+12-2| ... | ... | @@ -536,8 +536,8 @@ static void ast_parse_container_doc_comments(ParseContext *pc, Buf *buf) { |
| 536 | 536 | // <- TestDecl ContainerMembers |
| 537 | 537 | // / TopLevelComptime ContainerMembers |
| 538 | 538 | // / KEYWORD_pub? TopLevelDecl ContainerMembers |
| 539 | // / ContainerField COMMA ContainerMembers | |
| 540 | // / ContainerField | |
| 539 | // / KEYWORD_comptime? ContainerField COMMA ContainerMembers | |
| 540 | // / KEYWORD_comptime? ContainerField | |
| 541 | 541 | // / |
| 542 | 542 | static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { |
| 543 | 543 | AstNodeContainerDecl res = {}; |
| ... | ... | @@ -574,10 +574,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { |
| 574 | 574 | ast_error(pc, peek_token(pc), "expected function or variable declaration after pub"); |
| 575 | 575 | } |
| 576 | 576 | |
| 577 | Token *comptime_token = eat_token_if(pc, TokenIdKeywordCompTime); | |
| 578 | ||
| 577 | 579 | AstNode *container_field = ast_parse_container_field(pc); |
| 578 | 580 | if (container_field != nullptr) { |
| 579 | 581 | assert(container_field->type == NodeTypeStructField); |
| 580 | 582 | container_field->data.struct_field.doc_comments = doc_comment_buf; |
| 583 | container_field->data.struct_field.comptime_token = comptime_token; | |
| 581 | 584 | res.fields.append(container_field); |
| 582 | 585 | if (eat_token_if(pc, TokenIdComma) != nullptr) { |
| 583 | 586 | continue; |
| ... | ... | @@ -612,6 +615,13 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { |
| 612 | 615 | if (comptime == nullptr) |
| 613 | 616 | return nullptr; |
| 614 | 617 | |
| 618 | // 1 token lookahead because it could be a comptime struct field | |
| 619 | Token *lbrace = peek_token(pc); | |
| 620 | if (lbrace->id != TokenIdLBrace) { | |
| 621 | put_back_token(pc); | |
| 622 | return nullptr; | |
| 623 | } | |
| 624 | ||
| 615 | 625 | AstNode *block = ast_expect(pc, ast_parse_block_expr); |
| 616 | 626 | AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); |
| 617 | 627 | res->data.comptime_expr.expr = block; |
test/compile_errors.zig+10-1| ... | ... | @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add("comptime struct field, no init value", | |
| 6 | \\const Foo = struct { | |
| 7 | \\ comptime b: i32, | |
| 8 | \\}; | |
| 9 | \\export fn entry() void { | |
| 10 | \\ var f: Foo = undefined; | |
| 11 | \\} | |
| 12 | , "tmp.zig:2:5: error: comptime struct field missing initialization value"); | |
| 13 | ||
| 5 | 14 | cases.add( |
| 6 | 15 | "bad usage of @call", |
| 7 | 16 | \\export fn entry1() void { |
| ... | ... | @@ -32,7 +41,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 32 | 41 | "tmp.zig:15:43: error: unable to evaluate constant expression", |
| 33 | 42 | ); |
| 34 | 43 | |
| 35 | cases.add( | |
| 44 | cases.add("exported async function", | |
| 36 | 45 | \\export async fn foo() void {} |
| 37 | 46 | , "tmp.zig:1:1: error: exported function cannot be async"); |
| 38 | 47 |
test/stage1/behavior/struct.zig+10| ... | ... | @@ -789,3 +789,13 @@ test "struct with var field" { |
| 789 | 789 | expect(pt.x == 1); |
| 790 | 790 | expect(pt.y == 2); |
| 791 | 791 | } |
| 792 | ||
| 793 | test "comptime struct field" { | |
| 794 | const T = struct { | |
| 795 | a: i32, | |
| 796 | comptime b: i32 = 1234, | |
| 797 | }; | |
| 798 | ||
| 799 | var foo: T = undefined; | |
| 800 | comptime expect(foo.b == 1234); | |
| 801 | } |