Commit a05e224150

Andrew Kelley <andrew@ziglang.org>
2019-02-16 01:19:28
typecheck the panic function
this adds the prototype of panic to @import("builtin") and then uses it to do an implicit cast of the panic function to this prototype, rather than redoing all the implicit cast logic. closes #1894 closes #1895
1 parent 7293e01
src/all_types.hpp
@@ -1758,6 +1758,7 @@ struct CodeGen {
     ZigFn *cur_fn;
     ZigFn *main_fn;
     ZigFn *panic_fn;
+    TldFn *panic_tld_fn;
     AstNode *root_export_decl;
 
     CacheHash cache_hash;
src/analyze.cpp
@@ -1351,7 +1351,7 @@ static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *no
     size_t backward_branch_count = 0;
     return ir_eval_const_value(g, scope, node, type_entry,
             &backward_branch_count, default_backward_branch_quota,
-            nullptr, nullptr, node, type_name, nullptr);
+            nullptr, nullptr, node, type_name, nullptr, nullptr);
 }
 
 ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
@@ -3247,36 +3247,19 @@ static bool scope_is_root_decls(Scope *scope) {
     zig_unreachable();
 }
 
-static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, ZigType *fn_type) {
-    add_node_error(g, proto_node,
-            buf_sprintf("expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found '%s'",
-                buf_ptr(&fn_type->name)));
-}
-
-static void typecheck_panic_fn(CodeGen *g, ZigFn *panic_fn) {
-    AstNode *proto_node = panic_fn->proto_node;
-    assert(proto_node->type == NodeTypeFnProto);
-    ZigType *fn_type = panic_fn->type_entry;
-    FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
-    if (fn_type_id->param_count != 2) {
-        return wrong_panic_prototype(g, proto_node, fn_type);
-    }
-    ZigType *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
-            PtrLenUnknown, 0, 0, 0);
-    ZigType *const_u8_slice = get_slice_type(g, const_u8_ptr);
-    if (fn_type_id->param_info[0].type != const_u8_slice) {
-        return wrong_panic_prototype(g, proto_node, fn_type);
-    }
+void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn) {
+    ConstExprValue *panic_fn_type_val = get_builtin_value(g, "PanicFn");
+    assert(panic_fn_type_val != nullptr);
+    assert(panic_fn_type_val->type->id == ZigTypeIdMetaType);
+    ZigType *panic_fn_type = panic_fn_type_val->data.x_type;
 
-    ZigType *optional_ptr_to_stack_trace_type = get_optional_type(g, get_ptr_to_stack_trace_type(g));
-    if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) {
-        return wrong_panic_prototype(g, proto_node, fn_type);
-    }
+    AstNode *fake_decl = allocate<AstNode>(1);
+    *fake_decl = *panic_fn->proto_node;
+    fake_decl->type = NodeTypeSymbol;
+    fake_decl->data.symbol_expr.symbol = &panic_fn->symbol_name;
 
-    ZigType *actual_return_type = fn_type_id->return_type;
-    if (actual_return_type != g->builtin_types.entry_unreachable) {
-        return wrong_panic_prototype(g, proto_node, fn_type);
-    }
+    // call this for the side effects of casting to panic_fn_type
+    analyze_const_value(g, tld_fn->base.parent_scope, fake_decl, panic_fn_type, nullptr);
 }
 
 ZigType *get_test_fn_type(CodeGen *g) {
@@ -3371,18 +3354,18 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
         if (!fn_table_entry->type_entry->data.fn.is_generic) {
             if (fn_def_node)
                 g->fn_defs.append(fn_table_entry);
+        }
 
-            if (scope_is_root_decls(tld_fn->base.parent_scope) &&
-                (import == g->root_import || import->package == g->panic_package))
+        if (scope_is_root_decls(tld_fn->base.parent_scope) &&
+            (import == g->root_import || import->package == g->panic_package))
+        {
+            if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
+                g->main_fn = fn_table_entry;
+            } else if ((import->package == g->panic_package || g->have_pub_panic) &&
+                    buf_eql_str(&fn_table_entry->symbol_name, "panic"))
             {
-                if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
-                    g->main_fn = fn_table_entry;
-                } else if ((import->package == g->panic_package || g->have_pub_panic) &&
-                        buf_eql_str(&fn_table_entry->symbol_name, "panic"))
-                {
-                    g->panic_fn = fn_table_entry;
-                    typecheck_panic_fn(g, fn_table_entry);
-                }
+                g->panic_fn = fn_table_entry;
+                g->panic_tld_fn = tld_fn;
             }
         }
     } else if (source_node->type == NodeTypeTestDecl) {
src/analyze.hpp
@@ -239,4 +239,5 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry);
 Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
         ConstExprValue *const_val, ZigType *wanted_type);
 
+void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn);
 #endif
src/codegen.cpp
@@ -7144,6 +7144,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
         "    instruction_addresses: []usize,\n"
         "};\n\n");
 
+    buf_append_str(contents, "pub const PanicFn = fn([]const u8, ?*StackTrace) noreturn;\n\n");
+
     const char *cur_os = nullptr;
     {
         buf_appendf(contents, "pub const Os = enum {\n");
@@ -7913,6 +7915,8 @@ static void gen_root_source(CodeGen *g) {
         }
     }
 
+    typecheck_panic_fn(g, g->panic_tld_fn, g->panic_fn);
+
     report_errors_and_maybe_exit(g);
 
 }
src/ir.cpp
@@ -9949,7 +9949,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
 ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
         ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
         ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
-        IrExecutable *parent_exec)
+        IrExecutable *parent_exec, AstNode *expected_type_source_node)
 {
     if (expected_type != nullptr && type_is_invalid(expected_type))
         return &codegen->invalid_instruction->value;
@@ -9985,7 +9985,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod
     analyzed_executable->backward_branch_count = backward_branch_count;
     analyzed_executable->backward_branch_quota = backward_branch_quota;
     analyzed_executable->begin_scope = scope;
-    ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node);
+    ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, expected_type_source_node);
     if (type_is_invalid(result_type))
         return &codegen->invalid_instruction->value;
 
@@ -10863,10 +10863,13 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
             }
             break;
         }
+        case ConstCastResultIdFnIsGeneric:
+            add_error_note(ira->codegen, parent_msg, source_node,
+                    buf_sprintf("only one of the functions is generic"));
+            break;
         case ConstCastResultIdFnAlign: // TODO
         case ConstCastResultIdFnCC: // TODO
         case ConstCastResultIdFnVarArgs: // TODO
-        case ConstCastResultIdFnIsGeneric: // TODO
         case ConstCastResultIdFnReturnType: // TODO
         case ConstCastResultIdFnArgCount: // TODO
         case ConstCastResultIdFnGenericArgCount: // TODO
@@ -13856,7 +13859,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
             AstNode *body_node = fn_entry->body_node;
             result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
                 ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
-                nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
+                nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec, return_type_node);
 
             if (inferred_err_set_type != nullptr) {
                 inferred_err_set_type->data.error_set.infer_fn = nullptr;
@@ -14052,7 +14055,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
             ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope,
                     fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen),
                     ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
-                    nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec);
+                    nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr);
             IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
                     impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr);
             const_instruction->base.value = *align_result;
@@ -18464,7 +18467,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct
     ZigType *void_type = ira->codegen->builtin_types.entry_void;
     ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type,
         ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr,
-        &cimport_scope->buf, block_node, nullptr, nullptr);
+        &cimport_scope->buf, block_node, nullptr, nullptr, nullptr);
     if (type_is_invalid(cimport_result->type))
         return ira->codegen->invalid_instruction;
 
src/ir.hpp
@@ -16,7 +16,7 @@ bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry);
 ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
         ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
         ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
-        IrExecutable *parent_exec);
+        IrExecutable *parent_exec, AstNode *expected_type_source_node);
 
 ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable,
         ZigType *expected_type, AstNode *expected_type_source_node);
test/compile_errors.zig
@@ -346,13 +346,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
     );
 
     cases.add(
-        "Panic declared with wrong type signature in tests",
+        "wrong panic signature, runtime function",
         \\test "" {}
         \\
         \\pub fn panic() void {}
         \\
     ,
-        ".tmp_source.zig:3:5: error: expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn() void'",
+        ".tmp_source.zig:3:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn() void'",
+    );
+
+    cases.add(
+        "wrong panic signature, generic function",
+        \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
+        \\    while (true) {}
+        \\}
+    ,
+        ".tmp_source.zig:1:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn([]const u8,var)var'",
+        ".tmp_source.zig:1:5: note: only one of the functions is generic",
     );
 
     cases.add(