Merge remote-tracking branch 'upstream/master' into arm-support-improvement

This commit is contained in:
Robin Voetter 2019-09-04 15:55:54 +02:00
commit 5308eb7045
36 changed files with 964 additions and 124 deletions

View File

@ -23,14 +23,18 @@ find_program(GIT_EXE NAMES git)
if(GIT_EXE)
execute_process(
COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always
RESULT_VARIABLE EXIT_STATUS
OUTPUT_VARIABLE ZIG_GIT_REV
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(ZIG_GIT_REV MATCHES "\\^0$")
if(NOT("${ZIG_GIT_REV}" STREQUAL "${ZIG_VERSION}^0"))
message("WARNING: Tag does not match configured Zig version")
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
if(EXIT_STATUS EQUAL "0")
if(ZIG_GIT_REV MATCHES "\\^0$")
if(NOT("${ZIG_GIT_REV}" STREQUAL "${ZIG_VERSION}^0"))
message("WARNING: Tag does not match configured Zig version")
endif()
else()
set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}")
endif()
else()
set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}")
endif()
endif()
message("Configuring zig version ${ZIG_VERSION}")

View File

@ -138,12 +138,13 @@ pub fn build(b: *Builder) !void {
test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));
test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
test_step.dependOn(tests.addCliTests(b, test_filter, modes));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
test_step.dependOn(tests.addGenHTests(b, test_filter));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
test_step.dependOn(docs_step);
}

View File

@ -307,7 +307,7 @@ const Node = union(enum) {
const Toc = struct {
nodes: []Node,
toc: []u8,
urls: std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8),
urls: std.StringHashMap(Token),
};
const Action = enum {
@ -316,7 +316,7 @@ const Action = enum {
};
fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator);
var urls = std.StringHashMap(Token).init(allocator);
errdefer urls.deinit();
var header_stack_size: usize = 0;

View File

@ -5,7 +5,7 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const ArrayList = std.ArrayList;
const HashMap = std.HashMap;
const StringHashMap = std.StringHashMap;
fn trimStart(slice: []const u8, ch: u8) []const u8 {
var i: usize = 0;
@ -73,7 +73,7 @@ fn readFlagArguments(allocator: *Allocator, args: []const []const u8, required:
}
}
const HashMapFlags = HashMap([]const u8, FlagArg, std.hash.Fnv1a_32.hash, mem.eql_slice_u8);
const HashMapFlags = StringHashMap(FlagArg);
// A store for querying found flags and positional arguments.
pub const Args = struct {

View File

@ -249,7 +249,7 @@ pub const Compilation = struct {
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql);
const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
const TypeTable = std.StringHashMap(*Type);
const CompileErrList = std.ArrayList(*Msg);

View File

@ -20,7 +20,7 @@ pub const Decl = struct {
// TODO when we destroy the decl, deref the tree scope
tree_scope: *Scope.AstTree,
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
pub const Table = std.StringHashMap(*Decl);
pub fn cast(base: *Decl, comptime T: type) ?*T {
if (base.id != @field(Id, @typeName(T))) return null;

View File

@ -541,7 +541,7 @@ const Fmt = struct {
color: errmsg.Color,
loop: *event.Loop,
const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
const SeenMap = std.StringHashMap(void);
};
fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {

View File

@ -10,7 +10,7 @@ pub const Package = struct {
/// relative to root_src_dir
table: Table,
pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8);
pub const Table = std.StringHashMap(*Package);
/// makes internal copies of root_src_dir and root_src_path
/// allocator should be an arena allocator because Package never frees anything

View File

@ -343,7 +343,7 @@ const Fmt = struct {
color: errmsg.Color,
allocator: *mem.Allocator,
const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
const SeenMap = std.StringHashMap(void);
};
fn printErrMsgToFile(
@ -376,7 +376,7 @@ fn printErrMsgToFile(
const text = text_buf.toOwnedSlice();
const stream = &file.outStream().stream;
try stream.print( "{}:{}:{}: error: {}\n", path, start_loc.line + 1, start_loc.column + 1, text);
try stream.print("{}:{}:{}: error: {}\n", path, start_loc.line + 1, start_loc.column + 1, text);
if (!color_on) return;

View File

@ -1989,6 +1989,7 @@ struct CodeGen {
bool system_linker_hack;
bool reported_bad_link_libc_error;
bool is_dynamic; // shared library rather than static library. dynamic musl rather than static musl.
bool need_frame_size_prefix_data;
//////////////////////////// Participates in Input Parameter Cache Hash
/////// Note: there is a separate cache hash for builtin.zig, when adding fields,
@ -2003,6 +2004,7 @@ struct CodeGen {
ZigList<Buf *> assembly_files;
ZigList<CFile *> c_source_files;
ZigList<const char *> lib_dirs;
ZigList<const char *> framework_dirs;
ZigLibCInstallation *libc;

View File

@ -2671,6 +2671,10 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
}
}
if (!type_has_bits(struct_type)) {
assert(struct_type->abi_align == 0);
}
struct_type->data.structure.resolve_loop_flag_other = false;
if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) {
@ -4191,7 +4195,7 @@ bool fn_is_async(ZigFn *fn) {
return fn->inferred_async_node != inferred_async_none;
}
static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) {
void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) {
assert(fn->inferred_async_node != nullptr);
assert(fn->inferred_async_node != inferred_async_checking);
assert(fn->inferred_async_node != inferred_async_none);
@ -7687,8 +7691,13 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
ZigType *tag_type = union_type->data.unionation.tag_type;
uint32_t gen_field_count = union_type->data.unionation.gen_field_count;
if (gen_field_count == 0) {
union_type->llvm_type = get_llvm_type(g, tag_type);
union_type->llvm_di_type = get_llvm_di_type(g, tag_type);
if (tag_type == nullptr) {
union_type->llvm_type = g->builtin_types.entry_void->llvm_type;
union_type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type;
} else {
union_type->llvm_type = get_llvm_type(g, tag_type);
union_type->llvm_di_type = get_llvm_di_type(g, tag_type);
}
union_type->data.unionation.resolve_status = ResolveStatusLLVMFull;
return;
}

View File

@ -256,4 +256,6 @@ Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType *
ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field);
ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);
void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn);
#endif

View File

@ -3522,6 +3522,15 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir
assert(ptr_type->id == ZigTypeIdPointer);
if (!type_has_bits(ptr_type))
return nullptr;
if (instruction->ptr->ref_count == 0) {
// In this case, this StorePtr instruction should be elided. Something happened like this:
// var t = true;
// const x = if (t) Num.Two else unreachable;
// The if condition is a runtime value, so the StorePtr for `x = Num.Two` got generated
// (this instruction being rendered) but because of `else unreachable` the result ended
// up being a comptime const value.
return nullptr;
}
bool have_init_expr = !value_is_all_undef(&instruction->value->value);
if (have_init_expr) {
@ -3766,6 +3775,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {
}
static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) {
assert(g->need_frame_size_prefix_data);
LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type;
LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0);
LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, "");
@ -4103,6 +4113,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executable,
IrInstructionStructFieldPtr *instruction)
{
Error err;
if (instruction->base.value.special != ConstValSpecialRuntime)
return nullptr;
@ -4120,6 +4132,11 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa
return struct_ptr;
}
ZigType *struct_type = (struct_ptr_type->id == ZigTypeIdPointer) ?
struct_ptr_type->data.pointer.child_type : struct_ptr_type;
if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull)))
report_errors_and_exit(g);
assert(field->gen_index != SIZE_MAX);
return LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, "");
}
@ -7199,7 +7216,9 @@ static void do_code_gen(CodeGen *g) {
LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
LLVMValueRef size_val = LLVMConstInt(usize_type_ref, fn_table_entry->frame_type->abi_size, false);
ZigLLVMFunctionSetPrefixData(fn_table_entry->llvm_value, size_val);
if (g->need_frame_size_prefix_data) {
ZigLLVMFunctionSetPrefixData(fn_table_entry->llvm_value, size_val);
}
if (!g->strip_debug_symbols) {
AstNode *source_node = fn_table_entry->proto_node;
@ -8445,8 +8464,21 @@ static void init(CodeGen *g) {
Buf *producer = buf_sprintf("zig %d.%d.%d", ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH);
const char *flags = "";
unsigned runtime_version = 0;
// For macOS stack traces, we want to avoid having to parse the compilation unit debug
// info. As long as each debug info file has a path independent of the compilation unit
// directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug
// info. If we provide an absolute path to LLVM here for the compilation unit debug info,
// LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we pass "."
// for the compilation unit directory. This forces each debug file to have a directory
// rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug files will
// no longer reference DW_AT_comp_dir, for the purpose of being able to support the
// common practice of stripping all but the line number sections from an executable.
const char *compile_unit_dir = target_os_is_darwin(g->zig_target->os) ? "." :
buf_ptr(&g->root_package->root_src_dir);
ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(g->root_out_name),
buf_ptr(&g->root_package->root_src_dir));
compile_unit_dir);
g->compile_unit = ZigLLVMCreateCompileUnit(g->dbuilder, ZigLLVMLang_DW_LANG_C99(),
compile_unit_file, buf_ptr(producer), is_optimized, flags, runtime_version,
"", 0, !g->strip_debug_symbols);
@ -8873,6 +8905,15 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
for (size_t i = 0; i < g->test_fns.length; i += 1) {
ZigFn *test_fn_entry = g->test_fns.at(i);
if (fn_is_async(test_fn_entry)) {
ErrorMsg *msg = add_node_error(g, test_fn_entry->proto_node,
buf_create_from_str("test functions cannot be async"));
add_error_note(g, msg, test_fn_entry->proto_node,
buf_sprintf("this restriction may be lifted in the future. See https://github.com/ziglang/zig/issues/3117 for more details"));
add_async_error_notes(g, msg, test_fn_entry);
continue;
}
ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i];
this_val->special = ConstValSpecialStatic;
this_val->type = struct_type;
@ -8892,6 +8933,7 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) {
fn_field->data.x_ptr.mut = ConstPtrMutComptimeConst;
fn_field->data.x_ptr.data.fn.fn_entry = test_fn_entry;
}
report_errors_and_maybe_exit(g);
ConstExprValue *test_fn_slice = create_const_slice(g, test_fn_array, 0, g->test_fns.length, true);
@ -9803,6 +9845,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
cache_list_of_str(ch, g->llvm_argv, g->llvm_argv_len);
cache_list_of_str(ch, g->clang_argv, g->clang_argv_len);
cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length);
cache_list_of_str(ch, g->framework_dirs.items, g->framework_dirs.length);
if (g->libc) {
cache_buf(ch, &g->libc->include_dir);
cache_buf(ch, &g->libc->sys_include_dir);

View File

@ -330,6 +330,8 @@ static bool ir_should_inline(IrExecutable *exec, Scope *scope) {
while (scope != nullptr) {
if (scope->id == ScopeIdCompTime)
return true;
if (scope->id == ScopeIdTypeOf)
return false;
if (scope->id == ScopeIdFnDef)
break;
scope = scope->parent;
@ -14837,6 +14839,12 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in
if (align != 0) {
if ((err = type_resolve(ira->codegen, var_type, ResolveStatusAlignmentKnown)))
return ira->codegen->invalid_instruction;
if (!type_has_bits(var_type)) {
ir_add_error(ira, source_inst,
buf_sprintf("variable '%s' of zero-bit type '%s' has no in-memory representation, it cannot be aligned",
name_hint, buf_ptr(&var_type->name)));
return ira->codegen->invalid_instruction;
}
}
assert(result->base.value.data.x_ptr.special != ConstPtrSpecialInvalid);
@ -15648,6 +15656,32 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source
return &store_ptr->base;
}
static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstructionCallSrc *call_instruction,
ZigFn *fn_entry)
{
if (call_instruction->new_stack == nullptr)
return nullptr;
IrInstruction *new_stack = call_instruction->new_stack->child;
if (type_is_invalid(new_stack->value.type))
return ira->codegen->invalid_instruction;
if (call_instruction->is_async_call_builtin &&
fn_entry != nullptr && new_stack->value.type->id == ZigTypeIdPointer &&
new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame)
{
ZigType *needed_frame_type = get_pointer_to_type(ira->codegen,
get_fn_frame_type(ira->codegen, fn_entry), false);
return ir_implicit_cast(ira, new_stack, needed_frame_type);
} else {
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
ira->codegen->need_frame_size_prefix_data = true;
return ir_implicit_cast(ira, new_stack, u8_slice);
}
}
static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction,
ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref,
IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)
@ -15826,31 +15860,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
return ir_finish_anal(ira, new_instruction);
}
IrInstruction *casted_new_stack = nullptr;
if (call_instruction->new_stack != nullptr) {
IrInstruction *new_stack = call_instruction->new_stack->child;
if (type_is_invalid(new_stack->value.type))
return ira->codegen->invalid_instruction;
if (call_instruction->is_async_call_builtin &&
fn_entry != nullptr && new_stack->value.type->id == ZigTypeIdPointer &&
new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame)
{
ZigType *needed_frame_type = get_pointer_to_type(ira->codegen,
get_fn_frame_type(ira->codegen, fn_entry), false);
casted_new_stack = ir_implicit_cast(ira, new_stack, needed_frame_type);
if (type_is_invalid(casted_new_stack->value.type))
return ira->codegen->invalid_instruction;
} else {
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice);
if (type_is_invalid(casted_new_stack->value.type))
return ira->codegen->invalid_instruction;
}
}
if (fn_type->data.fn.is_generic) {
if (!fn_entry) {
ir_add_error(ira, call_instruction->fn_ref,
@ -16063,6 +16072,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
parent_fn_entry->calls_or_awaits_errorable_fn = true;
}
IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, call_instruction, impl_fn);
if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value.type))
return ira->codegen->invalid_instruction;
size_t impl_param_count = impl_fn_type_id->param_count;
if (call_instruction->is_async) {
IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry,
@ -16071,11 +16084,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
}
IrInstruction *result_loc;
if (call_instruction->is_async_call_builtin) {
result_loc = get_async_call_result_loc(ira, call_instruction, impl_fn_type_id->return_type);
if (result_loc != nullptr && type_is_invalid(result_loc->value.type))
return ira->codegen->invalid_instruction;
} else if (handle_is_ptr(impl_fn_type_id->return_type)) {
if (handle_is_ptr(impl_fn_type_id->return_type)) {
result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
impl_fn_type_id->return_type, nullptr, true, true, false);
if (result_loc != nullptr) {
@ -16087,6 +16096,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
result_loc = nullptr;
}
}
} else if (call_instruction->is_async_call_builtin) {
result_loc = get_async_call_result_loc(ira, call_instruction, impl_fn_type_id->return_type);
if (result_loc != nullptr && type_is_invalid(result_loc->value.type))
return ira->codegen->invalid_instruction;
} else {
result_loc = nullptr;
}
@ -16211,6 +16224,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
return ira->codegen->invalid_instruction;
}
IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, call_instruction, fn_entry);
if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value.type))
return ira->codegen->invalid_instruction;
if (call_instruction->is_async) {
IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref,
casted_args, call_param_count, casted_new_stack);
@ -16223,11 +16240,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
}
IrInstruction *result_loc;
if (call_instruction->is_async_call_builtin) {
result_loc = get_async_call_result_loc(ira, call_instruction, return_type);
if (result_loc != nullptr && type_is_invalid(result_loc->value.type))
return ira->codegen->invalid_instruction;
} else if (handle_is_ptr(return_type)) {
if (handle_is_ptr(return_type)) {
result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
return_type, nullptr, true, true, false);
if (result_loc != nullptr) {
@ -16239,6 +16252,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
result_loc = nullptr;
}
}
} else if (call_instruction->is_async_call_builtin) {
result_loc = get_async_call_result_loc(ira, call_instruction, return_type);
if (result_loc != nullptr && type_is_invalid(result_loc->value.type))
return ira->codegen->invalid_instruction;
} else {
result_loc = nullptr;
}
@ -17453,7 +17470,12 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
source_instr, container_ptr, container_type);
}
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
ZigType *field_type = resolve_union_field_type(ira->codegen, field);
if (field_type == nullptr)
return ira->codegen->invalid_instruction;
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
is_const, is_volatile, PtrLenSingle, 0, 0, 0, false);
if (instr_is_comptime(container_ptr)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
@ -17470,7 +17492,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
if (initializing) {
ConstExprValue *payload_val = create_const_vals(1);
payload_val->special = ConstValSpecialUndef;
payload_val->type = field->type_entry;
payload_val->type = field_type;
payload_val->parent.id = ConstParentIdUnion;
payload_val->parent.data.p_union.union_val = union_val;
@ -22523,6 +22545,8 @@ static IrInstruction *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstru
return ira->codegen->invalid_instruction;
}
ira->codegen->need_frame_size_prefix_data = true;
IrInstruction *result = ir_build_frame_size_gen(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, fn);
result->value.type = ira->codegen->builtin_types.entry_usize;

View File

@ -2510,6 +2510,12 @@ static void construct_linker_job_macho(LinkJob *lj) {
lj->args.append("dynamic_lookup");
}
for (size_t i = 0; i < g->framework_dirs.length; i += 1) {
const char *framework_dir = g->framework_dirs.at(i);
lj->args.append("-F");
lj->args.append(framework_dir);
}
for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
lj->args.append("-framework");
lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));

View File

@ -104,6 +104,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" -rdynamic add all symbols to the dynamic symbol table\n"
" -rpath [path] add directory to the runtime library search path\n"
" --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n"
" -F[dir] (darwin) add search path for frameworks\n"
" -framework [name] (darwin) link against framework\n"
" -mios-version-min [ver] (darwin) set iOS deployment target\n"
" -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n"
@ -454,6 +455,7 @@ int main(int argc, char **argv) {
ZigList<const char *> lib_dirs = {0};
ZigList<const char *> link_libs = {0};
ZigList<const char *> forbidden_link_libs = {0};
ZigList<const char *> framework_dirs = {0};
ZigList<const char *> frameworks = {0};
bool have_libc = false;
const char *target_string = nullptr;
@ -686,6 +688,8 @@ int main(int argc, char **argv) {
} else if (arg[1] == 'L' && arg[2] != 0) {
// alias for --library-path
lib_dirs.append(&arg[2]);
} else if (arg[1] == 'F' && arg[2] != 0) {
framework_dirs.append(&arg[2]);
} else if (strcmp(arg, "--pkg-begin") == 0) {
if (i + 2 >= argc) {
fprintf(stderr, "Expected 2 arguments after --pkg-begin\n");
@ -772,6 +776,8 @@ int main(int argc, char **argv) {
main_pkg_path = buf_create_from_str(argv[i]);
} else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
lib_dirs.append(argv[i]);
} else if (strcmp(arg, "-F") == 0) {
framework_dirs.append(argv[i]);
} else if (strcmp(arg, "--library") == 0) {
if (strcmp(argv[i], "c") == 0)
have_libc = true;
@ -1153,6 +1159,9 @@ int main(int argc, char **argv) {
for (size_t i = 0; i < lib_dirs.length; i += 1) {
codegen_add_lib_dir(g, lib_dirs.at(i));
}
for (size_t i = 0; i < framework_dirs.length; i += 1) {
g->framework_dirs.append(framework_dirs.at(i));
}
for (size_t i = 0; i < link_libs.length; i += 1) {
LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i)));
link_lib->provided_explicitly = true;

View File

@ -842,6 +842,7 @@ const char *ZigLLVMGetSubArchTypeName(ZigLLVM_SubArchType sub_arch) {
void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module) {
unwrap(module)->addModuleFlag(Module::Warning, "Debug Info Version", DEBUG_METADATA_VERSION);
unwrap(module)->addModuleFlag(Module::Warning, "Dwarf Version", 4);
}
void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module) {

View File

@ -1,5 +1,5 @@
const std = @import("std.zig");
const HashMap = std.HashMap;
const StringHashMap = std.StringHashMap;
const mem = std.mem;
const Allocator = mem.Allocator;
const testing = std.testing;
@ -9,7 +9,7 @@ const testing = std.testing;
pub const BufMap = struct {
hash_map: BufMapHashMap,
const BufMapHashMap = HashMap([]const u8, []const u8, mem.hash_slice_u8, mem.eql_slice_u8);
const BufMapHashMap = StringHashMap([]const u8);
pub fn init(allocator: *Allocator) BufMap {
var self = BufMap{ .hash_map = BufMapHashMap.init(allocator) };

View File

@ -1,5 +1,5 @@
const std = @import("std.zig");
const HashMap = @import("hash_map.zig").HashMap;
const StringHashMap = std.StringHashMap;
const mem = @import("mem.zig");
const Allocator = mem.Allocator;
const testing = std.testing;
@ -7,7 +7,7 @@ const testing = std.testing;
pub const BufSet = struct {
hash_map: BufSetHashMap,
const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
const BufSetHashMap = StringHashMap(void);
pub fn init(a: *Allocator) BufSet {
var self = BufSet{ .hash_map = BufSetHashMap.init(a) };

View File

@ -4,10 +4,11 @@ const io = std.io;
const fs = std.fs;
const mem = std.mem;
const debug = std.debug;
const panic = std.debug.panic;
const assert = debug.assert;
const warn = std.debug.warn;
const ArrayList = std.ArrayList;
const HashMap = std.HashMap;
const StringHashMap = std.StringHashMap;
const Allocator = mem.Allocator;
const process = std.process;
const BufSet = std.BufSet;
@ -42,8 +43,8 @@ pub const Builder = struct {
top_level_steps: ArrayList(*TopLevelStep),
install_prefix: ?[]const u8,
dest_dir: ?[]const u8,
lib_dir: ?[]const u8,
exe_dir: ?[]const u8,
lib_dir: []const u8,
exe_dir: []const u8,
install_path: []const u8,
search_prefixes: ArrayList([]const u8),
installed_files: ArrayList(InstalledFile),
@ -60,8 +61,8 @@ pub const Builder = struct {
C11,
};
const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);
const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);
const UserInputOptionsMap = StringHashMap(UserInputOption);
const AvailableOptionsMap = StringHashMap(AvailableOption);
const AvailableOption = struct {
name: []const u8,
@ -129,8 +130,8 @@ pub const Builder = struct {
.env_map = env_map,
.search_prefixes = ArrayList([]const u8).init(allocator),
.install_prefix = null,
.lib_dir = null,
.exe_dir = null,
.lib_dir = undefined,
.exe_dir = undefined,
.dest_dir = env_map.get("DESTDIR"),
.installed_files = ArrayList(InstalledFile).init(allocator),
.install_tls = TopLevelStep{
@ -163,11 +164,13 @@ pub const Builder = struct {
self.allocator.destroy(self);
}
/// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
pub fn setInstallPrefix(self: *Builder, optional_prefix: ?[]const u8) void {
self.install_prefix = optional_prefix;
}
fn resolveInstallPrefix(self: *Builder) void {
/// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
pub fn resolveInstallPrefix(self: *Builder) void {
if (self.dest_dir) |dest_dir| {
const install_prefix = self.install_prefix orelse "/usr";
self.install_path = fs.path.join(self.allocator, [_][]const u8{ dest_dir, install_prefix }) catch unreachable;
@ -437,7 +440,7 @@ pub const Builder = struct {
.description = description,
};
if ((self.available_options_map.put(name, available_option) catch unreachable) != null) {
debug.panic("Option '{}' declared twice", name);
panic("Option '{}' declared twice", name);
}
self.available_options_list.append(available_option) catch unreachable;
@ -463,8 +466,8 @@ pub const Builder = struct {
return null;
},
},
TypeId.Int => debug.panic("TODO integer options to build script"),
TypeId.Float => debug.panic("TODO float options to build script"),
TypeId.Int => panic("TODO integer options to build script"),
TypeId.Float => panic("TODO float options to build script"),
TypeId.String => switch (entry.value.value) {
UserValue.Flag => {
warn("Expected -D{} to be a string, but received a boolean.\n", name);
@ -478,7 +481,7 @@ pub const Builder = struct {
},
UserValue.Scalar => |s| return s,
},
TypeId.List => debug.panic("TODO list options to build script"),
TypeId.List => panic("TODO list options to build script"),
}
}
@ -644,8 +647,6 @@ pub const Builder = struct {
}
pub fn validateUserInputDidItFail(self: *Builder) bool {
self.resolveInstallPrefix();
// make sure all args are used
var it = self.user_input_options.iterator();
while (true) {
@ -855,7 +856,7 @@ pub const Builder = struct {
var stdout_file_in_stream = child.stdout.?.inStream();
try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
const term = child.wait() catch |err| std.debug.panic("unable to spawn {}: {}", argv[0], err);
const term = child.wait() catch |err| panic("unable to spawn {}: {}", argv[0], err);
switch (term) {
.Exited => |code| {
if (code != 0) {
@ -882,8 +883,8 @@ pub const Builder = struct {
fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
const base_dir = switch (dir) {
.Prefix => self.install_path,
.Bin => self.exe_dir.?,
.Lib => self.lib_dir.?,
.Bin => self.exe_dir,
.Lib => self.lib_dir,
};
return fs.path.resolve(
self.allocator,
@ -1228,6 +1229,7 @@ pub const LibExeObjStep = struct {
name_only_filename: []const u8,
strip: bool,
lib_paths: ArrayList([]const u8),
framework_dirs: ArrayList([]const u8),
frameworks: BufSet,
verbose_link: bool,
verbose_cc: bool,
@ -1317,6 +1319,9 @@ pub const LibExeObjStep = struct {
}
fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, is_dynamic: bool, ver: Version) LibExeObjStep {
if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", name);
}
var self = LibExeObjStep{
.strip = false,
.builder = builder,
@ -1341,6 +1346,7 @@ pub const LibExeObjStep = struct {
.include_dirs = ArrayList(IncludeDir).init(builder.allocator),
.link_objects = ArrayList(LinkObject).init(builder.allocator),
.lib_paths = ArrayList([]const u8).init(builder.allocator),
.framework_dirs = ArrayList([]const u8).init(builder.allocator),
.object_src = undefined,
.build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,
.c_std = Builder.CStd.C99,
@ -1614,6 +1620,10 @@ pub const LibExeObjStep = struct {
self.lib_paths.append(path) catch unreachable;
}
pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void {
self.framework_dirs.append(dir_path) catch unreachable;
}
pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
self.packages.append(Pkg{
.name = name,
@ -1860,8 +1870,8 @@ pub const LibExeObjStep = struct {
}
for (self.lib_paths.toSliceConst()) |lib_path| {
zig_args.append("--library-path") catch unreachable;
zig_args.append(lib_path) catch unreachable;
try zig_args.append("-L");
try zig_args.append(lib_path);
}
if (self.need_system_paths and self.target == Target.Native) {
@ -1882,6 +1892,11 @@ pub const LibExeObjStep = struct {
}
if (self.target.isDarwin()) {
for (self.framework_dirs.toSliceConst()) |dir| {
try zig_args.append("-F");
try zig_args.append(dir);
}
var it = self.frameworks.iterator();
while (it.next()) |entry| {
zig_args.append("-framework") catch unreachable;

View File

@ -110,9 +110,7 @@ fn convertRepr(comptime T: type, n: FloatRepr) T {
q.shiftLeft1(s); // q = p << 1
r.shiftLeft1(q); // r = p << 2
s.shiftLeft1(r); // p = p << 3
q.add(s); // p = (p << 3) + (p << 1)
exp -= 1;
s.add(q); // p = (p << 3) + (p << 1)
while (s.d2 & mask28 != 0) {
q.shiftRight1(s);
@ -402,6 +400,13 @@ test "fmt.parseFloat" {
expectEqual((try parseFloat(T, "+0")), 0.0);
expectEqual((try parseFloat(T, "-0")), 0.0);
expectEqual((try parseFloat(T, "0e0")), 0);
expectEqual((try parseFloat(T, "2e3")), 2000.0);
expectEqual((try parseFloat(T, "1e0")), 1.0);
expectEqual((try parseFloat(T, "-2e3")), -2000.0);
expectEqual((try parseFloat(T, "-1e0")), -1.0);
expectEqual((try parseFloat(T, "1.234e3")), 1234);
expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon));
expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon));
@ -413,6 +418,9 @@ test "fmt.parseFloat" {
expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T));
if (T != f16) {
expect(approxEq(T, try parseFloat(T, "1e-2"), 0.01, epsilon));
expect(approxEq(T, try parseFloat(T, "1234e-2"), 12.34, epsilon));
expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon));
expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon));
expect(approxEq(T, try parseFloat(T, "0.7062146892655368"), T(0.7062146892655368), epsilon));

View File

@ -23,9 +23,7 @@ pub fn StringHashMap(comptime V: type) type {
}
pub fn eqlString(a: []const u8, b: []const u8) bool {
if (a.len != b.len) return false;
if (a.ptr == b.ptr) return true;
return mem.compare(u8, a, b) == .Equal;
return mem.eql(u8, a, b);
}
pub fn hashString(s: []const u8) u32 {

View File

@ -989,7 +989,7 @@ test "json.validate" {
const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const ArrayList = std.ArrayList;
const HashMap = std.HashMap;
const StringHashMap = std.StringHashMap;
pub const ValueTree = struct {
arena: ArenaAllocator,
@ -1000,7 +1000,7 @@ pub const ValueTree = struct {
}
};
pub const ObjectMap = HashMap([]const u8, Value, mem.hash_slice_u8, mem.eql_slice_u8);
pub const ObjectMap = StringHashMap(Value);
pub const Value = union(enum) {
Null,

View File

@ -339,6 +339,7 @@ test "mem.lessThan" {
/// Compares two slices and returns whether they are equal.
pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
if (a.len != b.len) return false;
if (a.ptr == b.ptr) return true;
for (a) |item, index| {
if (b[index] != item) return false;
}
@ -738,47 +739,34 @@ test "writeIntBig and writeIntLittle" {
var buf9: [9]u8 = undefined;
writeIntBig(u0, &buf0, 0x0);
testing.expect(eql_slice_u8(buf0[0..], [_]u8{}));
testing.expect(eql(u8, buf0[0..], [_]u8{}));
writeIntLittle(u0, &buf0, 0x0);
testing.expect(eql_slice_u8(buf0[0..], [_]u8{}));
testing.expect(eql(u8, buf0[0..], [_]u8{}));
writeIntBig(u8, &buf1, 0x12);
testing.expect(eql_slice_u8(buf1[0..], [_]u8{0x12}));
testing.expect(eql(u8, buf1[0..], [_]u8{0x12}));
writeIntLittle(u8, &buf1, 0x34);
testing.expect(eql_slice_u8(buf1[0..], [_]u8{0x34}));
testing.expect(eql(u8, buf1[0..], [_]u8{0x34}));
writeIntBig(u16, &buf2, 0x1234);
testing.expect(eql_slice_u8(buf2[0..], [_]u8{ 0x12, 0x34 }));
testing.expect(eql(u8, buf2[0..], [_]u8{ 0x12, 0x34 }));
writeIntLittle(u16, &buf2, 0x5678);
testing.expect(eql_slice_u8(buf2[0..], [_]u8{ 0x78, 0x56 }));
testing.expect(eql(u8, buf2[0..], [_]u8{ 0x78, 0x56 }));
writeIntBig(u72, &buf9, 0x123456789abcdef024);
testing.expect(eql_slice_u8(buf9[0..], [_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }));
testing.expect(eql(u8, buf9[0..], [_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }));
writeIntLittle(u72, &buf9, 0xfedcba9876543210ec);
testing.expect(eql_slice_u8(buf9[0..], [_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }));
testing.expect(eql(u8, buf9[0..], [_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }));
writeIntBig(i8, &buf1, -1);
testing.expect(eql_slice_u8(buf1[0..], [_]u8{0xff}));
testing.expect(eql(u8, buf1[0..], [_]u8{0xff}));
writeIntLittle(i8, &buf1, -2);
testing.expect(eql_slice_u8(buf1[0..], [_]u8{0xfe}));
testing.expect(eql(u8, buf1[0..], [_]u8{0xfe}));
writeIntBig(i16, &buf2, -3);
testing.expect(eql_slice_u8(buf2[0..], [_]u8{ 0xff, 0xfd }));
testing.expect(eql(u8, buf2[0..], [_]u8{ 0xff, 0xfd }));
writeIntLittle(i16, &buf2, -4);
testing.expect(eql_slice_u8(buf2[0..], [_]u8{ 0xfc, 0xff }));
}
pub fn hash_slice_u8(k: []const u8) u32 {
// FNV 32-bit hash
var h: u32 = 2166136261;
for (k) |b| {
h = (h ^ b) *% 16777619;
}
return h;
}
pub fn eql_slice_u8(a: []const u8, b: []const u8) bool {
return eql(u8, a, b);
testing.expect(eql(u8, buf2[0..], [_]u8{ 0xfc, 0xff }));
}
/// Returns an iterator that iterates over the slices of `buffer` that are not

View File

@ -123,6 +123,7 @@ pub fn main() !void {
}
}
builder.resolveInstallPrefix();
try runBuild(builder);
if (builder.validateUserInputDidItFail())
@ -151,6 +152,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
builder.resolveInstallPrefix();
try runBuild(builder);
}

View File

@ -2419,6 +2419,94 @@ test "zig fmt: comment after empty comment" {
);
}
test "zig fmt: line comment in array" {
try testTransform(
\\test "a" {
\\ var arr = [_]u32{
\\ 0
\\ // 1,
\\ // 2,
\\ };
\\}
\\
,
\\test "a" {
\\ var arr = [_]u32{
\\ 0, // 1,
\\ // 2,
\\ };
\\}
\\
);
try testCanonical(
\\test "a" {
\\ var arr = [_]u32{
\\ 0,
\\ // 1,
\\ // 2,
\\ };
\\}
\\
);
}
test "zig fmt: comment after params" {
try testTransform(
\\fn a(
\\ b: u32
\\ // c: u32,
\\ // d: u32,
\\) void {}
\\
,
\\fn a(
\\ b: u32, // c: u32,
\\ // d: u32,
\\) void {}
\\
);
try testCanonical(
\\fn a(
\\ b: u32,
\\ // c: u32,
\\ // d: u32,
\\) void {}
\\
);
}
test "zig fmt: comment in array initializer/access" {
try testCanonical(
\\test "a" {
\\ var a = x{ //aa
\\ //bb
\\ };
\\ var a = []x{ //aa
\\ //bb
\\ };
\\ var b = [ //aa
\\ _
\\ ]x{ //aa
\\ //bb
\\ 9,
\\ };
\\ var c = b[ //aa
\\ 0
\\ ];
\\ var d = [_
\\ //aa
\\ ]x{ //aa
\\ //bb
\\ 9,
\\ };
\\ var e = d[0
\\ //aa
\\ ];
\\}
\\
);
}
test "zig fmt: comments at several places in struct init" {
try testTransform(
\\var bar = Bar{

View File

@ -483,9 +483,23 @@ fn renderExpression(
},
ast.Node.PrefixOp.Op.ArrayType => |array_index| {
try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [
try renderExpression(allocator, stream, tree, indent, start_col, array_index, Space.None);
try renderToken(tree, stream, tree.nextToken(array_index.lastToken()), indent, start_col, Space.None); // ]
const lbracket = prefix_op_node.op_token;
const rbracket = tree.nextToken(array_index.lastToken());
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
const starts_with_comment = tree.tokens.at(lbracket + 1).id == .LineComment;
const ends_with_comment = tree.tokens.at(rbracket - 1).id == .LineComment;
const new_indent = if (ends_with_comment) indent + indent_delta else indent;
const new_space = if (ends_with_comment) Space.Newline else Space.None;
try renderExpression(allocator, stream, tree, new_indent, start_col, array_index, new_space);
if (starts_with_comment) {
try stream.writeByte('\n');
}
if (ends_with_comment or starts_with_comment) {
try stream.writeByteNTimes(' ', indent);
}
try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
},
ast.Node.PrefixOp.Op.BitNot,
ast.Node.PrefixOp.Op.BoolNot,
@ -580,7 +594,18 @@ fn renderExpression(
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
try renderExpression(allocator, stream, tree, indent, start_col, index_expr, Space.None);
const starts_with_comment = tree.tokens.at(lbracket + 1).id == .LineComment;
const ends_with_comment = tree.tokens.at(rbracket - 1).id == .LineComment;
const new_indent = if (ends_with_comment) indent + indent_delta else indent;
const new_space = if (ends_with_comment) Space.Newline else Space.None;
try renderExpression(allocator, stream, tree, new_indent, start_col, index_expr, new_space);
if (starts_with_comment) {
try stream.writeByte('\n');
}
if (ends_with_comment or starts_with_comment) {
try stream.writeByteNTimes(' ', indent);
}
return renderToken(tree, stream, rbracket, indent, start_col, space); // ]
},
@ -615,7 +640,7 @@ fn renderExpression(
if (field_inits.len == 0) {
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
try renderToken(tree, stream, lbrace, indent + indent_delta, start_col, Space.None);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
}
@ -714,7 +739,7 @@ fn renderExpression(
try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
}
if (exprs.len == 1) {
if (exprs.len == 1 and tree.tokens.at(exprs.at(0).*.lastToken() + 1).id == .RBrace) {
const expr = exprs.at(0).*;
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
@ -775,7 +800,7 @@ fn renderExpression(
while (it.next()) |expr| : (i += 1) {
counting_stream.bytes_written = 0;
var dummy_col: usize = 0;
try renderExpression(allocator, &counting_stream.stream, tree, 0, &dummy_col, expr.*, Space.None);
try renderExpression(allocator, &counting_stream.stream, tree, indent, &dummy_col, expr.*, Space.None);
const width = @intCast(usize, counting_stream.bytes_written);
const col = i % row_size;
column_widths[col] = std.math.max(column_widths[col], width);
@ -1191,8 +1216,8 @@ fn renderExpression(
});
const src_params_trailing_comma = blk: {
const maybe_comma = tree.prevToken(rparen);
break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma;
const maybe_comma = tree.tokens.at(rparen - 1).id;
break :blk maybe_comma == .Comma or maybe_comma == .LineComment;
};
if (!src_params_trailing_comma) {

View File

@ -6462,4 +6462,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:5:30: error: expression value is ignored",
"tmp.zig:9:30: error: expression value is ignored",
);
cases.add(
"aligned variable of zero-bit type",
\\export fn f() void {
\\ var s: struct {} align(4) = undefined;
\\}
,
"tmp.zig:2:5: error: variable 's' of zero-bit type 'struct:2:12' has no in-memory representation, it cannot be aligned",
);
}

274
test/stack_traces.zig Normal file
View File

@ -0,0 +1,274 @@
const builtin = @import("builtin");
const std = @import("std");
const os = std.os;
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.StackTracesContext) void {
const source_return =
\\const std = @import("std");
\\
\\pub fn main() !void {
\\ return error.TheSkyIsFalling;
\\}
;
const source_try_return =
\\const std = @import("std");
\\
\\fn foo() !void {
\\ return error.TheSkyIsFalling;
\\}
\\
\\pub fn main() !void {
\\ try foo();
\\}
;
const source_try_try_return_return =
\\const std = @import("std");
\\
\\fn foo() !void {
\\ try bar();
\\}
\\
\\fn bar() !void {
\\ return make_error();
\\}
\\
\\fn make_error() !void {
\\ return error.TheSkyIsFalling;
\\}
\\
\\pub fn main() !void {
\\ try foo();
\\}
;
switch (builtin.os) {
.linux => {
cases.addCase(
"return",
source_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in main (test)
\\
,
// release-safe
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test)
\\
,
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
cases.addCase(
"try return",
source_try_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in foo (test)
\\source.zig:8:5: [address] in main (test)
\\
,
// release-safe
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test)
\\source.zig:8:5: [address] in std.special.posixCallMainAndExit (test)
\\
,
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
cases.addCase(
"try try return return",
source_try_try_return_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:12:5: [address] in make_error (test)
\\source.zig:8:5: [address] in bar (test)
\\source.zig:4:5: [address] in foo (test)
\\source.zig:16:5: [address] in main (test)
\\
,
// release-safe
\\error: TheSkyIsFalling
\\source.zig:12:5: [address] in std.special.posixCallMainAndExit (test)
\\source.zig:8:5: [address] in std.special.posixCallMainAndExit (test)
\\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test)
\\source.zig:16:5: [address] in std.special.posixCallMainAndExit (test)
\\
,
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
},
.macosx => {
cases.addCase(
"return",
source_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in _main.0 (test.o)
\\
,
// release-safe
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in _main (test.o)
\\
,
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
cases.addCase(
"try return",
source_try_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in _foo (test.o)
\\source.zig:8:5: [address] in _main.0 (test.o)
\\
,
// release-safe
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in _main (test.o)
\\source.zig:8:5: [address] in _main (test.o)
\\
,
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
cases.addCase(
"try try return return",
source_try_try_return_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:12:5: [address] in _make_error (test.o)
\\source.zig:8:5: [address] in _bar (test.o)
\\source.zig:4:5: [address] in _foo (test.o)
\\source.zig:16:5: [address] in _main.0 (test.o)
\\
,
// release-safe
\\error: TheSkyIsFalling
\\source.zig:12:5: [address] in _main (test.o)
\\source.zig:8:5: [address] in _main (test.o)
\\source.zig:4:5: [address] in _main (test.o)
\\source.zig:16:5: [address] in _main (test.o)
\\
,
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
},
.windows => {
cases.addCase(
"return",
source_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in main (test.obj)
\\
,
// release-safe
// --disabled-- results in segmenetation fault
"",
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
cases.addCase(
"try return",
source_try_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:4:5: [address] in foo (test.obj)
\\source.zig:8:5: [address] in main (test.obj)
\\
,
// release-safe
// --disabled-- results in segmenetation fault
"",
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
cases.addCase(
"try try return return",
source_try_try_return_return,
[_][]const u8{
// debug
\\error: TheSkyIsFalling
\\source.zig:12:5: [address] in make_error (test.obj)
\\source.zig:8:5: [address] in bar (test.obj)
\\source.zig:4:5: [address] in foo (test.obj)
\\source.zig:16:5: [address] in main (test.obj)
\\
,
// release-safe
// --disabled-- results in segmenetation fault
"",
// release-fast
\\error: TheSkyIsFalling
\\
,
// release-small
\\error: TheSkyIsFalling
\\
},
);
},
else => {},
}
}

View File

@ -30,6 +30,7 @@ comptime {
_ = @import("behavior/bugs/2114.zig");
_ = @import("behavior/bugs/2346.zig");
_ = @import("behavior/bugs/2578.zig");
_ = @import("behavior/bugs/2692.zig");
_ = @import("behavior/bugs/3112.zig");
_ = @import("behavior/bugs/394.zig");
_ = @import("behavior/bugs/421.zig");

View File

@ -1031,3 +1031,64 @@ test "@typeOf an async function call of generic fn with error union type" {
};
_ = async S.func(i32);
}
test "using @typeOf on a generic function call" {
const S = struct {
var global_frame: anyframe = undefined;
var global_ok = false;
var buf: [100]u8 align(16) = undefined;
fn amain(x: var) void {
if (x == 0) {
global_ok = true;
return;
}
suspend {
global_frame = @frame();
}
const F = @typeOf(async amain(x - 1));
const frame = @intToPtr(*F, @ptrToInt(&buf));
return await @asyncCall(frame, {}, amain, x - 1);
}
};
_ = async S.amain(u32(1));
resume S.global_frame;
expect(S.global_ok);
}
test "recursive call of await @asyncCall with struct return type" {
const S = struct {
var global_frame: anyframe = undefined;
var global_ok = false;
var buf: [100]u8 align(16) = undefined;
fn amain(x: var) Foo {
if (x == 0) {
global_ok = true;
return Foo{ .x = 1, .y = 2, .z = 3 };
}
suspend {
global_frame = @frame();
}
const F = @typeOf(async amain(x - 1));
const frame = @intToPtr(*F, @ptrToInt(&buf));
return await @asyncCall(frame, {}, amain, x - 1);
}
const Foo = struct {
x: u64,
y: u64,
z: u64,
};
};
var res: S.Foo = undefined;
var frame: @typeOf(async S.amain(u32(1))) = undefined;
_ = @asyncCall(&frame, &res, S.amain, u32(1));
resume S.global_frame;
expect(S.global_ok);
expect(res.x == 1);
expect(res.y == 2);
expect(res.z == 3);
}

View File

@ -0,0 +1,6 @@
fn foo(a: []u8) void {}
test "address of 0 length array" {
var pt: [0]u8 = undefined;
foo(&pt);
}

View File

@ -63,3 +63,14 @@ test "labeled break inside comptime if inside runtime if" {
}
expect(answer == 42);
}
test "const result loc, runtime if cond, else unreachable" {
const Num = enum {
One,
Two,
};
var t = true;
const x = if (t) Num.Two else unreachable;
if (x != .Two) @compileError("bad");
}

View File

@ -599,3 +599,36 @@ test "extern fn returns struct by value" {
S.entry();
comptime S.entry();
}
test "for loop over pointers to struct, getting field from struct pointer" {
const S = struct {
const Foo = struct {
name: []const u8,
};
var ok = true;
fn eql(a: []const u8) bool {
return true;
}
const ArrayList = struct {
fn toSlice(self: *ArrayList) []*Foo {
return ([*]*Foo)(undefined)[0..0];
}
};
fn doTheTest() void {
var objects: ArrayList = undefined;
for (objects.toSlice()) |obj| {
if (eql(obj.name)) {
ok = false;
}
}
expect(ok);
}
};
S.doTheTest();
}

View File

@ -457,3 +457,13 @@ test "@unionInit can modify a pointer value" {
value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2);
expect(value.Byte == 2);
}
test "union no tag with struct member" {
const Struct = struct {};
const Union = union {
s: Struct,
pub fn foo(self: *@This()) void {}
};
var u = Union{ .s = Struct{} };
u.foo();
}

View File

@ -16,6 +16,7 @@ const LibExeObjStep = build.LibExeObjStep;
const compare_output = @import("compare_output.zig");
const standalone = @import("standalone.zig");
const stack_traces = @import("stack_traces.zig");
const compile_errors = @import("compile_errors.zig");
const assemble_and_link = @import("assemble_and_link.zig");
const runtime_safety = @import("runtime_safety.zig");
@ -57,6 +58,21 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes:
return cases.step;
}
pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
const cases = b.allocator.create(StackTracesContext) catch unreachable;
cases.* = StackTracesContext{
.b = b,
.step = b.step("test-stack-traces", "Run the stack trace tests"),
.test_index = 0,
.test_filter = test_filter,
.modes = modes,
};
stack_traces.addCases(cases);
return cases.step;
}
pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
const cases = b.allocator.create(CompareOutputContext) catch unreachable;
cases.* = CompareOutputContext{
@ -549,6 +565,200 @@ pub const CompareOutputContext = struct {
}
};
pub const StackTracesContext = struct {
b: *build.Builder,
step: *build.Step,
test_index: usize,
test_filter: ?[]const u8,
modes: []const Mode,
const Expect = [@typeInfo(Mode).Enum.fields.len][]const u8;
pub fn addCase(
self: *StackTracesContext,
name: []const u8,
source: []const u8,
expect: Expect,
) void {
const b = self.b;
const source_pathname = fs.path.join(
b.allocator,
[_][]const u8{ b.cache_root, "source.zig" },
) catch unreachable;
for (self.modes) |mode| {
const expect_for_mode = expect[@enumToInt(mode)];
if (expect_for_mode.len == 0) continue;
const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "stack-trace", name, @tagName(mode)) catch unreachable;
if (self.test_filter) |filter| {
if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
}
const exe = b.addExecutable("test", source_pathname);
exe.setBuildMode(mode);
const write_source = b.addWriteFile(source_pathname, source);
exe.step.dependOn(&write_source.step);
const run_and_compare = RunAndCompareStep.create(
self,
exe,
annotated_case_name,
mode,
expect_for_mode,
);
self.step.dependOn(&run_and_compare.step);
}
}
const RunAndCompareStep = struct {
step: build.Step,
context: *StackTracesContext,
exe: *LibExeObjStep,
name: []const u8,
mode: Mode,
expect_output: []const u8,
test_index: usize,
pub fn create(
context: *StackTracesContext,
exe: *LibExeObjStep,
name: []const u8,
mode: Mode,
expect_output: []const u8,
) *RunAndCompareStep {
const allocator = context.b.allocator;
const ptr = allocator.create(RunAndCompareStep) catch unreachable;
ptr.* = RunAndCompareStep{
.step = build.Step.init("StackTraceCompareOutputStep", allocator, make),
.context = context,
.exe = exe,
.name = name,
.mode = mode,
.expect_output = expect_output,
.test_index = context.test_index,
};
ptr.step.dependOn(&exe.step);
context.test_index += 1;
return ptr;
}
fn make(step: *build.Step) !void {
const self = @fieldParentPtr(RunAndCompareStep, "step", step);
const b = self.context.b;
const full_exe_path = self.exe.getOutputPath();
var args = ArrayList([]const u8).init(b.allocator);
defer args.deinit();
args.append(full_exe_path) catch unreachable;
warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name);
const child = std.ChildProcess.init(args.toSliceConst(), b.allocator) catch unreachable;
defer child.deinit();
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
child.env_map = b.env_map;
child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
var stdout = Buffer.initNull(b.allocator);
var stderr = Buffer.initNull(b.allocator);
var stdout_file_in_stream = child.stdout.?.inStream();
var stderr_file_in_stream = child.stderr.?.inStream();
stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;
stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;
const term = child.wait() catch |err| {
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
};
switch (term) {
.Exited => |code| {
const expect_code: u32 = 1;
if (code != expect_code) {
warn("Process {} exited with error code {} but expected code {}\n", full_exe_path, code, expect_code);
printInvocation(args.toSliceConst());
return error.TestFailed;
}
},
.Signal => |signum| {
warn("Process {} terminated on signal {}\n", full_exe_path, signum);
printInvocation(args.toSliceConst());
return error.TestFailed;
},
.Stopped => |signum| {
warn("Process {} stopped on signal {}\n", full_exe_path, signum);
printInvocation(args.toSliceConst());
return error.TestFailed;
},
.Unknown => |code| {
warn("Process {} terminated unexpectedly with error code {}\n", full_exe_path, code);
printInvocation(args.toSliceConst());
return error.TestFailed;
},
}
// process result
// - keep only basename of source file path
// - replace address with symbolic string
// - skip empty lines
const got: []const u8 = got_result: {
var buf = try Buffer.initSize(b.allocator, 0);
defer buf.deinit();
var bytes = stderr.toSliceConst();
if (bytes.len != 0 and bytes[bytes.len - 1] == '\n') bytes = bytes[0 .. bytes.len - 1];
var it = mem.separate(bytes, "\n");
process_lines: while (it.next()) |line| {
if (line.len == 0) continue;
const delims = [_][]const u8{ ":", ":", ":", " in " };
var marks = [_]usize{0} ** 4;
// offset search past `[drive]:` on windows
var pos: usize = if (builtin.os == .windows) 2 else 0;
for (delims) |delim, i| {
marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse {
try buf.append(line);
try buf.append("\n");
continue :process_lines;
};
pos = marks[i] + delim.len;
}
pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse {
try buf.append(line);
try buf.append("\n");
continue :process_lines;
};
try buf.append(line[pos + 1 .. marks[2] + delims[2].len]);
try buf.append(" [address]");
try buf.append(line[marks[3]..]);
try buf.append("\n");
}
break :got_result buf.toOwnedSlice();
};
if (!mem.eql(u8, self.expect_output, got)) {
warn(
\\
\\========= Expected this output: =========
\\{}
\\================================================
\\{}
\\
, self.expect_output, got);
return error.TestFailed;
}
warn("OK\n");
}
};
};
pub const CompileErrorContext = struct {
b: *build.Builder,
step: *build.Step,