authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-30 15:35:30-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-30 15:46:11-04:00
log78f32259daa79aaced1c49bcdace936121a2ccc5
treedbb2441806129661ae061bd2ff825d7e84453152
parent7878f9660fcb8fa64fda0b2b67320fe9ddc2727b
signaturelock-open Commit is signed but in an unrecognized format.

default struct field initialization expressions

closes #485

7 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#}
22652265
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#}
2272const Foo = struct {
2273 a: i32 = 1234,
2274 b: i32,
2275};
2276
2277test "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 the2289 <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 struct1070 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_index1073 uint32_t bit_offset_in_host; // offset from the memory at gen_index
1073 uint32_t host_int_bytes; // size of host integer1074 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}
967967
968static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,968ConstExprValue *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;
21912189
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
251251
252void src_assert(bool ok, AstNode *source_node);252void src_assert(bool ok, AstNode *source_node);
253bool is_container(ZigType *type_entry);253bool is_container(ZigType *type_entry);
254ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name);
254255
255#endif256#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
1788717887
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");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub 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 );
54865501
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
564test "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}