Commit 7ec783876a

Andrew Kelley <superjoe30@gmail.com>
2018-01-12 05:04:08
functions which can return errors have secret stack trace param
See #651
1 parent 3268276
src/all_types.hpp
@@ -1595,6 +1595,8 @@ struct CodeGen {
     ZigList<AstNode *> tld_ref_source_node_stack;
 
     TypeTableEntry *align_amt_type;
+    TypeTableEntry *stack_trace_type;
+    TypeTableEntry *ptr_to_stack_trace_type;
 };
 
 enum VarLinkage {
src/analyze.cpp
@@ -869,6 +869,16 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) {
     zig_unreachable();
 }
 
+static TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
+    if (g->stack_trace_type == nullptr) {
+        ConstExprValue *stack_trace_type_val = get_builtin_value(g, "StackTrace");
+        assert(stack_trace_type_val->type->id == TypeTableEntryIdMetaType);
+        g->stack_trace_type = stack_trace_type_val->data.x_type;
+        g->ptr_to_stack_trace_type = get_pointer_to_type(g, g->stack_trace_type, false);
+    }
+    return g->ptr_to_stack_trace_type;
+}
+
 TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
     if (table_entry) {
@@ -915,10 +925,15 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
     if (!skip_debug_info) {
         bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
             handle_is_ptr(fn_type_id->return_type);
+        bool last_arg_error_return_trace = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || 
+            fn_type_id->return_type->id == TypeTableEntryIdPureError;
         // +1 for maybe making the first argument the return value
-        LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id->param_count);
-        // +1 because 0 is the return type and +1 for maybe making first arg ret val
-        ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(2 + fn_type_id->param_count);
+        // +1 for maybe last argument the error return trace
+        LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(2 + fn_type_id->param_count);
+        // +1 because 0 is the return type and
+        // +1 for maybe making first arg ret val and
+        // +1 for maybe last argument the error return trace
+        ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(3 + fn_type_id->param_count);
         param_di_types[0] = fn_type_id->return_type->di_type;
         size_t gen_param_index = 0;
         TypeTableEntry *gen_return_type;
@@ -965,6 +980,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
             }
         }
 
+        if (last_arg_error_return_trace) {
+            TypeTableEntry *gen_type = get_ptr_to_stack_trace_type(g);
+            gen_param_types[gen_param_index] = gen_type->type_ref;
+            gen_param_index += 1;
+            // after the gen_param_index += 1 because 0 is the return type
+            param_di_types[gen_param_index] = gen_type->di_type;
+        }
+
         fn_type->data.fn.gen_param_count = gen_param_index;
 
         fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
@@ -5527,3 +5550,13 @@ bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {
     return a == b;
 }
 
+ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
+    Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name));
+    resolve_top_level_decl(codegen, tld, false, nullptr);
+    assert(tld->id == TldIdVar);
+    TldVar *tld_var = (TldVar *)tld;
+    ConstExprValue *var_value = tld_var->var->value;
+    assert(var_value != nullptr);
+    return var_value;
+}
+
src/analyze.hpp
@@ -185,4 +185,8 @@ PackageTableEntry *new_anonymous_package(void);
 Buf *const_value_to_buffer(ConstExprValue *const_val);
 void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc);
 
+
+ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
+
+
 #endif
src/codegen.cpp
@@ -483,7 +483,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
         LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true);
     }
 
-    if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {
+    TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
+    if (return_type->id == TypeTableEntryIdUnreachable) {
         addLLVMFnAttr(fn_table_entry->llvm_value, "noreturn");
     }
 
@@ -520,13 +521,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
         // use the ABI alignment, which is fine.
     }
 
-    if (!type_has_bits(fn_type->data.fn.fn_type_id.return_type)) {
+    if (!type_has_bits(return_type)) {
         // nothing to do
-    } else if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdPointer ||
-                fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdFn)
-    {
+    } else if (return_type->id == TypeTableEntryIdPointer || return_type->id == TypeTableEntryIdFn) {
         addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
-    } else if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type) &&
+    } else if (handle_is_ptr(return_type) &&
             calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc))
     {
         addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
@@ -562,6 +561,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
             addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval");
         }
     }
+    if (return_type->id == TypeTableEntryIdErrorUnion || return_type->id == TypeTableEntryIdPureError) {
+        unsigned gen_index = LLVMCountParamTypes(fn_llvm_type) - 1;
+        addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull");
+    }
 
     return fn_table_entry->llvm_value;
 }
@@ -2330,7 +2333,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
     TypeTableEntry *src_return_type = fn_type_id->return_type;
     bool ret_has_bits = type_has_bits(src_return_type);
     bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
-    size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0);
+    bool last_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError;
+    size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (last_arg_err_ret_stack ? 1 : 0);
     bool is_var_args = fn_type_id->is_var_args;
     LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
     size_t gen_param_index = 0;
@@ -2348,6 +2352,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
             gen_param_index += 1;
         }
     }
+    if (last_arg_err_ret_stack) {
+        gen_param_values[gen_param_index] = LLVMGetUndef(g->ptr_to_stack_trace_type->type_ref);
+        gen_param_index += 1;
+    }
 
     ZigLLVM_FnInline fn_inline;
     switch (instruction->fn_inline) {
@@ -5088,6 +5096,13 @@ static void define_builtin_compile_vars(CodeGen *g) {
     os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
     Buf *contents = buf_alloc();
 
+    buf_append_str(contents,
+        "pub const StackTrace = struct {\n"
+        "    index: usize,\n"
+        "    instruction_addresses: [31]usize,\n"
+        "};\n\n"
+    );
+
     const char *cur_os = nullptr;
     {
         buf_appendf(contents, "pub const Os = enum {\n");
src/ir.cpp
@@ -8230,16 +8230,6 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out)
     return ir_resolve_bool(ira, value, out);
 }
 
-static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
-    Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name));
-    resolve_top_level_decl(codegen, tld, false, nullptr);
-    assert(tld->id == TldIdVar);
-    TldVar *tld_var = (TldVar *)tld;
-    ConstExprValue *var_value = tld_var->var->value;
-    assert(var_value != nullptr);
-    return var_value;
-}
-
 static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) {
     if (type_is_invalid(value->value.type))
         return false;