Commit b46d764fd9

Tadeo Kondrak <me@tadeo.ca>
2020-08-19 21:46:26
Implement @Type for structs without decls support
1 parent 0f67781
Changed files (5)
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 {
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;
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);
src/ir.cpp
@@ -25942,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) {
@@ -26145,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;
@@ -26188,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->id == ZigTypeIdEnum);
+            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, buf_create_from_str("a"));
+
+            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->id == ZigTypeIdStruct);
+                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;
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);
+}