default struct field initialization expressions

closes #485
This commit is contained in:
Andrew Kelley 2019-05-30 15:35:30 -04:00
parent 7878f9660f
commit 78f32259da
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
7 changed files with 89 additions and 23 deletions

View File

@ -2263,6 +2263,28 @@ test "linked list" {
}
{#code_end#}
{#header_open|Default Field Values#}
<p>
Each struct field may have an expression indicating the default field value. Such expressions
are executed at {#link|comptime#}, and allow the field to be omitted in a struct literal expression:
</p>
{#code_begin|test#}
const Foo = struct {
a: i32 = 1234,
b: i32,
};
test "default struct initialization fields" {
const x = Foo{
.b = 5,
};
if (x.a + x.b != 1239) {
@compileError("it's even comptime known!");
}
}
{#code_end#}
{#header_close#}
{#header_open|extern struct#}
<p>An {#syntax#}extern struct{#endsyntax#} has in-memory layout guaranteed to match the
C ABI for the target.</p>

View File

@ -1069,6 +1069,7 @@ struct TypeStructField {
size_t gen_index;
size_t offset; // byte offset from beginning of struct
AstNode *decl_node;
ConstExprValue *init_val; // null and then memoized
uint32_t bit_offset_in_host; // offset from the memory at gen_index
uint32_t host_int_bytes; // size of host integer
};

View File

@ -965,9 +965,7 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind
return entry;
}
static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry,
Buf *type_name)
{
ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name) {
size_t backward_branch_count = 0;
size_t backward_branch_quota = default_backward_branch_quota;
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) {
type_struct_field->src_index = i;
type_struct_field->gen_index = SIZE_MAX;
if (field_node->data.struct_field.value != nullptr) {
add_node_error(g, field_node->data.struct_field.value,
buf_sprintf("enums, not structs, support field assignment"));
}
if (field_type->id == ZigTypeIdOpaque) {
add_node_error(g, field_node->data.struct_field.type,
buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs"));

View File

@ -251,5 +251,6 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
void src_assert(bool ok, AstNode *source_node);
bool is_container(ZigType *type_entry);
ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name);
#endif

View File

@ -17887,10 +17887,37 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
bool any_missing = false;
for (size_t i = 0; i < actual_field_count; i += 1) {
if (!field_assign_nodes[i]) {
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;
if (field_assign_nodes[i]) continue;
// look for a default field value
TypeStructField *field = &container_type->data.structure.fields[i];
if (field->init_val == nullptr) {
// it's not memoized. time to go analyze it
assert(field->decl_node->type == NodeTypeStructField);
AstNode *init_node = field->decl_node->data.struct_field.value;
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);
}
if (type_is_invalid(field->init_val->type))
return ira->codegen->invalid_instruction;
IrInstruction *runtime_inst = ir_const(ira, instruction, field->init_val->type);
copy_const_val(&runtime_inst->value, field->init_val, true);
new_fields[i].value = runtime_inst;
new_fields[i].type_struct_field = field;
if (const_val.special == ConstValSpecialStatic) {
copy_const_val(&const_val.data.x_struct.fields[i], field->init_val, true);
}
}
if (any_missing)

View File

@ -2,6 +2,21 @@ const tests = @import("tests.zig");
const builtin = @import("builtin");
pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add(
"compile error in struct init expression",
\\const Foo = struct {
\\ a: i32 = crap,
\\ b: i32,
\\};
\\export fn entry() void {
\\ var x = Foo{
\\ .b = 5,
\\ };
\\}
,
"tmp.zig:2:14: error: use of undeclared identifier 'crap'",
);
cases.add(
"undefined as field type is rejected",
\\const Foo = struct {
@ -5484,18 +5499,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:10:31: error: expected type 'u2', found 'u3'",
);
cases.add(
"struct fields with value assignments",
\\const MultipleChoice = struct {
\\ A: i32 = 20,
\\};
\\export fn entry() void {
\\ var x: MultipleChoice = undefined;
\\}
,
"tmp.zig:2:14: error: enums, not structs, support field assignment",
);
cases.add(
"union fields with value assignments",
\\const MultipleChoice = union {

View File

@ -560,3 +560,21 @@ test "use within struct scope" {
};
expectEqual(i32(42), S.inner());
}
test "default struct initialization fields" {
const S = struct {
a: i32 = 1234,
b: i32,
};
const x = S{
.b = 5,
};
if (x.a + x.b != 1239) {
@compileError("it should be comptime known");
}
var five: i32 = 5;
const y = S{
.b = five,
};
expectEqual(1239, x.a + x.b);
}