commit 776bfb0ee6ccd3c8f4b15834c3c40e5f5d731f79 (tree)
parent 1a4059ed88740c0289b7fea5735115fa9481a8e5
Author: Andrew Kelley <andrew@ziglang.org>
Date: Thu, 20 Aug 2020 16:57:10 -0400
Merge pull request #6099 from tadeokondrak/@Type(.Struct)
Implement @Type for structs
Diffstat:
8 files changed, 174 insertions(+), 70 deletions(-)
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
@@ -259,7 +259,6 @@ pub const TypeInfo = union(enum) {
/// therefore must be kept in sync with the compiler implementation.
pub const StructField = struct {
name: []const u8,
- offset: ?comptime_int,
field_type: type,
default_value: anytype,
};
diff --git a/src/all_types.hpp b/src/all_types.hpp
@@ -1420,6 +1420,7 @@ struct ZigTypeStruct {
bool requires_comptime;
bool resolve_loop_flag_zero_bits;
bool resolve_loop_flag_other;
+ bool created_by_at_type;
};
struct ZigTypeOptional {
diff --git a/src/analyze.cpp b/src/analyze.cpp
@@ -138,7 +138,7 @@ void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope
dest->parent = parent;
}
-static ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type,
+ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type,
ZigType *import, Buf *bare_name)
{
ScopeDecls *scope = heap::c_allocator.create<ScopeDecls>();
@@ -2821,7 +2821,7 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
src_assert(struct_type->data.structure.fields == nullptr, decl_node);
struct_type->data.structure.fields = alloc_type_struct_fields(field_count);
- } else if (is_anon_container(struct_type)) {
+ } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
field_count = struct_type->data.structure.src_field_count;
src_assert(field_count == 0 || struct_type->data.structure.fields != nullptr, decl_node);
@@ -2856,7 +2856,7 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
- } else if (is_anon_container(struct_type)) {
+ } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
field_node = type_struct_field->decl_node;
src_assert(type_struct_field->type_entry != nullptr, field_node);
@@ -2883,7 +2883,7 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
type_struct_field->type_val = field_type_val;
if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
return ErrorSemanticAnalyzeFail;
- } else if (is_anon_container(struct_type)) {
+ } else if (is_anon_container(struct_type) || struct_type->data.structure.created_by_at_type) {
field_type_val = type_struct_field->type_val;
} else zig_unreachable();
@@ -8331,7 +8331,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
ZigLLVMDIFile *di_file;
ZigLLVMDIScope *di_scope;
unsigned line;
- if (decl_node != nullptr) {
+ if (decl_node != nullptr && !struct_type->data.structure.created_by_at_type) {
Scope *scope = &struct_type->data.structure.decls_scope->base;
ZigType *import = get_scope_import(scope);
di_file = import->data.structure.root_struct->di_file;
diff --git a/src/analyze.hpp b/src/analyze.hpp
@@ -116,6 +116,7 @@ void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool
void render_const_value(CodeGen *g, Buf *buf, ZigValue *const_val);
+ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, ZigType *import, Buf *bare_name);
ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent);
ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent);
ScopeDeferExpr *create_defer_expr_scope(CodeGen *g, AstNode *node, Scope *parent);
diff --git a/src/ir.cpp b/src/ir.cpp
@@ -25678,37 +25678,20 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
struct_field_val->type = type_info_struct_field_type;
ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 4);
- inner_fields[1]->special = ConstValSpecialStatic;
- inner_fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int);
-
- ZigType *field_type = resolve_struct_field_type(ira->codegen, struct_field);
- if (field_type == nullptr)
- return ErrorSemanticAnalyzeFail;
- if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown)))
- return err;
- if (!type_has_bits(ira->codegen, struct_field->type_entry)) {
- inner_fields[1]->data.x_optional = nullptr;
- } else {
- size_t byte_offset = struct_field->offset;
- inner_fields[1]->data.x_optional = ira->codegen->pass1_arena->create<ZigValue>();
- inner_fields[1]->data.x_optional->special = ConstValSpecialStatic;
- inner_fields[1]->data.x_optional->type = ira->codegen->builtin_types.entry_num_lit_int;
- bigint_init_unsigned(&inner_fields[1]->data.x_optional->data.x_bigint, byte_offset);
- }
- inner_fields[2]->special = ConstValSpecialStatic;
- inner_fields[2]->type = ira->codegen->builtin_types.entry_type;
- inner_fields[2]->data.x_type = struct_field->type_entry;
+ inner_fields[1]->special = ConstValSpecialStatic;
+ inner_fields[1]->type = ira->codegen->builtin_types.entry_type;
+ inner_fields[1]->data.x_type = struct_field->type_entry;
// default_value: anytype
- inner_fields[3]->special = ConstValSpecialStatic;
- inner_fields[3]->type = get_optional_type2(ira->codegen, struct_field->type_entry);
- if (inner_fields[3]->type == nullptr) return ErrorSemanticAnalyzeFail;
+ inner_fields[2]->special = ConstValSpecialStatic;
+ inner_fields[2]->type = get_optional_type2(ira->codegen, struct_field->type_entry);
+ if (inner_fields[2]->type == nullptr) return ErrorSemanticAnalyzeFail;
memoize_field_init_val(ira->codegen, type_entry, struct_field);
if(struct_field->init_val != nullptr && type_is_invalid(struct_field->init_val->type)){
return ErrorSemanticAnalyzeFail;
}
- set_optional_payload(inner_fields[3], struct_field->init_val);
+ set_optional_payload(inner_fields[2], struct_field->init_val);
ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee;
init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true);
@@ -25959,6 +25942,36 @@ static ZigType *get_const_field_meta_type_optional(IrAnalyze *ira, AstNode *sour
return value->data.x_optional->data.x_type;
}
+static Error get_const_field_buf(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value,
+ const char *name, size_t field_index, Buf *out)
+{
+ ZigValue *slice = get_const_field(ira, source_node, struct_value, name, field_index);
+ ZigValue *ptr = slice->data.x_struct.fields[slice_ptr_index];
+ ZigValue *len = slice->data.x_struct.fields[slice_len_index];
+ assert(ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
+ assert(ptr->data.x_ptr.data.base_array.elem_index == 0);
+ ZigValue *arr = ptr->data.x_ptr.data.base_array.array_val;
+ assert(arr->special == ConstValSpecialStatic);
+ switch (arr->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ return ErrorSemanticAnalyzeFail;
+ case ConstArraySpecialNone: {
+ buf_resize(out, 0);
+ size_t count = bigint_as_usize(&len->data.x_bigint);
+ for (size_t j = 0; j < count; j++) {
+ ZigValue *ch_val = &arr->data.x_array.data.s_none.elements[j];
+ unsigned ch = bigint_as_u32(&ch_val->data.x_bigint);
+ buf_append_char(out, ch);
+ }
+ break;
+ }
+ case ConstArraySpecialBuf:
+ buf_init_from_buf(out, arr->data.x_array.data.s_buf);
+ break;
+ }
+ return ErrorNone;
+}
+
static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeId tagTypeId, ZigValue *payload) {
Error err;
switch (tagTypeId) {
@@ -26162,30 +26175,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
assert(error->type == ir_type_info_get_type(ira, "Error", nullptr));
ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>();
err_entry->decl_node = source_instr->source_node;
- ZigValue *name_slice = get_const_field(ira, source_instr->source_node, error, "name", 0);
- ZigValue *name_ptr = name_slice->data.x_struct.fields[slice_ptr_index];
- ZigValue *name_len = name_slice->data.x_struct.fields[slice_len_index];
- assert(name_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
- assert(name_ptr->data.x_ptr.data.base_array.elem_index == 0);
- ZigValue *name_arr = name_ptr->data.x_ptr.data.base_array.array_val;
- assert(name_arr->special == ConstValSpecialStatic);
- switch (name_arr->data.x_array.special) {
- case ConstArraySpecialUndef:
- return ira->codegen->invalid_inst_gen->value->type;
- case ConstArraySpecialNone: {
- buf_resize(&err_entry->name, 0);
- size_t name_count = bigint_as_usize(&name_len->data.x_bigint);
- for (size_t j = 0; j < name_count; j++) {
- ZigValue *ch_val = &name_arr->data.x_array.data.s_none.elements[j];
- unsigned ch = bigint_as_u32(&ch_val->data.x_bigint);
- buf_append_char(&err_entry->name, ch);
- }
- break;
- }
- case ConstArraySpecialBuf:
- buf_init_from_buf(&err_entry->name, name_arr->data.x_array.data.s_buf);
- break;
- }
+ Error err;
+ if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name)))
+ return ira->codegen->invalid_inst_gen->value->type;
auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry);
if (existing_entry) {
err_entry->value = existing_entry->value->value;
@@ -26205,14 +26197,92 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
}
return err_set_type;
}
+ case ZigTypeIdStruct: {
+ assert(payload->special == ConstValSpecialStatic);
+ assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr));
+
+ ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0);
+ assert(layout_value->special == ConstValSpecialStatic);
+ assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));
+ ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);
+
+ ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 1);
+ assert(fields_value->special == ConstValSpecialStatic);
+ assert(is_slice(fields_value->type));
+ ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index];
+ ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index];
+ size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint);
+
+ ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 2);
+ assert(decls_value->special == ConstValSpecialStatic);
+ assert(is_slice(decls_value->type));
+ ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index];
+ size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint);
+ if (decls_len != 0) {
+ ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Struct.decls must be empty for @Type"));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+
+ bool is_tuple;
+ get_const_field_bool(ira, source_instr->source_node, payload, "is_tuple", 3, &is_tuple);
+
+ ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
+ buf_init_from_buf(&entry->name,
+ get_anon_type_name(ira->codegen, ira->old_irb.exec, "struct", source_instr->scope, source_instr->source_node, &entry->name));
+ entry->data.structure.decl_node = source_instr->source_node;
+ entry->data.structure.fields = alloc_type_struct_fields(fields_len);
+ entry->data.structure.fields_by_name.init(fields_len);
+ entry->data.structure.src_field_count = fields_len;
+ entry->data.structure.layout = layout;
+ entry->data.structure.special = is_tuple ? StructSpecialInferredTuple : StructSpecialNone;
+ entry->data.structure.created_by_at_type = true;
+ entry->data.structure.decls_scope = create_decls_scope(ira->codegen, nullptr, nullptr, entry, entry, &entry->name);
+
+ assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
+ assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0);
+ ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val;
+ assert(fields_arr->special == ConstValSpecialStatic);
+ assert(fields_arr->data.x_array.special == ConstArraySpecialNone);
+ for (size_t i = 0; i < fields_len; i++) {
+ ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i];
+ assert(field_value->type == ir_type_info_get_type(ira, "StructField", nullptr));
+ TypeStructField *field = entry->data.structure.fields[i];
+ field->name = buf_alloc();
+ if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name)))
+ return ira->codegen->invalid_inst_gen->value->type;
+ field->decl_node = source_instr->source_node;
+ ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1);
+ field->type_val = type_value;
+ field->type_entry = type_value->data.x_type;
+ if (entry->data.structure.fields_by_name.put_unique(field->name, field) != nullptr) {
+ ir_add_error(ira, source_instr, buf_sprintf("duplicate struct field '%s'", buf_ptr(field->name)));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+ ZigValue *default_value = get_const_field(ira, source_instr->source_node, field_value, "default_value", 2);
+ if (default_value->type->id == ZigTypeIdNull) {
+ field->init_val = nullptr;
+ } else if (default_value->type->id == ZigTypeIdOptional && default_value->type->data.maybe.child_type == field->type_entry) {
+ field->init_val = default_value->data.x_optional;
+ } else if (default_value->type == field->type_entry) {
+ field->init_val = default_value;
+ } else {
+ ir_add_error(ira, source_instr,
+ buf_sprintf("default_value of field '%s' is of type '%s', expected '%s' or '?%s'",
+ buf_ptr(field->name), buf_ptr(&default_value->type->name),
+ buf_ptr(&field->type_entry->name), buf_ptr(&field->type_entry->name)));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+ }
+
+ return entry;
+ }
case ZigTypeIdEnum:
+ case ZigTypeIdUnion:
ir_add_error(ira, source_instr, buf_sprintf(
"TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));
return ira->codegen->invalid_inst_gen->value->type;
- case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
- case ZigTypeIdStruct:
ir_add_error(ira, source_instr, buf_sprintf(
"@Type not available for 'TypeInfo.%s'", type_id_name(tagTypeId)));
return ira->codegen->invalid_inst_gen->value->type;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
@@ -1395,12 +1395,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:3:36: error: expected type 'std.builtin.TypeInfo', found 'std.builtin.Int'",
});
- cases.add("Struct unavailable for @Type",
+ cases.add("struct with declarations unavailable for @Type",
\\export fn entry() void {
- \\ _ = @Type(@typeInfo(struct { }));
+ \\ _ = @Type(@typeInfo(struct { const foo = 1; }));
\\}
, &[_][]const u8{
- "tmp.zig:2:15: error: @Type not available for 'TypeInfo.Struct'",
+ "tmp.zig:2:15: error: TypeInfo.Struct.decls must be empty for @Type",
});
cases.add("wrong type for argument tuple to @asyncCall",
diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig
@@ -236,3 +236,47 @@ test "Type.ErrorSet" {
_ = @Type(@typeInfo(error{A}));
_ = @Type(@typeInfo(error{ A, B, C }));
}
+
+test "Type.Struct" {
+ const A = @Type(@typeInfo(struct { x: u8, y: u32 }));
+ const infoA = @typeInfo(A).Struct;
+ testing.expectEqual(TypeInfo.ContainerLayout.Auto, infoA.layout);
+ testing.expectEqualSlices(u8, "x", infoA.fields[0].name);
+ testing.expectEqual(u8, infoA.fields[0].field_type);
+ testing.expectEqual(@as(?u8, null), infoA.fields[0].default_value);
+ testing.expectEqualSlices(u8, "y", infoA.fields[1].name);
+ testing.expectEqual(u32, infoA.fields[1].field_type);
+ testing.expectEqual(@as(?u32, null), infoA.fields[1].default_value);
+ testing.expectEqualSlices(TypeInfo.Declaration, &[_]TypeInfo.Declaration{}, infoA.decls);
+ testing.expectEqual(@as(bool, false), infoA.is_tuple);
+
+ var a = A{ .x = 0, .y = 1 };
+ testing.expectEqual(@as(u8, 0), a.x);
+ testing.expectEqual(@as(u32, 1), a.y);
+ a.y += 1;
+ testing.expectEqual(@as(u32, 2), a.y);
+
+ const B = @Type(@typeInfo(extern struct { x: u8, y: u32 = 5 }));
+ const infoB = @typeInfo(B).Struct;
+ testing.expectEqual(TypeInfo.ContainerLayout.Extern, infoB.layout);
+ testing.expectEqualSlices(u8, "x", infoB.fields[0].name);
+ testing.expectEqual(u8, infoB.fields[0].field_type);
+ testing.expectEqual(@as(?u8, null), infoB.fields[0].default_value);
+ testing.expectEqualSlices(u8, "y", infoB.fields[1].name);
+ testing.expectEqual(u32, infoB.fields[1].field_type);
+ testing.expectEqual(@as(?u32, 5), infoB.fields[1].default_value);
+ testing.expectEqual(@as(usize, 0), infoB.decls.len);
+ testing.expectEqual(@as(bool, false), infoB.is_tuple);
+
+ const C = @Type(@typeInfo(packed struct { x: u8 = 3, y: u32 = 5 }));
+ const infoC = @typeInfo(C).Struct;
+ testing.expectEqual(TypeInfo.ContainerLayout.Packed, infoC.layout);
+ testing.expectEqualSlices(u8, "x", infoC.fields[0].name);
+ testing.expectEqual(u8, infoC.fields[0].field_type);
+ testing.expectEqual(@as(?u8, 3), infoC.fields[0].default_value);
+ testing.expectEqualSlices(u8, "y", infoC.fields[1].name);
+ testing.expectEqual(u32, infoC.fields[1].field_type);
+ testing.expectEqual(@as(?u32, 5), infoC.fields[1].default_value);
+ testing.expectEqual(@as(usize, 0), infoC.decls.len);
+ testing.expectEqual(@as(bool, false), infoC.is_tuple);
+}
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
@@ -238,7 +238,6 @@ fn testStruct() void {
expect(struct_info == .Struct);
expect(struct_info.Struct.layout == .Packed);
expect(struct_info.Struct.fields.len == 4);
- expect(struct_info.Struct.fields[1].offset == null);
expect(struct_info.Struct.fields[2].field_type == *TestStruct);
expect(struct_info.Struct.fields[2].default_value == null);
expect(struct_info.Struct.fields[3].default_value.? == 4);
@@ -320,16 +319,6 @@ fn testAnyFrame() void {
}
}
-test "type info: optional field unwrapping" {
- const Struct = struct {
- cdOffset: u32,
- };
-
- const field = @typeInfo(Struct).Struct.fields[0];
-
- _ = field.offset orelse 0;
-}
-
test "type info: pass to function" {
_ = passTypeInfo(@typeInfo(void));
_ = comptime passTypeInfo(@typeInfo(void));