Merge branch 'master' into blake2

This commit is contained in:
Marc Tiehuis 2018-01-17 00:20:06 +13:00
commit 66a24c9c00
50 changed files with 874 additions and 362 deletions

View File

@ -54,7 +54,7 @@ that counts as "freestanding" for the purposes of this table.
| | freestanding | linux | macosx | windows | other |
|-------------|--------------|---------|---------|---------|---------|
|i386 | OK | planned | OK | OK | planned |
|i386 | OK | planned | OK | planned | planned |
|x86_64 | OK | OK | OK | OK | planned |
|arm | OK | planned | planned | N/A | planned |
|aarch64 | OK | planned | planned | planned | planned |

View File

@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const io = std.io;
pub fn build(b: &Builder) {
pub fn build(b: &Builder) -> %void {
const mode = b.standardReleaseOptions();
var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
@ -36,7 +36,7 @@ pub fn build(b: &Builder) {
const test_step = b.step("test", "Run all the tests");
// find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library
const build_info = b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});
const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});
var index: usize = 0;
const cmake_binary_dir = nextValue(&index, build_info);
const cxx_compiler = nextValue(&index, build_info);
@ -68,7 +68,7 @@ pub fn build(b: &Builder) {
dependOnLib(exe, llvm);
if (exe.target.getOs() == builtin.Os.linux) {
const libstdcxx_path_padded = b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();
exe.addObjectFile(libstdcxx_path);
@ -155,9 +155,9 @@ const LibraryDep = struct {
};
fn findLLVM(b: &Builder, llvm_config_exe: []const u8) -> %LibraryDep {
const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"});
const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"});
const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"});
const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"});
var result = LibraryDep {
.libs = ArrayList([]const u8).init(b.allocator),

View File

@ -142,6 +142,7 @@
<li><a href="#builtin-TagType">@TagType</a></li>
<li><a href="#builtin-EnumTagType">@EnumTagType</a></li>
<li><a href="#builtin-errorName">@errorName</a></li>
<li><a href="#builtin-errorReturnTrace">@errorReturnTrace</a></li>
<li><a href="#builtin-fence">@fence</a></li>
<li><a href="#builtin-fieldParentPtr">@fieldParentPtr</a></li>
<li><a href="#builtin-frameAddress">@frameAddress</a></li>
@ -4412,6 +4413,13 @@ test.zig:6:2: error: found compile log statement
or all calls have a compile-time known value for <code>err</code>, then no
error name table will be generated.
</p>
<h3 id="builtin-errorReturnTrace">@errorReturnTrace</h3>
<pre><code class="zig">@errorReturnTrace() -&gt; ?&amp;builtin.StackTrace</code></pre>
<p>
If the binary is built with error return tracing, and this function is invoked in a
function that calls a function with an error or error union return type, returns a
stack trace object. Otherwise returns `null`.
</p>
<h3 id="builtin-fence">@fence</h3>
<pre><code class="zig">@fence(order: AtomicOrder)</code></pre>
<p>

View File

@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) {
pub fn build(b: &Builder) -> %void {
const obj = b.addObject("base64", "base64.zig");
const exe = b.addCExecutable("test");

View File

@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) {
pub fn build(b: &Builder) -> %void {
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
const exe = b.addCExecutable("test");

View File

@ -1146,12 +1146,6 @@ fn testCanonical(source: []const u8) {
}
test "zig fmt" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
testCanonical(
\\extern fn puts(s: &const u8) -> c_int;
\\

View File

@ -1205,6 +1205,7 @@ struct FnTableEntry {
uint32_t alignstack_value;
ZigList<FnExport> export_list;
bool calls_errorable_function;
};
uint32_t fn_table_entry_hash(FnTableEntry*);
@ -1273,6 +1274,7 @@ enum BuiltinFnId {
BuiltinFnIdSetAlignStack,
BuiltinFnIdArgType,
BuiltinFnIdExport,
BuiltinFnIdErrorReturnTrace,
};
struct BuiltinFnEntry {
@ -1498,6 +1500,7 @@ struct CodeGen {
Buf triple_str;
BuildMode build_mode;
bool is_test_build;
bool have_err_ret_tracing;
uint32_t target_os_index;
uint32_t target_arch_index;
uint32_t target_environ_index;
@ -1530,6 +1533,7 @@ struct CodeGen {
FnTableEntry *panic_fn;
LLVMValueRef cur_ret_ptr;
LLVMValueRef cur_fn_val;
LLVMValueRef cur_err_ret_trace_val;
bool c_want_stdint;
bool c_want_stdbool;
AstNode *root_export_decl;
@ -1572,6 +1576,8 @@ struct CodeGen {
size_t largest_err_name_len;
LLVMValueRef safety_crash_err_fn;
LLVMValueRef return_err_fn;
IrInstruction *invalid_instruction;
ConstExprValue const_void_val;
@ -1595,6 +1601,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 {
@ -1896,6 +1904,7 @@ enum IrInstructionId {
IrInstructionIdSetAlignStack,
IrInstructionIdArgType,
IrInstructionIdExport,
IrInstructionIdErrorReturnTrace,
};
struct IrInstruction {
@ -2717,6 +2726,10 @@ struct IrInstructionExport {
IrInstruction *target;
};
struct IrInstructionErrorReturnTrace {
IrInstruction base;
};
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;

View File

@ -869,6 +869,16 @@ static const char *calling_convention_fn_type_str(CallingConvention cc) {
zig_unreachable();
}
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,16 @@ 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 prefix_arg_error_return_trace = g->have_err_ret_tracing &&
(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;
@ -936,6 +952,14 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
}
fn_type->data.fn.gen_return_type = gen_return_type;
if (prefix_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_info = allocate<FnGenParamInfo>(fn_type_id->param_count);
for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
@ -1168,6 +1192,9 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
}
TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
if (type_is_invalid(type_entry)) {
return g->builtin_types.entry_invalid;
}
if (fn_type_id.cc != CallingConventionUnspecified) {
type_ensure_zero_bits_known(g, type_entry);
if (!type_has_bits(type_entry)) {
@ -2204,6 +2231,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
// is a pointer to this very struct, or a function pointer with parameters that
// reference such a type.
union_type->data.unionation.zero_bits_known = true;
union_type->data.unionation.zero_bits_loop_flag = false;
if (union_type->data.unionation.abi_alignment == 0) {
if (union_type->data.unionation.layout == ContainerLayoutPacked) {
union_type->data.unionation.abi_alignment = 1;
@ -2558,7 +2586,7 @@ static bool scope_is_root_decls(Scope *scope) {
static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) {
add_node_error(g, proto_node,
buf_sprintf("expected 'fn([]const u8) -> unreachable', found '%s'",
buf_sprintf("expected 'fn([]const u8, ?&builtin.StackTrace) -> unreachable', found '%s'",
buf_ptr(&fn_type->name)));
}
@ -2567,7 +2595,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
assert(proto_node->type == NodeTypeFnProto);
TypeTableEntry *fn_type = panic_fn->type_entry;
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
if (fn_type_id->param_count != 1) {
if (fn_type_id->param_count != 2) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
@ -2576,6 +2604,11 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g));
if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) {
return wrong_panic_prototype(g, proto_node, fn_type);
}
TypeTableEntry *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);
@ -2680,13 +2713,6 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
{
if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
g->main_fn = fn_table_entry;
TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
if (actual_return_type != err_void) {
add_node_error(g, fn_proto->return_type,
buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
buf_ptr(&actual_return_type->name)));
}
} else if ((import->package == g->panic_package || g->have_pub_panic) &&
buf_eql_str(&fn_table_entry->symbol_name, "panic"))
{
@ -5527,3 +5553,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;
}

View File

@ -185,4 +185,9 @@ 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);
TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);
#endif

View File

@ -404,6 +404,19 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
zig_unreachable();
}
static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_entry) {
if (!g->have_err_ret_tracing) {
return UINT32_MAX;
}
TypeTableEntry *fn_type = fn_table_entry->type_entry;
TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) {
return UINT32_MAX;
}
bool first_arg_ret = type_has_bits(return_type) && handle_is_ptr(return_type);
return first_arg_ret ? 1 : 0;
}
static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
if (fn_table_entry->llvm_value)
return fn_table_entry->llvm_value;
@ -483,7 +496,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 +534,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");
@ -563,6 +575,11 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
}
}
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
}
return fn_table_entry->llvm_value;
}
@ -864,16 +881,25 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {
return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0));
}
static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {
static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg) {
assert(g->panic_fn != nullptr);
LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, "");
if (stack_trace_arg == nullptr) {
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
stack_trace_arg = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
}
LLVMValueRef args[] = {
msg_arg,
stack_trace_arg,
};
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_FnInlineAuto, "");
LLVMSetTailCall(call_instruction, true);
LLVMBuildUnreachable(g->builder);
}
static void gen_debug_safety_crash(CodeGen *g, PanicMsgId msg_id) {
gen_panic(g, get_panic_msg_ptr_val(g, msg_id));
gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr);
}
static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {
@ -895,6 +921,87 @@ static LLVMValueRef get_memcpy_fn_val(CodeGen *g) {
return g->memcpy_fn_val;
}
static LLVMValueRef get_return_err_fn(CodeGen *g) {
if (g->return_err_fn != nullptr)
return g->return_err_fn;
assert(g->err_tag_type != nullptr);
LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
LLVMTypeRef arg_types[] = {
// error return trace pointer
get_ptr_to_stack_trace_type(g)->type_ref,
// return address
ptr_u8,
};
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
addLLVMFnAttr(fn_val, "cold");
LLVMSetLinkage(fn_val, LLVMInternalLinkage);
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
addLLVMArgAttr(fn_val, (unsigned)1, "nonnull");
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
}
LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
LLVMPositionBuilderAtEnd(g->builder, entry_block);
ZigLLVMClearCurrentDebugLocation(g->builder);
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
// stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address;
LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, "");
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, "");
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, "");
LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, "");
LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, "");
LLVMValueRef address_indices[] = {
modded_val,
};
LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, "");
LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, "");
LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, LLVMGetParam(fn_val, 1), usize_type_ref, "");
LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, return_address, usize_type_ref, "");
gen_store_untyped(g, address_value, address_slot, 0, false);
// stack_trace.index += 1;
LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
// return;
LLVMBuildRetVoid(g->builder);
LLVMPositionBuilderAtEnd(g->builder, prev_block);
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
g->return_err_fn = fn_val;
return fn_val;
}
static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
if (g->safety_crash_err_fn != nullptr)
return g->safety_crash_err_fn;
@ -953,7 +1060,11 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef offset_buf_ptr = LLVMConstInBoundsGEP(global_array, offset_ptr_indices, 2);
Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_fail_unwrap"), false);
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), &g->err_tag_type->type_ref, 1, false);
LLVMTypeRef arg_types[] = {
g->ptr_to_stack_trace_type->type_ref,
g->err_tag_type->type_ref,
};
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
addLLVMFnAttr(fn_val, "noreturn");
addLLVMFnAttr(fn_val, "cold");
@ -975,7 +1086,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMPositionBuilderAtEnd(g->builder, entry_block);
ZigLLVMClearCurrentDebugLocation(g->builder);
LLVMValueRef err_val = LLVMGetParam(fn_val, 0);
LLVMValueRef err_val = LLVMGetParam(fn_val, 1);
LLVMValueRef err_table_indices[] = {
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
@ -1005,7 +1116,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
LLVMValueRef global_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, global_slice, slice_len_index, "");
gen_store(g, full_buf_len, global_slice_len_field_ptr, u8_ptr_type);
gen_panic(g, global_slice);
gen_panic(g, global_slice, LLVMGetParam(fn_val, 0));
LLVMPositionBuilderAtEnd(g->builder, prev_block);
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
@ -1016,8 +1127,18 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),
LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val;
if (err_ret_trace_val == nullptr) {
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
}
LLVMValueRef args[] = {
err_ret_trace_val,
err_val,
};
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, get_llvm_cc(g, CallingConventionUnspecified),
ZigLLVM_FnInlineAuto, "");
LLVMSetTailCall(call_instruction, true);
LLVMBuildUnreachable(g->builder);
}
@ -1296,6 +1417,35 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
TypeTableEntry *return_type = return_instruction->value->value.type;
if (g->have_err_ret_tracing) {
bool is_err_return = false;
if (return_type->id == TypeTableEntryIdErrorUnion) {
if (return_instruction->value->value.special == ConstValSpecialStatic) {
is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
} else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
// TODO: emit a branch to check if the return value is an error
}
} else if (return_type->id == TypeTableEntryIdPureError) {
is_err_return = true;
}
if (is_err_return) {
LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "ReturnError");
LLVMValueRef block_address = LLVMBlockAddress(g->cur_fn_val, return_block);
LLVMValueRef return_err_fn = get_return_err_fn(g);
LLVMValueRef args[] = {
g->cur_err_ret_trace_val,
block_address,
};
LLVMBuildBr(g->builder, return_block);
LLVMPositionBuilderAtEnd(g->builder, return_block);
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 2,
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
LLVMSetTailCall(call_instruction, true);
}
}
if (handle_is_ptr(return_type)) {
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
assert(g->cur_ret_ptr);
@ -2330,7 +2480,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 prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError);
size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_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;
@ -2338,6 +2489,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
gen_param_values[gen_param_index] = instruction->tmp_ptr;
gen_param_index += 1;
}
if (prefix_arg_err_ret_stack) {
gen_param_values[gen_param_index] = g->cur_err_ret_trace_val;
gen_param_index += 1;
}
for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) {
IrInstruction *param_instruction = instruction->args[call_i];
TypeTableEntry *param_type = param_instruction->value.type;
@ -2881,6 +3036,16 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
return target_val;
}
static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable,
IrInstructionErrorReturnTrace *instruction)
{
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
if (g->cur_err_ret_trace_val == nullptr) {
return LLVMConstNull(ptr_to_stack_trace_type->type_ref);
}
return g->cur_err_ret_trace_val;
}
static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
switch (atomic_order) {
case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
@ -3474,7 +3639,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec
}
static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {
gen_panic(g, ir_llvm_value(g, instruction->msg));
gen_panic(g, ir_llvm_value(g, instruction->msg), nullptr);
return nullptr;
}
@ -3654,6 +3819,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction);
case IrInstructionIdAlignCast:
return ir_render_align_cast(g, executable, (IrInstructionAlignCast *)instruction);
case IrInstructionIdErrorReturnTrace:
return ir_render_error_return_trace(g, executable, (IrInstructionErrorReturnTrace *)instruction);
}
zig_unreachable();
}
@ -4493,7 +4660,8 @@ static void do_code_gen(CodeGen *g) {
LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
g->cur_fn = fn_table_entry;
g->cur_fn_val = fn;
if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) {
TypeTableEntry *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
if (handle_is_ptr(return_type)) {
g->cur_ret_ptr = LLVMGetParam(fn, 0);
} else {
g->cur_ret_ptr = nullptr;
@ -4502,6 +4670,42 @@ static void do_code_gen(CodeGen *g) {
build_all_basic_blocks(g, fn_table_entry);
clear_debug_source_node(g);
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
} else if (g->have_err_ret_tracing && fn_table_entry->calls_errorable_function) {
// TODO call graph analysis to find out what this number needs to be for every function
static const size_t stack_trace_ptr_count = 30;
TypeTableEntry *usize = g->builtin_types.entry_usize;
TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count);
LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses",
get_abi_alignment(g, array_type));
g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, "");
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
LLVMValueRef zero = LLVMConstNull(usize->type_ref);
LLVMValueRef indices[] = {zero, zero};
LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val,
indices, 2, "");
gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr,
get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false));
size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
} else {
g->cur_err_ret_trace_val = nullptr;
}
// allocate temporary stack data
for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) {
IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
@ -5064,6 +5268,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);
create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
}
static const char *bool_to_str(bool b) {
@ -5088,6 +5293,12 @@ 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: []usize,\n"
"};\n\n");
const char *cur_os = nullptr;
{
buf_appendf(contents, "pub const Os = enum {\n");
@ -5233,6 +5444,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
@ -5251,6 +5463,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->compile_var_import = add_source_file(g, g->compile_var_package, abs_full_path, contents);
scan_import(g, g->compile_var_import);
}
static void init(CodeGen *g) {
@ -5359,6 +5572,8 @@ static void init(CodeGen *g) {
}
}
g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease;
define_builtin_fns(g);
define_builtin_compile_vars(g);
}

View File

@ -572,6 +572,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) {
return IrInstructionIdArgType;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) {
return IrInstructionIdErrorReturnTrace;
}
template<typename T>
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
@ -2305,6 +2309,12 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s
return &instruction->base;
}
static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) {
IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);
return &instruction->base;
}
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@ -3731,6 +3741,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
}
case BuiltinFnIdErrorReturnTrace:
{
return ir_build_error_return_trace(irb, scope, node);
}
}
zig_unreachable();
}
@ -8230,16 +8244,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;
@ -9578,6 +9582,24 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
return ira->codegen->builtin_types.entry_void;
}
static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
IrInstructionErrorReturnTrace *instruction)
{
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
if (fn_entry == nullptr || !fn_entry->calls_errorable_function || !ira->codegen->have_err_ret_tracing) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
out_val->data.x_maybe = nullptr;
return nullable_type;
}
IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
instruction->base.source_node);
ir_link_new_instruction(new_instruction, &instruction->base);
return nullable_type;
}
static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
{
@ -9836,7 +9858,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
if (fn_proto_node->data.fn_proto.is_var_args) {
ir_add_error(ira, &call_instruction->base,
buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/andrewrk/zig/issues/313"));
buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/zig-lang/zig/issues/313"));
return ira->codegen->builtin_types.entry_invalid;
}
@ -10053,9 +10075,21 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
ir_add_alloca(ira, new_call_instruction, return_type);
if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {
parent_fn_entry->calls_errorable_function = true;
}
return ir_finish_anal(ira, return_type);
}
FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_type_id->return_type != nullptr);
assert(parent_fn_entry != nullptr);
if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
parent_fn_entry->calls_errorable_function = true;
}
IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);
size_t next_arg_index = 0;
if (first_arg_ptr) {
@ -13977,7 +14011,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
ensure_complete_type(ira->codegen, type_entry);
type_ensure_zero_bits_known(ira->codegen, type_entry);
if (type_is_invalid(type_entry))
return ira->codegen->builtin_types.entry_invalid;
@ -15322,6 +15356,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction);
case IrInstructionIdExport:
return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
case IrInstructionIdErrorReturnTrace:
return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
}
zig_unreachable();
}
@ -15505,6 +15541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdOpaqueType:
case IrInstructionIdArgType:
case IrInstructionIdTagType:
case IrInstructionIdErrorReturnTrace:
return false;
case IrInstructionIdAsm:
{

View File

@ -996,6 +996,10 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
}
}
static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
fprintf(irp->f, "@errorReturnTrace()");
}
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
@ -1308,6 +1312,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdExport:
ir_print_export(irp, (IrInstructionExport *)instruction);
break;
case IrInstructionIdErrorReturnTrace:
ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction);
break;
}
fprintf(irp->f, "\n");
}

View File

@ -247,11 +247,11 @@ pub const Builder = struct {
defer wanted_steps.deinit();
if (step_names.len == 0) {
wanted_steps.append(&self.default_step) catch unreachable;
try wanted_steps.append(&self.default_step);
} else {
for (step_names) |step_name| {
const s = try self.getTopLevelStepByName(step_name);
wanted_steps.append(s) catch unreachable;
try wanted_steps.append(s);
}
}
@ -721,11 +721,9 @@ pub const Builder = struct {
return error.FileNotFound;
}
pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 {
pub fn exec(self: &Builder, argv: []const []const u8) -> %[]u8 {
const max_output_size = 100 * 1024;
const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) catch |err| {
std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err));
};
const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |code| {
if (code != 0) {

View File

@ -267,22 +267,12 @@ fn Sha2_32(comptime params: Sha2Params32) -> type { return struct {
};}
test "sha224 single" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// https://github.com/zig-lang/zig/issues/537
return;
}
htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "");
htest.assertEqualHash(Sha224, "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc");
htest.assertEqualHash(Sha224, "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
test "sha224 streaming" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// https://github.com/zig-lang/zig/issues/537
return;
}
var h = Sha224.init();
var out: [28]u8 = undefined;
@ -303,22 +293,12 @@ test "sha224 streaming" {
}
test "sha256 single" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// https://github.com/zig-lang/zig/issues/537
return;
}
htest.assertEqualHash(Sha256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "");
htest.assertEqualHash(Sha256, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc");
htest.assertEqualHash(Sha256, "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
test "sha256 streaming" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// https://github.com/zig-lang/zig/issues/537
return;
}
var h = Sha256.init();
var out: [32]u8 = undefined;
@ -622,11 +602,6 @@ fn Sha2_64(comptime params: Sha2Params64) -> type { return struct {
};}
test "sha384 single" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// https://github.com/zig-lang/zig/issues/537
return;
}
const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
htest.assertEqualHash(Sha384, h1, "");
@ -638,11 +613,6 @@ test "sha384 single" {
}
test "sha384 streaming" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// https://github.com/zig-lang/zig/issues/537
return;
}
var h = Sha384.init();
var out: [48]u8 = undefined;
@ -666,11 +636,6 @@ test "sha384 streaming" {
}
test "sha512 single" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// https://github.com/zig-lang/zig/issues/537
return;
}
const h1 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
htest.assertEqualHash(Sha512, h1, "");
@ -682,11 +647,6 @@ test "sha512 single" {
}
test "sha512 streaming" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// https://github.com/zig-lang/zig/issues/537
return;
}
var h = Sha512.init();
var out: [64]u8 = undefined;

View File

@ -13,6 +13,10 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
error MissingDebugInfo;
error InvalidDebugInfo;
error UnsupportedDebugInfo;
error UnknownObjectFormat;
error TodoSupportCoffDebugInfo;
error TodoSupportMachoDebugInfo;
error TodoSupportCOFFDebugInfo;
/// Tries to write to stderr, unbuffered, and ignores any error returned.
@ -37,10 +41,43 @@ fn getStderrStream() -> %&io.OutStream {
}
}
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpStackTrace() {
var self_debug_info: ?&ElfStackTrace = null;
pub fn getSelfDebugInfo() -> %&ElfStackTrace {
if (self_debug_info) |info| {
return info;
} else {
const info = try openSelfDebugInfo(global_allocator);
self_debug_info = info;
return info;
}
}
/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpCurrentStackTrace() {
const stderr = getStderrStream() catch return;
writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch return;
const debug_info = getSelfDebugInfo() catch |err| {
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
defer debug_info.close();
writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), 1) catch |err| {
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
return;
};
}
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) {
const stderr = getStderrStream() catch return;
const debug_info = getSelfDebugInfo() catch |err| {
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
defer debug_info.close();
writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
return;
};
}
/// This function invokes undefined behavior when `ok` is `false`.
@ -88,7 +125,21 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
const stderr = getStderrStream() catch os.abort();
stderr.print(format ++ "\n", args) catch os.abort();
writeStackTrace(stderr, global_allocator, stderr_file.isTty(), 1) catch os.abort();
dumpCurrentStackTrace();
os.abort();
}
pub fn panicWithTrace(trace: &const builtin.StackTrace, comptime format: []const u8, args: ...) -> noreturn {
if (panicking) {
os.abort();
} else {
panicking = true;
}
const stderr = getStderrStream() catch os.abort();
stderr.print(format ++ "\n", args) catch os.abort();
dumpStackTrace(trace);
dumpCurrentStackTrace();
os.abort();
}
@ -101,12 +152,91 @@ const RESET = "\x1b[0m";
error PathNotFound;
error InvalidDebugInfo;
pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty_color: bool,
ignore_frame_count: usize) -> %void
pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
debug_info: &ElfStackTrace, tty_color: bool) -> %void
{
var frame_index: usize = undefined;
var frames_left: usize = undefined;
if (stack_trace.index < stack_trace.instruction_addresses.len) {
frame_index = 0;
frames_left = stack_trace.index;
} else {
frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len;
frames_left = stack_trace.instruction_addresses.len;
}
while (frames_left != 0) : ({
frames_left -= 1;
frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
}) {
const return_address = stack_trace.instruction_addresses[frame_index];
try printSourceAtAddress(debug_info, out_stream, return_address);
}
}
pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,
debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) -> %void
{
var ignored_count: usize = 0;
var fp = @ptrToInt(@frameAddress());
while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
if (ignored_count < ignore_frame_count) {
ignored_count += 1;
continue;
}
const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
try printSourceAtAddress(debug_info, out_stream, return_address);
}
}
fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) -> %void {
if (builtin.os == builtin.Os.windows) {
return error.UnsupportedDebugInfo;
}
// TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
// at compile time. I'll call it issue #313
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
const compile_unit = findCompileUnit(debug_info, address) catch {
try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
address);
return;
};
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
defer line_info.deinit();
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
line_info.file_name, line_info.line, line_info.column,
address, compile_unit_name);
if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
if (line_info.column == 0) {
try out_stream.write("\n");
} else {
{var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
try out_stream.writeByte(' ');
}}
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
}
} else |err| switch (err) {
error.EndOfFile, error.PathNotFound => {},
else => return err,
}
} else |err| switch (err) {
error.MissingDebugInfo, error.InvalidDebugInfo => {
try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
},
else => return err,
}
}
pub fn openSelfDebugInfo(allocator: &mem.Allocator) -> %&ElfStackTrace {
switch (builtin.object_format) {
builtin.ObjectFormat.elf => {
var stack_trace = ElfStackTrace {
const st = try allocator.create(ElfStackTrace);
*st = ElfStackTrace {
.self_exe_file = undefined,
.elf = undefined,
.debug_info = undefined,
@ -117,12 +247,11 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
.compile_unit_list = ArrayList(CompileUnit).init(allocator),
};
const st = &stack_trace;
st.self_exe_file = try os.openSelfExe();
defer st.self_exe_file.close();
%defer st.self_exe_file.close();
try st.elf.openFile(allocator, &st.self_exe_file);
defer st.elf.close();
%defer st.elf.close();
st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
@ -130,67 +259,19 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
try scanAllCompileUnits(st);
var ignored_count: usize = 0;
var fp = @ptrToInt(@frameAddress());
while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
if (ignored_count < ignore_frame_count) {
ignored_count += 1;
continue;
}
const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
// TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
// at compile time. I'll call it issue #313
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
const compile_unit = findCompileUnit(st, return_address) catch {
try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
return_address);
continue;
};
const compile_unit_name = try compile_unit.die.getAttrString(st, DW.AT_name);
if (getLineNumberInfo(st, compile_unit, usize(return_address) - 1)) |line_info| {
defer line_info.deinit();
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
line_info.file_name, line_info.line, line_info.column,
return_address, compile_unit_name);
if (printLineFromFile(st.allocator(), out_stream, line_info)) {
if (line_info.column == 0) {
try out_stream.write("\n");
} else {
{var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
try out_stream.writeByte(' ');
}}
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
}
} else |err| switch (err) {
error.EndOfFile, error.PathNotFound => {},
else => return err,
}
} else |err| switch (err) {
error.MissingDebugInfo, error.InvalidDebugInfo => {
try out_stream.print(ptr_hex ++ " in ??? ({})\n",
return_address, compile_unit_name);
},
else => return err,
}
}
return st;
},
builtin.ObjectFormat.coff => {
try out_stream.write("(stack trace unavailable for COFF object format)\n");
return error.TodoSupportCoffDebugInfo;
},
builtin.ObjectFormat.macho => {
try out_stream.write("(stack trace unavailable for Mach-O object format)\n");
return error.TodoSupportMachoDebugInfo;
},
builtin.ObjectFormat.wasm => {
try out_stream.write("(stack trace unavailable for WASM object format)\n");
return error.TodoSupportCOFFDebugInfo;
},
builtin.ObjectFormat.unknown => {
try out_stream.write("(stack trace unavailable for unknown object format)\n");
return error.UnknownObjectFormat;
},
}
}
@ -228,7 +309,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
}
}
const ElfStackTrace = struct {
pub const ElfStackTrace = struct {
self_exe_file: io.File,
elf: elf.Elf,
debug_info: &elf.SectionHeader,
@ -248,6 +329,11 @@ const ElfStackTrace = struct {
const in_stream = &in_file_stream.stream;
return readStringRaw(self.allocator(), in_stream);
}
pub fn close(self: &ElfStackTrace) {
self.self_exe_file.close();
self.elf.close();
}
};
const PcRange = struct {

View File

@ -224,7 +224,7 @@ pub const File = struct {
};
}
},
else => @compileError("unsupported OS"),
else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
}
}

View File

@ -8,11 +8,6 @@ const os = std.os;
const builtin = @import("builtin");
test "write a file, read it, then delete it" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
var data: [1024]u8 = undefined;
var rng = Rand.init(1234);
rng.fillBytes(data[0..]);

View File

@ -55,11 +55,6 @@ fn acosh64(x: f64) -> f64 {
}
test "math.acosh" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(acosh(f32(1.5)) == acosh32(1.5));
assert(acosh(f64(1.5)) == acosh64(1.5));
}

View File

@ -146,11 +146,6 @@ test "math.cos" {
}
test "math.cos32" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
const epsilon = 0.000001;
assert(math.approxEq(f32, cos32(0.0), 1.0, epsilon));

View File

@ -81,11 +81,6 @@ fn cosh64(x: f64) -> f64 {
}
test "math.cosh" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(cosh(f32(1.5)) == cosh32(1.5));
assert(cosh(f64(1.5)) == cosh64(1.5));
}

View File

@ -359,11 +359,6 @@ pub fn divTrunc(comptime T: type, numerator: T, denominator: T) -> %T {
}
test "math.divTrunc" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
testDivTrunc();
comptime testDivTrunc();
}
@ -389,11 +384,6 @@ pub fn divFloor(comptime T: type, numerator: T, denominator: T) -> %T {
}
test "math.divFloor" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
testDivFloor();
comptime testDivFloor();
}
@ -423,11 +413,6 @@ pub fn divExact(comptime T: type, numerator: T, denominator: T) -> %T {
}
test "math.divExact" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
testDivExact();
comptime testDivExact();
}

View File

@ -147,11 +147,6 @@ pub fn ln_64(x_: f64) -> f64 {
}
test "math.ln" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(ln(f32(0.2)) == ln_32(0.2));
assert(ln(f64(0.2)) == ln_64(0.2));
}

View File

@ -56,11 +56,6 @@ test "math.log float" {
}
test "math.log float_special" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(log(f32, 2, 0.2301974) == math.log2(f32(0.2301974)));
assert(log(f32, 10, 0.2301974) == math.log10(f32(0.2301974)));

View File

@ -172,11 +172,6 @@ pub fn log10_64(x_: f64) -> f64 {
}
test "math.log10" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(log10(f32(0.2)) == log10_32(0.2));
assert(log10(f64(0.2)) == log10_64(0.2));
}

View File

@ -170,11 +170,6 @@ pub fn log2_64(x_: f64) -> f64 {
}
test "math.log2" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(log2(f32(0.2)) == log2_32(0.2));
assert(log2(f64(0.2)) == log2_64(0.2));
}

View File

@ -176,12 +176,6 @@ fn isOddInteger(x: f64) -> bool {
}
test "math.pow" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
const epsilon = 0.000001;
assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon));

View File

@ -98,11 +98,6 @@ test "math.round" {
}
test "math.round32" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(round32(1.3) == 1.0);
assert(round32(-1.3) == -1.0);
assert(round32(0.2) == 0.0);

View File

@ -150,11 +150,6 @@ test "math.sin" {
}
test "math.sin32" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
const epsilon = 0.000001;
assert(math.approxEq(f32, sin32(0.0), 0.0, epsilon));

View File

@ -88,11 +88,6 @@ fn sinh64(x: f64) -> f64 {
}
test "math.sinh" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(sinh(f32(1.5)) == sinh32(1.5));
assert(sinh(f64(1.5)) == sinh64(1.5));
}

View File

@ -136,11 +136,6 @@ test "math.tan" {
}
test "math.tan32" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
const epsilon = 0.000001;
assert(math.approxEq(f32, tan32(0.0), 0.0, epsilon));

View File

@ -112,11 +112,6 @@ fn tanh64(x: f64) -> f64 {
}
test "math.tanh" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
assert(tanh(f32(1.5)) == tanh32(1.5));
assert(tanh(f64(1.5)) == tanh64(1.5));
}

View File

@ -148,7 +148,7 @@ pub coldcc fn abort() -> noreturn {
}
/// Exits the program cleanly with the specified status code.
pub coldcc fn exit(status: i32) -> noreturn {
pub coldcc fn exit(status: u8) -> noreturn {
if (builtin.link_libc) {
c.exit(status);
}
@ -157,14 +157,7 @@ pub coldcc fn exit(status: i32) -> noreturn {
posix.exit(status);
},
Os.windows => {
// Map a possibly negative status code to a non-negative status for the systems default
// integer width.
const p_status = if (@sizeOf(c_uint) < @sizeOf(u32))
@truncate(c_uint, @bitCast(u32, status))
else
c_uint(@bitCast(u32, status));
windows.ExitProcess(p_status);
windows.ExitProcess(status);
},
else => @compileError("Unsupported OS"),
}

View File

@ -194,11 +194,6 @@ fn MersenneTwister(
}
test "rand float 32" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
var r = Rand.init(42);
var i: usize = 0;
while (i < 1000) : (i += 1) {
@ -209,11 +204,6 @@ test "rand float 32" {
}
test "rand.MT19937_64" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
var rng = MT19937_64.init(rand_test.mt64_seed);
for (rand_test.mt64_data) |value| {
assert(value == rng.get());
@ -221,11 +211,6 @@ test "rand.MT19937_64" {
}
test "rand.MT19937_32" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
var rng = MT19937_32.init(rand_test.mt32_seed);
for (rand_test.mt32_data) |value| {
assert(value == rng.get());
@ -233,11 +218,6 @@ test "rand.MT19937_32" {
}
test "rand.Rand.range" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
var r = Rand.init(42);
testRange(&r, -4, 3);
testRange(&r, -4, -1);

View File

@ -1020,11 +1020,6 @@ fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> bool {
}
test "std.sort" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
const u8cases = [][]const []const u8 {
[][]const u8{"", ""},
[][]const u8{"a", "a"},
@ -1061,11 +1056,6 @@ test "std.sort" {
}
test "std.sort descending" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
const rev_cases = [][]const []const i32 {
[][]const i32{[]i32{}, []i32{}},
[][]const i32{[]i32{1}, []i32{1}},
@ -1085,11 +1075,6 @@ test "std.sort descending" {
}
test "another sort case" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
var arr = []i32{ 5, 3, 1, 2, 4 };
sort(i32, arr[0..], i32asc);
@ -1097,11 +1082,6 @@ test "another sort case" {
}
test "sort fuzz testing" {
if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
// TODO get this test passing
// https://github.com/zig-lang/zig/issues/537
return;
}
var rng = std.rand.Rand.init(0x12345678);
const test_case_count = 10;
var i: usize = 0;

View File

@ -21,8 +21,7 @@ comptime {
}
extern fn zenMain() -> noreturn {
root.main() catch std.os.posix.exit(1);
std.os.posix.exit(0);
std.os.posix.exit(callMain());
}
nakedcc fn _start() -> noreturn {
@ -43,29 +42,55 @@ nakedcc fn _start() -> noreturn {
extern fn WinMainCRTStartup() -> noreturn {
@setAlignStack(16);
root.main() catch std.os.windows.ExitProcess(1);
std.os.windows.ExitProcess(0);
std.os.windows.ExitProcess(callMain());
}
fn posixCallMainAndExit() -> noreturn {
const argc = *argc_ptr;
const argv = @ptrCast(&&u8, &argc_ptr[1]);
const envp = @ptrCast(&?&u8, &argv[argc + 1]);
callMain(argc, argv, envp) catch std.os.posix.exit(1);
std.os.posix.exit(0);
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
}
fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) -> u8 {
std.os.ArgIteratorPosix.raw = argv[0..argc];
var env_count: usize = 0;
while (envp[env_count] != null) : (env_count += 1) {}
std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
return root.main();
return callMain();
}
extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
callMain(usize(c_argc), c_argv, c_envp) catch return 1;
return 0;
return callMainWithArgs(usize(c_argc), c_argv, c_envp);
}
fn callMain() -> u8 {
switch (@typeId(@typeOf(root.main).ReturnType)) {
builtin.TypeId.NoReturn => {
root.main();
},
builtin.TypeId.Void => {
root.main();
return 0;
},
builtin.TypeId.Int => {
if (@typeOf(root.main).ReturnType.bit_count != 8) {
@compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
}
return root.main();
},
builtin.TypeId.ErrorUnion => {
root.main() catch |err| {
std.debug.warn("error: {}\n", @errorName(err));
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace);
}
return 1;
};
return 0;
},
else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"),
}
}

View File

@ -14,7 +14,7 @@ pub fn main() -> %void {
var arg_it = os.args();
// TODO use a more general purpose allocator here
var inc_allocator = std.heap.IncrementingAllocator.init(40 * 1024 * 1024) catch unreachable;
var inc_allocator = try std.heap.IncrementingAllocator.init(40 * 1024 * 1024);
defer inc_allocator.deinit();
const allocator = &inc_allocator.allocator;
@ -107,12 +107,12 @@ pub fn main() -> %void {
return usageAndErr(&builder, false, try stderr_stream);
}
} else {
targets.append(arg) catch unreachable;
try targets.append(arg);
}
}
builder.setInstallPrefix(prefix);
root.build(&builder);
try root.build(&builder);
if (builder.validateUserInputDidItFail())
return usageAndErr(&builder, true, try stderr_stream);
@ -129,7 +129,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
root.build(builder);
try root.build(builder);
}
// This usage text has to be synchronized with src/main.cpp

View File

@ -5,7 +5,7 @@ const builtin = @import("builtin");
// Avoid dragging in the debug safety mechanisms into this .o file,
// unless we're trying to test this file.
pub coldcc fn panic(msg: []const u8) -> noreturn {
pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
if (builtin.is_test) {
@import("std").debug.panic("{}", msg);
} else {

View File

@ -74,7 +74,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
// Avoid dragging in the debug safety mechanisms into this .o file,
// unless we're trying to test this file.
pub coldcc fn panic(msg: []const u8) -> noreturn {
pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
if (is_test) {
@import("std").debug.panic("{}", msg);
} else {

View File

@ -4,14 +4,18 @@
// have to be added in the compiler.
const builtin = @import("builtin");
const std = @import("std");
pub coldcc fn panic(msg: []const u8) -> noreturn {
pub coldcc fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) -> noreturn {
switch (builtin.os) {
// TODO: fix panic in zen.
builtin.Os.freestanding, builtin.Os.zen => {
while (true) {}
},
else => {
if (error_return_trace) |trace| {
@import("std").debug.panicWithTrace(trace, "{}", msg);
}
@import("std").debug.panic("{}", msg);
},
}

View File

@ -8,10 +8,7 @@ pub fn main() -> %void {
for (test_fn_list) |test_fn, i| {
warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
test_fn.func() catch |err| {
warn("{}\n", err);
return err;
};
try test_fn.func();
warn("OK\n");
}

View File

@ -16,4 +16,5 @@ pub fn addCases(cases: &tests.BuildExamplesContext) {
cases.addBuildFile("test/standalone/issue_339/build.zig");
cases.addBuildFile("test/standalone/pkg_import/build.zig");
cases.addBuildFile("test/standalone/use_alias/build.zig");
cases.addBuildFile("test/standalone/brace_expansion/build.zig");
}

View File

@ -1,6 +1,15 @@
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("wrong return type for main",
\\pub fn main() -> f32 { }
, "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
cases.add("double ?? on main return value",
\\pub fn main() -> ??void {
\\}
, "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
cases.add("bad identifier in function with struct defined inside function which references local const",
\\export fn entry() {
\\ const BlockKind = u32;
@ -1059,15 +1068,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
,
".tmp_source.zig:2:5: error: expected type 'void', found 'error'");
cases.add("wrong return type for main",
\\pub fn main() { }
, ".tmp_source.zig:1:15: error: expected return type of main to be '%void', instead is 'void'");
cases.add("double ?? on main return value",
\\pub fn main() -> ??void {
\\}
, ".tmp_source.zig:1:18: error: expected return type of main to be '%void', instead is '??void'");
cases.add("invalid pointer for var type",
\\extern fn ext() -> usize;
\\var bytes: [ext()]u8 = undefined;

View File

@ -2,7 +2,7 @@ const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompareOutputContext) {
cases.addDebugSafety("calling panic",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@ -11,7 +11,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("out of bounds slice access",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@ -25,7 +25,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer addition overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -39,7 +39,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer subtraction overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -53,7 +53,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer multiplication overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -67,7 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer negation overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -81,7 +81,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed integer division overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -95,7 +95,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed shift left overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unsigned shift left overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -123,7 +123,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed shift right overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -137,7 +137,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unsigned shift right overflow",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("integer division by zero",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -164,7 +164,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("exact division failure",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -178,7 +178,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("cast []u8 to bigger slice of wrong size",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -192,7 +192,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("value does not fit in shortening cast",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -206,7 +206,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("signed integer not fitting in cast to unsigned integer",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Whatever;
@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("unwrap error",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {
\\ @import("std").os.exit(126); // good
\\ }
@ -236,7 +236,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("cast integer to error and no code matches",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\pub fn main() -> %void {
@ -248,7 +248,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("@alignCast misaligned",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\error Wrong;
@ -265,7 +265,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
);
cases.addDebugSafety("bad union field access",
\\pub fn panic(message: []const u8) -> noreturn {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) -> noreturn {
\\ @import("std").os.exit(126);
\\}
\\

View File

@ -0,0 +1,9 @@
const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) -> %void {
const main = b.addTest("main.zig");
main.setBuildMode(b.standardReleaseOptions());
const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
}

View File

@ -0,0 +1,254 @@
const std = @import("std");
const io = std.io;
const mem = std.mem;
const debug = std.debug;
const assert = debug.assert;
const Buffer = std.Buffer;
const ArrayList = std.ArrayList;
error InvalidInput;
error OutOfMem;
const Token = union(enum) {
Word: []const u8,
OpenBrace,
CloseBrace,
Comma,
Eof,
};
var global_allocator: &mem.Allocator = undefined;
fn tokenize(input:[] const u8) -> %ArrayList(Token) {
const State = enum {
Start,
Word,
};
var token_list = ArrayList(Token).init(global_allocator);
var tok_begin: usize = undefined;
var state = State.Start;
for (input) |b, i| {
switch (state) {
State.Start => switch (b) {
'a'...'z', 'A'...'Z' => {
state = State.Word;
tok_begin = i;
},
'{' => try token_list.append(Token.OpenBrace),
'}' => try token_list.append(Token.CloseBrace),
',' => try token_list.append(Token.Comma),
else => return error.InvalidInput,
},
State.Word => switch (b) {
'a'...'z', 'A'...'Z' => {},
'{', '}', ',' => {
try token_list.append(Token { .Word = input[tok_begin..i] });
switch (b) {
'{' => try token_list.append(Token.OpenBrace),
'}' => try token_list.append(Token.CloseBrace),
',' => try token_list.append(Token.Comma),
else => unreachable,
}
state = State.Start;
},
else => return error.InvalidInput,
},
}
}
switch (state) {
State.Start => {},
State.Word => try token_list.append(Token {.Word = input[tok_begin..] }),
}
try token_list.append(Token.Eof);
return token_list;
}
const Node = union(enum) {
Scalar: []const u8,
List: ArrayList(Node),
Combine: []Node,
};
fn parse(tokens: &const ArrayList(Token), token_index: &usize) -> %Node {
const first_token = tokens.items[*token_index];
*token_index += 1;
const result_node = switch (first_token) {
Token.Word => |word| Node { .Scalar = word },
Token.OpenBrace => blk: {
var list = ArrayList(Node).init(global_allocator);
while (true) {
try list.append(try parse(tokens, token_index));
const token = tokens.items[*token_index];
*token_index += 1;
switch (token) {
Token.CloseBrace => break,
Token.Comma => continue,
else => return error.InvalidInput,
}
}
break :blk Node { .List = list };
},
else => return error.InvalidInput,
};
switch (tokens.items[*token_index]) {
Token.Word, Token.OpenBrace => {
const pair = try global_allocator.alloc(Node, 2);
pair[0] = result_node;
pair[1] = try parse(tokens, token_index);
return Node { .Combine = pair };
},
else => return result_node,
}
}
fn expandString(input: []const u8, output: &Buffer) -> %void {
const tokens = try tokenize(input);
if (tokens.len == 1) {
return output.resize(0);
}
var token_index: usize = 0;
const root = try parse(tokens, &token_index);
const last_token = tokens.items[token_index];
switch (last_token) {
Token.Eof => {},
else => return error.InvalidInput,
}
var result_list = ArrayList(Buffer).init(global_allocator);
defer result_list.deinit();
try expandNode(root, &result_list);
try output.resize(0);
for (result_list.toSliceConst()) |buf, i| {
if (i != 0) {
try output.appendByte(' ');
}
try output.append(buf.toSliceConst());
}
}
const ListOfBuffer0 = ArrayList(Buffer); // TODO this is working around a compiler bug, fix and delete this
fn expandNode(node: &const Node, output: &ListOfBuffer0) -> %void {
assert(output.len == 0);
switch (*node) {
Node.Scalar => |scalar| {
try output.append(try Buffer.init(global_allocator, scalar));
},
Node.Combine => |pair| {
const a_node = pair[0];
const b_node = pair[1];
var child_list_a = ArrayList(Buffer).init(global_allocator);
try expandNode(a_node, &child_list_a);
var child_list_b = ArrayList(Buffer).init(global_allocator);
try expandNode(b_node, &child_list_b);
for (child_list_a.toSliceConst()) |buf_a| {
for (child_list_b.toSliceConst()) |buf_b| {
var combined_buf = try Buffer.initFromBuffer(buf_a);
try combined_buf.append(buf_b.toSliceConst());
try output.append(combined_buf);
}
}
},
Node.List => |list| {
for (list.toSliceConst()) |child_node| {
var child_list = ArrayList(Buffer).init(global_allocator);
try expandNode(child_node, &child_list);
for (child_list.toSliceConst()) |buf| {
try output.append(buf);
}
}
},
}
}
pub fn main() -> %void {
var stdin_file = try io.getStdIn();
var stdout_file = try io.getStdOut();
var inc_allocator = try std.heap.IncrementingAllocator.init(2 * 1024 * 1024);
defer inc_allocator.deinit();
global_allocator = &inc_allocator.allocator;
var stdin_buf = try Buffer.initSize(global_allocator, 0);
defer stdin_buf.deinit();
var stdin_adapter = io.FileInStream.init(&stdin_file);
try stdin_adapter.stream.readAllBuffer(&stdin_buf, @maxValue(usize));
var result_buf = try Buffer.initSize(global_allocator, 0);
defer result_buf.deinit();
try expandString(stdin_buf.toSlice(), &result_buf);
try stdout_file.write(result_buf.toSliceConst());
}
test "invalid inputs" {
global_allocator = std.debug.global_allocator;
expectError("}ABC", error.InvalidInput);
expectError("{ABC", error.InvalidInput);
expectError("}{", error.InvalidInput);
expectError("{}", error.InvalidInput);
expectError("A,B,C", error.InvalidInput);
expectError("{A{B,C}", error.InvalidInput);
expectError("{A,}", error.InvalidInput);
expectError("\n", error.InvalidInput);
}
fn expectError(test_input: []const u8, expected_err: error) {
var output_buf = Buffer.initSize(global_allocator, 0) catch unreachable;
defer output_buf.deinit();
if (expandString("}ABC", &output_buf)) {
unreachable;
} else |err| {
assert(expected_err == err);
}
}
test "valid inputs" {
global_allocator = std.debug.global_allocator;
expectExpansion("{x,y,z}", "x y z");
expectExpansion("{A,B}{x,y}", "Ax Ay Bx By");
expectExpansion("{A,B{x,y}}", "A Bx By");
expectExpansion("{ABC}", "ABC");
expectExpansion("{A,B,C}", "A B C");
expectExpansion("ABC", "ABC");
expectExpansion("", "");
expectExpansion("{A,B}{C,{x,y}}{g,h}", "ACg ACh Axg Axh Ayg Ayh BCg BCh Bxg Bxh Byg Byh");
expectExpansion("{A,B}{C,C{x,y}}{g,h}", "ACg ACh ACxg ACxh ACyg ACyh BCg BCh BCxg BCxh BCyg BCyh");
expectExpansion("{A,B}a", "Aa Ba");
expectExpansion("{C,{x,y}}", "C x y");
expectExpansion("z{C,{x,y}}", "zC zx zy");
expectExpansion("a{b,c{d,e{f,g}}}", "ab acd acef aceg");
expectExpansion("a{x,y}b", "axb ayb");
expectExpansion("z{{a,b}}", "za zb");
expectExpansion("a{b}", "ab");
}
fn expectExpansion(test_input: []const u8, expected_result: []const u8) {
var result = Buffer.initSize(global_allocator, 0) catch unreachable;
defer result.deinit();
expandString(test_input, &result) catch unreachable;
assert(mem.eql(u8, result.toSlice(), expected_result));
}

View File

@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) {
pub fn build(b: &Builder) -> %void {
const obj = b.addObject("test", "test.zig");
const test_step = b.step("test", "Test the program");

View File

@ -1,4 +1,5 @@
pub fn panic(msg: []const u8) -> noreturn { @breakpoint(); while (true) {} }
const StackTrace = @import("builtin").StackTrace;
pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) -> noreturn { @breakpoint(); while (true) {} }
fn bar() -> %void {}

View File

@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) {
pub fn build(b: &Builder) -> %void {
const exe = b.addExecutable("test", "test.zig");
exe.addPackagePath("my_pkg", "pkg.zig");

View File

@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) {
pub fn build(b: &Builder) -> %void {
b.addCIncludePath(".");
const main = b.addTest("main.zig");

View File

@ -42,14 +42,10 @@ const test_targets = []TestTarget {
.arch = builtin.Arch.x86_64,
.environ = builtin.Environ.msvc,
},
TestTarget {
.os = builtin.Os.windows,
.arch = builtin.Arch.i386,
.environ = builtin.Environ.msvc,
},
};
error TestFailed;
error CompilationIncorrectlySucceeded;
const max_stdout_size = 1 * 1024 * 1024; // 1 MB
@ -607,8 +603,7 @@ pub const CompileErrorContext = struct {
switch (term) {
Term.Exited => |code| {
if (code == 0) {
warn("Compilation incorrectly succeeded\n");
return error.TestFailed;
return error.CompilationIncorrectlySucceeded;
}
},
else => {