| author | |
| committer | |
| log | 78f32259daa79aaced1c49bcdace936121a2ccc5 |
| tree | dbb2441806129661ae061bd2ff825d7e84453152 |
| parent | 7878f9660fcb8fa64fda0b2b67320fe9ddc2727b |
| signature |
closes #4857 files changed, 89 insertions(+), 23 deletions(-)
doc/langref.html.in+22| ... | @@ -2263,6 +2263,28 @@ test "linked list" { | ... | @@ -2263,6 +2263,28 @@ test "linked list" { |
| 2263 | } | 2263 | } |
| 2264 | {#code_end#} | 2264 | {#code_end#} |
| 2265 | 2265 | ||
| 2266 | {#header_open|Default Field Values#} | ||
| 2267 | <p> | ||
| 2268 | Each struct field may have an expression indicating the default field value. Such expressions | ||
| 2269 | are executed at {#link|comptime#}, and allow the field to be omitted in a struct literal expression: | ||
| 2270 | </p> | ||
| 2271 | {#code_begin|test#} | ||
| 2272 | const Foo = struct { | ||
| 2273 | a: i32 = 1234, | ||
| 2274 | b: i32, | ||
| 2275 | }; | ||
| 2276 | |||
| 2277 | test "default struct initialization fields" { | ||
| 2278 | const x = Foo{ | ||
| 2279 | .b = 5, | ||
| 2280 | }; | ||
| 2281 | if (x.a + x.b != 1239) { | ||
| 2282 | @compileError("it's even comptime known!"); | ||
| 2283 | } | ||
| 2284 | } | ||
| 2285 | {#code_end#} | ||
| 2286 | {#header_close#} | ||
| 2287 | |||
| 2266 | {#header_open|extern struct#} | 2288 | {#header_open|extern struct#} |
| 2267 | <p>An {#syntax#}extern struct{#endsyntax#} has in-memory layout guaranteed to match the | 2289 | <p>An {#syntax#}extern struct{#endsyntax#} has in-memory layout guaranteed to match the |
| 2268 | C ABI for the target.</p> | 2290 | C ABI for the target.</p> |
src/all_types.hpp+1| ... | @@ -1069,6 +1069,7 @@ struct TypeStructField { | ... | @@ -1069,6 +1069,7 @@ struct TypeStructField { |
| 1069 | size_t gen_index; | 1069 | size_t gen_index; |
| 1070 | size_t offset; // byte offset from beginning of struct | 1070 | size_t offset; // byte offset from beginning of struct |
| 1071 | AstNode *decl_node; | 1071 | AstNode *decl_node; |
| 1072 | ConstExprValue *init_val; // null and then memoized | ||
| 1072 | uint32_t bit_offset_in_host; // offset from the memory at gen_index | 1073 | uint32_t bit_offset_in_host; // offset from the memory at gen_index |
| 1073 | uint32_t host_int_bytes; // size of host integer | 1074 | uint32_t host_int_bytes; // size of host integer |
| 1074 | }; | 1075 | }; |
src/analyze.cpp+1-7| ... | @@ -965,9 +965,7 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind | ... | @@ -965,9 +965,7 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind |
| 965 | return entry; | 965 | return entry; |
| 966 | } | 966 | } |
| 967 | 967 | ||
| 968 | static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, | 968 | ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name) { |
| 969 | Buf *type_name) | ||
| 970 | { | ||
| 971 | size_t backward_branch_count = 0; | 969 | size_t backward_branch_count = 0; |
| 972 | size_t backward_branch_quota = default_backward_branch_quota; | 970 | size_t backward_branch_quota = default_backward_branch_quota; |
| 973 | return ir_eval_const_value(g, scope, node, type_entry, | 971 | return ir_eval_const_value(g, scope, node, type_entry, |
| ... | @@ -2189,10 +2187,6 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { | ... | @@ -2189,10 +2187,6 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { |
| 2189 | type_struct_field->src_index = i; | 2187 | type_struct_field->src_index = i; |
| 2190 | type_struct_field->gen_index = SIZE_MAX; | 2188 | type_struct_field->gen_index = SIZE_MAX; |
| 2191 | 2189 | ||
| 2192 | if (field_node->data.struct_field.value != nullptr) { | ||
| 2193 | add_node_error(g, field_node->data.struct_field.value, | ||
| 2194 | buf_sprintf("enums, not structs, support field assignment")); | ||
| 2195 | } | ||
| 2196 | if (field_type->id == ZigTypeIdOpaque) { | 2190 | if (field_type->id == ZigTypeIdOpaque) { |
| 2197 | add_node_error(g, field_node->data.struct_field.type, | 2191 | add_node_error(g, field_node->data.struct_field.type, |
| 2198 | buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs")); | 2192 | buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs")); |
src/analyze.hpp+1| ... | @@ -251,5 +251,6 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa | ... | @@ -251,5 +251,6 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa |
| 251 | 251 | ||
| 252 | void src_assert(bool ok, AstNode *source_node); | 252 | void src_assert(bool ok, AstNode *source_node); |
| 253 | bool is_container(ZigType *type_entry); | 253 | bool is_container(ZigType *type_entry); |
| 254 | ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name); | ||
| 254 | 255 | ||
| 255 | #endif | 256 | #endif |
src/ir.cpp+31-4| ... | @@ -17887,10 +17887,37 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc | ... | @@ -17887,10 +17887,37 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc |
| 17887 | 17887 | ||
| 17888 | bool any_missing = false; | 17888 | bool any_missing = false; |
| 17889 | for (size_t i = 0; i < actual_field_count; i += 1) { | 17889 | for (size_t i = 0; i < actual_field_count; i += 1) { |
| 17890 | if (!field_assign_nodes[i]) { | 17890 | if (field_assign_nodes[i]) continue; |
| 17891 | ir_add_error_node(ira, instruction->source_node, | 17891 | |
| 17892 | buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); | 17892 | // look for a default field value |
| 17893 | any_missing = true; | 17893 | TypeStructField *field = &container_type->data.structure.fields[i]; |
| 17894 | if (field->init_val == nullptr) { | ||
| 17895 | // it's not memoized. time to go analyze it | ||
| 17896 | assert(field->decl_node->type == NodeTypeStructField); | ||
| 17897 | AstNode *init_node = field->decl_node->data.struct_field.value; | ||
| 17898 | if (init_node == nullptr) { | ||
| 17899 | ir_add_error_node(ira, instruction->source_node, | ||
| 17900 | buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name))); | ||
| 17901 | any_missing = true; | ||
| 17902 | continue; | ||
| 17903 | } | ||
| 17904 | // scope is not the scope of the struct init, it's the scope of the struct type decl | ||
| 17905 | Scope *analyze_scope = &get_container_scope(container_type)->base; | ||
| 17906 | // memoize it | ||
| 17907 | field->init_val = analyze_const_value(ira->codegen, analyze_scope, init_node, | ||
| 17908 | field->type_entry, nullptr); | ||
| 17909 | } | ||
| 17910 | if (type_is_invalid(field->init_val->type)) | ||
| 17911 | return ira->codegen->invalid_instruction; | ||
| 17912 | |||
| 17913 | IrInstruction *runtime_inst = ir_const(ira, instruction, field->init_val->type); | ||
| 17914 | copy_const_val(&runtime_inst->value, field->init_val, true); | ||
| 17915 | |||
| 17916 | new_fields[i].value = runtime_inst; | ||
| 17917 | new_fields[i].type_struct_field = field; | ||
| 17918 | |||
| 17919 | if (const_val.special == ConstValSpecialStatic) { | ||
| 17920 | copy_const_val(&const_val.data.x_struct.fields[i], field->init_val, true); | ||
| 17894 | } | 17921 | } |
| 17895 | } | 17922 | } |
| 17896 | if (any_missing) | 17923 | if (any_missing) |
test/compile_errors.zig+15-12| ... | @@ -2,6 +2,21 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,21 @@ 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( | ||
| 6 | "compile error in struct init expression", | ||
| 7 | \\const Foo = struct { | ||
| 8 | \\ a: i32 = crap, | ||
| 9 | \\ b: i32, | ||
| 10 | \\}; | ||
| 11 | \\export fn entry() void { | ||
| 12 | \\ var x = Foo{ | ||
| 13 | \\ .b = 5, | ||
| 14 | \\ }; | ||
| 15 | \\} | ||
| 16 | , | ||
| 17 | "tmp.zig:2:14: error: use of undeclared identifier 'crap'", | ||
| 18 | ); | ||
| 19 | |||
| 5 | cases.add( | 20 | cases.add( |
| 6 | "undefined as field type is rejected", | 21 | "undefined as field type is rejected", |
| 7 | \\const Foo = struct { | 22 | \\const Foo = struct { |
| ... | @@ -5484,18 +5499,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -5484,18 +5499,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5484 | "tmp.zig:10:31: error: expected type 'u2', found 'u3'", | 5499 | "tmp.zig:10:31: error: expected type 'u2', found 'u3'", |
| 5485 | ); | 5500 | ); |
| 5486 | 5501 | ||
| 5487 | cases.add( | ||
| 5488 | "struct fields with value assignments", | ||
| 5489 | \\const MultipleChoice = struct { | ||
| 5490 | \\ A: i32 = 20, | ||
| 5491 | \\}; | ||
| 5492 | \\export fn entry() void { | ||
| 5493 | \\ var x: MultipleChoice = undefined; | ||
| 5494 | \\} | ||
| 5495 | , | ||
| 5496 | "tmp.zig:2:14: error: enums, not structs, support field assignment", | ||
| 5497 | ); | ||
| 5498 | |||
| 5499 | cases.add( | 5502 | cases.add( |
| 5500 | "union fields with value assignments", | 5503 | "union fields with value assignments", |
| 5501 | \\const MultipleChoice = union { | 5504 | \\const MultipleChoice = union { |
test/stage1/behavior/struct.zig+18| ... | @@ -560,3 +560,21 @@ test "use within struct scope" { | ... | @@ -560,3 +560,21 @@ test "use within struct scope" { |
| 560 | }; | 560 | }; |
| 561 | expectEqual(i32(42), S.inner()); | 561 | expectEqual(i32(42), S.inner()); |
| 562 | } | 562 | } |
| 563 | |||
| 564 | test "default struct initialization fields" { | ||
| 565 | const S = struct { | ||
| 566 | a: i32 = 1234, | ||
| 567 | b: i32, | ||
| 568 | }; | ||
| 569 | const x = S{ | ||
| 570 | .b = 5, | ||
| 571 | }; | ||
| 572 | if (x.a + x.b != 1239) { | ||
| 573 | @compileError("it should be comptime known"); | ||
| 574 | } | ||
| 575 | var five: i32 = 5; | ||
| 576 | const y = S{ | ||
| 577 | .b = five, | ||
| 578 | }; | ||
| 579 | expectEqual(1239, x.a + x.b); | ||
| 580 | } |