mirror of
https://github.com/ziglang/zig.git
synced 2025-02-05 20:30:37 +00:00
parent
7878f9660f
commit
78f32259da
@ -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>
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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"));
|
||||
|
@ -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
|
||||
|
35
src/ir.cpp
35
src/ir.cpp
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user