authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-01 20:47:35-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-01 20:47:35-05:00
logde5c0c9f4092a9d5914013e3428af2252da0be81
tree601e35d18140e4a83ca2550b9aeaa7e1415d48e7
parent6bade0b825c37699346a414568e79fe4c1918409
parent6568be575cb87c2f54aad2dfa20d1f35471d2224

Merge remote-tracking branch 'origin/master' into async


13 files changed, 649 insertions(+), 140 deletions(-)

CMakeLists.txt+1
......@@ -386,6 +386,7 @@ set(ZIG_STD_FILES
386386 "index.zig"
387387 "io.zig"
388388 "linked_list.zig"
389 "macho.zig"
389390 "math/acos.zig"
390391 "math/acosh.zig"
391392 "math/asin.zig"
doc/langref.html.in+84-15
......@@ -2782,30 +2782,96 @@ test "fn reflection" {
27822782 {#header_close#}
27832783 {#header_close#}
27842784 {#header_open|Errors#}
2785 {#header_open|Error Set Type#}
27852786 <p>
2786 One of the distinguishing features of Zig is its exception handling strategy.
2787 An error set is like an {#link|enum#}.
2788 However, each error name across the entire compilation gets assigned an unsigned integer
2789 greater than 0. You are allowed to declare the same error name more than once, and if you do, it
2790 gets assigned the same integer value.
27872791 </p>
27882792 <p>
2789 TODO rewrite the errors section to take into account error sets
2793 The number of unique error values across the entire compilation should determine the size of the error set type.
2794 However right now it is hard coded to be a <code>u16</code>. See <a href="https://github.com/zig-lang/zig/issues/786">#768</a>.
27902795 </p>
27912796 <p>
2792 These error values are assigned an unsigned integer value greater than 0 at
2793 compile time. You are allowed to declare the same error value more than once,
2794 and if you do, it gets assigned the same integer value.
2797 You can implicitly cast an error from a subset to its superset:
27952798 </p>
2799 {#code_begin|test#}
2800const std = @import("std");
2801
2802const FileOpenError = error {
2803 AccessDenied,
2804 OutOfMemory,
2805 FileNotFound,
2806};
2807
2808const AllocationError = error {
2809 OutOfMemory,
2810};
2811
2812test "implicit cast subset to superset" {
2813 const err = foo(AllocationError.OutOfMemory);
2814 std.debug.assert(err == FileOpenError.OutOfMemory);
2815}
2816
2817fn foo(err: AllocationError) FileOpenError {
2818 return err;
2819}
2820 {#code_end#}
27962821 <p>
2797 You can refer to these error values with the error namespace such as
2798 <code>error.FileNotFound</code>.
2822 But you cannot implicitly cast an error from a superset to a subset:
2823 </p>
2824 {#code_begin|test_err|not a member of destination error set#}
2825const FileOpenError = error {
2826 AccessDenied,
2827 OutOfMemory,
2828 FileNotFound,
2829};
2830
2831const AllocationError = error {
2832 OutOfMemory,
2833};
2834
2835test "implicit cast superset to subset" {
2836 foo(FileOpenError.OutOfMemory) catch {};
2837}
2838
2839fn foo(err: FileOpenError) AllocationError {
2840 return err;
2841}
2842 {#code_end#}
2843 <p>
2844 There is a shortcut for declaring an error set with only 1 value, and then getting that value:
2845 </p>
2846 {#code_begin|syntax#}
2847const err = error.FileNotFound;
2848 {#code_end#}
2849 <p>This is equivalent to:</p>
2850 {#code_begin|syntax#}
2851const err = (error {FileNotFound}).FileNotFound;
2852 {#code_end#}
2853 <p>
2854 This becomes useful when using {#link|Inferred Error Sets#}.
2855 </p>
2856 {#header_open|The Global Error Set#}
2857 <p><code>error</code> refers to the global error set.
2858 This is the error set that contains all errors in the entire compilation unit.
2859 It is a superset of all other error sets and a subset of none of them.
27992860 </p>
28002861 <p>
2801 Each error value across the entire compilation unit gets a unique integer,
2802 and this determines the size of the error set type.
2862 You can implicitly cast any error set to the global one, and you can explicitly
2863 cast an error of global error set to a non-global one. This inserts a language-level
2864 assert to make sure the error value is in fact in the destination error set.
28032865 </p>
28042866 <p>
2805 The error set type is one of the error values, and in the same way that pointers
2806 cannot be null, a error set instance is always an error.
2867 The global error set should generally be avoided when possible, because it prevents
2868 the compiler from knowing what errors are possible at compile-time. Knowing
2869 the error set at compile-time is better for generated documentationt and for
2870 helpful error messages such as forgetting a possible error value in a {#link|switch#}.
28072871 </p>
2808 {#code_begin|syntax#}const pure_error = error.FileNotFound;{#code_end#}
2872 {#header_close#}
2873 {#header_close#}
2874 {#header_open|Error Union Type#}
28092875 <p>
28102876 Most of the time you will not find yourself using an error set type. Instead,
28112877 likely you will be using the error union type. This is when you take an error set
......@@ -2918,7 +2984,6 @@ fn doAThing(str: []u8) !void {
29182984 a panic in Debug and ReleaseSafe modes and undefined behavior in ReleaseFast mode. So, while we're debugging the
29192985 application, if there <em>was</em> a surprise error here, the application would crash
29202986 appropriately.
2921 TODO: mention error return traces
29222987 </p>
29232988 <p>
29242989 Finally, you may want to take a different action for every situation. For that, we combine
......@@ -2986,7 +3051,7 @@ fn createFoo(param: i32) !Foo {
29863051 </li>
29873052 </ul>
29883053 {#see_also|defer|if|switch#}
2989 {#header_open|Error Union Type#}
3054
29903055 <p>An error union is created with the <code>!</code> binary operator.
29913056 You can use compile-time reflection to access the child type of an error union:</p>
29923057 {#code_begin|test#}
......@@ -3008,8 +3073,12 @@ test "error union" {
30083073 comptime assert(@typeOf(foo).ErrorSet == error);
30093074}
30103075 {#code_end#}
3076 <p>TODO the <code>||</code> operator for error sets</p>
3077 {#header_open|Inferred Error Sets#}
3078 <p>TODO</p>
30113079 {#header_close#}
3012 {#header_open|Error Set Type#}
3080 {#header_close#}
3081 {#header_open|Error Return Traces#}
30133082 <p>TODO</p>
30143083 {#header_close#}
30153084 {#header_close#}
src/analyze.cpp+3-1
......@@ -1083,7 +1083,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
10831083 gen_param_info->src_index = i;
10841084 gen_param_info->gen_index = SIZE_MAX;
10851085
1086 ensure_complete_type(g, type_entry);
1086 type_ensure_zero_bits_known(g, type_entry);
10871087 if (type_has_bits(type_entry)) {
10881088 TypeTableEntry *gen_type;
10891089 if (handle_is_ptr(type_entry)) {
......@@ -2240,6 +2240,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
22402240
22412241 if (enum_type->data.enumeration.zero_bits_loop_flag) {
22422242 enum_type->data.enumeration.zero_bits_known = true;
2243 enum_type->data.enumeration.zero_bits_loop_flag = false;
22432244 return;
22442245 }
22452246
......@@ -2394,6 +2395,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
23942395 // the alignment is pointer width, then assert that the first field is within that
23952396 // alignment
23962397 struct_type->data.structure.zero_bits_known = true;
2398 struct_type->data.structure.zero_bits_loop_flag = false;
23972399 if (struct_type->data.structure.abi_alignment == 0) {
23982400 if (struct_type->data.structure.layout == ContainerLayoutPacked) {
23992401 struct_type->data.structure.abi_alignment = 1;
src/ir.cpp+22-2
......@@ -4510,7 +4510,13 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
45104510 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
45114511 }
45124512
4513 // Temporarily set the name of the IrExecutable to the VariableDeclaration
4514 // so that the struct or enum from the init expression inherits the name.
4515 Buf *old_exec_name = irb->exec->name;
4516 irb->exec->name = variable_declaration->symbol;
45134517 IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
4518 irb->exec->name = old_exec_name;
4519
45144520 if (init_value == irb->codegen->invalid_instruction)
45154521 return init_value;
45164522
......@@ -9504,16 +9510,30 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
95049510
95059511 // explicit cast from child type of maybe type to maybe type
95069512 if (wanted_type->id == TypeTableEntryIdMaybe) {
9507 if (types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node).id == ConstCastResultIdOk) {
9513 TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
9514 if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) {
95089515 return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
95099516 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
95109517 actual_type->id == TypeTableEntryIdNumLitFloat)
95119518 {
9512 if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.maybe.child_type, true)) {
9519 if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
95139520 return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
95149521 } else {
95159522 return ira->codegen->invalid_instruction;
95169523 }
9524 } else if (wanted_child_type->id == TypeTableEntryIdPointer &&
9525 wanted_child_type->data.pointer.is_const &&
9526 (actual_type->id == TypeTableEntryIdPointer || is_container(actual_type)))
9527 {
9528 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_type, value);
9529 if (type_is_invalid(cast1->value.type))
9530 return ira->codegen->invalid_instruction;
9531
9532 IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
9533 if (type_is_invalid(cast2->value.type))
9534 return ira->codegen->invalid_instruction;
9535
9536 return cast2;
95179537 }
95189538 }
95199539
std/debug/index.zig+92-57
......@@ -5,6 +5,7 @@ const io = std.io;
55const os = std.os;
66const elf = std.elf;
77const DW = std.dwarf;
8const macho = std.macho;
89const ArrayList = std.ArrayList;
910const builtin = @import("builtin");
1011
......@@ -178,43 +179,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
178179}
179180
180181fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
181 if (builtin.os == builtin.Os.windows) {
182 return error.UnsupportedDebugInfo;
183 }
184182 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
185183 // at compile time. I'll call it issue #313
186184 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
187185
188 const compile_unit = findCompileUnit(debug_info, address) catch {
189 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
190 address);
191 return;
192 };
193 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
194 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
195 defer line_info.deinit();
196 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
197 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
198 line_info.file_name, line_info.line, line_info.column,
199 address, compile_unit_name);
200 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
201 if (line_info.column == 0) {
202 try out_stream.write("\n");
203 } else {
204 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
205 try out_stream.writeByte(' ');
206 }}
207 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
186 switch (builtin.os) {
187 builtin.Os.windows => return error.UnsupportedDebugInfo,
188 builtin.Os.macosx => {
189 // TODO(bnoordhuis) It's theoretically possible to obtain the
190 // compilation unit from the symbtab but it's not that useful
191 // in practice because the compiler dumps everything in a single
192 // object file. Future improvement: use external dSYM data when
193 // available.
194 const unknown = macho.Symbol { .name = "???", .address = address };
195 const symbol = debug_info.symbol_table.search(address) ?? &unknown;
196 try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++
197 DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n",
198 symbol.name, address);
199 },
200 else => {
201 const compile_unit = findCompileUnit(debug_info, address) catch {
202 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
203 address);
204 return;
205 };
206 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
207 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
208 defer line_info.deinit();
209 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
210 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
211 line_info.file_name, line_info.line, line_info.column,
212 address, compile_unit_name);
213 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
214 if (line_info.column == 0) {
215 try out_stream.write("\n");
216 } else {
217 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
218 try out_stream.writeByte(' ');
219 }}
220 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
221 }
222 } else |err| switch (err) {
223 error.EndOfFile => {},
224 else => return err,
225 }
226 } else |err| switch (err) {
227 error.MissingDebugInfo, error.InvalidDebugInfo => {
228 try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
229 },
230 else => return err,
208231 }
209 } else |err| switch (err) {
210 error.EndOfFile => {},
211 else => return err,
212 }
213 } else |err| switch (err) {
214 error.MissingDebugInfo, error.InvalidDebugInfo => {
215 try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
216232 },
217 else => return err,
218233 }
219234}
220235
......@@ -222,6 +237,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
222237 switch (builtin.object_format) {
223238 builtin.ObjectFormat.elf => {
224239 const st = try allocator.create(ElfStackTrace);
240 errdefer allocator.destroy(st);
225241 *st = ElfStackTrace {
226242 .self_exe_file = undefined,
227243 .elf = undefined,
......@@ -247,12 +263,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
247263 try scanAllCompileUnits(st);
248264 return st;
249265 },
266 builtin.ObjectFormat.macho => {
267 var exe_file = try os.openSelfExe();
268 defer exe_file.close();
269
270 const st = try allocator.create(ElfStackTrace);
271 errdefer allocator.destroy(st);
272
273 *st = ElfStackTrace {
274 .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)),
275 };
276
277 return st;
278 },
250279 builtin.ObjectFormat.coff => {
251280 return error.TodoSupportCoffDebugInfo;
252281 },
253 builtin.ObjectFormat.macho => {
254 return error.TodoSupportMachoDebugInfo;
255 },
256282 builtin.ObjectFormat.wasm => {
257283 return error.TodoSupportCOFFDebugInfo;
258284 },
......@@ -295,31 +321,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con
295321 }
296322}
297323
298pub const ElfStackTrace = struct {
299 self_exe_file: os.File,
300 elf: elf.Elf,
301 debug_info: &elf.SectionHeader,
302 debug_abbrev: &elf.SectionHeader,
303 debug_str: &elf.SectionHeader,
304 debug_line: &elf.SectionHeader,
305 debug_ranges: ?&elf.SectionHeader,
306 abbrev_table_list: ArrayList(AbbrevTableHeader),
307 compile_unit_list: ArrayList(CompileUnit),
308
309 pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
310 return self.abbrev_table_list.allocator;
311 }
324pub const ElfStackTrace = switch (builtin.os) {
325 builtin.Os.macosx => struct {
326 symbol_table: macho.SymbolTable,
312327
313 pub fn readString(self: &ElfStackTrace) ![]u8 {
314 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
315 const in_stream = &in_file_stream.stream;
316 return readStringRaw(self.allocator(), in_stream);
317 }
328 pub fn close(self: &ElfStackTrace) void {
329 self.symbol_table.deinit();
330 }
331 },
332 else => struct {
333 self_exe_file: os.File,
334 elf: elf.Elf,
335 debug_info: &elf.SectionHeader,
336 debug_abbrev: &elf.SectionHeader,
337 debug_str: &elf.SectionHeader,
338 debug_line: &elf.SectionHeader,
339 debug_ranges: ?&elf.SectionHeader,
340 abbrev_table_list: ArrayList(AbbrevTableHeader),
341 compile_unit_list: ArrayList(CompileUnit),
342
343 pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
344 return self.abbrev_table_list.allocator;
345 }
318346
319 pub fn close(self: &ElfStackTrace) void {
320 self.self_exe_file.close();
321 self.elf.close();
322 }
347 pub fn readString(self: &ElfStackTrace) ![]u8 {
348 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
349 const in_stream = &in_file_stream.stream;
350 return readStringRaw(self.allocator(), in_stream);
351 }
352
353 pub fn close(self: &ElfStackTrace) void {
354 self.self_exe_file.close();
355 self.elf.close();
356 }
357 },
323358};
324359
325360const PcRange = struct {
std/fmt/index.zig+4-6
......@@ -550,12 +550,6 @@ test "parse unsigned comptime" {
550550 }
551551}
552552
553// Dummy field because of https://github.com/zig-lang/zig/issues/557.
554// At top level because of https://github.com/zig-lang/zig/issues/675.
555const Struct = struct {
556 unused: u8,
557};
558
559553test "fmt.format" {
560554 {
561555 var buf1: [32]u8 = undefined;
......@@ -588,6 +582,10 @@ test "fmt.format" {
588582 assert(mem.eql(u8, result, "u3: 5\n"));
589583 }
590584 {
585 // Dummy field because of https://github.com/zig-lang/zig/issues/557.
586 const Struct = struct {
587 unused: u8,
588 };
591589 var buf1: [32]u8 = undefined;
592590 const value = Struct {
593591 .unused = 42,
std/index.zig+2
......@@ -21,6 +21,7 @@ pub const endian = @import("endian.zig");
2121pub const fmt = @import("fmt/index.zig");
2222pub const heap = @import("heap.zig");
2323pub const io = @import("io.zig");
24pub const macho = @import("macho.zig");
2425pub const math = @import("math/index.zig");
2526pub const mem = @import("mem.zig");
2627pub const net = @import("net.zig");
......@@ -51,6 +52,7 @@ test "std" {
5152 _ = @import("endian.zig");
5253 _ = @import("fmt/index.zig");
5354 _ = @import("io.zig");
55 _ = @import("macho.zig");
5456 _ = @import("math/index.zig");
5557 _ = @import("mem.zig");
5658 _ = @import("heap.zig");
std/macho.zig created+170
......@@ -0,0 +1,170 @@
1const builtin = @import("builtin");
2const std = @import("index.zig");
3const io = std.io;
4const mem = std.mem;
5
6const MH_MAGIC_64 = 0xFEEDFACF;
7const MH_PIE = 0x200000;
8const LC_SYMTAB = 2;
9
10const MachHeader64 = packed struct {
11 magic: u32,
12 cputype: u32,
13 cpusubtype: u32,
14 filetype: u32,
15 ncmds: u32,
16 sizeofcmds: u32,
17 flags: u32,
18 reserved: u32,
19};
20
21const LoadCommand = packed struct {
22 cmd: u32,
23 cmdsize: u32,
24};
25
26const SymtabCommand = packed struct {
27 symoff: u32,
28 nsyms: u32,
29 stroff: u32,
30 strsize: u32,
31};
32
33const Nlist64 = packed struct {
34 n_strx: u32,
35 n_type: u8,
36 n_sect: u8,
37 n_desc: u16,
38 n_value: u64,
39};
40
41pub const Symbol = struct {
42 name: []const u8,
43 address: u64,
44
45 fn addressLessThan(lhs: &const Symbol, rhs: &const Symbol) bool {
46 return lhs.address < rhs.address;
47 }
48};
49
50pub const SymbolTable = struct {
51 allocator: &mem.Allocator,
52 symbols: []const Symbol,
53 strings: []const u8,
54
55 // Doubles as an eyecatcher to calculate the PIE slide, see loadSymbols().
56 // Ideally we'd use _mh_execute_header because it's always at 0x100000000
57 // in the image but as it's located in a different section than executable
58 // code, its displacement is different.
59 pub fn deinit(self: &SymbolTable) void {
60 self.allocator.free(self.symbols);
61 self.symbols = []const Symbol {};
62
63 self.allocator.free(self.strings);
64 self.strings = []const u8 {};
65 }
66
67 pub fn search(self: &const SymbolTable, address: usize) ?&const Symbol {
68 var min: usize = 0;
69 var max: usize = self.symbols.len - 1; // Exclude sentinel.
70 while (min < max) {
71 const mid = min + (max - min) / 2;
72 const curr = &self.symbols[mid];
73 const next = &self.symbols[mid + 1];
74 if (address >= next.address) {
75 min = mid + 1;
76 } else if (address < curr.address) {
77 max = mid;
78 } else {
79 return curr;
80 }
81 }
82 return null;
83 }
84};
85
86pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable {
87 var file = in.file;
88 try file.seekTo(0);
89
90 var hdr: MachHeader64 = undefined;
91 try readOneNoEof(in, MachHeader64, &hdr);
92 if (hdr.magic != MH_MAGIC_64) return error.MissingDebugInfo;
93 const is_pie = MH_PIE == (hdr.flags & MH_PIE);
94
95 var pos: usize = @sizeOf(@typeOf(hdr));
96 var ncmd: u32 = hdr.ncmds;
97 while (ncmd != 0) : (ncmd -= 1) {
98 try file.seekTo(pos);
99 var lc: LoadCommand = undefined;
100 try readOneNoEof(in, LoadCommand, &lc);
101 if (lc.cmd == LC_SYMTAB) break;
102 pos += lc.cmdsize;
103 } else {
104 return error.MissingDebugInfo;
105 }
106
107 var cmd: SymtabCommand = undefined;
108 try readOneNoEof(in, SymtabCommand, &cmd);
109
110 try file.seekTo(cmd.symoff);
111 var syms = try allocator.alloc(Nlist64, cmd.nsyms);
112 defer allocator.free(syms);
113 try readNoEof(in, Nlist64, syms);
114
115 try file.seekTo(cmd.stroff);
116 var strings = try allocator.alloc(u8, cmd.strsize);
117 errdefer allocator.free(strings);
118 try in.stream.readNoEof(strings);
119
120 var nsyms: usize = 0;
121 for (syms) |sym| if (isSymbol(sym)) nsyms += 1;
122 if (nsyms == 0) return error.MissingDebugInfo;
123
124 var symbols = try allocator.alloc(Symbol, nsyms + 1); // Room for sentinel.
125 errdefer allocator.free(symbols);
126
127 var pie_slide: usize = 0;
128 var nsym: usize = 0;
129 for (syms) |sym| {
130 if (!isSymbol(sym)) continue;
131 const start = sym.n_strx;
132 const end = ??mem.indexOfScalarPos(u8, strings, start, 0);
133 const name = strings[start..end];
134 const address = sym.n_value;
135 symbols[nsym] = Symbol { .name = name, .address = address };
136 nsym += 1;
137 if (is_pie and mem.eql(u8, name, "_SymbolTable_deinit")) {
138 pie_slide = @ptrToInt(SymbolTable.deinit) - address;
139 }
140 }
141
142 // Effectively a no-op, lld emits symbols in ascending order.
143 std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
144
145 // Insert the sentinel. Since we don't know where the last function ends,
146 // we arbitrarily limit it to the start address + 4 KB.
147 const top = symbols[nsyms - 1].address + 4096;
148 symbols[nsyms] = Symbol { .name = "", .address = top };
149
150 if (pie_slide != 0) {
151 for (symbols) |*symbol| symbol.address += pie_slide;
152 }
153
154 return SymbolTable {
155 .allocator = allocator,
156 .symbols = symbols,
157 .strings = strings,
158 };
159}
160
161fn readNoEof(in: &io.FileInStream, comptime T: type, result: []T) !void {
162 return in.stream.readNoEof(([]u8)(result));
163}
164fn readOneNoEof(in: &io.FileInStream, comptime T: type, result: &T) !void {
165 return readNoEof(in, T, result[0..1]);
166}
167
168fn isSymbol(sym: &const Nlist64) bool {
169 return sym.n_value != 0 and sym.n_desc == 0;
170}
std/os/child_process.zig-50
......@@ -32,9 +32,6 @@ pub const ChildProcess = struct {
3232
3333 pub argv: []const []const u8,
3434
35 /// Possibly called from a signal handler. Must set this before calling `spawn`.
36 pub onTerm: ?fn(&ChildProcess)void,
37
3835 /// Leave as null to use the current env map using the supplied allocator.
3936 pub env_map: ?&const BufMap,
4037
......@@ -102,7 +99,6 @@ pub const ChildProcess = struct {
10299 .err_pipe = undefined,
103100 .llnode = undefined,
104101 .term = null,
105 .onTerm = null,
106102 .env_map = null,
107103 .cwd = null,
108104 .uid = if (is_windows) {} else null,
......@@ -124,7 +120,6 @@ pub const ChildProcess = struct {
124120 self.gid = user_info.gid;
125121 }
126122
127 /// onTerm can be called before `spawn` returns.
128123 /// On success must call `kill` or `wait`.
129124 pub fn spawn(self: &ChildProcess) !void {
130125 if (is_windows) {
......@@ -165,9 +160,6 @@ pub const ChildProcess = struct {
165160 }
166161
167162 pub fn killPosix(self: &ChildProcess) !Term {
168 block_SIGCHLD();
169 defer restore_SIGCHLD();
170
171163 if (self.term) |term| {
172164 self.cleanupStreams();
173165 return term;
......@@ -246,9 +238,6 @@ pub const ChildProcess = struct {
246238 }
247239
248240 fn waitPosix(self: &ChildProcess) !Term {
249 block_SIGCHLD();
250 defer restore_SIGCHLD();
251
252241 if (self.term) |term| {
253242 self.cleanupStreams();
254243 return term;
......@@ -298,10 +287,6 @@ pub const ChildProcess = struct {
298287
299288 fn handleWaitResult(self: &ChildProcess, status: i32) void {
300289 self.term = self.cleanupAfterWait(status);
301
302 if (self.onTerm) |onTerm| {
303 onTerm(self);
304 }
305290 }
306291
307292 fn cleanupStreams(self: &ChildProcess) void {
......@@ -347,9 +332,6 @@ pub const ChildProcess = struct {
347332 }
348333
349334 fn spawnPosix(self: &ChildProcess) !void {
350 // TODO atomically set a flag saying that we already did this
351 install_SIGCHLD_handler();
352
353335 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try makePipe() else undefined;
354336 errdefer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); };
355337
......@@ -387,11 +369,9 @@ pub const ChildProcess = struct {
387369 const err_pipe = try makePipe();
388370 errdefer destroyPipe(err_pipe);
389371
390 block_SIGCHLD();
391372 const pid_result = posix.fork();
392373 const pid_err = posix.getErrno(pid_result);
393374 if (pid_err > 0) {
394 restore_SIGCHLD();
395375 return switch (pid_err) {
396376 posix.EAGAIN, posix.ENOMEM, posix.ENOSYS => error.SystemResources,
397377 else => os.unexpectedErrorPosix(pid_err),
......@@ -399,7 +379,6 @@ pub const ChildProcess = struct {
399379 }
400380 if (pid_result == 0) {
401381 // we are the child
402 restore_SIGCHLD();
403382
404383 setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch
405384 |err| forkChildErrReport(err_pipe[1], err);
......@@ -451,8 +430,6 @@ pub const ChildProcess = struct {
451430 // TODO make this atomic so it works even with threads
452431 children_nodes.prepend(&self.llnode);
453432
454 restore_SIGCHLD();
455
456433 if (self.stdin_behavior == StdIo.Pipe) { os.close(stdin_pipe[0]); }
457434 if (self.stdout_behavior == StdIo.Pipe) { os.close(stdout_pipe[1]); }
458435 if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
......@@ -824,30 +801,3 @@ fn handleTerm(pid: i32, status: i32) void {
824801 }
825802 }
826803}
827
828const sigchld_set = x: {
829 var signal_set = posix.empty_sigset;
830 posix.sigaddset(&signal_set, posix.SIGCHLD);
831 break :x signal_set;
832};
833
834fn block_SIGCHLD() void {
835 const err = posix.getErrno(posix.sigprocmask(posix.SIG_BLOCK, &sigchld_set, null));
836 assert(err == 0);
837}
838
839fn restore_SIGCHLD() void {
840 const err = posix.getErrno(posix.sigprocmask(posix.SIG_UNBLOCK, &sigchld_set, null));
841 assert(err == 0);
842}
843
844const sigchld_action = posix.Sigaction {
845 .handler = sigchld_handler,
846 .mask = posix.empty_sigset,
847 .flags = posix.SA_RESTART | posix.SA_NOCLDSTOP,
848};
849
850fn install_SIGCHLD_handler() void {
851 const err = posix.getErrno(posix.sigaction(posix.SIGCHLD, &sigchld_action, null));
852 assert(err == 0);
853}
std/unicode.zig+140-9
......@@ -1,4 +1,5 @@
11const std = @import("./index.zig");
2const debug = std.debug;
23
34/// Given the first byte of a UTF-8 codepoint,
45/// returns a number 1-4 indicating the total length of the codepoint in bytes.
......@@ -25,8 +26,8 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
2526 };
2627}
2728pub fn utf8Decode2(bytes: []const u8) !u32 {
28 std.debug.assert(bytes.len == 2);
29 std.debug.assert(bytes[0] & 0b11100000 == 0b11000000);
29 debug.assert(bytes.len == 2);
30 debug.assert(bytes[0] & 0b11100000 == 0b11000000);
3031 var value: u32 = bytes[0] & 0b00011111;
3132
3233 if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation;
......@@ -38,8 +39,8 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
3839 return value;
3940}
4041pub fn utf8Decode3(bytes: []const u8) !u32 {
41 std.debug.assert(bytes.len == 3);
42 std.debug.assert(bytes[0] & 0b11110000 == 0b11100000);
42 debug.assert(bytes.len == 3);
43 debug.assert(bytes[0] & 0b11110000 == 0b11100000);
4344 var value: u32 = bytes[0] & 0b00001111;
4445
4546 if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation;
......@@ -56,8 +57,8 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
5657 return value;
5758}
5859pub fn utf8Decode4(bytes: []const u8) !u32 {
59 std.debug.assert(bytes.len == 4);
60 std.debug.assert(bytes[0] & 0b11111000 == 0b11110000);
60 debug.assert(bytes.len == 4);
61 debug.assert(bytes[0] & 0b11111000 == 0b11110000);
6162 var value: u32 = bytes[0] & 0b00000111;
6263
6364 if (bytes[1] & 0b11000000 != 0b10000000) return error.Utf8ExpectedContinuation;
......@@ -78,6 +79,136 @@ pub fn utf8Decode4(bytes: []const u8) !u32 {
7879 return value;
7980}
8081
82pub fn utf8ValidateSlice(s: []const u8) bool {
83 var i: usize = 0;
84 while (i < s.len) {
85 if (utf8ByteSequenceLength(s[i])) |cp_len| {
86 if (i + cp_len > s.len) {
87 return false;
88 }
89
90 if (utf8Decode(s[i..i+cp_len])) |_| {} else |_| { return false; }
91 i += cp_len;
92 } else |err| {
93 return false;
94 }
95 }
96 return true;
97}
98
99const Utf8View = struct {
100 bytes: []const u8,
101
102 pub fn init(s: []const u8) !Utf8View {
103 if (!utf8ValidateSlice(s)) {
104 return error.InvalidUtf8;
105 }
106
107 return initUnchecked(s);
108 }
109
110 pub fn initUnchecked(s: []const u8) Utf8View {
111 return Utf8View {
112 .bytes = s,
113 };
114 }
115
116 pub fn initComptime(comptime s: []const u8) Utf8View {
117 if (comptime init(s)) |r| {
118 return r;
119 } else |err| switch (err) {
120 error.InvalidUtf8 => {
121 @compileError("invalid utf8");
122 unreachable;
123 }
124 }
125 }
126
127 pub fn Iterator(s: &const Utf8View) Utf8Iterator {
128 return Utf8Iterator {
129 .bytes = s.bytes,
130 .i = 0,
131 };
132 }
133};
134
135const Utf8Iterator = struct {
136 bytes: []const u8,
137 i: usize,
138
139 pub fn nextCodepointSlice(it: &Utf8Iterator) ?[]const u8 {
140 if (it.i >= it.bytes.len) {
141 return null;
142 }
143
144 const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable;
145
146 it.i += cp_len;
147 return it.bytes[it.i-cp_len..it.i];
148 }
149
150 pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {
151 const slice = it.nextCodepointSlice() ?? return null;
152
153 const r = switch (slice.len) {
154 1 => u32(slice[0]),
155 2 => utf8Decode2(slice),
156 3 => utf8Decode3(slice),
157 4 => utf8Decode4(slice),
158 else => unreachable,
159 };
160
161 return r catch unreachable;
162 }
163};
164
165test "utf8 iterator on ascii" {
166 const s = Utf8View.initComptime("abc");
167
168 var it1 = s.Iterator();
169 debug.assert(std.mem.eql(u8, "a", ??it1.nextCodepointSlice()));
170 debug.assert(std.mem.eql(u8, "b", ??it1.nextCodepointSlice()));
171 debug.assert(std.mem.eql(u8, "c", ??it1.nextCodepointSlice()));
172 debug.assert(it1.nextCodepointSlice() == null);
173
174 var it2 = s.Iterator();
175 debug.assert(??it2.nextCodepoint() == 'a');
176 debug.assert(??it2.nextCodepoint() == 'b');
177 debug.assert(??it2.nextCodepoint() == 'c');
178 debug.assert(it2.nextCodepoint() == null);
179}
180
181test "utf8 view bad" {
182 // Compile-time error.
183 // const s3 = Utf8View.initComptime("\xfe\xf2");
184
185 const s = Utf8View.init("hel\xadlo");
186 if (s) |_| { unreachable; } else |err| { debug.assert(err == error.InvalidUtf8); }
187}
188
189test "utf8 view ok" {
190 const s = Utf8View.initComptime("東京市");
191
192 var it1 = s.Iterator();
193 debug.assert(std.mem.eql(u8, "東", ??it1.nextCodepointSlice()));
194 debug.assert(std.mem.eql(u8, "京", ??it1.nextCodepointSlice()));
195 debug.assert(std.mem.eql(u8, "市", ??it1.nextCodepointSlice()));
196 debug.assert(it1.nextCodepointSlice() == null);
197
198 var it2 = s.Iterator();
199 debug.assert(??it2.nextCodepoint() == 0x6771);
200 debug.assert(??it2.nextCodepoint() == 0x4eac);
201 debug.assert(??it2.nextCodepoint() == 0x5e02);
202 debug.assert(it2.nextCodepoint() == null);
203}
204
205test "bad utf8 slice" {
206 debug.assert(utf8ValidateSlice("abc"));
207 debug.assert(!utf8ValidateSlice("abc\xc0"));
208 debug.assert(!utf8ValidateSlice("abc\xc0abc"));
209 debug.assert(utf8ValidateSlice("abc\xdf\xbf"));
210}
211
81212test "valid utf8" {
82213 testValid("\x00", 0x0);
83214 testValid("\x20", 0x20);
......@@ -145,17 +276,17 @@ fn testError(bytes: []const u8, expected_err: error) void {
145276 if (testDecode(bytes)) |_| {
146277 unreachable;
147278 } else |err| {
148 std.debug.assert(err == expected_err);
279 debug.assert(err == expected_err);
149280 }
150281}
151282
152283fn testValid(bytes: []const u8, expected_codepoint: u32) void {
153 std.debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint);
284 debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint);
154285}
155286
156287fn testDecode(bytes: []const u8) !u32 {
157288 const length = try utf8ByteSequenceLength(bytes[0]);
158289 if (bytes.len < length) return error.UnexpectedEof;
159 std.debug.assert(bytes.len == length);
290 debug.assert(bytes.len == length);
160291 return utf8Decode(bytes);
161292}
test/cases/cast.zig+102
......@@ -32,6 +32,108 @@ fn funcWithConstPtrPtr(x: &const &i32) void {
3232 **x += 1;
3333}
3434
35test "implicitly cast a container to a const pointer of it" {
36 const z = Struct(void) { .x = void{} };
37 assert(0 == @sizeOf(@typeOf(z)));
38 assert(void{} == Struct(void).pointer(z).x);
39 assert(void{} == Struct(void).pointer(&z).x);
40 assert(void{} == Struct(void).maybePointer(z).x);
41 assert(void{} == Struct(void).maybePointer(&z).x);
42 assert(void{} == Struct(void).maybePointer(null).x);
43 const s = Struct(u8) { .x = 42 };
44 assert(0 != @sizeOf(@typeOf(s)));
45 assert(42 == Struct(u8).pointer(s).x);
46 assert(42 == Struct(u8).pointer(&s).x);
47 assert(42 == Struct(u8).maybePointer(s).x);
48 assert(42 == Struct(u8).maybePointer(&s).x);
49 assert(0 == Struct(u8).maybePointer(null).x);
50 const u = Union { .x = 42 };
51 assert(42 == Union.pointer(u).x);
52 assert(42 == Union.pointer(&u).x);
53 assert(42 == Union.maybePointer(u).x);
54 assert(42 == Union.maybePointer(&u).x);
55 assert(0 == Union.maybePointer(null).x);
56 const e = Enum.Some;
57 assert(Enum.Some == Enum.pointer(e));
58 assert(Enum.Some == Enum.pointer(&e));
59 assert(Enum.Some == Enum.maybePointer(e));
60 assert(Enum.Some == Enum.maybePointer(&e));
61 assert(Enum.None == Enum.maybePointer(null));
62}
63
64fn Struct(comptime T: type) type {
65 return struct {
66 const Self = this;
67 x: T,
68
69 fn pointer(self: &const Self) Self {
70 return *self;
71 }
72
73 fn maybePointer(self: ?&const Self) Self {
74 const none = Self { .x = if (T == void) void{} else 0 };
75 return *(self ?? &none);
76 }
77 };
78}
79
80const Union = union {
81 x: u8,
82
83 fn pointer(self: &const Union) Union {
84 return *self;
85 }
86
87 fn maybePointer(self: ?&const Union) Union {
88 const none = Union { .x = 0 };
89 return *(self ?? &none);
90 }
91};
92
93const Enum = enum {
94 None,
95 Some,
96
97 fn pointer(self: &const Enum) Enum {
98 return *self;
99 }
100
101 fn maybePointer(self: ?&const Enum) Enum {
102 return *(self ?? &Enum.None);
103 }
104};
105
106test "implicitly cast indirect pointer to maybe-indirect pointer" {
107 const S = struct {
108 const Self = this;
109 x: u8,
110 fn constConst(p: &const &const Self) u8 {
111 return (*p).x;
112 }
113 fn maybeConstConst(p: ?&const &const Self) u8 {
114 return (*??p).x;
115 }
116 fn constConstConst(p: &const &const &const Self) u8 {
117 return (**p).x;
118 }
119 fn maybeConstConstConst(p: ?&const &const &const Self) u8 {
120 return (**??p).x;
121 }
122 };
123 const s = S { .x = 42 };
124 const p = &s;
125 const q = &p;
126 const r = &q;
127 assert(42 == S.constConst(p));
128 assert(42 == S.constConst(q));
129 assert(42 == S.maybeConstConst(p));
130 assert(42 == S.maybeConstConst(q));
131 assert(42 == S.constConstConst(q));
132 assert(42 == S.constConstConst(r));
133 assert(42 == S.maybeConstConstConst(q));
134 assert(42 == S.maybeConstConstConst(r));
135}
136
35137test "explicit cast from integer to error type" {
36138 testCastIntToErr(error.ItBroke);
37139 comptime testCastIntToErr(error.ItBroke);
test/cases/misc.zig+17
......@@ -499,12 +499,29 @@ test "@canImplicitCast" {
499499}
500500
501501test "@typeName" {
502 const Struct = struct {
503 };
504 const Union = union {
505 unused: u8,
506 };
507 const Enum = enum {
508 Unused,
509 };
502510 comptime {
503511 assert(mem.eql(u8, @typeName(i64), "i64"));
504512 assert(mem.eql(u8, @typeName(&usize), "&usize"));
513 // https://github.com/zig-lang/zig/issues/675
514 assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)"));
515 assert(mem.eql(u8, @typeName(Struct), "Struct"));
516 assert(mem.eql(u8, @typeName(Union), "Union"));
517 assert(mem.eql(u8, @typeName(Enum), "Enum"));
505518 }
506519}
507520
521fn TypeFromFn(comptime T: type) type {
522 return struct {};
523}
524
508525test "volatile load and store" {
509526 var number: i32 = 1234;
510527 const ptr = (&volatile i32)(&number);
test/compile_errors.zig+12
......@@ -3090,4 +3090,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
30903090 ,
30913091 ".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields",
30923092 ".tmp_source.zig:3:5: note: field 'A' has type 'i32'");
3093
3094 cases.add("self-referencing function pointer field",
3095 \\const S = struct {
3096 \\ f: fn(_: S) void,
3097 \\};
3098 \\fn f(_: S) void {
3099 \\}
3100 \\export fn entry() void {
3101 \\ var _ = S { .f = f };
3102 \\}
3103 ,
3104 ".tmp_source.zig:4:9: error: type 'S' is not copyable; cannot pass by value");
30933105}