Commit 20049caaba

Andrew Kelley <andrew@ziglang.org>
2019-08-23 19:28:26
add lazy value for fn prototypes
this case now works: ```zig const Node = struct { field: fn (*Node) *Node, }; ```
1 parent 3865b6a
src/all_types.hpp
@@ -303,11 +303,12 @@ enum LazyValueId {
     LazyValueIdAlignOf,
     LazyValueIdPtrType,
     LazyValueIdSliceType,
+    LazyValueIdFnType,
 };
 
 struct LazyValue {
-    LazyValueId id;
     IrExecutable *exec;
+    LazyValueId id;
 };
 
 struct LazyValueAlignOf {
@@ -317,23 +318,35 @@ struct LazyValueAlignOf {
 
 struct LazyValueSliceType {
     LazyValue base;
-    ZigType *elem_type;
-    ConstExprValue *align_val; // can be null
     bool is_const;
     bool is_volatile;
     bool is_allowzero;
+
+    ZigType *elem_type;
+    ConstExprValue *align_val; // can be null
 };
 
 struct LazyValuePtrType {
     LazyValue base;
+    bool is_const;
+    bool is_volatile;
+    bool is_allowzero;
+
     ZigType *elem_type;
     ConstExprValue *align_val; // can be null
     PtrLen ptr_len;
     uint32_t bit_offset_in_host;
     uint32_t host_int_bytes;
-    bool is_const;
-    bool is_volatile;
-    bool is_allowzero;
+};
+
+struct LazyValueFnType {
+    LazyValue base;
+    bool is_generic;
+
+    AstNode *proto_node;
+    ConstExprValue **param_types;
+    ConstExprValue *align_val; // can be null
+    ConstExprValue *return_type;
 };
 
 struct ConstExprValue {
src/analyze.cpp
@@ -986,11 +986,16 @@ static Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, Zi
         case LazyValueIdSliceType:
             *is_zero_bits = false;
             return ErrorNone;
+        case LazyValueIdFnType: {
+            LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(type_val->data.x_lazy);
+            *is_zero_bits = lazy_fn_type->is_generic;
+            return ErrorNone;
+        }
     }
     zig_unreachable();
 }
 
-static Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool *is_opaque_type) {
+Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool *is_opaque_type) {
     if (type_val->special != ConstValSpecialLazy) {
         assert(type_val->special == ConstValSpecialStatic);
         *is_opaque_type = (type_val->data.x_type->id == ZigTypeIdOpaque);
@@ -1002,6 +1007,7 @@ static Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_va
             zig_unreachable();
         case LazyValueIdSliceType:
         case LazyValueIdPtrType:
+        case LazyValueIdFnType:
             *is_opaque_type = false;
             return ErrorNone;
     }
@@ -1028,6 +1034,34 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue
                 return ReqCompTimeInvalid;
             return type_requires_comptime(g, lazy_ptr_type->elem_type, parent_type);
         }
+        case LazyValueIdFnType: {
+            LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(type_val->data.x_lazy);
+            if (lazy_fn_type->is_generic)
+                return ReqCompTimeYes;
+            switch (type_val_resolve_requires_comptime(g, lazy_fn_type->return_type, parent_type)) {
+                case ReqCompTimeInvalid:
+                    return ReqCompTimeInvalid;
+                case ReqCompTimeYes:
+                    return ReqCompTimeYes;
+                case ReqCompTimeNo:
+                    break;
+            }
+            size_t param_count = lazy_fn_type->proto_node->data.fn_proto.params.length;
+            for (size_t i = 0; i < param_count; i += 1) {
+                AstNode *param_node = lazy_fn_type->proto_node->data.fn_proto.params.at(i);
+                bool param_is_var_args = param_node->data.param_decl.is_var_args;
+                if (param_is_var_args) break;
+                switch (type_val_resolve_requires_comptime(g, lazy_fn_type->param_types[i], parent_type)) {
+                    case ReqCompTimeInvalid:
+                        return ReqCompTimeInvalid;
+                    case ReqCompTimeYes:
+                        return ReqCompTimeYes;
+                    case ReqCompTimeNo:
+                        break;
+                }
+            }
+            return ReqCompTimeNo;
+        }
     }
     zig_unreachable();
 }
@@ -1047,6 +1081,7 @@ static Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, si
             zig_unreachable();
         case LazyValueIdSliceType:
         case LazyValueIdPtrType:
+        case LazyValueIdFnType:
             *abi_align = g->builtin_types.entry_usize->abi_align;
             return ErrorNone;
     }
@@ -1061,8 +1096,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, Cons
         case LazyValueIdInvalid:
         case LazyValueIdAlignOf:
             zig_unreachable();
-        case LazyValueIdSliceType:
-            return OnePossibleValueNo; // it has the len field
+        case LazyValueIdSliceType: // it has the len field
+        case LazyValueIdFnType:
+            return OnePossibleValueNo;
         case LazyValueIdPtrType: {
             Error err;
             bool zero_bits;
@@ -2395,10 +2431,12 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
         } else if (packed) {
             field->align = 1;
         } else {
-            if ((err = type_val_resolve_abi_align(g, field->type_val, &field->align))) {
+            size_t result_abi_align;
+            if ((err = type_val_resolve_abi_align(g, field->type_val, &result_abi_align))) {
                 struct_type->data.structure.resolve_status = ResolveStatusInvalid;
                 return err;
             }
+            field->align = result_abi_align;
         }
 
         if (field->align > struct_type->abi_align) {
src/analyze.hpp
@@ -247,4 +247,6 @@ ConstExprValue *analyze_const_value_allow_lazy(CodeGen *g, Scope *scope, AstNode
 void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn);
 bool fn_is_async(ZigFn *fn);
 
+Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool *is_opaque_type);
+
 #endif
src/ir.cpp
@@ -22869,84 +22869,65 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct
     AstNode *proto_node = instruction->base.source_node;
     assert(proto_node->type == NodeTypeFnProto);
 
+    IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
+    result->value.special = ConstValSpecialLazy;
+
+    LazyValueFnType *lazy_fn_type = allocate<LazyValueFnType>(1);
+    result->value.data.x_lazy = &lazy_fn_type->base;
+    lazy_fn_type->base.id = LazyValueIdFnType;
+    lazy_fn_type->base.exec = ira->new_irb.exec;
+
     if (proto_node->data.fn_proto.auto_err_set) {
         ir_add_error(ira, &instruction->base,
             buf_sprintf("inferring error set of return type valid only for function definitions"));
         return ira->codegen->invalid_instruction;
     }
 
-    FnTypeId fn_type_id = {0};
-    init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
+    size_t param_count = proto_node->data.fn_proto.params.length;
+    lazy_fn_type->proto_node = proto_node;
+    lazy_fn_type->param_types = allocate<ConstExprValue *>(param_count);
 
-    for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
-        AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
+    for (size_t param_index = 0; param_index < param_count; param_index += 1) {
+        AstNode *param_node = proto_node->data.fn_proto.params.at(param_index);
         assert(param_node->type == NodeTypeParamDecl);
 
         bool param_is_var_args = param_node->data.param_decl.is_var_args;
         if (param_is_var_args) {
-            if (fn_type_id.cc == CallingConventionC) {
-                fn_type_id.param_count = fn_type_id.next_param_index;
-                continue;
-            } else if (fn_type_id.cc == CallingConventionUnspecified) {
-                return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
+            if (proto_node->data.fn_proto.cc == CallingConventionC) {
+                break;
+            } else if (proto_node->data.fn_proto.cc == CallingConventionUnspecified) {
+                lazy_fn_type->is_generic = true;
+                return result;
             } else {
                 zig_unreachable();
             }
         }
-        FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
-        param_info->is_noalias = param_node->data.param_decl.is_noalias;
 
-        if (instruction->param_types[fn_type_id.next_param_index] == nullptr) {
-            param_info->type = nullptr;
-            return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
-        } else {
-            IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->child;
-            ZigType *param_type = ir_resolve_type(ira, param_type_value);
-            if (type_is_invalid(param_type))
-                return ira->codegen->invalid_instruction;
-            switch (type_requires_comptime(ira->codegen, param_type, nullptr)) {
-            case ReqCompTimeYes:
-                if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
-                    ir_add_error(ira, param_type_value,
-                        buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
-                            buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
-                    return ira->codegen->invalid_instruction;
-                }
-                param_info->type = param_type;
-                fn_type_id.next_param_index += 1;
-                return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
-            case ReqCompTimeInvalid:
-                return ira->codegen->invalid_instruction;
-            case ReqCompTimeNo:
-                break;
-            }
-            if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) {
-                ir_add_error(ira, param_type_value,
-                    buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
-                        buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
-                return ira->codegen->invalid_instruction;
-            }
-            param_info->type = param_type;
+        if (instruction->param_types[param_index] == nullptr) {
+            lazy_fn_type->is_generic = true;
+            return result;
         }
 
+        IrInstruction *param_type_value = instruction->param_types[param_index]->child;
+        if (type_is_invalid(param_type_value->value.type))
+            return ira->codegen->invalid_instruction;
+        ConstExprValue *param_type_val = ir_resolve_const(ira, param_type_value, LazyOk);
+        if (param_type_val == nullptr)
+            return ira->codegen->invalid_instruction;
+        lazy_fn_type->param_types[param_index] = param_type_val;
     }
 
     if (instruction->align_value != nullptr) {
-        if (!ir_resolve_align(ira, instruction->align_value->child, &fn_type_id.alignment))
+        lazy_fn_type->align_val = ir_resolve_const(ira, instruction->align_value->child, LazyOk);
+        if (lazy_fn_type->align_val == nullptr)
             return ira->codegen->invalid_instruction;
     }
 
-    IrInstruction *return_type_value = instruction->return_type->child;
-    fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
-    if (type_is_invalid(fn_type_id.return_type))
-        return ira->codegen->invalid_instruction;
-    if (fn_type_id.return_type->id == ZigTypeIdOpaque) {
-        ir_add_error(ira, instruction->return_type,
-            buf_sprintf("return type cannot be opaque"));
-        return ira->codegen->invalid_instruction;
-    }
+    lazy_fn_type->return_type = ir_resolve_const(ira, instruction->return_type->child, LazyOk);
+    if (lazy_fn_type->return_type == nullptr)
+         return ira->codegen->invalid_instruction;
 
-    return ir_const_type(ira, &instruction->base, get_fn_type(ira->codegen, &fn_type_id));
+    return result;
 }
 
 static IrInstruction *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstructionTestComptime *instruction) {
@@ -25492,6 +25473,82 @@ bool ir_has_side_effects(IrInstruction *instruction) {
     zig_unreachable();
 }
 
+static ZigType *ir_resolve_lazy_fn_type(CodeGen *codegen, IrExecutable *exec, AstNode *source_node,
+        LazyValueFnType *lazy_fn_type)
+{
+    AstNode *proto_node = lazy_fn_type->proto_node;
+
+    FnTypeId fn_type_id = {0};
+    init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
+
+    for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
+        AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
+        assert(param_node->type == NodeTypeParamDecl);
+
+        bool param_is_var_args = param_node->data.param_decl.is_var_args;
+        if (param_is_var_args) {
+            if (fn_type_id.cc == CallingConventionC) {
+                fn_type_id.param_count = fn_type_id.next_param_index;
+                continue;
+            } else if (fn_type_id.cc == CallingConventionUnspecified) {
+                return get_generic_fn_type(codegen, &fn_type_id);
+            } else {
+                zig_unreachable();
+            }
+        }
+        FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
+        param_info->is_noalias = param_node->data.param_decl.is_noalias;
+
+        if (lazy_fn_type->param_types[fn_type_id.next_param_index] == nullptr) {
+            param_info->type = nullptr;
+            return get_generic_fn_type(codegen, &fn_type_id);
+        } else {
+            ZigType *param_type = ir_resolve_const_type(codegen, exec, source_node,
+                    lazy_fn_type->param_types[fn_type_id.next_param_index]);
+            if (type_is_invalid(param_type))
+                return nullptr;
+            switch (type_requires_comptime(codegen, param_type, nullptr)) {
+            case ReqCompTimeYes:
+                if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
+                    exec_add_error_node(codegen, exec, source_node,
+                        buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
+                            buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
+                    return nullptr;
+                }
+                param_info->type = param_type;
+                fn_type_id.next_param_index += 1;
+                return get_generic_fn_type(codegen, &fn_type_id);
+            case ReqCompTimeInvalid:
+                return nullptr;
+            case ReqCompTimeNo:
+                break;
+            }
+            if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) {
+                exec_add_error_node(codegen, exec, source_node,
+                    buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
+                        buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
+                return nullptr;
+            }
+            param_info->type = param_type;
+        }
+    }
+
+    if (lazy_fn_type->align_val != nullptr) {
+        if (!ir_resolve_const_align(codegen, exec, source_node, lazy_fn_type->align_val, &fn_type_id.alignment))
+            return nullptr;
+    }
+
+    fn_type_id.return_type = ir_resolve_const_type(codegen, exec, source_node, lazy_fn_type->return_type);
+    if (type_is_invalid(fn_type_id.return_type))
+        return nullptr;
+    if (fn_type_id.return_type->id == ZigTypeIdOpaque) {
+        exec_add_error_node(codegen, exec, source_node, buf_create_from_str("return type cannot be opaque"));
+        return nullptr;
+    }
+
+    return get_fn_type(codegen, &fn_type_id);
+}
+
 static Error ir_resolve_lazy_raw(CodeGen *codegen, AstNode *source_node, ConstExprValue *val) {
     Error err;
     if (val->special != ConstValSpecialLazy)
@@ -25551,6 +25608,16 @@ static Error ir_resolve_lazy_raw(CodeGen *codegen, AstNode *source_node, ConstEx
             val->special = ConstValSpecialStatic;
             return ErrorNone;
         }
+        case LazyValueIdFnType: {
+            ZigType *fn_type = ir_resolve_lazy_fn_type(codegen, exec, source_node,
+                    reinterpret_cast<LazyValueFnType *>(val->data.x_lazy));
+            if (fn_type == nullptr)
+                return ErrorSemanticAnalyzeFail;
+            val->special = ConstValSpecialStatic;
+            assert(val->type->id == ZigTypeIdMetaType);
+            val->data.x_type = fn_type;
+            return ErrorNone;
+        }
     }
     zig_unreachable();
 }