From f4519c520a81439e524428258ad5aa441c9b0687 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 27 Aug 2019 16:55:58 -0400 Subject: [PATCH] support self-referential struct through a slice of optional by making optionals even more lazy closes #1805 --- src/all_types.hpp | 3 +- src/analyze.cpp | 192 +++++++++++++++++++++--------- src/analyze.hpp | 1 + src/ir.cpp | 95 ++++++++------- test/stage1/behavior/optional.zig | 19 +++ 5 files changed, 211 insertions(+), 99 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 83dcb8ba10..38964d0091 100644 --- a/src/all_types.hpp +++ b/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 { diff --git a/src/analyze.cpp b/src/analyze.cpp index 893c8121a5..965bd57e02 100644 --- a/src/analyze.cpp +++ b/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(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(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(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; } + if (type_is_nonnull_ptr(child_type) || child_type->id == ZigTypeIdErrorSet) { + 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_is_nonnull_ptr(child_type) || child_type->id == ZigTypeIdErrorSet) { - type->llvm_type = child_llvm_type; - type->llvm_di_type = child_llvm_di_type; - return; - } + 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: diff --git a/src/analyze.hpp b/src/analyze.hpp index ef079a94cd..ebfd11f514 100644 --- a/src/analyze.hpp +++ b/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 diff --git a/src/ir.cpp b/src/ir.cpp index c1656a1711..15fa4ccbe1 100644 --- a/src/ir.cpp +++ b/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; diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig index ee3cb4aef9..8cc90d10a4 100644 --- a/test/stage1/behavior/optional.zig +++ b/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); +}