Commit f4519c520a

Andrew Kelley <andrew@ziglang.org>
2019-08-27 22:55:58
support self-referential struct through a slice of optional
by making optionals even more lazy closes #1805
1 parent c1fd7ed
Changed files (5)
src/all_types.hpp
@@ -329,7 +329,7 @@ struct LazyValueSliceType {
     LazyValue base;
 
     IrAnalyze *ira;
-    ZigType *elem_type;
+    IrInstruction *elem_type;
     IrInstruction *align_inst; // can be null
 
     bool is_const;
@@ -1222,6 +1222,7 @@ struct ZigTypeStruct {
 
 struct ZigTypeOptional {
     ZigType *child_type;
+    ResolveStatus resolve_status;
 };
 
 struct ZigTypeErrorUnion {
src/analyze.cpp
@@ -566,6 +566,7 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
     }
 
     entry->data.maybe.child_type = child_type;
+    entry->data.maybe.resolve_status = ResolveStatusSizeKnown;
 
     child_type->optional_parent = entry;
     return entry;
@@ -1055,9 +1056,7 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue
             zig_unreachable();
         case LazyValueIdSliceType: {
             LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(type_val->data.x_lazy);
-            if (type_is_invalid(lazy_slice_type->elem_type))
-                return ReqCompTimeInvalid;
-            return type_requires_comptime(g, lazy_slice_type->elem_type);
+            return type_val_resolve_requires_comptime(g, &lazy_slice_type->elem_type->value);
         }
         case LazyValueIdPtrType: {
             LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);
@@ -1099,6 +1098,42 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue
     zig_unreachable();
 }
 
+static Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstExprValue *type_val,
+        size_t *abi_size, size_t *size_in_bits)
+{
+    Error err;
+    if (type_val->data.x_lazy->id == LazyValueIdOptType) {
+        if ((err = ir_resolve_lazy(g, source_node, type_val)))
+            return err;
+    }
+    if (type_val->special != ConstValSpecialLazy) {
+        assert(type_val->special == ConstValSpecialStatic);
+        ZigType *ty = type_val->data.x_type;
+        if ((err = type_resolve(g, ty, ResolveStatusSizeKnown)))
+            return err;
+        *abi_size = ty->abi_size;
+        *size_in_bits = ty->size_in_bits;
+        return ErrorNone;
+    }
+    switch (type_val->data.x_lazy->id) {
+        case LazyValueIdInvalid:
+        case LazyValueIdAlignOf:
+            zig_unreachable();
+        case LazyValueIdSliceType:
+            *abi_size = g->builtin_types.entry_usize->abi_size * 2;
+            *size_in_bits = g->builtin_types.entry_usize->size_in_bits * 2;
+            return ErrorNone;
+        case LazyValueIdPtrType:
+        case LazyValueIdFnType:
+            *abi_size = g->builtin_types.entry_usize->abi_size;
+            *size_in_bits = g->builtin_types.entry_usize->size_in_bits;
+            return ErrorNone;
+        case LazyValueIdOptType:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
 Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align) {
     Error err;
     if (type_val->special != ConstValSpecialLazy) {
@@ -1767,6 +1802,17 @@ static size_t get_abi_size_bytes(size_t size_in_bits, size_t pointer_size_bytes)
     return align_forward(store_size_bytes, abi_align);
 }
 
+ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field) {
+    Error err;
+    if (struct_field->type_entry == nullptr) {
+        if ((err = ir_resolve_lazy(g, struct_field->decl_node, struct_field->type_val))) {
+            return nullptr;
+        }
+        struct_field->type_entry = struct_field->type_val->data.x_type;
+    }
+    return struct_field->type_entry;
+}
+
 static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
     assert(struct_type->id == ZigTypeIdStruct);
 
@@ -1801,40 +1847,6 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
 
     uint32_t *host_int_bytes = packed ? allocate<uint32_t>(struct_type->data.structure.gen_field_count) : nullptr;
 
-    // Resolve types for fields and then resolve sizes of all the field types.
-    // This is done before the offset loop because the offset loop has to look ahead.
-    for (size_t i = 0; i < field_count; i += 1) {
-        AstNode *field_source_node = decl_node->data.container_decl.fields.at(i);
-        TypeStructField *field = &struct_type->data.structure.fields[i];
-
-        if ((err = ir_resolve_lazy(g, field_source_node, field->type_val))) {
-            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
-            return err;
-        }
-        field->type_entry = field->type_val->data.x_type;
-
-        if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) {
-            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
-            return err;
-        }
-
-        if (struct_type->data.structure.layout == ContainerLayoutExtern &&
-            !type_allowed_in_extern(g, field->type_entry))
-        {
-            add_node_error(g, field_source_node,
-                    buf_sprintf("extern structs cannot contain fields of type '%s'",
-                        buf_ptr(&field->type_entry->name)));
-            struct_type->data.structure.resolve_status = ResolveStatusInvalid;
-            return ErrorSemanticAnalyzeFail;
-        } else if (packed) {
-            if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field->type_entry, field_source_node))) {
-                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
-                return err;
-            }
-        }
-
-    }
-
     size_t packed_bits_offset = 0;
     size_t next_offset = 0;
     size_t first_packed_bits_offset_misalign = SIZE_MAX;
@@ -1847,13 +1859,25 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
         TypeStructField *field = &struct_type->data.structure.fields[i];
         if (field->gen_index == SIZE_MAX)
             continue;
-        ZigType *field_type = field->type_entry;
-        assert(field_type != nullptr);
 
         field->gen_index = gen_field_index;
         field->offset = next_offset;
 
         if (packed) {
+            ZigType *field_type = resolve_struct_field_type(g, field);
+            if (field_type == nullptr) {
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+                return err;
+            }
+            if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) {
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+                return err;
+            }
+            if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field->type_entry, field->decl_node))) {
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+                return err;
+            }
+
             size_t field_size_in_bits = type_size_bits(g, field_type);
             size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
 
@@ -1889,6 +1913,15 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
             }
             packed_bits_offset = next_packed_bits_offset;
         } else {
+            size_t field_abi_size;
+            size_t field_size_in_bits;
+            if ((err = type_val_resolve_abi_size(g, field->decl_node, field->type_val,
+                &field_abi_size, &field_size_in_bits)))
+            {
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+                return err;
+            }
+
             gen_field_index += 1;
             size_t next_src_field_index = i + 1;
             for (; next_src_field_index < field_count; next_src_field_index += 1) {
@@ -1898,7 +1931,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
             }
             size_t next_align = (next_src_field_index == field_count) ?
                 abi_align : struct_type->data.structure.fields[next_src_field_index].align;
-            next_offset = next_field_offset(next_offset, abi_align, field_type->abi_size, next_align);
+            next_offset = next_field_offset(next_offset, abi_align, field_abi_size, next_align);
             size_in_bits = next_offset * 8;
         }
     }
@@ -1917,6 +1950,36 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
     struct_type->data.structure.resolve_loop_flag_other = false;
     struct_type->data.structure.host_int_bytes = host_int_bytes;
 
+
+    // Resolve types for fields
+    if (!packed) {
+        for (size_t i = 0; i < field_count; i += 1) {
+            TypeStructField *field = &struct_type->data.structure.fields[i];
+            ZigType *field_type = resolve_struct_field_type(g, field);
+            if (field_type == nullptr) {
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+                return err;
+            }
+
+            if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) {
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+                return err;
+            }
+
+            if (struct_type->data.structure.layout == ContainerLayoutExtern &&
+                !type_allowed_in_extern(g, field_type))
+            {
+                add_node_error(g, field->decl_node,
+                        buf_sprintf("extern structs cannot contain fields of type '%s'",
+                            buf_ptr(&field_type->name)));
+                struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+                return ErrorSemanticAnalyzeFail;
+            }
+
+        }
+    }
+
+
     return ErrorNone;
 }
 
@@ -7669,8 +7732,11 @@ static void resolve_llvm_types_integer(CodeGen *g, ZigType *type) {
     type->llvm_type = LLVMIntType(type->size_in_bits);
 }
 
-static void resolve_llvm_types_optional(CodeGen *g, ZigType *type) {
-    if (type->llvm_di_type != nullptr) return;
+static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
+    assert(type->id == ZigTypeIdOptional);
+    assert(type->data.maybe.resolve_status != ResolveStatusInvalid);
+    assert(type->data.maybe.resolve_status >= ResolveStatusSizeKnown);
+    if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
 
     LLVMTypeRef bool_llvm_type = get_llvm_type(g, g->builtin_types.entry_bool);
     ZigLLVMDIType *bool_llvm_di_type = get_llvm_di_type(g, g->builtin_types.entry_bool);
@@ -7679,30 +7745,41 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type) {
     if (!type_has_bits(child_type)) {
         type->llvm_type = bool_llvm_type;
         type->llvm_di_type = bool_llvm_di_type;
+        type->data.maybe.resolve_status = ResolveStatusLLVMFull;
         return;
     }
 
-    LLVMTypeRef child_llvm_type = get_llvm_type(g, child_type);
-    ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type);
-
     if (type_is_nonnull_ptr(child_type) || child_type->id == ZigTypeIdErrorSet) {
-        type->llvm_type = child_llvm_type;
-        type->llvm_di_type = child_llvm_di_type;
+        type->llvm_type = get_llvm_type(g, child_type);
+        type->llvm_di_type = get_llvm_di_type(g, child_type);
+        type->data.maybe.resolve_status = ResolveStatusLLVMFull;
         return;
     }
 
+    ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
+    ZigLLVMDIFile *di_file = nullptr;
+    unsigned line = 0;
+
+    if (type->data.maybe.resolve_status < ResolveStatusLLVMFwdDecl) {
+        type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name));
+        unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
+        type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
+            dwarf_kind, buf_ptr(&type->name),
+            compile_unit_scope, di_file, line);
+
+        type->data.maybe.resolve_status = ResolveStatusLLVMFwdDecl;
+        if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
+    }
+
+    LLVMTypeRef child_llvm_type = get_llvm_type(g, child_type);
+    ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type);
+    if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
+
     LLVMTypeRef elem_types[] = {
         get_llvm_type(g, child_type),
         LLVMInt1Type(),
     };
-    type->llvm_type = LLVMStructType(elem_types, 2, false);
-
-    ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
-    ZigLLVMDIFile *di_file = nullptr;
-    unsigned line = 0;
-    type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
-        ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
-        compile_unit_scope, di_file, line);
+    LLVMStructSetBody(type->llvm_type, elem_types, 2, false);
 
     uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_llvm_type);
     uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_llvm_type);
@@ -7737,6 +7814,7 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type) {
 
     ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
     type->llvm_di_type = replacement_di_type;
+    type->data.maybe.resolve_status = ResolveStatusLLVMFull;
 }
 
 static void resolve_llvm_types_error_union(CodeGen *g, ZigType *type) {
@@ -8180,7 +8258,7 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r
         case ZigTypeIdInt:
             return resolve_llvm_types_integer(g, type);
         case ZigTypeIdOptional:
-            return resolve_llvm_types_optional(g, type);
+            return resolve_llvm_types_optional(g, type, wanted_resolve_status);
         case ZigTypeIdErrorUnion:
             return resolve_llvm_types_error_union(g, type);
         case ZigTypeIdArray:
src/analyze.hpp
@@ -248,5 +248,6 @@ bool fn_is_async(ZigFn *fn);
 
 Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align);
 ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field);
+ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);
 
 #endif
src/ir.cpp
@@ -17127,11 +17127,14 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
         TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing)
 {
     Error err;
-    switch (type_has_one_possible_value(ira->codegen, field->type_entry)) {
+    ZigType *field_type = resolve_struct_field_type(ira->codegen, field);
+    if (field_type == nullptr)
+        return ira->codegen->invalid_instruction;
+    switch (type_has_one_possible_value(ira->codegen, field_type)) {
         case OnePossibleValueInvalid:
             return ira->codegen->invalid_instruction;
         case OnePossibleValueYes: {
-            IrInstruction *elem = ir_const(ira, source_instr, field->type_entry);
+            IrInstruction *elem = ir_const(ira, source_instr, field_type);
             return ir_get_ref(ira, source_instr, elem, false, false);
         }
         case OnePossibleValueNo:
@@ -17146,7 +17149,7 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
         get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes;
     bool is_const = struct_ptr->value.type->data.pointer.is_const;
     bool is_volatile = struct_ptr->value.type->data.pointer.is_volatile;
-    ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
+    ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
             is_const, is_volatile, PtrLenSingle, field->align,
             (uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
             (uint32_t)host_int_bytes_for_result_type, false);
@@ -17945,48 +17948,14 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
             return ira->codegen->invalid_instruction;
     }
 
-    lazy_slice_type->elem_type = ir_resolve_type(ira, slice_type_instruction->child_type->child);
-    if (type_is_invalid(lazy_slice_type->elem_type))
+    lazy_slice_type->elem_type = slice_type_instruction->child_type->child;
+    if (ir_resolve_type_lazy(ira, lazy_slice_type->elem_type) == nullptr)
         return ira->codegen->invalid_instruction;
 
     lazy_slice_type->is_const = slice_type_instruction->is_const;
     lazy_slice_type->is_volatile = slice_type_instruction->is_volatile;
     lazy_slice_type->is_allowzero = slice_type_instruction->is_allow_zero;
 
-    switch (lazy_slice_type->elem_type->id) {
-        case ZigTypeIdInvalid: // handled above
-            zig_unreachable();
-        case ZigTypeIdUnreachable:
-        case ZigTypeIdUndefined:
-        case ZigTypeIdNull:
-        case ZigTypeIdArgTuple:
-        case ZigTypeIdOpaque:
-            ir_add_error_node(ira, slice_type_instruction->base.source_node,
-                    buf_sprintf("slice of type '%s' not allowed", buf_ptr(&lazy_slice_type->elem_type->name)));
-            return ira->codegen->invalid_instruction;
-        case ZigTypeIdMetaType:
-        case ZigTypeIdVoid:
-        case ZigTypeIdBool:
-        case ZigTypeIdInt:
-        case ZigTypeIdFloat:
-        case ZigTypeIdPointer:
-        case ZigTypeIdArray:
-        case ZigTypeIdStruct:
-        case ZigTypeIdComptimeFloat:
-        case ZigTypeIdComptimeInt:
-        case ZigTypeIdEnumLiteral:
-        case ZigTypeIdOptional:
-        case ZigTypeIdErrorUnion:
-        case ZigTypeIdErrorSet:
-        case ZigTypeIdEnum:
-        case ZigTypeIdUnion:
-        case ZigTypeIdFn:
-        case ZigTypeIdBoundFn:
-        case ZigTypeIdVector:
-        case ZigTypeIdFnFrame:
-        case ZigTypeIdAnyFrame:
-            break;
-    }
     return result;
 }
 
@@ -20430,6 +20399,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
                     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(struct_field->type_entry)) {
                         inner_fields[1].data.x_optional = nullptr;
                     } else {
@@ -25588,11 +25562,50 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) {
                 if (!ir_resolve_align(ira, lazy_slice_type->align_inst, &align_bytes))
                     return ErrorSemanticAnalyzeFail;
             }
+            ZigType *elem_type = ir_resolve_type(ira, lazy_slice_type->elem_type);
+            if (type_is_invalid(elem_type))
+                return ErrorSemanticAnalyzeFail;
+
+            switch (elem_type->id) {
+                case ZigTypeIdInvalid: // handled above
+                    zig_unreachable();
+                case ZigTypeIdUnreachable:
+                case ZigTypeIdUndefined:
+                case ZigTypeIdNull:
+                case ZigTypeIdArgTuple:
+                case ZigTypeIdOpaque:
+                    ir_add_error(ira, lazy_slice_type->elem_type,
+                        buf_sprintf("slice of type '%s' not allowed", buf_ptr(&elem_type->name)));
+                    return ErrorSemanticAnalyzeFail;
+                case ZigTypeIdMetaType:
+                case ZigTypeIdVoid:
+                case ZigTypeIdBool:
+                case ZigTypeIdInt:
+                case ZigTypeIdFloat:
+                case ZigTypeIdPointer:
+                case ZigTypeIdArray:
+                case ZigTypeIdStruct:
+                case ZigTypeIdComptimeFloat:
+                case ZigTypeIdComptimeInt:
+                case ZigTypeIdEnumLiteral:
+                case ZigTypeIdOptional:
+                case ZigTypeIdErrorUnion:
+                case ZigTypeIdErrorSet:
+                case ZigTypeIdEnum:
+                case ZigTypeIdUnion:
+                case ZigTypeIdFn:
+                case ZigTypeIdBoundFn:
+                case ZigTypeIdVector:
+                case ZigTypeIdFnFrame:
+                case ZigTypeIdAnyFrame:
+                    break;
+            }
+
             ResolveStatus needed_status = (align_bytes == 0) ?
                 ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown;
-            if ((err = type_resolve(ira->codegen, lazy_slice_type->elem_type, needed_status)))
+            if ((err = type_resolve(ira->codegen, elem_type, needed_status)))
                 return err;
-            ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, lazy_slice_type->elem_type,
+            ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type,
                     lazy_slice_type->is_const, lazy_slice_type->is_volatile, PtrLenUnknown, align_bytes,
                     0, 0, lazy_slice_type->is_allowzero);
             val->special = ConstValSpecialStatic;
test/stage1/behavior/optional.zig
@@ -100,3 +100,22 @@ test "nested orelse" {
     S.entry();
     comptime S.entry();
 }
+
+test "self-referential struct through a slice of optional" {
+    const S = struct {
+        const Node = struct {
+            children: []?Node,
+            data: ?u8,
+
+            fn new() Node {
+                return Node{
+                    .children = undefined,
+                    .data = null,
+                };
+            }
+        };
+    };
+
+    var n = S.Node.new();
+    expect(n.data == null);
+}