From edcf8e0636c5a2674ce2b3e568929232c8cc61eb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 13 Mar 2020 17:55:40 +0100 Subject: [PATCH 01/12] std: Multithreaded-aware panic handler Gracefully handle the case of several threads panicking at the same time. --- lib/std/debug.zig | 52 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 0a7a0dee7e..cdbba2367b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -235,9 +235,17 @@ pub fn panic(comptime format: []const u8, args: var) noreturn { panicExtra(null, first_trace_addr, format, args); } -/// TODO multithreaded awareness +/// Non-zero whenever the program triggered a panic. +/// The counter is incremented/decremented atomically. var panicking: u8 = 0; +// Locked to avoid interleaving panic messages from multiple threads. +var panic_mutex = std.Mutex.init(); + +/// Counts how many times the panic handler is invoked by this thread. +/// This is used to catch and handle panics triggered by the panic handler. +threadlocal var panic_stage: usize = 0; + pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { @setCold(true); @@ -247,25 +255,47 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c resetSegfaultHandler(); } - switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { + switch (panic_stage) { 0 => { - const stderr = getStderrStream(); - noasync stderr.print(format ++ "\n", args) catch os.abort(); - if (trace) |t| { - dumpStackTrace(t.*); + panic_stage = 1; + + _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst); + + // Make sure to release the mutex when done + { + const held = panic_mutex.acquire(); + defer held.release(); + + const stderr = getStderrStream(); + noasync stderr.print(format ++ "\n", args) catch os.abort(); + if (trace) |t| { + dumpStackTrace(t.*); + } + dumpCurrentStackTrace(first_trace_addr); + } + + if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) { + // Another thread is panicking, wait for the last one to finish + // and call abort() + + // XXX: Find a nicer way to loop forever + while (true) {} } - dumpCurrentStackTrace(first_trace_addr); }, 1 => { - // TODO detect if a different thread caused the panic, because in that case - // we would want to return here instead of calling abort, so that the thread - // which first called panic can finish printing a stack trace. - warn("Panicked during a panic. Aborting.\n", .{}); + panic_stage = 2; + + // A panic happened while trying to print a previous panic message, + // we're still holding the mutex but that's fine as we're going to + // call abort() + const stderr = getStderrStream(); + noasync stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort(); }, else => { // Panicked while printing "Panicked during a panic." }, } + os.abort(); } From e496ef26dabb3a2da4821c7c0c2e4ffc6f7d86ce Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 13 Mar 2020 18:40:18 +0100 Subject: [PATCH 02/12] Nicer idle wait loop Trying to acquire twice the same mutex generates an idle loop. --- lib/std/debug.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index cdbba2367b..f2c736bbba 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -278,8 +278,11 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c // Another thread is panicking, wait for the last one to finish // and call abort() - // XXX: Find a nicer way to loop forever - while (true) {} + // Here we sleep forever without hammering the CPU by causing a + // deadlock + var deadlock = std.Mutex.init(); + _ = deadlock.acquire(); + _ = deadlock.acquire(); } }, 1 => { From 2501e80500f99c1ede6daf2f7c50c2dec3c9675c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 13 Mar 2020 19:20:18 +0100 Subject: [PATCH 03/12] Even better idle waiting method --- lib/std/debug.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index f2c736bbba..5600990924 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -278,11 +278,11 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c // Another thread is panicking, wait for the last one to finish // and call abort() - // Here we sleep forever without hammering the CPU by causing a - // deadlock - var deadlock = std.Mutex.init(); - _ = deadlock.acquire(); - _ = deadlock.acquire(); + // Sleep forever without hammering the CPU + var event = std.ResetEvent.init(); + event.wait(); + + unreachable; } }, 1 => { From 541e763010403d8233a6420756a1e8393f1dd4c2 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 20 Mar 2020 12:59:55 +0100 Subject: [PATCH 04/12] ir: Peer type resolution between ?[]T and *[N]T Closes #4767 --- lib/std/net.zig | 4 ++-- src/ir.cpp | 17 ++++++++++++++--- test/stage1/behavior/slice.zig | 8 ++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index b9c1281191..0a789b0dde 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -820,7 +820,7 @@ fn linuxLookupNameFromHosts( // Skip to the delimiter in the stream, to fix parsing try stream.skipUntilDelimiterOrEof('\n'); // Use the truncated line. A truncated comment or hostname will be handled correctly. - break :blk @as([]u8, &line_buf); // TODO the cast should not be necessary + break :blk &line_buf; }, else => |e| return e, }) |line| { @@ -1017,7 +1017,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { // Skip to the delimiter in the stream, to fix parsing try stream.skipUntilDelimiterOrEof('\n'); // Give an empty line to the while loop, which will be skipped. - break :blk @as([]u8, line_buf[0..0]); // TODO the cast should not be necessary + break :blk line_buf[0..0]; }, else => |e| return e, }) |line| { diff --git a/src/ir.cpp b/src/ir.cpp index b3b3198ce1..b3e24fed1f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12350,11 +12350,22 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT prev_type->data.pointer.child_type->id == ZigTypeIdArray && prev_type->data.pointer.ptr_len == PtrLenSingle && ((cur_type->id == ZigTypeIdErrorUnion && is_slice(cur_type->data.error_union.payload_type)) || - is_slice(cur_type))) + (cur_type->id == ZigTypeIdOptional && is_slice(cur_type->data.maybe.child_type)) || + is_slice(cur_type))) { ZigType *array_type = prev_type->data.pointer.child_type; - ZigType *slice_type = (cur_type->id == ZigTypeIdErrorUnion) ? - cur_type->data.error_union.payload_type : cur_type; + ZigType *slice_type; + switch (cur_type->id) { + case ZigTypeIdErrorUnion: + slice_type = cur_type->data.error_union.payload_type; + break; + case ZigTypeIdOptional: + slice_type = cur_type->data.maybe.child_type; + break; + default: + slice_type = cur_type; + break; + } ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || !prev_type->data.pointer.is_const) && diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index d58132cb08..f7d6037a1f 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -159,6 +159,7 @@ test "slice syntax resulting in pointer-to-array" { testSlice(); testSliceZ(); testSlice0(); + testSliceOpt(); testSliceAlign(); } @@ -249,6 +250,13 @@ test "slice syntax resulting in pointer-to-array" { comptime expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8); } + fn testSliceOpt() void { + var array: [2]u8 = [2]u8{ 1, 2 }; + var slice: ?[]u8 = &array; + comptime expect(@TypeOf(&array, slice) == ?[]u8); + comptime expect(@TypeOf(slice.?[0..2]) == *[2]u8); + } + fn testSlice0() void { { var array = [0]u8{}; From 245dc9d930a4f11a7ce719d3b9aa272a6a1127ff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Mar 2020 12:59:37 -0400 Subject: [PATCH 05/12] include ld symbols when generating glibc dummy objects closes #4748 --- lib/libc/glibc/abi.txt | 135 +++++++++++++++++++++++++++++++++++++++++ lib/libc/glibc/fns.txt | 9 +++ tools/update_glibc.zig | 29 +++++---- 3 files changed, 160 insertions(+), 13 deletions(-) diff --git a/lib/libc/glibc/abi.txt b/lib/libc/glibc/abi.txt index 089b9c077e..4d8d6f5255 100644 --- a/lib/libc/glibc/abi.txt +++ b/lib/libc/glibc/abi.txt @@ -193,6 +193,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu 29 + 29 29 @@ -514,6 +515,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu 29 29 29 +29 29 @@ -697,6 +699,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu 29 29 29 + 29 29 29 @@ -819,6 +822,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu 29 29 29 +29 29 29 @@ -904,6 +908,9 @@ aarch64-linux-gnu aarch64_be-linux-gnu 29 29 29 +29 + + 29 29 29 @@ -1004,6 +1011,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu 29 29 29 +29 29 29 @@ -1033,6 +1041,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu 29 29 29 +29 29 29 @@ -3920,6 +3929,7 @@ s390x-linux-gnu 5 + 27 27 @@ -4241,6 +4251,7 @@ s390x-linux-gnu 5 5 5 +5 11 27 @@ -4424,6 +4435,7 @@ s390x-linux-gnu 19 19 5 + 5 5 28 @@ -4543,6 +4555,7 @@ s390x-linux-gnu 27 16 + 5 5 15 @@ -4631,6 +4644,9 @@ s390x-linux-gnu 16 5 5 + + +12 5 5 5 @@ -4731,6 +4747,7 @@ s390x-linux-gnu 5 5 5 +5 5 5 @@ -4756,6 +4773,7 @@ s390x-linux-gnu 5 5 5 +5 31 5 24 5 12 16 24 5 12 16 @@ -7645,6 +7663,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf + 27 @@ -7968,6 +7987,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf 16 16 16 +16 27 @@ -8151,6 +8171,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf 19 19 16 + 16 16 28 @@ -8273,6 +8294,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf 16 16 16 +16 16 16 @@ -8358,6 +8380,9 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf 16 16 16 +16 + + 16 16 16 @@ -8458,6 +8483,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf 16 16 16 +16 16 16 @@ -8484,6 +8510,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf 16 16 16 +16 24 16 24 16 16 @@ -11374,6 +11401,7 @@ sparc-linux-gnu sparcel-linux-gnu 0 + 27 27 @@ -11693,6 +11721,7 @@ sparc-linux-gnu sparcel-linux-gnu 0 0 1 +1 0 0 3 11 @@ -11878,6 +11907,7 @@ sparc-linux-gnu sparcel-linux-gnu 19 19 0 + 0 1 28 @@ -11997,6 +12027,7 @@ sparc-linux-gnu sparcel-linux-gnu 33 16 + 5 0 15 @@ -12085,6 +12116,9 @@ sparc-linux-gnu sparcel-linux-gnu 16 0 0 +12 + + 1 1 1 @@ -12183,6 +12217,7 @@ sparc-linux-gnu sparcel-linux-gnu 1 1 1 +1 0 0 @@ -12207,6 +12242,7 @@ sparc-linux-gnu sparcel-linux-gnu 0 0 0 +0 5 0 0 @@ -15101,6 +15137,7 @@ sparcv9-linux-gnu 5 5 + 27 27 @@ -15422,6 +15459,7 @@ sparcv9-linux-gnu 5 5 5 +5 11 27 @@ -15605,6 +15643,7 @@ sparcv9-linux-gnu 19 19 5 + 5 5 28 @@ -15724,6 +15763,7 @@ sparcv9-linux-gnu 27 16 + 5 5 15 @@ -15812,6 +15852,9 @@ sparcv9-linux-gnu 16 5 5 +12 + + 5 5 5 @@ -15912,6 +15955,7 @@ sparcv9-linux-gnu 5 5 5 +5 5 5 @@ -15938,6 +15982,7 @@ sparcv9-linux-gnu 5 5 5 +5 24 28 5 12 16 24 28 5 12 16 5 14 @@ -18828,6 +18873,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64 0 + 27 27 @@ -19147,6 +19193,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64 0 0 5 +5 0 0 11 @@ -19332,6 +19379,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64 19 19 0 + 0 5 28 @@ -19450,6 +19498,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64 27 27 +16 16 5 0 @@ -19539,6 +19588,9 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64 16 0 0 +12 + + 5 5 5 @@ -19637,6 +19689,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64 5 5 5 +5 0 0 0 @@ -19661,6 +19714,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64 0 0 0 +0 5 0 0 @@ -22555,6 +22609,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32 0 + 27 27 @@ -22874,6 +22929,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32 0 0 5 +5 0 0 11 @@ -23059,6 +23115,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32 19 19 0 + 0 5 28 @@ -23177,6 +23234,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32 27 27 +16 16 5 0 @@ -23266,6 +23324,9 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32 16 0 0 +12 + + 5 5 5 @@ -23364,6 +23425,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32 5 5 5 +5 0 0 0 @@ -23388,6 +23450,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32 0 0 0 +0 5 0 0 @@ -26282,6 +26345,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf 0 + 27 27 @@ -26601,6 +26665,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf 0 0 5 +5 0 0 11 @@ -26786,6 +26851,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf 19 19 0 + 0 5 28 @@ -26904,6 +26970,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf 27 +16 16 5 0 @@ -26993,6 +27060,9 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf 16 0 0 +12 + + 5 5 5 @@ -27091,6 +27161,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf 5 5 5 +5 0 0 0 @@ -27115,6 +27186,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf 0 0 0 +0 5 0 0 @@ -30009,6 +30081,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi 0 + 27 27 @@ -30328,6 +30401,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi 0 0 5 +5 0 0 11 @@ -30513,6 +30587,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi 19 19 0 + 0 5 28 @@ -30631,6 +30706,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi 27 +16 16 5 0 @@ -30720,6 +30796,9 @@ mipsel-linux-gnueabi mips-linux-gnueabi 16 0 0 +12 + + 5 5 5 @@ -30818,6 +30897,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi 5 5 5 +5 0 0 0 @@ -30842,6 +30922,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi 0 0 0 +0 5 0 0 @@ -33734,6 +33815,7 @@ x86_64-linux-gnu + 27 @@ -34057,6 +34139,7 @@ x86_64-linux-gnu 10 10 10 +10 11 27 36 @@ -34240,6 +34323,7 @@ x86_64-linux-gnu 19 19 10 + 10 10 28 @@ -34359,6 +34443,7 @@ x86_64-linux-gnu 27 16 + 10 10 15 @@ -34447,6 +34532,9 @@ x86_64-linux-gnu 16 10 10 +12 + + 10 10 10 @@ -34547,6 +34635,7 @@ x86_64-linux-gnu 10 10 10 +10 10 10 @@ -34573,6 +34662,7 @@ x86_64-linux-gnu 10 10 10 +10 24 10 12 16 24 10 12 16 10 14 @@ -37461,6 +37551,7 @@ x86_64-linux-gnux32 + 28 @@ -37784,6 +37875,7 @@ x86_64-linux-gnux32 28 28 28 +28 28 36 @@ -37967,6 +38059,7 @@ x86_64-linux-gnux32 28 28 28 + 28 28 28 @@ -38086,6 +38179,7 @@ x86_64-linux-gnux32 28 28 + 28 28 28 @@ -38174,6 +38268,9 @@ x86_64-linux-gnux32 28 28 28 +28 + + 28 28 28 @@ -38274,6 +38371,7 @@ x86_64-linux-gnux32 28 28 28 +28 28 28 @@ -38303,6 +38401,7 @@ x86_64-linux-gnux32 28 28 28 +28 28 28 @@ -41190,6 +41289,7 @@ i386-linux-gnu 0 +12 27 36 27 @@ -41509,6 +41609,7 @@ i386-linux-gnu 0 0 1 +1 0 0 3 11 @@ -41694,6 +41795,7 @@ i386-linux-gnu 19 19 0 + 0 1 28 @@ -41813,6 +41915,7 @@ i386-linux-gnu 27 16 + 5 0 15 @@ -41901,6 +42004,9 @@ i386-linux-gnu 16 0 0 +12 + + 1 1 1 @@ -41999,6 +42105,7 @@ i386-linux-gnu 1 1 1 +1 0 0 @@ -42023,6 +42130,7 @@ i386-linux-gnu 0 0 0 +0 5 0 0 @@ -44915,6 +45023,7 @@ powerpc64le-linux-gnu + 29 @@ -45238,6 +45347,7 @@ powerpc64le-linux-gnu 29 29 29 +29 29 36 @@ -45421,6 +45531,7 @@ powerpc64le-linux-gnu 29 29 29 +33 29 29 29 @@ -45540,6 +45651,7 @@ powerpc64le-linux-gnu 29 29 + 29 29 29 @@ -45628,6 +45740,9 @@ powerpc64le-linux-gnu 29 29 29 +29 +32 + 29 29 29 @@ -45728,6 +45843,7 @@ powerpc64le-linux-gnu 29 29 29 +29 29 29 @@ -45757,6 +45873,7 @@ powerpc64le-linux-gnu 29 29 29 +29 29 29 @@ -48642,6 +48759,7 @@ powerpc64-linux-gnu + 27 @@ -48965,6 +49083,7 @@ powerpc64-linux-gnu 12 12 12 +12 27 @@ -49148,6 +49267,7 @@ powerpc64-linux-gnu 19 19 12 +33 12 12 28 @@ -49267,6 +49387,7 @@ powerpc64-linux-gnu 27 16 + 12 12 15 @@ -49355,6 +49476,9 @@ powerpc64-linux-gnu 16 12 12 +12 +32 + 12 12 12 @@ -49455,6 +49579,7 @@ powerpc64-linux-gnu 12 12 12 +12 12 12 @@ -49480,6 +49605,7 @@ powerpc64-linux-gnu 12 12 12 +12 12 15 24 12 16 24 12 16 @@ -52369,6 +52495,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf + 27 @@ -52690,6 +52817,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf 0 0 1 +1 0 0 3 11 @@ -52875,6 +53003,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf 19 19 0 +33 0 1 28 @@ -52994,6 +53123,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf 27 13 16 + 5 0 15 @@ -53082,6 +53212,9 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf 16 0 0 +12 +32 + 1 1 1 @@ -53180,6 +53313,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf 1 1 1 +1 0 0 @@ -53204,6 +53338,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf 0 0 0 +0 5 0 0 diff --git a/lib/libc/glibc/fns.txt b/lib/libc/glibc/fns.txt index 32dc37ce17..4e2e126b0e 100644 --- a/lib/libc/glibc/fns.txt +++ b/lib/libc/glibc/fns.txt @@ -192,6 +192,7 @@ _Qp_uitoq c _Qp_uxtoq c _Qp_xtoq c ___brk_addr c +___tls_get_addr ld __acos_finite m __acosf128_finite m __acosf_finite m @@ -511,6 +512,7 @@ __libc_memalign c __libc_pvalloc c __libc_realloc c __libc_sa_len c +__libc_stack_end ld __libc_start_main c __libc_valloc c __libpthread_version_placeholder pthread @@ -696,6 +698,7 @@ __open_2 c __openat64_2 c __openat_2 c __overflow c +__parse_hwcap_and_convert_at_platform ld __pipe c __poll c __poll_chk c @@ -815,6 +818,7 @@ __sqrtf_finite m __sqrtl_finite m __sqrtsf2 c __stack_chk_fail c +__stack_chk_guard ld __statfs c __stpcpy c __stpcpy_chk c @@ -903,6 +907,9 @@ __sysctl c __syslog_chk c __sysv_signal c __timezone c +__tls_get_addr ld +__tls_get_addr_opt ld +__tls_get_offset ld __toascii_l c __tolower_l c __toupper_l c @@ -999,6 +1006,7 @@ __ynf128_finite m __ynf_finite m __ynl_finite m _authenticate c +_dl_mcount ld _dl_mcount_wrapper c _dl_mcount_wrapper_check c _environ c @@ -1024,6 +1032,7 @@ _pthread_cleanup_pop pthread _pthread_cleanup_pop_restore pthread _pthread_cleanup_push pthread _pthread_cleanup_push_defer pthread +_r_debug ld _res c _res_hconf c _rpc_dtablesize c diff --git a/tools/update_glibc.zig b/tools/update_glibc.zig index 84522aabe4..4a5b1751a2 100644 --- a/tools/update_glibc.zig +++ b/tools/update_glibc.zig @@ -20,6 +20,7 @@ const lib_names = [_][]const u8{ "m", "pthread", "rt", + "ld", }; // fpu/nofpu are hardcoded elsewhere, based on .gnueabi/.gnueabihf with an exception for .arm @@ -154,22 +155,24 @@ pub fn main() !void { const fn_set = &target_funcs_gop.kv.value.list; for (lib_names) |lib_name, lib_name_index| { - const basename = try fmt.allocPrint(allocator, "lib{}.abilist", .{lib_name}); + const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib"; + const basename = try fmt.allocPrint(allocator, "{}{}.abilist", .{ lib_prefix, lib_name }); const abi_list_filename = blk: { - if (abi_list.targets[0].abi == .gnuabi64 and std.mem.eql(u8, lib_name, "c")) { + const is_c = std.mem.eql(u8, lib_name, "c"); + const is_m = std.mem.eql(u8, lib_name, "m"); + const is_ld = std.mem.eql(u8, lib_name, "ld"); + if (abi_list.targets[0].abi == .gnuabi64 and (is_c or is_ld)) { break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "n64", basename }); - } else if (abi_list.targets[0].abi == .gnuabin32 and std.mem.eql(u8, lib_name, "c")) { + } else if (abi_list.targets[0].abi == .gnuabin32 and (is_c or is_ld)) { break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "n32", basename }); } else if (abi_list.targets[0].arch != .arm and abi_list.targets[0].abi == .gnueabihf and - (std.mem.eql(u8, lib_name, "c") or - (std.mem.eql(u8, lib_name, "m") and abi_list.targets[0].arch == .powerpc))) + (is_c or (is_m and abi_list.targets[0].arch == .powerpc))) { break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "fpu", basename }); } else if (abi_list.targets[0].arch != .arm and abi_list.targets[0].abi == .gnueabi and - (std.mem.eql(u8, lib_name, "c") or - (std.mem.eql(u8, lib_name, "m") and abi_list.targets[0].arch == .powerpc))) + (is_c or (is_m and abi_list.targets[0].arch == .powerpc))) { break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "nofpu", basename }); } else if (abi_list.targets[0].arch == .arm) { @@ -234,8 +237,8 @@ pub fn main() !void { const vers_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "vers.txt" }); const vers_txt_file = try fs.cwd().createFile(vers_txt_path, .{}); defer vers_txt_file.close(); - var buffered = std.io.BufferedOutStream(fs.File.WriteError).init(&vers_txt_file.outStream().stream); - const vers_txt = &buffered.stream; + var buffered = std.io.bufferedOutStream(vers_txt_file.outStream()); + const vers_txt = buffered.outStream(); for (global_ver_list) |name, i| { _ = global_ver_set.put(name, i) catch unreachable; try vers_txt.print("{}\n", .{name}); @@ -246,8 +249,8 @@ pub fn main() !void { const fns_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "fns.txt" }); const fns_txt_file = try fs.cwd().createFile(fns_txt_path, .{}); defer fns_txt_file.close(); - var buffered = std.io.BufferedOutStream(fs.File.WriteError).init(&fns_txt_file.outStream().stream); - const fns_txt = &buffered.stream; + var buffered = std.io.bufferedOutStream(fns_txt_file.outStream()); + const fns_txt = buffered.outStream(); for (global_fn_list) |name, i| { const kv = global_fn_set.get(name).?; kv.value.index = i; @@ -277,8 +280,8 @@ pub fn main() !void { const abilist_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "abi.txt" }); const abilist_txt_file = try fs.cwd().createFile(abilist_txt_path, .{}); defer abilist_txt_file.close(); - var buffered = std.io.BufferedOutStream(fs.File.WriteError).init(&abilist_txt_file.outStream().stream); - const abilist_txt = &buffered.stream; + var buffered = std.io.bufferedOutStream(abilist_txt_file.outStream()); + const abilist_txt = buffered.outStream(); // first iterate over the abi lists for (abi_lists) |*abi_list, abi_index| { From 7438d0fc31f70b3a6d19e1da9a4e3f7918fc9d66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Mar 2020 14:39:05 -0400 Subject: [PATCH 06/12] glibc: include ld symbols and proper soname for ld --- src-self-hosted/stage2.zig | 8 ++++++++ src/all_types.hpp | 1 + src/codegen.cpp | 1 + src/glibc.cpp | 10 ++++++++++ src/link.cpp | 8 ++++---- src/stage2.h | 1 + 6 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index a1d741bc9b..78aa278005 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -909,6 +909,7 @@ const Stage2Target = extern struct { os_builtin_str: ?[*:0]const u8, dynamic_linker: ?[*:0]const u8, + standard_dynamic_linker_path: ?[*:0]const u8, fn fromTarget(self: *Stage2Target, cross_target: CrossTarget) !void { const allocator = std.heap.c_allocator; @@ -1119,6 +1120,12 @@ const Stage2Target = extern struct { } }; + const std_dl = target.standardDynamicLinkerPath(); + const std_dl_z = if (std_dl.get()) |dl| + (try mem.dupeZ(std.heap.c_allocator, u8, dl)).ptr + else + null; + const cache_hash_slice = cache_hash.toOwnedSlice(); self.* = .{ .arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch @@ -1134,6 +1141,7 @@ const Stage2Target = extern struct { .is_native = cross_target.isNative(), .glibc_or_darwin_version = glibc_or_darwin_version, .dynamic_linker = dynamic_linker, + .standard_dynamic_linker_path = std_dl_z, }; } }; diff --git a/src/all_types.hpp b/src/all_types.hpp index 6719d78a92..3f143b35c1 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2267,6 +2267,7 @@ struct CodeGen { Buf *zig_lib_dir; Buf *zig_std_dir; Buf *version_script_path; + Buf *override_soname; const char **llvm_argv; size_t llvm_argv_len; diff --git a/src/codegen.cpp b/src/codegen.cpp index 8fae16e551..c2ce2ac3eb 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -10510,6 +10510,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_str(ch, g->libc->kernel32_lib_dir); } cache_buf_opt(ch, g->version_script_path); + cache_buf_opt(ch, g->override_soname); // gen_c_objects appends objects to g->link_objects which we want to include in the hash gen_c_objects(g); diff --git a/src/glibc.cpp b/src/glibc.cpp index da5c1d5290..75b77ec122 100644 --- a/src/glibc.cpp +++ b/src/glibc.cpp @@ -16,6 +16,7 @@ static const ZigGLibCLib glibc_libs[] = { {"pthread", 0}, {"dl", 2}, {"rt", 1}, + {"ld", 2}, }; Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose) { @@ -330,6 +331,8 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con return err; } + bool is_ld = (strcmp(lib->name, "ld") == 0); + CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node); codegen_set_lib_version(child_gen, lib->sover, 0, 0); child_gen->is_dynamic = true; @@ -337,6 +340,13 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con child_gen->version_script_path = map_file_path; child_gen->enable_cache = false; child_gen->output_dir = dummy_dir; + if (is_ld) { + assert(g->zig_target->standard_dynamic_linker_path != nullptr); + Buf *ld_basename = buf_alloc(); + os_path_split(buf_create_from_str(g->zig_target->standard_dynamic_linker_path), + nullptr, ld_basename); + child_gen->override_soname = ld_basename; + } codegen_build_and_link(child_gen); } diff --git a/src/link.cpp b/src/link.cpp index 3f7772bb08..0ae47c8432 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -1651,7 +1651,6 @@ static void construct_linker_job_elf(LinkJob *lj) { bool is_lib = g->out_type == OutTypeLib; bool is_dyn_lib = g->is_dynamic && is_lib; - Buf *soname = nullptr; if (!g->have_dynamic_link) { if (g->zig_target->arch == ZigLLVM_arm || g->zig_target->arch == ZigLLVM_armeb || g->zig_target->arch == ZigLLVM_thumb || g->zig_target->arch == ZigLLVM_thumbeb) @@ -1662,15 +1661,13 @@ static void construct_linker_job_elf(LinkJob *lj) { } } else if (is_dyn_lib) { lj->args.append("-shared"); - - assert(buf_len(&g->bin_file_output_path) != 0); - soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize, buf_ptr(g->root_out_name), g->version_major); } if (target_requires_pie(g->zig_target) && g->out_type == OutTypeExe) { lj->args.append("-pie"); } + assert(buf_len(&g->bin_file_output_path) != 0); lj->args.append("-o"); lj->args.append(buf_ptr(&g->bin_file_output_path)); @@ -1740,6 +1737,9 @@ static void construct_linker_job_elf(LinkJob *lj) { } if (is_dyn_lib) { + Buf *soname = (g->override_soname == nullptr) ? + buf_sprintf("lib%s.so.%" ZIG_PRI_usize, buf_ptr(g->root_out_name), g->version_major) : + g->override_soname; lj->args.append("-soname"); lj->args.append(buf_ptr(soname)); diff --git a/src/stage2.h b/src/stage2.h index 24fd664b3c..9ba7e062b1 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -291,6 +291,7 @@ struct ZigTarget { size_t cache_hash_len; const char *os_builtin_str; const char *dynamic_linker; + const char *standard_dynamic_linker_path; }; // ABI warning From 3a2c4908891cdc9f64f0e94eb570ce084f8bc57c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Mar 2020 18:33:36 -0400 Subject: [PATCH 07/12] "generate .h files" feature is no longer supported in stage1 --- CMakeLists.txt | 1 - build.zig | 3 ++- lib/std/build.zig | 18 ++++++++---------- lib/std/start.zig | 4 ++++ src/main.cpp | 16 ++++++++++------ test/standalone/mix_o_files/test.c | 8 +++++--- test/standalone/shared_library/test.c | 8 +++++++- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3d6a7fb68..6f2a88d34b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -622,7 +622,6 @@ set(BUILD_LIBSTAGE2_ARGS "build-lib" --cache on --output-dir "${CMAKE_BINARY_DIR}" ${LIBSTAGE2_RELEASE_ARG} - --disable-gen-h --bundle-compiler-rt -fPIC -lc diff --git a/build.zig b/build.zig index 6a41e6ef64..f8b41f2dc4 100644 --- a/build.zig +++ b/build.zig @@ -134,7 +134,8 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter)); - test_step.dependOn(tests.addGenHTests(b, test_filter)); + // tests for this feature are disabled until we have the self-hosted compiler available + //test_step.dependOn(tests.addGenHTests(b, test_filter)); test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); test_step.dependOn(docs_step); } diff --git a/lib/std/build.zig b/lib/std/build.zig index 09c77168d6..ac411d6cf6 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1121,7 +1121,7 @@ pub const LibExeObjStep = struct { emit_llvm_ir: bool = false, emit_asm: bool = false, emit_bin: bool = true, - disable_gen_h: bool, + emit_h: bool = false, bundle_compiler_rt: bool, disable_stack_probing: bool, disable_sanitize_c: bool, @@ -1281,7 +1281,6 @@ pub const LibExeObjStep = struct { .exec_cmd_args = null, .name_prefix = "", .filter = null, - .disable_gen_h = false, .bundle_compiler_rt = false, .disable_stack_probing = false, .disable_sanitize_c = false, @@ -1600,8 +1599,9 @@ pub const LibExeObjStep = struct { self.main_pkg_path = dir_path; } - pub fn setDisableGenH(self: *LibExeObjStep, value: bool) void { - self.disable_gen_h = value; + /// Deprecated; just set the field directly. + pub fn setDisableGenH(self: *LibExeObjStep, is_disabled: bool) void { + self.emit_h = !is_disabled; } pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?[]const u8) void { @@ -1632,7 +1632,7 @@ pub const LibExeObjStep = struct { /// the make step, from a step that has declared a dependency on this one. pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { assert(self.kind != Kind.Exe); - assert(!self.disable_gen_h); + assert(self.emit_h); return fs.path.join( self.builder.allocator, &[_][]const u8{ self.output_dir.?, self.out_h_filename }, @@ -1884,6 +1884,7 @@ pub const LibExeObjStep = struct { if (self.emit_llvm_ir) try zig_args.append("-femit-llvm-ir"); if (self.emit_asm) try zig_args.append("-femit-asm"); if (!self.emit_bin) try zig_args.append("-fno-emit-bin"); + if (self.emit_h) try zig_args.append("-femit-h"); if (self.strip) { try zig_args.append("--strip"); @@ -1929,9 +1930,6 @@ pub const LibExeObjStep = struct { if (self.is_dynamic) { try zig_args.append("-dynamic"); } - if (self.disable_gen_h) { - try zig_args.append("--disable-gen-h"); - } if (self.bundle_compiler_rt) { try zig_args.append("--bundle-compiler-rt"); } @@ -2069,7 +2067,7 @@ pub const LibExeObjStep = struct { try zig_args.append("-isystem"); try zig_args.append(self.builder.pathFromRoot(include_path)); }, - .OtherStep => |other| if (!other.disable_gen_h) { + .OtherStep => |other| if (other.emit_h) { const h_path = other.getOutputHPath(); try zig_args.append("-isystem"); try zig_args.append(fs.path.dirname(h_path).?); @@ -2209,7 +2207,7 @@ const InstallArtifactStep = struct { break :blk InstallDir.Lib; } } else null, - .h_dir = if (artifact.kind == .Lib and !artifact.disable_gen_h) .Header else null, + .h_dir = if (artifact.kind == .Lib and artifact.emit_h) .Header else null, }; self.step.dependOn(&artifact.step); artifact.install_step = self; diff --git a/lib/std/start.zig b/lib/std/start.zig index 857af03b56..1a4997edbd 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -41,6 +41,10 @@ fn _DllMainCRTStartup( fdwReason: std.os.windows.DWORD, lpReserved: std.os.windows.LPVOID, ) callconv(.Stdcall) std.os.windows.BOOL { + if (!builtin.single_threaded) { + _ = @import("start_windows_tls.zig"); + } + if (@hasDecl(root, "DllMain")) { return root.DllMain(hinstDLL, fdwReason, lpReserved); } diff --git a/src/main.cpp b/src/main.cpp index 06d132da8b..76d6eadb14 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,7 +54,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --cache-dir [path] override the local cache directory\n" " --cache [auto|off|on] build in cache, print output path to stdout\n" " --color [auto|off|on] enable or disable colored error messages\n" - " --disable-gen-h do not generate a C header file (.h)\n" " --disable-valgrind omit valgrind client requests in debug builds\n" " --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker\n" " --enable-valgrind include valgrind client requests release builds\n" @@ -77,6 +76,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " -fno-emit-asm (default) do not output .s (assembly code)\n" " -femit-llvm-ir produce a .ll file with LLVM IR\n" " -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n" + " -femit-h generate a C header file (.h)\n" + " -fno-emit-h (default) do not generate a C header file (.h)\n" " --libc [file] Provide a file which specifies libc paths\n" " --name [name] override output name\n" " --output-dir [dir] override output directory (defaults to cwd)\n" @@ -431,6 +432,7 @@ static int main0(int argc, char **argv) { bool emit_bin = true; bool emit_asm = false; bool emit_llvm_ir = false; + bool emit_h = false; const char *cache_dir = nullptr; CliPkg *cur_pkg = heap::c_allocator.create(); BuildMode build_mode = BuildModeDebug; @@ -439,7 +441,6 @@ static int main0(int argc, char **argv) { bool system_linker_hack = false; TargetSubsystem subsystem = TargetSubsystemAuto; bool want_single_threaded = false; - bool disable_gen_h = false; bool bundle_compiler_rt = false; Buf *override_lib_dir = nullptr; Buf *main_pkg_path = nullptr; @@ -660,9 +661,7 @@ static int main0(int argc, char **argv) { } else if (strcmp(arg, "--system-linker-hack") == 0) { system_linker_hack = true; } else if (strcmp(arg, "--single-threaded") == 0) { - want_single_threaded = true; - } else if (strcmp(arg, "--disable-gen-h") == 0) { - disable_gen_h = true; + want_single_threaded = true;; } else if (strcmp(arg, "--bundle-compiler-rt") == 0) { bundle_compiler_rt = true; } else if (strcmp(arg, "--test-cmd-bin") == 0) { @@ -719,6 +718,11 @@ static int main0(int argc, char **argv) { emit_llvm_ir = true; } else if (strcmp(arg, "-fno-emit-llvm-ir") == 0) { emit_llvm_ir = false; + } else if (strcmp(arg, "-femit-h") == 0) { + emit_h = true; + } else if (strcmp(arg, "-fno-emit-h") == 0 || strcmp(arg, "--disable-gen-h") == 0) { + // the --disable-gen-h is there to support godbolt. once they upgrade to -fno-emit-h then we can remove this + emit_h = false; } else if (str_starts_with(arg, "-mcpu=")) { mcpu = arg + strlen("-mcpu="); } else if (i + 1 >= argc) { @@ -1202,7 +1206,7 @@ static int main0(int argc, char **argv) { g->verbose_cc = verbose_cc; g->verbose_llvm_cpu_features = verbose_llvm_cpu_features; g->output_dir = output_dir; - g->disable_gen_h = disable_gen_h; + g->disable_gen_h = !emit_h; g->bundle_compiler_rt = bundle_compiler_rt; codegen_set_errmsg_color(g, color); g->system_linker_hack = system_linker_hack; diff --git a/test/standalone/mix_o_files/test.c b/test/standalone/mix_o_files/test.c index 7f9f751a4a..e5e77a8f3e 100644 --- a/test/standalone/mix_o_files/test.c +++ b/test/standalone/mix_o_files/test.c @@ -1,10 +1,12 @@ -// This header is generated by zig from base64.zig -#include "base64.h" - #include #include #include +// TODO we would like to #include "base64.h" here but this feature has been disabled in +// the stage1 compiler. Users will have to wait until self-hosted is available for +// the "generate .h file" feature. +size_t decode_base_64(uint8_t *dest_ptr, size_t dest_len, const uint8_t *source_ptr, size_t source_len); + extern int *x_ptr; int main(int argc, char **argv) { diff --git a/test/standalone/shared_library/test.c b/test/standalone/shared_library/test.c index b60a6a5a75..f178f78b45 100644 --- a/test/standalone/shared_library/test.c +++ b/test/standalone/shared_library/test.c @@ -1,6 +1,12 @@ -#include "mathtest.h" #include +// TODO we would like to #include "mathtest.h" here but this feature has been disabled in +// the stage1 compiler. Users will have to wait until self-hosted is available for +// the "generate .h file" feature. + +#include +int32_t add(int32_t a, int32_t b); + int main(int argc, char **argv) { assert(add(42, 1337) == 1379); return 0; From 153c6cf92e3459038c4ab8251a463163ac89b116 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Mar 2020 21:33:37 -0400 Subject: [PATCH 08/12] ci: disable test-gen-h on sr.ht and drone --- ci/drone/linux_script | 3 ++- ci/srht/freebsd_script | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ci/drone/linux_script b/ci/drone/linux_script index 61a5908dee..a0d2406f89 100755 --- a/ci/drone/linux_script +++ b/ci/drone/linux_script @@ -26,7 +26,8 @@ make -j$(nproc) install # TODO test-cli is hitting https://github.com/ziglang/zig/issues/3526 ./zig build test-asm-link test-runtime-safety # TODO test-translate-c is hitting https://github.com/ziglang/zig/issues/3526 -./zig build test-gen-h +# TODO disabled until we are shipping self-hosted +#./zig build test-gen-h # TODO test-compile-errors is hitting https://github.com/ziglang/zig/issues/3526 # TODO building docs is hitting https://github.com/ziglang/zig/issues/3526 diff --git a/ci/srht/freebsd_script b/ci/srht/freebsd_script index 12cd2d5406..6cf4cb7582 100755 --- a/ci/srht/freebsd_script +++ b/ci/srht/freebsd_script @@ -42,7 +42,8 @@ release/bin/zig build test-asm-link release/bin/zig build test-runtime-safety release/bin/zig build test-translate-c release/bin/zig build test-run-translated-c -release/bin/zig build test-gen-h +# TODO disabled until we are shipping self-hosted +#release/bin/zig build test-gen-h release/bin/zig build test-compile-errors release/bin/zig build docs From 128e70ff3a056e5b800c624f7157b00fd624509b Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 19 Mar 2020 21:03:38 +0100 Subject: [PATCH 09/12] ir: Allow errdefer with payload Closes #1265 --- lib/std/zig/ast.zig | 7 +-- lib/std/zig/parse.zig | 7 ++- lib/std/zig/parser_test.zig | 13 ++++ lib/std/zig/render.zig | 3 + src/all_types.hpp | 1 + src/ir.cpp | 105 ++++++++++++++++++++++++--------- src/parser.cpp | 11 +++- test/stage1/behavior/defer.zig | 23 +++++++- 8 files changed, 135 insertions(+), 35 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 8caaad2d4f..5445d3435f 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -1032,6 +1032,7 @@ pub const Node = struct { pub const Defer = struct { base: Node = Node{ .id = .Defer }, defer_token: TokenIndex, + payload: ?*Node, expr: *Node, pub fn iterate(self: *Defer, index: usize) ?*Node { @@ -1833,8 +1834,7 @@ pub const Node = struct { var i = index; switch (self.kind) { - .Break, - .Continue => |maybe_label| { + .Break, .Continue => |maybe_label| { if (maybe_label) |label| { if (i < 1) return label; i -= 1; @@ -1861,8 +1861,7 @@ pub const Node = struct { } switch (self.kind) { - .Break, - .Continue => |maybe_label| { + .Break, .Continue => |maybe_label| { if (maybe_label) |label| { return label.lastToken(); } diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index b0c3e6d759..2fcaaaab2d 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -465,7 +465,7 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No /// / KEYWORD_noasync BlockExprStatement /// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) /// / KEYWORD_defer BlockExprStatement -/// / KEYWORD_errdefer BlockExprStatement +/// / KEYWORD_errdefer Payload? BlockExprStatement /// / IfStatement /// / LabeledStatement /// / SwitchExpr @@ -526,6 +526,10 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No const defer_token = eatToken(it, .Keyword_defer) orelse eatToken(it, .Keyword_errdefer); if (defer_token) |token| { + const payload = if (tree.tokens.at(token).id == .Keyword_errdefer) + try parsePayload(arena, it, tree) + else + null; const expr_node = try expectNode(arena, it, tree, parseBlockExprStatement, .{ .ExpectedBlockOrExpression = .{ .token = it.index }, }); @@ -533,6 +537,7 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No node.* = .{ .defer_token = token, .expr = expr_node, + .payload = payload, }; return &node.base; } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index e1fe07a57c..b71a78221f 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1,3 +1,16 @@ +test "zig fmt: noasync block" { + try testCanonical( + \\pub fn main() anyerror!void { + \\ errdefer |a| x += 1; + \\ errdefer |a| {} + \\ errdefer |a| { + \\ x += 1; + \\ } + \\} + \\ + ); +} + test "zig fmt: noasync block" { try testCanonical( \\pub fn main() anyerror!void { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 23dc9e02ac..37d058bebf 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -376,6 +376,9 @@ fn renderExpression( const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); try renderToken(tree, stream, defer_node.defer_token, indent, start_col, Space.Space); + if (defer_node.payload) |payload| { + try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); + } return renderExpression(allocator, stream, tree, indent, start_col, defer_node.expr, space); }, .Comptime => { diff --git a/src/all_types.hpp b/src/all_types.hpp index 3f143b35c1..b1450f08da 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -744,6 +744,7 @@ struct AstNodeReturnExpr { struct AstNodeDefer { ReturnKind kind; + AstNode *err_payload; AstNode *expr; // temporary data used in IR generation diff --git a/src/ir.cpp b/src/ir.cpp index b3e24fed1f..531c1b2432 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -272,6 +272,10 @@ static ResultLoc *no_result_loc(void); static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value); static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst *source_instr); static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty); +static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name, + bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime); +static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, + IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime); static void destroy_instruction_src(IrInstSrc *inst) { switch (inst->id) { @@ -5011,39 +5015,73 @@ static IrInstSrc *ir_mark_gen(IrInstSrc *instruction) { return instruction; } -static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) { +static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool *is_noreturn, IrInstSrc *err_value) { Scope *scope = inner_scope; - bool is_noreturn = false; + if (is_noreturn != nullptr) *is_noreturn = false; while (scope != outer_scope) { if (!scope) - return is_noreturn; + return true; switch (scope->id) { case ScopeIdDefer: { AstNode *defer_node = scope->source_node; assert(defer_node->type == NodeTypeDefer); ReturnKind defer_kind = defer_node->data.defer.kind; - if (defer_kind == ReturnKindUnconditional || - (gen_error_defers && defer_kind == ReturnKindError)) - { - AstNode *defer_expr_node = defer_node->data.defer.expr; - Scope *defer_expr_scope = defer_node->data.defer.expr_scope; - IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); - if (defer_expr_value != irb->codegen->invalid_inst_src) { - if (defer_expr_value->is_noreturn) { - is_noreturn = true; - } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, - defer_expr_value)); - } + AstNode *defer_expr_node = defer_node->data.defer.expr; + AstNode *defer_var_node = defer_node->data.defer.err_payload; + + if (defer_kind == ReturnKindError && err_value == nullptr) { + // This is an `errdefer` but we're generating code for a + // `return` that doesn't return an error, skip it + scope = scope->parent; + continue; + } + + Scope *defer_expr_scope = defer_node->data.defer.expr_scope; + if (defer_var_node != nullptr) { + assert(defer_kind == ReturnKindError); + assert(defer_var_node->type == NodeTypeSymbol); + Buf *var_name = defer_var_node->data.symbol_expr.symbol; + + if (defer_expr_node->type == NodeTypeUnreachable) { + add_node_error(irb->codegen, defer_var_node, + buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); + return false; } + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, defer_expr_scope)) { + is_comptime = ir_build_const_bool(irb, defer_expr_scope, + defer_expr_node, true); + } else { + is_comptime = ir_build_test_comptime(irb, defer_expr_scope, + defer_expr_node, err_value); + } + + ZigVar *err_var = ir_create_var(irb, defer_var_node, defer_expr_scope, + var_name, true, true, false, is_comptime); + build_decl_var_and_init(irb, defer_expr_scope, defer_var_node, err_var, err_value, + buf_ptr(var_name), is_comptime); + + defer_expr_scope = err_var->child_scope; + } + + IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); + if (defer_expr_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + if (defer_expr_value->is_noreturn) { + if (is_noreturn != nullptr) *is_noreturn = true; + } else { + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, + defer_expr_value)); } scope = scope->parent; continue; } case ScopeIdDecls: case ScopeIdFnDef: - return is_noreturn; + return true; case ScopeIdBlock: case ScopeIdVarDecl: case ScopeIdLoop: @@ -5060,7 +5098,7 @@ static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope zig_unreachable(); } } - return is_noreturn; + return true; } static void ir_set_cursor_at_end_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { @@ -5146,7 +5184,8 @@ static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, bool have_err_defers = defer_counts[ReturnKindError] > 0; if (!have_err_defers && !irb->codegen->have_err_ret_tracing) { // only generate unconditional defers - ir_gen_defers_for_block(irb, scope, outer_scope, false); + if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr); result_loc_ret->base.source_instruction = result; return result; @@ -5169,14 +5208,16 @@ static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBasicBlockSrc *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt"); ir_set_cursor_at_end_and_append_block(irb, err_block); - ir_gen_defers_for_block(irb, scope, outer_scope, true); + if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, return_value)) + return irb->codegen->invalid_inst_src; if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr_src(irb, scope, node); } ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ok_block); - ir_gen_defers_for_block(irb, scope, outer_scope, false); + if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block); @@ -5213,7 +5254,12 @@ static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, result_loc_ret->base.id = ResultLocIdReturn; ir_build_reset_result(irb, scope, node, &result_loc_ret->base); ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base); - if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) { + + bool is_noreturn = false; + if (!ir_gen_defers_for_block(irb, scope, outer_scope, &is_noreturn, err_val)) { + return irb->codegen->invalid_inst_src; + } + if (!is_noreturn) { if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr_src(irb, scope, node); } @@ -5415,7 +5461,8 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode * bool is_return_from_fn = block_node == irb->main_block_node; if (!is_return_from_fn) { - ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); + if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; } IrInstSrc *result; @@ -5440,7 +5487,8 @@ static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode * result_loc_ret->base.id = ResultLocIdReturn; ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base); ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base)); - ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false); + if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; return ir_mark_gen(ir_build_return_src(irb, child_scope, result->base.source_node, result)); } @@ -9240,7 +9288,8 @@ static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope } IrBasicBlockSrc *dest_block = block_scope->end_block; - ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false); + if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; block_scope->incoming_blocks->append(irb->current_basic_block); block_scope->incoming_values->append(result_value); @@ -9314,7 +9363,8 @@ static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *n } IrBasicBlockSrc *dest_block = loop_scope->break_block; - ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false); + if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; loop_scope->incoming_blocks->append(irb->current_basic_block); loop_scope->incoming_values->append(result_value); @@ -9373,7 +9423,8 @@ static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstN } IrBasicBlockSrc *dest_block = loop_scope->continue_block; - ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false); + if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime)); } diff --git a/src/parser.cpp b/src/parser.cpp index cdd254962e..20ad179a75 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -879,7 +879,7 @@ static AstNode *ast_parse_container_field(ParseContext *pc) { // / KEYWORD_noasync BlockExprStatement // / KEYWORD_suspend (SEMICOLON / BlockExprStatement) // / KEYWORD_defer BlockExprStatement -// / KEYWORD_errdefer BlockExprStatement +// / KEYWORD_errdefer Payload? BlockExprStatement // / IfStatement // / LabeledStatement // / SwitchExpr @@ -923,12 +923,18 @@ static AstNode *ast_parse_statement(ParseContext *pc) { if (defer == nullptr) defer = eat_token_if(pc, TokenIdKeywordErrdefer); if (defer != nullptr) { + Token *payload = (defer->id == TokenIdKeywordErrdefer) ? + ast_parse_payload(pc) : nullptr; AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); AstNode *res = ast_create_node(pc, NodeTypeDefer, defer); + res->data.defer.kind = ReturnKindUnconditional; res->data.defer.expr = statement; - if (defer->id == TokenIdKeywordErrdefer) + if (defer->id == TokenIdKeywordErrdefer) { res->data.defer.kind = ReturnKindError; + if (payload != nullptr) + res->data.defer.err_payload = token_symbol(pc, payload); + } return res; } @@ -3032,6 +3038,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont break; case NodeTypeDefer: visit_field(&node->data.defer.expr, visit, context); + visit_field(&node->data.defer.err_payload, visit, context); break; case NodeTypeVariableDeclaration: visit_field(&node->data.variable_declaration.type, visit, context); diff --git a/test/stage1/behavior/defer.zig b/test/stage1/behavior/defer.zig index 5a643609fd..7cefafa9a0 100644 --- a/test/stage1/behavior/defer.zig +++ b/test/stage1/behavior/defer.zig @@ -1,4 +1,6 @@ -const expect = @import("std").testing.expect; +const std = @import("std"); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; var result: [3]u8 = undefined; var index: usize = undefined; @@ -93,3 +95,22 @@ test "return variable while defer expression in scope to modify it" { S.doTheTest(); comptime S.doTheTest(); } + +test "errdefer with payload" { + const S = struct { + fn foo() !i32 { + errdefer |a| { + expectEqual(error.One, a); + } + return error.One; + } + fn doTheTest() void { + _ = foo() catch |err| switch (err) { + error.One => {}, + else => unreachable, + }; + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} From 28dbc5883763fb2b87ef8d186a57c3971d3414bc Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 20 Mar 2020 22:23:51 +0100 Subject: [PATCH 10/12] Address review comments --- lib/std/zig/parser_test.zig | 2 +- test/compile_errors.zig | 12 ++++++++++++ test/stage1/behavior/defer.zig | 6 ++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index b71a78221f..894f726fb1 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1,4 +1,4 @@ -test "zig fmt: noasync block" { +test "zig fmt: errdefer with payload" { try testCanonical( \\pub fn main() anyerror!void { \\ errdefer |a| x += 1; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e3e1462173..d2e298032f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,18 @@ const tests = @import("tests.zig"); const std = @import("std"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest("unused variable error on errdefer", + \\fn foo() !void { + \\ errdefer |a| unreachable; + \\ return error.A; + \\} + \\export fn entry() void { + \\ foo() catch unreachable; + \\} + , &[_][]const u8{ + "tmp.zig:2:15: error: unused variable: 'a'", + }); + cases.addTest("shift on type with non-power-of-two size", \\export fn entry() void { \\ const S = struct { diff --git a/test/stage1/behavior/defer.zig b/test/stage1/behavior/defer.zig index 7cefafa9a0..6bfeb485cc 100644 --- a/test/stage1/behavior/defer.zig +++ b/test/stage1/behavior/defer.zig @@ -1,6 +1,7 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; +const expectError = std.testing.expectError; var result: [3]u8 = undefined; var index: usize = undefined; @@ -105,10 +106,7 @@ test "errdefer with payload" { return error.One; } fn doTheTest() void { - _ = foo() catch |err| switch (err) { - error.One => {}, - else => unreachable, - }; + expectError(error.One, foo()); } }; S.doTheTest(); From dc79f181a56cad17f429bbb7fd176e49bf3d66da Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 21 Mar 2020 18:33:54 +0100 Subject: [PATCH 11/12] ir: Disallow comparison between enum literal and untagged enum Closes #4770 --- src/ir.cpp | 9 +++++++++ test/compile_errors.zig | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 531c1b2432..590b81d3be 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16286,6 +16286,15 @@ static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_i IrInstGen *union_val = op1->value->type->id == ZigTypeIdUnion ? op1 : op2; IrInstGen *enum_val = op1->value->type->id == ZigTypeIdUnion ? op2 : op1; + if (!is_tagged_union(union_val->value->type)) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("comparison of union and enum literal is only valid for tagged union types")); + add_error_note(ira->codegen, msg, union_val->value->type->data.unionation.decl_node, + buf_sprintf("type %s is not a tagged union", + buf_ptr(&union_val->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + ZigType *tag_type = union_val->value->type->data.unionation.tag_type; assert(tag_type != nullptr); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d2e298032f..f19ff11471 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -14,6 +14,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:2:15: error: unused variable: 'a'", }); + cases.addTest("comparison of non-tagged union and enum literal", + \\export fn entry() void { + \\ const U = union { A: u32, B: u64 }; + \\ var u = U{ .A = 42 }; + \\ var ok = u == .A; + \\} + , &[_][]const u8{ + "tmp.zig:4:16: error: comparison of union and enum literal is only valid for tagged union types", + "tmp.zig:2:15: note: type U is not a tagged union", + }); + cases.addTest("shift on type with non-power-of-two size", \\export fn entry() void { \\ const S = struct { From a8fa1ecd89eebd444736d73da4ebf24af74daea8 Mon Sep 17 00:00:00 2001 From: dbandstra Date: Sat, 21 Mar 2020 19:53:05 -0700 Subject: [PATCH 12/12] fix build.zig addBuildOption function for stream refactor --- lib/std/build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index ac411d6cf6..c603fd861b 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1679,7 +1679,7 @@ pub const LibExeObjStep = struct { } pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { - const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream; + const out = self.build_options_contents.outStream(); out.print("pub const {} = {};\n", .{ name, value }) catch unreachable; }