From d5e788072d8b207334c0eab0f1289317a55c9344 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 8 Dec 2019 17:38:03 +0100 Subject: [PATCH] Make array types (quasi-)lazy Fixes #3843 --- src/all_types.hpp | 10 +++ src/analyze.cpp | 33 ++++++++ src/ir.cpp | 137 ++++++++++++++++++-------------- test/stage1/behavior/struct.zig | 9 +++ 4 files changed, 131 insertions(+), 58 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 6e1bfb5ad5..8c6f7456b3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -322,6 +322,7 @@ enum LazyValueId { LazyValueIdSliceType, LazyValueIdFnType, LazyValueIdErrUnionType, + LazyValueIdArrayType, }; struct LazyValue { @@ -355,6 +356,15 @@ struct LazyValueSliceType { bool is_allowzero; }; +struct LazyValueArrayType { + LazyValue base; + + IrAnalyze *ira; + IrInstruction *sentinel; // can be null + IrInstruction *elem_type; + uint64_t length; +}; + struct LazyValuePtrType { LazyValue base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 2c10234c21..10576d4fb9 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1147,6 +1147,21 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent parent_type_val, is_zero_bits); } } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + + if (lazy_array_type->length < 1) { + *is_zero_bits = true; + return ErrorNone; + } + + if ((err = type_val_resolve_zero_bits(g, lazy_array_type->elem_type->value, + parent_type, nullptr, is_zero_bits))) + return err; + + return ErrorNone; + } case LazyValueIdOptType: case LazyValueIdSliceType: case LazyValueIdErrUnionType: @@ -1181,6 +1196,7 @@ Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_o case LazyValueIdFnType: case LazyValueIdOptType: case LazyValueIdErrUnionType: + case LazyValueIdArrayType: *is_opaque_type = false; return ErrorNone; } @@ -1208,6 +1224,10 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ZigValue *type LazyValueOptType *lazy_opt_type = reinterpret_cast(type_val->data.x_lazy); return type_val_resolve_requires_comptime(g, lazy_opt_type->payload_type->value); } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_requires_comptime(g, lazy_array_type->elem_type->value); + } case LazyValueIdFnType: { LazyValueFnType *lazy_fn_type = reinterpret_cast(type_val->data.x_lazy); if (lazy_fn_type->is_generic) @@ -1305,6 +1325,7 @@ start_over: return ErrorNone; case LazyValueIdOptType: case LazyValueIdErrUnionType: + case LazyValueIdArrayType: if ((err = ir_resolve_lazy(g, source_node, type_val))) return err; goto start_over; @@ -1340,6 +1361,11 @@ Error type_val_resolve_abi_align(CodeGen *g, ZigValue *type_val, uint32_t *abi_a LazyValueOptType *lazy_opt_type = reinterpret_cast(type_val->data.x_lazy); return type_val_resolve_abi_align(g, lazy_opt_type->payload_type->value, abi_align); } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + return type_val_resolve_abi_align(g, lazy_array_type->elem_type->value, abi_align); + } case LazyValueIdErrUnionType: { LazyValueErrUnionType *lazy_err_union_type = reinterpret_cast(type_val->data.x_lazy); @@ -1370,6 +1396,13 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV case LazyValueIdOptType: // it has the optional bit case LazyValueIdFnType: return OnePossibleValueNo; + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = + reinterpret_cast(type_val->data.x_lazy); + if (lazy_array_type->length < 1) + return OnePossibleValueYes; + return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value); + } case LazyValueIdPtrType: { Error err; bool zero_bits; diff --git a/src/ir.cpp b/src/ir.cpp index 0f7b3bf990..f9985d602a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -20551,73 +20551,28 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, IrInstructionArrayType *array_type_instruction) { - Error err; + IrInstruction *result = ir_const(ira, &array_type_instruction->base, ira->codegen->builtin_types.entry_type); + result->value->special = ConstValSpecialLazy; - IrInstruction *size_value = array_type_instruction->size->child; - uint64_t size; - if (!ir_resolve_usize(ira, size_value, &size)) + LazyValueArrayType *lazy_array_type = allocate(1, "LazyValueArrayType"); + lazy_array_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_array_type->base; + lazy_array_type->base.id = LazyValueIdArrayType; + + lazy_array_type->elem_type = array_type_instruction->child_type->child; + if (ir_resolve_type_lazy(ira, lazy_array_type->elem_type) == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *child_type_value = array_type_instruction->child_type->child; - ZigType *child_type = ir_resolve_type(ira, child_type_value); - if (type_is_invalid(child_type)) + if (!ir_resolve_usize(ira, array_type_instruction->size->child, &lazy_array_type->length)) return ira->codegen->invalid_instruction; - ZigValue *sentinel_val; if (array_type_instruction->sentinel != nullptr) { - IrInstruction *uncasted_sentinel = array_type_instruction->sentinel->child; - if (type_is_invalid(uncasted_sentinel->value->type)) + lazy_array_type->sentinel = array_type_instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_array_type->sentinel, LazyOk) == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *sentinel = ir_implicit_cast(ira, uncasted_sentinel, child_type); - if (type_is_invalid(sentinel->value->type)) - return ira->codegen->invalid_instruction; - sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); - if (sentinel_val == nullptr) - return ira->codegen->invalid_instruction; - } else { - sentinel_val = nullptr; } - switch (child_type->id) { - case ZigTypeIdInvalid: // handled above - zig_unreachable(); - case ZigTypeIdUnreachable: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdArgTuple: - case ZigTypeIdOpaque: - ir_add_error_node(ira, array_type_instruction->base.source_node, - buf_sprintf("array of type '%s' not allowed", buf_ptr(&child_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: - { - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; - ZigType *result_type = get_array_type(ira->codegen, child_type, size, sentinel_val); - return ir_const_type(ira, &array_type_instruction->base, result_type); - } - } - zig_unreachable(); + return result; } static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructionSizeOf *instruction) { @@ -29016,6 +28971,72 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_array_type->ira; + + ZigType *elem_type = ir_resolve_type(ira, lazy_array_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_array_type->elem_type, + buf_sprintf("array 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; + } + + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return err; + + ZigValue *sentinel_val = nullptr; + if (lazy_array_type->sentinel != nullptr) { + if (type_is_invalid(lazy_array_type->sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + IrInstruction *sentinel = ir_implicit_cast(ira, lazy_array_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } + + assert(val->type->id == ZigTypeIdMetaType); + val->data.x_type = get_array_type(ira->codegen, elem_type, lazy_array_type->length, sentinel_val); + val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } case LazyValueIdOptType: { LazyValueOptType *lazy_opt_type = reinterpret_cast(val->data.x_lazy); IrAnalyze *ira = lazy_opt_type->ira; diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index 9ff2076049..2d51498a85 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -813,3 +813,12 @@ test "anon struct literal field value initialized with fn call" { S.doTheTest(); comptime S.doTheTest(); } + +test "self-referencing struct via array member" { + const T = struct { + children: [1]*@This(), + }; + var x: T = undefined; + x = T{ .children = .{&x} }; + expect(x.children[0] == &x); +}