clean up error return tracing

* error return tracing is disabled in release-fast mode
 * add @errorReturnTrace
 * zig build API changes build return type from `void` to `%void`
 * allow `void`, `noreturn`, and `u8` from main. closes #535
This commit is contained in:
Andrew Kelley 2018-01-15 00:01:02 -05:00
parent d973b40884
commit 7b57454cc1
22 changed files with 197 additions and 111 deletions

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

@ -1274,6 +1274,7 @@ enum BuiltinFnId {
BuiltinFnIdSetAlignStack,
BuiltinFnIdArgType,
BuiltinFnIdExport,
BuiltinFnIdErrorReturnTrace,
};
struct BuiltinFnEntry {
@ -1499,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;
@ -1902,6 +1904,7 @@ enum IrInstructionId {
IrInstructionIdSetAlignStack,
IrInstructionIdArgType,
IrInstructionIdExport,
IrInstructionIdErrorReturnTrace,
};
struct IrInstruction {
@ -2723,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

@ -925,8 +925,9 @@ 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 = fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
fn_type_id->return_type->id == TypeTableEntryIdPureError;
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
// +1 for maybe last argument the error return trace
LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(2 + fn_type_id->param_count);
@ -2711,13 +2712,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"))
{

View File

@ -404,7 +404,10 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
zig_unreachable();
}
static uint32_t get_err_ret_trace_arg_index(FnTableEntry *fn_table_entry) {
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) {
@ -572,7 +575,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
}
}
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(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");
}
@ -1415,31 +1418,33 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
TypeTableEntry *return_type = return_instruction->value->value.type;
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
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;
}
} 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);
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);
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)) {
@ -2475,7 +2480,7 @@ 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);
bool prefix_arg_err_ret_stack = src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError;
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);
@ -3031,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;
@ -3804,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();
}
@ -4653,10 +4670,10 @@ 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(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) {
g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
} else if (fn_table_entry->calls_errorable_function) {
} 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;
@ -5251,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) {
@ -5553,6 +5571,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();
}
@ -9568,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)
{
@ -15324,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();
}
@ -15507,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);
}
}

View File

@ -56,7 +56,7 @@ pub fn dumpCurrentStackTrace() {
}
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpStackTrace(stack_trace: &builtin.StackTrace) {
pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) {
const stderr = getStderrStream() catch return;
const debug_info = openSelfDebugInfo(global_allocator) catch |err| {
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
@ -127,7 +127,7 @@ const RESET = "\x1b[0m";
error PathNotFound;
error InvalidDebugInfo;
pub fn writeStackTrace(stack_trace: &builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
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;
@ -167,6 +167,9 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
}
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}";
@ -177,7 +180,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
return;
};
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
if (getLineNumberInfo(debug_info, compile_unit, usize(address) - 1)) |line_info| {
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",

View File

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

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

@ -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) catch unreachable;
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) catch unreachable;
try root.build(builder);
}
// This usage text has to be synchronized with src/main.cpp

View File

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

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

@ -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

@ -50,6 +50,7 @@ const test_targets = []TestTarget {
};
error TestFailed;
error CompilationIncorrectlySucceeded;
const max_stdout_size = 1 * 1024 * 1024; // 1 MB
@ -607,8 +608,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 => {