Skip to content

Commit 2bb3e1a

Browse files
committed
stage1: implement type coercion of anon struct literal to struct
closes #3672
1 parent e7207bc commit 2bb3e1a

File tree

2 files changed

+171
-8
lines changed

2 files changed

+171
-8
lines changed

src/ir.cpp

+138-8
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so
286286
IrInstGen *struct_operand, TypeStructField *field);
287287
static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right);
288288
static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right);
289+
static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field);
289290

290291
#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
291292
#define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
@@ -14703,10 +14704,139 @@ static IrInstGen *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInst* sou
1470314704
}
1470414705

1470514706
static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* source_instr,
14706-
IrInstGen *value, ZigType *wanted_type)
14707+
IrInstGen *struct_operand, ZigType *wanted_type)
1470714708
{
14708-
ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon struct literal to struct"));
14709-
return ira->codegen->invalid_inst_gen;
14709+
Error err;
14710+
14711+
IrInstGen *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false);
14712+
if (type_is_invalid(struct_ptr->value->type))
14713+
return ira->codegen->invalid_inst_gen;
14714+
14715+
if (wanted_type->data.structure.resolve_status == ResolveStatusBeingInferred) {
14716+
ir_add_error(ira, source_instr, buf_sprintf("type coercion of anon struct literal to inferred struct"));
14717+
return ira->codegen->invalid_inst_gen;
14718+
}
14719+
14720+
if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown)))
14721+
return ira->codegen->invalid_inst_gen;
14722+
14723+
size_t actual_field_count = wanted_type->data.structure.src_field_count;
14724+
size_t instr_field_count = struct_operand->value->type->data.structure.src_field_count;
14725+
14726+
bool need_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope)
14727+
|| type_requires_comptime(ira->codegen, wanted_type) == ReqCompTimeYes;
14728+
bool is_comptime = true;
14729+
14730+
// Determine if the struct_operand will be comptime.
14731+
// Also emit compile errors for missing fields and duplicate fields.
14732+
AstNode **field_assign_nodes = heap::c_allocator.allocate<AstNode *>(actual_field_count);
14733+
ZigValue **field_values = heap::c_allocator.allocate<ZigValue *>(actual_field_count);
14734+
IrInstGen **casted_fields = heap::c_allocator.allocate<IrInstGen *>(actual_field_count);
14735+
IrInstGen *const_result = ir_const(ira, source_instr, wanted_type);
14736+
14737+
for (size_t i = 0; i < instr_field_count; i += 1) {
14738+
TypeStructField *src_field = struct_operand->value->type->data.structure.fields[i];
14739+
TypeStructField *dst_field = find_struct_type_field(wanted_type, src_field->name);
14740+
if (dst_field == nullptr) {
14741+
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("no field named '%s' in struct '%s'",
14742+
buf_ptr(src_field->name), buf_ptr(&wanted_type->name)));
14743+
if (wanted_type->data.structure.decl_node) {
14744+
add_error_note(ira->codegen, msg, wanted_type->data.structure.decl_node,
14745+
buf_sprintf("struct '%s' declared here", buf_ptr(&wanted_type->name)));
14746+
}
14747+
add_error_note(ira->codegen, msg, src_field->decl_node,
14748+
buf_sprintf("field '%s' declared here", buf_ptr(src_field->name)));
14749+
return ira->codegen->invalid_inst_gen;
14750+
}
14751+
14752+
ir_assert(src_field->decl_node != nullptr, source_instr);
14753+
AstNode *existing_assign_node = field_assign_nodes[dst_field->src_index];
14754+
if (existing_assign_node != nullptr) {
14755+
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("duplicate field"));
14756+
add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here"));
14757+
return ira->codegen->invalid_inst_gen;
14758+
}
14759+
field_assign_nodes[dst_field->src_index] = src_field->decl_node;
14760+
14761+
IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, src_field, struct_ptr,
14762+
struct_operand->value->type, false);
14763+
if (type_is_invalid(field_ptr->value->type))
14764+
return ira->codegen->invalid_inst_gen;
14765+
IrInstGen *field_value = ir_get_deref(ira, source_instr, field_ptr, nullptr);
14766+
if (type_is_invalid(field_value->value->type))
14767+
return ira->codegen->invalid_inst_gen;
14768+
IrInstGen *casted_value = ir_implicit_cast(ira, field_value, dst_field->type_entry);
14769+
if (type_is_invalid(casted_value->value->type))
14770+
return ira->codegen->invalid_inst_gen;
14771+
14772+
casted_fields[dst_field->src_index] = casted_value;
14773+
if (need_comptime || instr_is_comptime(casted_value)) {
14774+
ZigValue *field_val = ir_resolve_const(ira, casted_value, UndefOk);
14775+
if (field_val == nullptr)
14776+
return ira->codegen->invalid_inst_gen;
14777+
field_val->parent.id = ConstParentIdStruct;
14778+
field_val->parent.data.p_struct.struct_val = const_result->value;
14779+
field_val->parent.data.p_struct.field_index = dst_field->src_index;
14780+
field_values[dst_field->src_index] = field_val;
14781+
} else {
14782+
is_comptime = false;
14783+
}
14784+
}
14785+
14786+
bool any_missing = false;
14787+
for (size_t i = 0; i < actual_field_count; i += 1) {
14788+
if (field_assign_nodes[i] != nullptr) continue;
14789+
14790+
// look for a default field value
14791+
TypeStructField *field = wanted_type->data.structure.fields[i];
14792+
memoize_field_init_val(ira->codegen, wanted_type, field);
14793+
if (field->init_val == nullptr) {
14794+
ir_add_error(ira, source_instr,
14795+
buf_sprintf("missing field: '%s'", buf_ptr(field->name)));
14796+
any_missing = true;
14797+
continue;
14798+
}
14799+
if (type_is_invalid(field->init_val->type))
14800+
return ira->codegen->invalid_inst_gen;
14801+
ZigValue *init_val_copy = ira->codegen->pass1_arena->create<ZigValue>();
14802+
copy_const_val(ira->codegen, init_val_copy, field->init_val);
14803+
init_val_copy->parent.id = ConstParentIdStruct;
14804+
init_val_copy->parent.data.p_struct.struct_val = const_result->value;
14805+
init_val_copy->parent.data.p_struct.field_index = i;
14806+
field_values[i] = init_val_copy;
14807+
casted_fields[i] = ir_const_move(ira, source_instr, init_val_copy);
14808+
}
14809+
if (any_missing)
14810+
return ira->codegen->invalid_inst_gen;
14811+
14812+
if (is_comptime) {
14813+
heap::c_allocator.deallocate(field_assign_nodes, actual_field_count);
14814+
IrInstGen *const_result = ir_const(ira, source_instr, wanted_type);
14815+
const_result->value->data.x_struct.fields = field_values;
14816+
return const_result;
14817+
}
14818+
14819+
IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, no_result_loc(),
14820+
wanted_type, nullptr, true, true);
14821+
if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) {
14822+
return ira->codegen->invalid_inst_gen;
14823+
}
14824+
14825+
for (size_t i = 0; i < actual_field_count; i += 1) {
14826+
TypeStructField *field = wanted_type->data.structure.fields[i];
14827+
IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc_inst, wanted_type, true);
14828+
if (type_is_invalid(field_ptr->value->type))
14829+
return ira->codegen->invalid_inst_gen;
14830+
IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, field_ptr, casted_fields[i], true);
14831+
if (type_is_invalid(store_ptr_inst->value->type))
14832+
return ira->codegen->invalid_inst_gen;
14833+
}
14834+
14835+
heap::c_allocator.deallocate(field_assign_nodes, actual_field_count);
14836+
heap::c_allocator.deallocate(field_values, actual_field_count);
14837+
heap::c_allocator.deallocate(casted_fields, actual_field_count);
14838+
14839+
return ir_get_deref(ira, source_instr, result_loc_inst, nullptr);
1471014840
}
1471114841

1471214842
static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* source_instr,
@@ -14727,7 +14857,7 @@ static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* sou
1472714857
TypeUnionField *union_field = find_union_type_field(union_type, only_field->name);
1472814858
if (union_field == nullptr) {
1472914859
ir_add_error_node(ira, only_field->decl_node,
14730-
buf_sprintf("no member named '%s' in union '%s'",
14860+
buf_sprintf("no field named '%s' in union '%s'",
1473114861
buf_ptr(only_field->name), buf_ptr(&union_type->name)));
1473214862
return ira->codegen->invalid_inst_gen;
1473314863
}
@@ -22407,7 +22537,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
2240722537
usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
2240822538
} else {
2240922539
ir_add_error_node(ira, source_node,
22410-
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
22540+
buf_sprintf("no field named '%s' in '%s'", buf_ptr(field_name),
2241122541
buf_ptr(&container_type->name)));
2241222542
return ira->codegen->invalid_inst_gen;
2241322543
}
@@ -23834,7 +23964,7 @@ static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instructi
2383423964
TypeUnionField *type_field = find_union_type_field(union_type, field_name);
2383523965
if (type_field == nullptr) {
2383623966
ir_add_error_node(ira, field_source_node,
23837-
buf_sprintf("no member named '%s' in union '%s'",
23967+
buf_sprintf("no field named '%s' in union '%s'",
2383823968
buf_ptr(field_name), buf_ptr(&union_type->name)));
2383923969
return ira->codegen->invalid_inst_gen;
2384023970
}
@@ -23930,7 +24060,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
2393024060
TypeStructField *type_field = find_struct_type_field(container_type, field->name);
2393124061
if (!type_field) {
2393224062
ir_add_error_node(ira, field->source_node,
23933-
buf_sprintf("no member named '%s' in struct '%s'",
24063+
buf_sprintf("no field named '%s' in struct '%s'",
2393424064
buf_ptr(field->name), buf_ptr(&container_type->name)));
2393524065
return ira->codegen->invalid_inst_gen;
2393624066
}
@@ -23965,7 +24095,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
2396524095
memoize_field_init_val(ira->codegen, container_type, field);
2396624096
if (field->init_val == nullptr) {
2396724097
ir_add_error(ira, source_instr,
23968-
buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name)));
24098+
buf_sprintf("missing field: '%s'", buf_ptr(field->name)));
2396924099
any_missing = true;
2397024100
continue;
2397124101
}

test/stage1/behavior/struct.zig

+33
Original file line numberDiff line numberDiff line change
@@ -851,3 +851,36 @@ test "struct with union field" {
851851
expectEqual(@as(u32, 2), True.ref);
852852
expectEqual(true, True.kind.Bool);
853853
}
854+
855+
test "type coercion of anon struct literal to struct" {
856+
const S = struct {
857+
const S2 = struct {
858+
A: u32,
859+
B: []const u8,
860+
C: void,
861+
D: Foo = .{},
862+
};
863+
864+
const Foo = struct {
865+
field: i32 = 1234,
866+
};
867+
868+
fn doTheTest() void {
869+
var y: u32 = 42;
870+
const t0 = .{ .A = 123, .B = "foo", .C = {} };
871+
const t1 = .{ .A = y, .B = "foo", .C = {} };
872+
const y0: S2 = t0;
873+
var y1: S2 = t1;
874+
expect(y0.A == 123);
875+
expect(std.mem.eql(u8, y0.B, "foo"));
876+
expect(y0.C == {});
877+
expect(y0.D.field == 1234);
878+
expect(y1.A == y);
879+
expect(std.mem.eql(u8, y1.B, "foo"));
880+
expect(y1.C == {});
881+
expect(y1.D.field == 1234);
882+
}
883+
};
884+
S.doTheTest();
885+
comptime S.doTheTest();
886+
}

0 commit comments

Comments
 (0)