Commit d5e788072d

LemonBoy <thatlemon@gmail.com>
2019-12-08 17:38:03
Make array types (quasi-)lazy
Fixes #3843
1 parent 1cb19d1
Changed files (4)
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;
 
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<LazyValueArrayType *>(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<LazyValueOptType *>(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<LazyValueArrayType *>(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<LazyValueFnType *>(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<LazyValueOptType *>(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<LazyValueArrayType *>(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<LazyValueErrUnionType *>(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<LazyValueArrayType *>(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;
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;
+
+    LazyValueArrayType *lazy_array_type = allocate<LazyValueArrayType>(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;
 
-    IrInstruction *size_value = array_type_instruction->size->child;
-    uint64_t size;
-    if (!ir_resolve_usize(ira, size_value, &size))
+    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))
-            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)
+        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;
-    } 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<LazyValueArrayType *>(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<LazyValueOptType *>(val->data.x_lazy);
             IrAnalyze *ira = lazy_opt_type->ira;
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);
+}