| 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,6 +1006,7 @@ struct AstNodeStructField { |
| 1006 | // populated if the "align(A)" is present | 1006 | // populated if the "align(A)" is present |
| 1007 | AstNode *align_expr; | 1007 | AstNode *align_expr; |
| 1008 | Buf doc_comments; | 1008 | Buf doc_comments; |
| 1009 | Token *comptime_token; | ||
| 1009 | }; | 1010 | }; |
| 1010 | 1011 | ||
| 1011 | struct AstNodeStringLiteral { | 1012 | struct AstNodeStringLiteral { |
src/analyze.cpp+10| ... | @@ -2736,6 +2736,16 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { | ... | @@ -2736,6 +2736,16 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { |
| 2736 | field_node = decl_node->data.container_decl.fields.at(i); | 2736 | field_node = decl_node->data.container_decl.fields.at(i); |
| 2737 | type_struct_field->name = field_node->data.struct_field.name; | 2737 | type_struct_field->name = field_node->data.struct_field.name; |
| 2738 | type_struct_field->decl_node = field_node; | 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 | if (field_node->data.struct_field.type == nullptr) { | 2750 | if (field_node->data.struct_field.type == nullptr) { |
| 2741 | add_node_error(g, field_node, buf_sprintf("struct field missing type")); | 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,6 +19514,18 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, |
| 19514 | return ira->codegen->invalid_instruction; | 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 | static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr, | 19529 | static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr, |
| 19518 | TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing) | 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,6 +19535,7 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction |
| 19523 | return ira->codegen->invalid_instruction; | 19535 | return ira->codegen->invalid_instruction; |
| 19524 | if (field->is_comptime) { | 19536 | if (field->is_comptime) { |
| 19525 | IrInstruction *elem = ir_const(ira, source_instr, field_type); | 19537 | IrInstruction *elem = ir_const(ira, source_instr, field_type); |
| 19538 | memoize_field_init_val(ira->codegen, struct_type, field); | ||
| 19526 | copy_const_val(elem->value, field->init_val); | 19539 | copy_const_val(elem->value, field->init_val); |
| 19527 | return ir_get_ref(ira, source_instr, elem, true, false); | 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,25 +21569,12 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc |
| 21556 | 21569 | ||
| 21557 | // look for a default field value | 21570 | // look for a default field value |
| 21558 | TypeStructField *field = container_type->data.structure.fields[i]; | 21571 | TypeStructField *field = container_type->data.structure.fields[i]; |
| 21572 | memoize_field_init_val(ira->codegen, container_type, field); | ||
| 21559 | if (field->init_val == nullptr) { | 21573 | if (field->init_val == nullptr) { |
| 21560 | // it's not memoized. time to go analyze it | 21574 | ir_add_error_node(ira, instruction->source_node, |
| 21561 | AstNode *init_node; | 21575 | buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name))); |
| 21562 | if (field->decl_node->type == NodeTypeStructField) { | 21576 | any_missing = true; |
| 21563 | init_node = field->decl_node->data.struct_field.value; | 21577 | continue; |
| 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); | ||
| 21578 | } | 21578 | } |
| 21579 | if (type_is_invalid(field->init_val->type)) | 21579 | if (type_is_invalid(field->init_val->type)) |
| 21580 | return ira->codegen->invalid_instruction; | 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,8 +536,8 @@ static void ast_parse_container_doc_comments(ParseContext *pc, Buf *buf) { |
| 536 | // <- TestDecl ContainerMembers | 536 | // <- TestDecl ContainerMembers |
| 537 | // / TopLevelComptime ContainerMembers | 537 | // / TopLevelComptime ContainerMembers |
| 538 | // / KEYWORD_pub? TopLevelDecl ContainerMembers | 538 | // / KEYWORD_pub? TopLevelDecl ContainerMembers |
| 539 | // / ContainerField COMMA ContainerMembers | 539 | // / KEYWORD_comptime? ContainerField COMMA ContainerMembers |
| 540 | // / ContainerField | 540 | // / KEYWORD_comptime? ContainerField |
| 541 | // / | 541 | // / |
| 542 | static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { | 542 | static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { |
| 543 | AstNodeContainerDecl res = {}; | 543 | AstNodeContainerDecl res = {}; |
| ... | @@ -574,10 +574,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { | ... | @@ -574,10 +574,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { |
| 574 | ast_error(pc, peek_token(pc), "expected function or variable declaration after pub"); | 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 | AstNode *container_field = ast_parse_container_field(pc); | 579 | AstNode *container_field = ast_parse_container_field(pc); |
| 578 | if (container_field != nullptr) { | 580 | if (container_field != nullptr) { |
| 579 | assert(container_field->type == NodeTypeStructField); | 581 | assert(container_field->type == NodeTypeStructField); |
| 580 | container_field->data.struct_field.doc_comments = doc_comment_buf; | 582 | container_field->data.struct_field.doc_comments = doc_comment_buf; |
| 583 | container_field->data.struct_field.comptime_token = comptime_token; | ||
| 581 | res.fields.append(container_field); | 584 | res.fields.append(container_field); |
| 582 | if (eat_token_if(pc, TokenIdComma) != nullptr) { | 585 | if (eat_token_if(pc, TokenIdComma) != nullptr) { |
| 583 | continue; | 586 | continue; |
| ... | @@ -612,6 +615,13 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { | ... | @@ -612,6 +615,13 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { |
| 612 | if (comptime == nullptr) | 615 | if (comptime == nullptr) |
| 613 | return nullptr; | 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 | AstNode *block = ast_expect(pc, ast_parse_block_expr); | 625 | AstNode *block = ast_expect(pc, ast_parse_block_expr); |
| 616 | AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); | 626 | AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); |
| 617 | res->data.comptime_expr.expr = block; | 627 | res->data.comptime_expr.expr = block; |
test/compile_errors.zig+10-1| ... | @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 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 | cases.add( | 14 | cases.add( |
| 6 | "bad usage of @call", | 15 | "bad usage of @call", |
| 7 | \\export fn entry1() void { | 16 | \\export fn entry1() void { |
| ... | @@ -32,7 +41,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -32,7 +41,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 32 | "tmp.zig:15:43: error: unable to evaluate constant expression", | 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 | \\export async fn foo() void {} | 45 | \\export async fn foo() void {} |
| 37 | , "tmp.zig:1:1: error: exported function cannot be async"); | 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,3 +789,13 @@ test "struct with var field" { |
| 789 | expect(pt.x == 1); | 789 | expect(pt.x == 1); |
| 790 | expect(pt.y == 2); | 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 | } |