authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-23 12:56:41-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-23 12:56:41-05:00
log9cfd7dea19c4c54e2754119c04c06cd57cfb759d
tree3b7950d86f77467b43aa323f80563f678cf6d2fb
parent78bc62fd3415dc1db72c916075c9956fdec407aa
parentb66547e98c9034e52c5647735b47dc24939c8d15

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


22 files changed, 991 insertions(+), 183 deletions(-)

CMakeLists.txt+1
......@@ -429,6 +429,7 @@ set(ZIG_STD_FILES
429429 "index.zig"
430430 "io.zig"
431431 "linked_list.zig"
432 "macho.zig"
432433 "math/acos.zig"
433434 "math/acosh.zig"
434435 "math/asin.zig"
src/analyze.cpp+12-7
......@@ -2278,17 +2278,16 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
22782278 return;
22792279
22802280 if (struct_type->data.structure.zero_bits_loop_flag) {
2281 // If we get here it's due to recursion. From this we conclude that the struct is
2282 // not zero bits, and if abi_alignment == 0 we further conclude that the first field
2283 // is a pointer to this very struct, or a function pointer with parameters that
2284 // reference such a type.
2281 // If we get here it's due to recursion. This is a design flaw in the compiler,
2282 // we should be able to still figure out alignment, but here we give up and say that
2283 // the alignment is pointer width, then assert that the first field is within that
2284 // alignment
22852285 struct_type->data.structure.zero_bits_known = true;
22862286 if (struct_type->data.structure.abi_alignment == 0) {
22872287 if (struct_type->data.structure.layout == ContainerLayoutPacked) {
22882288 struct_type->data.structure.abi_alignment = 1;
22892289 } else {
2290 struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref,
2291 LLVMPointerType(LLVMInt8Type(), 0));
2290 struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0));
22922291 }
22932292 }
22942293 return;
......@@ -2352,11 +2351,17 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
23522351 if (gen_field_index == 0) {
23532352 if (struct_type->data.structure.layout == ContainerLayoutPacked) {
23542353 struct_type->data.structure.abi_alignment = 1;
2355 } else {
2354 } else if (struct_type->data.structure.abi_alignment == 0) {
23562355 // Alignment of structs is the alignment of the first field, for now.
23572356 // TODO change this when we re-order struct fields (issue #168)
23582357 struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type);
23592358 assert(struct_type->data.structure.abi_alignment != 0);
2359 } else {
2360 // due to a design flaw in the compiler we assumed that alignment was
2361 // pointer width, so we assert that this wasn't violated.
2362 if (get_abi_alignment(g, field_type) > struct_type->data.structure.abi_alignment) {
2363 zig_panic("compiler design flaw: incorrect alignment assumption");
2364 }
23602365 }
23612366 }
23622367
src/codegen.cpp+2
......@@ -4201,6 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
42014201 continue;
42024202 }
42034203 ConstExprValue *field_val = &const_val->data.x_struct.fields[i];
4204 assert(field_val->type != nullptr);
42044205 LLVMValueRef val = gen_const_val(g, field_val, "");
42054206 fields[type_struct_field->gen_index] = val;
42064207 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
......@@ -4373,6 +4374,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
43734374 }
43744375 }
43754376 }
4377 zig_unreachable();
43764378 case TypeTableEntryIdErrorUnion:
43774379 {
43784380 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
src/ir.cpp+54-31
......@@ -4172,7 +4172,13 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
41724172 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
41734173 }
41744174
4175 // Temporarily set the name of the IrExecutable to the VariableDeclaration
4176 // so that the struct or enum from the init expression inherits the name.
4177 Buf *old_exec_name = irb->exec->name;
4178 irb->exec->name = variable_declaration->symbol;
41754179 IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
4180 irb->exec->name = old_exec_name;
4181
41764182 if (init_value == irb->codegen->invalid_instruction)
41774183 return init_value;
41784184
......@@ -6727,8 +6733,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
67276733 result.id = ConstCastResultIdFnReturnType;
67286734 result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
67296735 *result.data.return_type = child;
6736 return result;
67306737 }
6731 return result;
67326738 }
67336739 if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
67346740 result.id = ConstCastResultIdFnArgCount;
......@@ -8183,7 +8189,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi
81838189 }
81848190
81858191 if (instr_is_comptime(value)) {
8186 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
8192 ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
81878193 if (!val)
81888194 return ira->codegen->invalid_instruction;
81898195 bool final_is_const = (value->value.type->id == TypeTableEntryIdMetaType) ? is_const : true;
......@@ -9975,15 +9981,18 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
99759981 ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;
99769982 }
99779983 } else {
9978 if (float_cmp_zero(&op2->value) == CmpEQ) {
9984 IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
9985 if (casted_op2 == ira->codegen->invalid_instruction)
9986 return ira->codegen->builtin_types.entry_invalid;
9987 if (float_cmp_zero(&casted_op2->value) == CmpEQ) {
99799988 // the division by zero error will be caught later, but we don't
99809989 // have a remainder function ambiguity problem
99819990 ok = true;
99829991 } else {
99839992 ConstExprValue rem_result;
99849993 ConstExprValue mod_result;
9985 float_rem(&rem_result, &op1->value, &op2->value);
9986 float_mod(&mod_result, &op1->value, &op2->value);
9994 float_rem(&rem_result, &op1->value, &casted_op2->value);
9995 float_mod(&mod_result, &op1->value, &casted_op2->value);
99879996 ok = float_cmp(&rem_result, &mod_result) == CmpEQ;
99889997 }
99899998 }
......@@ -14928,6 +14937,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
1492814937 ConstExprValue *parent_ptr;
1492914938 size_t abs_offset;
1493014939 size_t rel_end;
14940 bool ptr_is_undef = false;
1493114941 if (array_type->id == TypeTableEntryIdArray) {
1493214942 array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value);
1493314943 abs_offset = 0;
......@@ -14935,7 +14945,12 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
1493514945 parent_ptr = nullptr;
1493614946 } else if (array_type->id == TypeTableEntryIdPointer) {
1493714947 parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value);
14938 switch (parent_ptr->data.x_ptr.special) {
14948 if (parent_ptr->special == ConstValSpecialUndef) {
14949 array_val = nullptr;
14950 abs_offset = 0;
14951 rel_end = SIZE_MAX;
14952 ptr_is_undef = true;
14953 } else switch (parent_ptr->data.x_ptr.special) {
1493914954 case ConstPtrSpecialInvalid:
1494014955 case ConstPtrSpecialDiscard:
1494114956 zig_unreachable();
......@@ -14989,7 +15004,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
1498915004 }
1499015005
1499115006 uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint);
14992 if (start_scalar > rel_end) {
15007 if (!ptr_is_undef && start_scalar > rel_end) {
1499315008 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
1499415009 return ira->codegen->builtin_types.entry_invalid;
1499515010 }
......@@ -15000,12 +15015,18 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
1500015015 } else {
1500115016 end_scalar = rel_end;
1500215017 }
15003 if (end_scalar > rel_end) {
15004 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
15005 return ira->codegen->builtin_types.entry_invalid;
15018 if (!ptr_is_undef) {
15019 if (end_scalar > rel_end) {
15020 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
15021 return ira->codegen->builtin_types.entry_invalid;
15022 }
15023 if (start_scalar > end_scalar) {
15024 ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));
15025 return ira->codegen->builtin_types.entry_invalid;
15026 }
1500615027 }
15007 if (start_scalar > end_scalar) {
15008 ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));
15028 if (ptr_is_undef && start_scalar != end_scalar) {
15029 ir_add_error(ira, &instruction->base, buf_sprintf("non-zero length slice of undefined pointer"));
1500915030 return ira->codegen->builtin_types.entry_invalid;
1501015031 }
1501115032
......@@ -15021,25 +15042,27 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
1502115042 if (array_type->id == TypeTableEntryIdArray) {
1502215043 ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut;
1502315044 }
15024 } else {
15025 switch (parent_ptr->data.x_ptr.special) {
15026 case ConstPtrSpecialInvalid:
15027 case ConstPtrSpecialDiscard:
15028 zig_unreachable();
15029 case ConstPtrSpecialRef:
15030 init_const_ptr_ref(ira->codegen, ptr_val,
15031 parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));
15032 break;
15033 case ConstPtrSpecialBaseArray:
15034 zig_unreachable();
15035 case ConstPtrSpecialBaseStruct:
15036 zig_panic("TODO");
15037 case ConstPtrSpecialHardCodedAddr:
15038 init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,
15039 parent_ptr->type->data.pointer.child_type,
15040 parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar,
15041 slice_is_const(return_type));
15042 }
15045 } else if (ptr_is_undef) {
15046 ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type,
15047 slice_is_const(return_type));
15048 ptr_val->special = ConstValSpecialUndef;
15049 } else switch (parent_ptr->data.x_ptr.special) {
15050 case ConstPtrSpecialInvalid:
15051 case ConstPtrSpecialDiscard:
15052 zig_unreachable();
15053 case ConstPtrSpecialRef:
15054 init_const_ptr_ref(ira->codegen, ptr_val,
15055 parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));
15056 break;
15057 case ConstPtrSpecialBaseArray:
15058 zig_unreachable();
15059 case ConstPtrSpecialBaseStruct:
15060 zig_panic("TODO");
15061 case ConstPtrSpecialHardCodedAddr:
15062 init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,
15063 parent_ptr->type->data.pointer.child_type,
15064 parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar,
15065 slice_is_const(return_type));
1504315066 }
1504415067
1504515068 ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index];
src/tokenizer.cpp-2
......@@ -125,7 +125,6 @@ static const struct ZigKeyword zig_keywords[] = {
125125 {"false", TokenIdKeywordFalse},
126126 {"fn", TokenIdKeywordFn},
127127 {"for", TokenIdKeywordFor},
128 {"goto", TokenIdKeywordGoto},
129128 {"if", TokenIdKeywordIf},
130129 {"inline", TokenIdKeywordInline},
131130 {"nakedcc", TokenIdKeywordNakedCC},
......@@ -1542,7 +1541,6 @@ const char * token_name(TokenId id) {
15421541 case TokenIdKeywordFalse: return "false";
15431542 case TokenIdKeywordFn: return "fn";
15441543 case TokenIdKeywordFor: return "for";
1545 case TokenIdKeywordGoto: return "goto";
15461544 case TokenIdKeywordIf: return "if";
15471545 case TokenIdKeywordInline: return "inline";
15481546 case TokenIdKeywordNakedCC: return "nakedcc";
src/tokenizer.hpp-1
......@@ -66,7 +66,6 @@ enum TokenId {
6666 TokenIdKeywordFalse,
6767 TokenIdKeywordFn,
6868 TokenIdKeywordFor,
69 TokenIdKeywordGoto,
7069 TokenIdKeywordIf,
7170 TokenIdKeywordInline,
7271 TokenIdKeywordNakedCC,
std/debug/index.zig+94-59
......@@ -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
......@@ -47,7 +48,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {
4748pub fn dumpCurrentStackTrace() void {
4849 const stderr = getStderrStream() catch return;
4950 const debug_info = getSelfDebugInfo() catch |err| {
50 stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
51 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
5152 return;
5253 };
5354 defer debug_info.close();
......@@ -61,7 +62,7 @@ pub fn dumpCurrentStackTrace() void {
6162pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
6263 const stderr = getStderrStream() catch return;
6364 const debug_info = getSelfDebugInfo() catch |err| {
64 stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
65 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
6566 return;
6667 };
6768 defer debug_info.close();
......@@ -180,43 +181,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
180181}
181182
182183fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
183 if (builtin.os == builtin.Os.windows) {
184 return error.UnsupportedDebugInfo;
185 }
186184 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
187185 // at compile time. I'll call it issue #313
188186 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
189187
190 const compile_unit = findCompileUnit(debug_info, address) catch {
191 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
192 address);
193 return;
194 };
195 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
196 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
197 defer line_info.deinit();
198 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
199 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
200 line_info.file_name, line_info.line, line_info.column,
201 address, compile_unit_name);
202 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
203 if (line_info.column == 0) {
204 try out_stream.write("\n");
205 } else {
206 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
207 try out_stream.writeByte(' ');
208 }}
209 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
188 switch (builtin.os) {
189 builtin.Os.windows => return error.UnsupportedDebugInfo,
190 builtin.Os.macosx => {
191 // TODO(bnoordhuis) It's theoretically possible to obtain the
192 // compilation unit from the symbtab but it's not that useful
193 // in practice because the compiler dumps everything in a single
194 // object file. Future improvement: use external dSYM data when
195 // available.
196 const unknown = macho.Symbol { .name = "???", .address = address };
197 const symbol = debug_info.symbol_table.search(address) ?? &unknown;
198 try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++
199 DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n",
200 symbol.name, address);
201 },
202 else => {
203 const compile_unit = findCompileUnit(debug_info, address) catch {
204 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
205 address);
206 return;
207 };
208 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
209 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
210 defer line_info.deinit();
211 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
212 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
213 line_info.file_name, line_info.line, line_info.column,
214 address, compile_unit_name);
215 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
216 if (line_info.column == 0) {
217 try out_stream.write("\n");
218 } else {
219 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
220 try out_stream.writeByte(' ');
221 }}
222 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
223 }
224 } else |err| switch (err) {
225 error.EndOfFile => {},
226 else => return err,
227 }
228 } else |err| switch (err) {
229 error.MissingDebugInfo, error.InvalidDebugInfo => {
230 try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
231 },
232 else => return err,
210233 }
211 } else |err| switch (err) {
212 error.EndOfFile => {},
213 else => return err,
214 }
215 } else |err| switch (err) {
216 error.MissingDebugInfo, error.InvalidDebugInfo => {
217 try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
218234 },
219 else => return err,
220235 }
221236}
222237
......@@ -224,6 +239,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
224239 switch (builtin.object_format) {
225240 builtin.ObjectFormat.elf => {
226241 const st = try allocator.create(ElfStackTrace);
242 errdefer allocator.destroy(st);
227243 *st = ElfStackTrace {
228244 .self_exe_file = undefined,
229245 .elf = undefined,
......@@ -249,12 +265,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
249265 try scanAllCompileUnits(st);
250266 return st;
251267 },
268 builtin.ObjectFormat.macho => {
269 var exe_file = try os.openSelfExe();
270 defer exe_file.close();
271
272 const st = try allocator.create(ElfStackTrace);
273 errdefer allocator.destroy(st);
274
275 *st = ElfStackTrace {
276 .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)),
277 };
278
279 return st;
280 },
252281 builtin.ObjectFormat.coff => {
253282 return error.TodoSupportCoffDebugInfo;
254283 },
255 builtin.ObjectFormat.macho => {
256 return error.TodoSupportMachoDebugInfo;
257 },
258284 builtin.ObjectFormat.wasm => {
259285 return error.TodoSupportCOFFDebugInfo;
260286 },
......@@ -297,31 +323,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con
297323 }
298324}
299325
300pub const ElfStackTrace = struct {
301 self_exe_file: os.File,
302 elf: elf.Elf,
303 debug_info: &elf.SectionHeader,
304 debug_abbrev: &elf.SectionHeader,
305 debug_str: &elf.SectionHeader,
306 debug_line: &elf.SectionHeader,
307 debug_ranges: ?&elf.SectionHeader,
308 abbrev_table_list: ArrayList(AbbrevTableHeader),
309 compile_unit_list: ArrayList(CompileUnit),
310
311 pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
312 return self.abbrev_table_list.allocator;
313 }
326pub const ElfStackTrace = switch (builtin.os) {
327 builtin.Os.macosx => struct {
328 symbol_table: macho.SymbolTable,
314329
315 pub fn readString(self: &ElfStackTrace) ![]u8 {
316 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
317 const in_stream = &in_file_stream.stream;
318 return readStringRaw(self.allocator(), in_stream);
319 }
330 pub fn close(self: &ElfStackTrace) void {
331 self.symbol_table.deinit();
332 }
333 },
334 else => struct {
335 self_exe_file: os.File,
336 elf: elf.Elf,
337 debug_info: &elf.SectionHeader,
338 debug_abbrev: &elf.SectionHeader,
339 debug_str: &elf.SectionHeader,
340 debug_line: &elf.SectionHeader,
341 debug_ranges: ?&elf.SectionHeader,
342 abbrev_table_list: ArrayList(AbbrevTableHeader),
343 compile_unit_list: ArrayList(CompileUnit),
344
345 pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
346 return self.abbrev_table_list.allocator;
347 }
320348
321 pub fn close(self: &ElfStackTrace) void {
322 self.self_exe_file.close();
323 self.elf.close();
324 }
349 pub fn readString(self: &ElfStackTrace) ![]u8 {
350 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
351 const in_stream = &in_file_stream.stream;
352 return readStringRaw(self.allocator(), in_stream);
353 }
354
355 pub fn close(self: &ElfStackTrace) void {
356 self.self_exe_file.close();
357 self.elf.close();
358 }
359 },
325360};
326361
327362const 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/mem.zig+7
......@@ -42,6 +42,9 @@ pub const Allocator = struct {
4242 fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
4343 n: usize) ![]align(alignment) T
4444 {
45 if (n == 0) {
46 return (&align(alignment) T)(undefined)[0..0];
47 }
4548 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
4649 const byte_slice = try self.allocFn(self, byte_count, alignment);
4750 assert(byte_slice.len == byte_count);
......@@ -62,6 +65,10 @@ pub const Allocator = struct {
6265 if (old_mem.len == 0) {
6366 return self.alloc(T, n);
6467 }
68 if (n == 0) {
69 self.free(old_mem);
70 return (&align(alignment) T)(undefined)[0..0];
71 }
6572
6673 const old_byte_slice = ([]u8)(old_mem);
6774 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
std/os/linux/index.zig+15-4
......@@ -329,6 +329,8 @@ pub const TIOCGPKT = 0x80045438;
329329pub const TIOCGPTLCK = 0x80045439;
330330pub const TIOCGEXCL = 0x80045440;
331331
332pub const EPOLL_CLOEXEC = O_CLOEXEC;
333
332334pub const EPOLL_CTL_ADD = 1;
333335pub const EPOLL_CTL_DEL = 2;
334336pub const EPOLL_CTL_MOD = 3;
......@@ -751,22 +753,31 @@ pub fn fstat(fd: i32, stat_buf: &Stat) usize {
751753 return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf));
752754}
753755
754pub const epoll_data = u64;
756pub const epoll_data = extern union {
757 ptr: usize,
758 fd: i32,
759 @"u32": u32,
760 @"u64": u64,
761};
755762
756763pub const epoll_event = extern struct {
757764 events: u32,
758 data: epoll_data
765 data: epoll_data,
759766};
760767
761768pub fn epoll_create() usize {
762 return arch.syscall1(arch.SYS_epoll_create, usize(1));
769 return epoll_create1(0);
770}
771
772pub fn epoll_create1(flags: usize) usize {
773 return arch.syscall1(arch.SYS_epoll_create1, flags);
763774}
764775
765776pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
766777 return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
767778}
768779
769pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: i32, timeout: i32) usize {
780pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: u32, timeout: i32) usize {
770781 return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
771782}
772783
std/os/linux/test.zig+1-1
......@@ -25,7 +25,7 @@ test "timer" {
2525
2626 var event = linux.epoll_event {
2727 .events = linux.EPOLLIN | linux.EPOLLOUT | linux.EPOLLET,
28 .data = 0
28 .data = linux.epoll_data { .ptr = 0 },
2929 };
3030
3131 err = linux.epoll_ctl(i32(epoll_fd), linux.EPOLL_CTL_ADD, i32(timer_fd), &event);
std/zig/ast.zig+211-6
......@@ -6,6 +6,7 @@ const mem = std.mem;
66
77pub const Node = struct {
88 id: Id,
9 comment: ?&NodeLineComment,
910
1011 pub const Id = enum {
1112 Root,
......@@ -18,7 +19,9 @@ pub const Node = struct {
1819 PrefixOp,
1920 IntegerLiteral,
2021 FloatLiteral,
22 StringLiteral,
2123 BuiltinCall,
24 LineComment,
2225 };
2326
2427 pub fn iterate(base: &Node, index: usize) ?&Node {
......@@ -33,7 +36,45 @@ pub const Node = struct {
3336 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
3437 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
3538 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
39 Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index),
3640 Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
41 Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
42 };
43 }
44
45 pub fn firstToken(base: &Node) Token {
46 return switch (base.id) {
47 Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),
48 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).firstToken(),
49 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).firstToken(),
50 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
51 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
52 Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
53 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
54 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
55 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
56 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
57 Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).firstToken(),
58 Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
59 Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
60 };
61 }
62
63 pub fn lastToken(base: &Node) Token {
64 return switch (base.id) {
65 Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),
66 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).lastToken(),
67 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).lastToken(),
68 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
69 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
70 Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
71 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
72 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
73 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
74 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
75 Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).lastToken(),
76 Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
77 Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
3778 };
3879 }
3980};
......@@ -41,6 +82,7 @@ pub const Node = struct {
4182pub const NodeRoot = struct {
4283 base: Node,
4384 decls: ArrayList(&Node),
85 eof_token: Token,
4486
4587 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {
4688 if (index < self.decls.len) {
......@@ -48,6 +90,14 @@ pub const NodeRoot = struct {
4890 }
4991 return null;
5092 }
93
94 pub fn firstToken(self: &NodeRoot) Token {
95 return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken();
96 }
97
98 pub fn lastToken(self: &NodeRoot) Token {
99 return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken();
100 }
51101};
52102
53103pub const NodeVarDecl = struct {
......@@ -62,6 +112,7 @@ pub const NodeVarDecl = struct {
62112 type_node: ?&Node,
63113 align_node: ?&Node,
64114 init_node: ?&Node,
115 semicolon_token: Token,
65116
66117 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
67118 var i = index;
......@@ -83,6 +134,18 @@ pub const NodeVarDecl = struct {
83134
84135 return null;
85136 }
137
138 pub fn firstToken(self: &NodeVarDecl) Token {
139 if (self.visib_token) |visib_token| return visib_token;
140 if (self.comptime_token) |comptime_token| return comptime_token;
141 if (self.extern_token) |extern_token| return extern_token;
142 assert(self.lib_name == null);
143 return self.mut_token;
144 }
145
146 pub fn lastToken(self: &NodeVarDecl) Token {
147 return self.semicolon_token;
148 }
86149};
87150
88151pub const NodeIdentifier = struct {
......@@ -92,6 +155,14 @@ pub const NodeIdentifier = struct {
92155 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
93156 return null;
94157 }
158
159 pub fn firstToken(self: &NodeIdentifier) Token {
160 return self.name_token;
161 }
162
163 pub fn lastToken(self: &NodeIdentifier) Token {
164 return self.name_token;
165 }
95166};
96167
97168pub const NodeFnProto = struct {
......@@ -100,7 +171,7 @@ pub const NodeFnProto = struct {
100171 fn_token: Token,
101172 name_token: ?Token,
102173 params: ArrayList(&Node),
103 return_type: &Node,
174 return_type: ReturnType,
104175 var_args_token: ?Token,
105176 extern_token: ?Token,
106177 inline_token: ?Token,
......@@ -109,6 +180,12 @@ pub const NodeFnProto = struct {
109180 lib_name: ?&Node, // populated if this is an extern declaration
110181 align_expr: ?&Node, // populated if align(A) is present
111182
183 pub const ReturnType = union(enum) {
184 Explicit: &Node,
185 Infer: Token,
186 InferErrorSet: &Node,
187 };
188
112189 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
113190 var i = index;
114191
......@@ -117,8 +194,18 @@ pub const NodeFnProto = struct {
117194 i -= 1;
118195 }
119196
120 if (i < 1) return self.return_type;
121 i -= 1;
197 switch (self.return_type) {
198 // TODO allow this and next prong to share bodies since the types are the same
199 ReturnType.Explicit => |node| {
200 if (i < 1) return node;
201 i -= 1;
202 },
203 ReturnType.InferErrorSet => |node| {
204 if (i < 1) return node;
205 i -= 1;
206 },
207 ReturnType.Infer => {},
208 }
122209
123210 if (self.align_expr) |align_expr| {
124211 if (i < 1) return align_expr;
......@@ -135,6 +222,25 @@ pub const NodeFnProto = struct {
135222
136223 return null;
137224 }
225
226 pub fn firstToken(self: &NodeFnProto) Token {
227 if (self.visib_token) |visib_token| return visib_token;
228 if (self.extern_token) |extern_token| return extern_token;
229 assert(self.lib_name == null);
230 if (self.inline_token) |inline_token| return inline_token;
231 if (self.cc_token) |cc_token| return cc_token;
232 return self.fn_token;
233 }
234
235 pub fn lastToken(self: &NodeFnProto) Token {
236 if (self.body_node) |body_node| return body_node.lastToken();
237 switch (self.return_type) {
238 // TODO allow this and next prong to share bodies since the types are the same
239 ReturnType.Explicit => |node| return node.lastToken(),
240 ReturnType.InferErrorSet => |node| return node.lastToken(),
241 ReturnType.Infer => |token| return token,
242 }
243 }
138244};
139245
140246pub const NodeParamDecl = struct {
......@@ -153,6 +259,18 @@ pub const NodeParamDecl = struct {
153259
154260 return null;
155261 }
262
263 pub fn firstToken(self: &NodeParamDecl) Token {
264 if (self.comptime_token) |comptime_token| return comptime_token;
265 if (self.noalias_token) |noalias_token| return noalias_token;
266 if (self.name_token) |name_token| return name_token;
267 return self.type_node.firstToken();
268 }
269
270 pub fn lastToken(self: &NodeParamDecl) Token {
271 if (self.var_args_token) |var_args_token| return var_args_token;
272 return self.type_node.lastToken();
273 }
156274};
157275
158276pub const NodeBlock = struct {
......@@ -169,6 +287,14 @@ pub const NodeBlock = struct {
169287
170288 return null;
171289 }
290
291 pub fn firstToken(self: &NodeBlock) Token {
292 return self.begin_token;
293 }
294
295 pub fn lastToken(self: &NodeBlock) Token {
296 return self.end_token;
297 }
172298};
173299
174300pub const NodeInfixOp = struct {
......@@ -181,6 +307,7 @@ pub const NodeInfixOp = struct {
181307 const InfixOp = enum {
182308 EqualEqual,
183309 BangEqual,
310 Period,
184311 };
185312
186313 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
......@@ -190,8 +317,9 @@ pub const NodeInfixOp = struct {
190317 i -= 1;
191318
192319 switch (self.op) {
193 InfixOp.EqualEqual => {},
194 InfixOp.BangEqual => {},
320 InfixOp.EqualEqual,
321 InfixOp.BangEqual,
322 InfixOp.Period => {},
195323 }
196324
197325 if (i < 1) return self.rhs;
......@@ -199,6 +327,14 @@ pub const NodeInfixOp = struct {
199327
200328 return null;
201329 }
330
331 pub fn firstToken(self: &NodeInfixOp) Token {
332 return self.lhs.firstToken();
333 }
334
335 pub fn lastToken(self: &NodeInfixOp) Token {
336 return self.rhs.lastToken();
337 }
202338};
203339
204340pub const NodePrefixOp = struct {
......@@ -209,6 +345,7 @@ pub const NodePrefixOp = struct {
209345
210346 const PrefixOp = union(enum) {
211347 Return,
348 Try,
212349 AddrOf: AddrOfInfo,
213350 };
214351 const AddrOfInfo = struct {
......@@ -223,7 +360,8 @@ pub const NodePrefixOp = struct {
223360 var i = index;
224361
225362 switch (self.op) {
226 PrefixOp.Return => {},
363 PrefixOp.Return,
364 PrefixOp.Try => {},
227365 PrefixOp.AddrOf => |addr_of_info| {
228366 if (addr_of_info.align_expr) |align_expr| {
229367 if (i < 1) return align_expr;
......@@ -237,6 +375,14 @@ pub const NodePrefixOp = struct {
237375
238376 return null;
239377 }
378
379 pub fn firstToken(self: &NodePrefixOp) Token {
380 return self.op_token;
381 }
382
383 pub fn lastToken(self: &NodePrefixOp) Token {
384 return self.rhs.lastToken();
385 }
240386};
241387
242388pub const NodeIntegerLiteral = struct {
......@@ -246,6 +392,14 @@ pub const NodeIntegerLiteral = struct {
246392 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {
247393 return null;
248394 }
395
396 pub fn firstToken(self: &NodeIntegerLiteral) Token {
397 return self.token;
398 }
399
400 pub fn lastToken(self: &NodeIntegerLiteral) Token {
401 return self.token;
402 }
249403};
250404
251405pub const NodeFloatLiteral = struct {
......@@ -255,12 +409,21 @@ pub const NodeFloatLiteral = struct {
255409 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {
256410 return null;
257411 }
412
413 pub fn firstToken(self: &NodeFloatLiteral) Token {
414 return self.token;
415 }
416
417 pub fn lastToken(self: &NodeFloatLiteral) Token {
418 return self.token;
419 }
258420};
259421
260422pub const NodeBuiltinCall = struct {
261423 base: Node,
262424 builtin_token: Token,
263425 params: ArrayList(&Node),
426 rparen_token: Token,
264427
265428 pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node {
266429 var i = index;
......@@ -270,4 +433,46 @@ pub const NodeBuiltinCall = struct {
270433
271434 return null;
272435 }
436
437 pub fn firstToken(self: &NodeBuiltinCall) Token {
438 return self.builtin_token;
439 }
440
441 pub fn lastToken(self: &NodeBuiltinCall) Token {
442 return self.rparen_token;
443 }
444};
445
446pub const NodeStringLiteral = struct {
447 base: Node,
448 token: Token,
449
450 pub fn iterate(self: &NodeStringLiteral, index: usize) ?&Node {
451 return null;
452 }
453
454 pub fn firstToken(self: &NodeStringLiteral) Token {
455 return self.token;
456 }
457
458 pub fn lastToken(self: &NodeStringLiteral) Token {
459 return self.token;
460 }
461};
462
463pub const NodeLineComment = struct {
464 base: Node,
465 lines: ArrayList(Token),
466
467 pub fn iterate(self: &NodeLineComment, index: usize) ?&Node {
468 return null;
469 }
470
471 pub fn firstToken(self: &NodeLineComment) Token {
472 return self.lines.at(0);
473 }
474
475 pub fn lastToken(self: &NodeLineComment) Token {
476 return self.lines.at(self.lines.len - 1);
477 }
273478};
std/zig/parser.zig+304-40
......@@ -18,6 +18,7 @@ pub const Parser = struct {
1818 put_back_tokens: [2]Token,
1919 put_back_count: usize,
2020 source_file_name: []const u8,
21 pending_line_comment_node: ?&ast.NodeLineComment,
2122
2223 pub const Tree = struct {
2324 root_node: &ast.NodeRoot,
......@@ -43,6 +44,7 @@ pub const Parser = struct {
4344 .put_back_count = 0,
4445 .source_file_name = source_file_name,
4546 .utility_bytes = []align(utility_bytes_align) u8{},
47 .pending_line_comment_node = null,
4648 };
4749 }
4850
......@@ -69,6 +71,11 @@ pub const Parser = struct {
6971 }
7072 };
7173
74 const ExpectTokenSave = struct {
75 id: Token.Id,
76 ptr: &Token,
77 };
78
7279 const State = union(enum) {
7380 TopLevel,
7481 TopLevelExtern: ?Token,
......@@ -85,13 +92,17 @@ pub const Parser = struct {
8592 VarDeclAlign: &ast.NodeVarDecl,
8693 VarDeclEq: &ast.NodeVarDecl,
8794 ExpectToken: @TagType(Token.Id),
95 ExpectTokenSave: ExpectTokenSave,
8896 FnProto: &ast.NodeFnProto,
8997 FnProtoAlign: &ast.NodeFnProto,
98 FnProtoReturnType: &ast.NodeFnProto,
9099 ParamDecl: &ast.NodeFnProto,
91100 ParamDeclComma,
92101 FnDef: &ast.NodeFnProto,
93102 Block: &ast.NodeBlock,
94103 Statement: &ast.NodeBlock,
104 ExprListItemOrEnd: &ArrayList(&ast.Node),
105 ExprListCommaOrEnd: &ArrayList(&ast.Node),
95106 };
96107
97108 /// Returns an AST tree, allocated with the parser's allocator.
......@@ -122,6 +133,33 @@ pub const Parser = struct {
122133 // warn("\n");
123134 //}
124135
136 // look for line comments
137 while (true) {
138 const token = self.getNextToken();
139 if (token.id == Token.Id.LineComment) {
140 const node = blk: {
141 if (self.pending_line_comment_node) |comment_node| {
142 break :blk comment_node;
143 } else {
144 const comment_node = try arena.create(ast.NodeLineComment);
145 *comment_node = ast.NodeLineComment {
146 .base = ast.Node {
147 .id = ast.Node.Id.LineComment,
148 .comment = null,
149 },
150 .lines = ArrayList(Token).init(arena),
151 };
152 self.pending_line_comment_node = comment_node;
153 break :blk comment_node;
154 }
155 };
156 try node.lines.append(token);
157 continue;
158 }
159 self.putBackToken(token);
160 break;
161 }
162
125163 // This gives us 1 free append that can't fail
126164 const state = stack.pop();
127165
......@@ -133,7 +171,10 @@ pub const Parser = struct {
133171 stack.append(State { .TopLevelExtern = token }) catch unreachable;
134172 continue;
135173 },
136 Token.Id.Eof => return Tree {.root_node = root_node, .arena_allocator = arena_allocator},
174 Token.Id.Eof => {
175 root_node.eof_token = token;
176 return Tree {.root_node = root_node, .arena_allocator = arena_allocator};
177 },
137178 else => {
138179 self.putBackToken(token);
139180 stack.append(State { .TopLevelExtern = null }) catch unreachable;
......@@ -176,7 +217,7 @@ pub const Parser = struct {
176217 stack.append(State.TopLevel) catch unreachable;
177218 // TODO shouldn't need these casts
178219 const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token,
179 ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
220 ctx.extern_token, (?Token)(null), ctx.visib_token, (?Token)(null));
180221 try stack.append(State { .FnDef = fn_proto });
181222 try stack.append(State { .FnProto = fn_proto });
182223 continue;
......@@ -228,13 +269,19 @@ pub const Parser = struct {
228269 const token = self.getNextToken();
229270 if (token.id == Token.Id.Equal) {
230271 var_decl.eq_token = token;
231 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
272 stack.append(State {
273 .ExpectTokenSave = ExpectTokenSave {
274 .id = Token.Id.Semicolon,
275 .ptr = &var_decl.semicolon_token,
276 },
277 }) catch unreachable;
232278 try stack.append(State {
233279 .Expression = DestPtr {.NullableField = &var_decl.init_node},
234280 });
235281 continue;
236282 }
237283 if (token.id == Token.Id.Semicolon) {
284 var_decl.semicolon_token = token;
238285 continue;
239286 }
240287 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
......@@ -244,6 +291,11 @@ pub const Parser = struct {
244291 continue;
245292 },
246293
294 State.ExpectTokenSave => |expect_token_save| {
295 *expect_token_save.ptr = try self.eatToken(expect_token_save.id);
296 continue;
297 },
298
247299 State.Expression => |dest_ptr| {
248300 // save the dest_ptr for later
249301 stack.append(state) catch unreachable;
......@@ -261,6 +313,12 @@ pub const Parser = struct {
261313 try stack.append(State.ExpectOperand);
262314 continue;
263315 },
316 Token.Id.Keyword_try => {
317 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
318 ast.NodePrefixOp.PrefixOp.Try) });
319 try stack.append(State.ExpectOperand);
320 continue;
321 },
264322 Token.Id.Ampersand => {
265323 const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
266324 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
......@@ -297,6 +355,40 @@ pub const Parser = struct {
297355 try stack.append(State.AfterOperand);
298356 continue;
299357 },
358 Token.Id.Builtin => {
359 const node = try arena.create(ast.NodeBuiltinCall);
360 *node = ast.NodeBuiltinCall {
361 .base = self.initNode(ast.Node.Id.BuiltinCall),
362 .builtin_token = token,
363 .params = ArrayList(&ast.Node).init(arena),
364 .rparen_token = undefined,
365 };
366 try stack.append(State {
367 .Operand = &node.base
368 });
369 try stack.append(State.AfterOperand);
370 try stack.append(State {.ExprListItemOrEnd = &node.params });
371 try stack.append(State {
372 .ExpectTokenSave = ExpectTokenSave {
373 .id = Token.Id.LParen,
374 .ptr = &node.rparen_token,
375 },
376 });
377 continue;
378 },
379 Token.Id.StringLiteral => {
380 const node = try arena.create(ast.NodeStringLiteral);
381 *node = ast.NodeStringLiteral {
382 .base = self.initNode(ast.Node.Id.StringLiteral),
383 .token = token,
384 };
385 try stack.append(State {
386 .Operand = &node.base
387 });
388 try stack.append(State.AfterOperand);
389 continue;
390 },
391
300392 else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
301393 }
302394 },
......@@ -321,6 +413,13 @@ pub const Parser = struct {
321413 try stack.append(State.ExpectOperand);
322414 continue;
323415 },
416 Token.Id.Period => {
417 try stack.append(State {
418 .InfixOp = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Period)
419 });
420 try stack.append(State.ExpectOperand);
421 continue;
422 },
324423 else => {
325424 // no postfix/infix operator after this operand.
326425 self.putBackToken(token);
......@@ -352,6 +451,29 @@ pub const Parser = struct {
352451 }
353452 },
354453
454 State.ExprListItemOrEnd => |params| {
455 var token = self.getNextToken();
456 switch (token.id) {
457 Token.Id.RParen => continue,
458 else => {
459 self.putBackToken(token);
460 stack.append(State { .ExprListCommaOrEnd = params }) catch unreachable;
461 try stack.append(State { .Expression = DestPtr{.List = params} });
462 },
463 }
464 },
465
466 State.ExprListCommaOrEnd => |params| {
467 var token = self.getNextToken();
468 switch (token.id) {
469 Token.Id.Comma => {
470 stack.append(State { .ExprListItemOrEnd = params }) catch unreachable;
471 },
472 Token.Id.RParen => continue,
473 else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
474 }
475 },
476
355477 State.AddrOfModifiers => |addr_of_info| {
356478 var token = self.getNextToken();
357479 switch (token.id) {
......@@ -414,11 +536,37 @@ pub const Parser = struct {
414536 }
415537 self.putBackToken(token);
416538 stack.append(State {
417 .TypeExpr = DestPtr {.Field = &fn_proto.return_type},
539 .FnProtoReturnType = fn_proto,
418540 }) catch unreachable;
419541 continue;
420542 },
421543
544 State.FnProtoReturnType => |fn_proto| {
545 const token = self.getNextToken();
546 switch (token.id) {
547 Token.Id.Keyword_var => {
548 fn_proto.return_type = ast.NodeFnProto.ReturnType { .Infer = token };
549 },
550 Token.Id.Bang => {
551 fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
552 stack.append(State {
553 .TypeExpr = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
554 }) catch unreachable;
555 },
556 else => {
557 self.putBackToken(token);
558 fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
559 stack.append(State {
560 .TypeExpr = DestPtr {.Field = &fn_proto.return_type.Explicit},
561 }) catch unreachable;
562 },
563 }
564 if (token.id == Token.Id.Keyword_align) {
565 @panic("TODO fn proto align");
566 }
567 continue;
568 },
569
422570 State.ParamDecl => |fn_proto| {
423571 var token = self.getNextToken();
424572 if (token.id == Token.Id.RParen) {
......@@ -539,17 +687,25 @@ pub const Parser = struct {
539687 State.PrefixOp => unreachable,
540688 State.Operand => unreachable,
541689 }
542 @import("std").debug.panic("{}", @tagName(state));
543 //unreachable;
544690 }
545691 }
546692
693 fn initNode(self: &Parser, id: ast.Node.Id) ast.Node {
694 if (self.pending_line_comment_node) |comment_node| {
695 self.pending_line_comment_node = null;
696 return ast.Node {.id = id, .comment = comment_node};
697 }
698 return ast.Node {.id = id, .comment = null };
699 }
700
547701 fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot {
548702 const node = try arena.create(ast.NodeRoot);
549703
550704 *node = ast.NodeRoot {
551 .base = ast.Node {.id = ast.Node.Id.Root},
705 .base = self.initNode(ast.Node.Id.Root),
552706 .decls = ArrayList(&ast.Node).init(arena),
707 // initialized when we get the eof token
708 .eof_token = undefined,
553709 };
554710 return node;
555711 }
......@@ -560,7 +716,7 @@ pub const Parser = struct {
560716 const node = try arena.create(ast.NodeVarDecl);
561717
562718 *node = ast.NodeVarDecl {
563 .base = ast.Node {.id = ast.Node.Id.VarDecl},
719 .base = self.initNode(ast.Node.Id.VarDecl),
564720 .visib_token = *visib_token,
565721 .mut_token = *mut_token,
566722 .comptime_token = *comptime_token,
......@@ -572,6 +728,7 @@ pub const Parser = struct {
572728 // initialized later
573729 .name_token = undefined,
574730 .eq_token = undefined,
731 .semicolon_token = undefined,
575732 };
576733 return node;
577734 }
......@@ -582,7 +739,7 @@ pub const Parser = struct {
582739 const node = try arena.create(ast.NodeFnProto);
583740
584741 *node = ast.NodeFnProto {
585 .base = ast.Node {.id = ast.Node.Id.FnProto},
742 .base = self.initNode(ast.Node.Id.FnProto),
586743 .visib_token = *visib_token,
587744 .name_token = null,
588745 .fn_token = *fn_token,
......@@ -603,7 +760,7 @@ pub const Parser = struct {
603760 const node = try arena.create(ast.NodeParamDecl);
604761
605762 *node = ast.NodeParamDecl {
606 .base = ast.Node {.id = ast.Node.Id.ParamDecl},
763 .base = self.initNode(ast.Node.Id.ParamDecl),
607764 .comptime_token = null,
608765 .noalias_token = null,
609766 .name_token = null,
......@@ -617,7 +774,7 @@ pub const Parser = struct {
617774 const node = try arena.create(ast.NodeBlock);
618775
619776 *node = ast.NodeBlock {
620 .base = ast.Node {.id = ast.Node.Id.Block},
777 .base = self.initNode(ast.Node.Id.Block),
621778 .begin_token = *begin_token,
622779 .end_token = undefined,
623780 .statements = ArrayList(&ast.Node).init(arena),
......@@ -629,7 +786,7 @@ pub const Parser = struct {
629786 const node = try arena.create(ast.NodeInfixOp);
630787
631788 *node = ast.NodeInfixOp {
632 .base = ast.Node {.id = ast.Node.Id.InfixOp},
789 .base = self.initNode(ast.Node.Id.InfixOp),
633790 .op_token = *op_token,
634791 .lhs = undefined,
635792 .op = *op,
......@@ -642,7 +799,7 @@ pub const Parser = struct {
642799 const node = try arena.create(ast.NodePrefixOp);
643800
644801 *node = ast.NodePrefixOp {
645 .base = ast.Node {.id = ast.Node.Id.PrefixOp},
802 .base = self.initNode(ast.Node.Id.PrefixOp),
646803 .op_token = *op_token,
647804 .op = *op,
648805 .rhs = undefined,
......@@ -654,7 +811,7 @@ pub const Parser = struct {
654811 const node = try arena.create(ast.NodeIdentifier);
655812
656813 *node = ast.NodeIdentifier {
657 .base = ast.Node {.id = ast.Node.Id.Identifier},
814 .base = self.initNode(ast.Node.Id.Identifier),
658815 .name_token = *name_token,
659816 };
660817 return node;
......@@ -664,7 +821,7 @@ pub const Parser = struct {
664821 const node = try arena.create(ast.NodeIntegerLiteral);
665822
666823 *node = ast.NodeIntegerLiteral {
667 .base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
824 .base = self.initNode(ast.Node.Id.IntegerLiteral),
668825 .token = *token,
669826 };
670827 return node;
......@@ -674,7 +831,7 @@ pub const Parser = struct {
674831 const node = try arena.create(ast.NodeFloatLiteral);
675832
676833 *node = ast.NodeFloatLiteral {
677 .base = ast.Node {.id = ast.Node.Id.FloatLiteral},
834 .base = self.initNode(ast.Node.Id.FloatLiteral),
678835 .token = *token,
679836 };
680837 return node;
......@@ -712,11 +869,11 @@ pub const Parser = struct {
712869
713870 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
714871 const loc = self.tokenizer.getTokenLocation(token);
715 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
872 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, token.line + 1, token.column + 1, args);
716873 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
717874 {
718875 var i: usize = 0;
719 while (i < loc.column) : (i += 1) {
876 while (i < token.column) : (i += 1) {
720877 warn(" ");
721878 }
722879 }
......@@ -808,11 +965,26 @@ pub const Parser = struct {
808965 defer self.deinitUtilityArrayList(stack);
809966
810967 {
968 try stack.append(RenderState { .Text = "\n"});
969
811970 var i = root_node.decls.len;
812971 while (i != 0) {
813972 i -= 1;
814973 const decl = root_node.decls.items[i];
815974 try stack.append(RenderState {.TopLevelDecl = decl});
975 if (i != 0) {
976 try stack.append(RenderState {
977 .Text = blk: {
978 const prev_node = root_node.decls.at(i - 1);
979 const prev_line_index = prev_node.lastToken().line;
980 const this_line_index = decl.firstToken().line;
981 if (this_line_index - prev_line_index >= 2) {
982 break :blk "\n\n";
983 }
984 break :blk "\n";
985 },
986 });
987 }
816988 }
817989 }
818990
......@@ -842,7 +1014,6 @@ pub const Parser = struct {
8421014
8431015 try stream.print("(");
8441016
845 try stack.append(RenderState { .Text = "\n" });
8461017 if (fn_proto.body_node == null) {
8471018 try stack.append(RenderState { .Text = ";" });
8481019 }
......@@ -860,7 +1031,6 @@ pub const Parser = struct {
8601031 },
8611032 ast.Node.Id.VarDecl => {
8621033 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
863 try stack.append(RenderState { .Text = "\n"});
8641034 try stack.append(RenderState { .VarDecl = var_decl});
8651035
8661036 },
......@@ -927,19 +1097,35 @@ pub const Parser = struct {
9271097 },
9281098 ast.Node.Id.Block => {
9291099 const block = @fieldParentPtr(ast.NodeBlock, "base", base);
930 try stream.write("{");
931 try stack.append(RenderState { .Text = "}"});
932 try stack.append(RenderState.PrintIndent);
933 try stack.append(RenderState { .Indent = indent});
934 try stack.append(RenderState { .Text = "\n"});
935 var i = block.statements.len;
936 while (i != 0) {
937 i -= 1;
938 const statement_node = block.statements.items[i];
939 try stack.append(RenderState { .Statement = statement_node});
1100 if (block.statements.len == 0) {
1101 try stream.write("{}");
1102 } else {
1103 try stream.write("{");
1104 try stack.append(RenderState { .Text = "}"});
9401105 try stack.append(RenderState.PrintIndent);
941 try stack.append(RenderState { .Indent = indent + indent_delta});
942 try stack.append(RenderState { .Text = "\n" });
1106 try stack.append(RenderState { .Indent = indent});
1107 try stack.append(RenderState { .Text = "\n"});
1108 var i = block.statements.len;
1109 while (i != 0) {
1110 i -= 1;
1111 const statement_node = block.statements.items[i];
1112 try stack.append(RenderState { .Statement = statement_node});
1113 try stack.append(RenderState.PrintIndent);
1114 try stack.append(RenderState { .Indent = indent + indent_delta});
1115 try stack.append(RenderState {
1116 .Text = blk: {
1117 if (i != 0) {
1118 const prev_statement_node = block.statements.items[i - 1];
1119 const prev_line_index = prev_statement_node.lastToken().line;
1120 const this_line_index = statement_node.firstToken().line;
1121 if (this_line_index - prev_line_index >= 2) {
1122 break :blk "\n\n";
1123 }
1124 }
1125 break :blk "\n";
1126 },
1127 });
1128 }
9431129 }
9441130 },
9451131 ast.Node.Id.InfixOp => {
......@@ -952,7 +1138,9 @@ pub const Parser = struct {
9521138 ast.NodeInfixOp.InfixOp.BangEqual => {
9531139 try stack.append(RenderState { .Text = " != "});
9541140 },
955 else => unreachable,
1141 ast.NodeInfixOp.InfixOp.Period => {
1142 try stack.append(RenderState { .Text = "."});
1143 },
9561144 }
9571145 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
9581146 },
......@@ -963,6 +1151,9 @@ pub const Parser = struct {
9631151 ast.NodePrefixOp.PrefixOp.Return => {
9641152 try stream.write("return ");
9651153 },
1154 ast.NodePrefixOp.PrefixOp.Try => {
1155 try stream.write("try ");
1156 },
9661157 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
9671158 try stream.write("&");
9681159 if (addr_of_info.volatile_token != null) {
......@@ -977,7 +1168,6 @@ pub const Parser = struct {
9771168 try stack.append(RenderState { .Expression = align_expr});
9781169 }
9791170 },
980 else => unreachable,
9811171 }
9821172 },
9831173 ast.Node.Id.IntegerLiteral => {
......@@ -988,7 +1178,30 @@ pub const Parser = struct {
9881178 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
9891179 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
9901180 },
991 else => unreachable,
1181 ast.Node.Id.StringLiteral => {
1182 const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base);
1183 try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
1184 },
1185 ast.Node.Id.BuiltinCall => {
1186 const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base);
1187 try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token));
1188 try stack.append(RenderState { .Text = ")"});
1189 var i = builtin_call.params.len;
1190 while (i != 0) {
1191 i -= 1;
1192 const param_node = builtin_call.params.at(i);
1193 try stack.append(RenderState { .Expression = param_node});
1194 if (i != 0) {
1195 try stack.append(RenderState { .Text = ", " });
1196 }
1197 }
1198 },
1199 ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
1200 ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
1201
1202 ast.Node.Id.Root,
1203 ast.Node.Id.VarDecl,
1204 ast.Node.Id.ParamDecl => unreachable,
9921205 },
9931206 RenderState.FnProtoRParen => |fn_proto| {
9941207 try stream.print(")");
......@@ -1000,9 +1213,26 @@ pub const Parser = struct {
10001213 try stack.append(RenderState { .Expression = body_node});
10011214 try stack.append(RenderState { .Text = " "});
10021215 }
1003 try stack.append(RenderState { .Expression = fn_proto.return_type});
1216 switch (fn_proto.return_type) {
1217 ast.NodeFnProto.ReturnType.Explicit => |node| {
1218 try stack.append(RenderState { .Expression = node});
1219 },
1220 ast.NodeFnProto.ReturnType.Infer => {
1221 try stream.print("var");
1222 },
1223 ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
1224 try stream.print("!");
1225 try stack.append(RenderState { .Expression = node});
1226 },
1227 }
10041228 },
10051229 RenderState.Statement => |base| {
1230 if (base.comment) |comment| {
1231 for (comment.lines.toSliceConst()) |line_token| {
1232 try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
1233 try stream.writeByteNTimes(' ', indent);
1234 }
1235 }
10061236 switch (base.id) {
10071237 ast.Node.Id.VarDecl => {
10081238 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
......@@ -1040,10 +1270,7 @@ pub const Parser = struct {
10401270var fixed_buffer_mem: [100 * 1024]u8 = undefined;
10411271
10421272fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
1043 var padded_source: [0x100]u8 = undefined;
1044 std.mem.copy(u8, padded_source[0..source.len], source);
1045
1046 var tokenizer = Tokenizer.init(padded_source[0..source.len]);
1273 var tokenizer = Tokenizer.init(source);
10471274 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
10481275 defer parser.deinit();
10491276
......@@ -1098,6 +1325,43 @@ fn testCanonical(source: []const u8) !void {
10981325}
10991326
11001327test "zig fmt" {
1328 try testCanonical(
1329 \\const std = @import("std");
1330 \\
1331 \\pub fn main() !void {
1332 \\ // If this program is run without stdout attached, exit with an error.
1333 \\ // another comment
1334 \\ var stdout_file = try std.io.getStdOut;
1335 \\}
1336 \\
1337 );
1338
1339 try testCanonical(
1340 \\const std = @import("std");
1341 \\
1342 \\pub fn main() !void {
1343 \\ var stdout_file = try std.io.getStdOut;
1344 \\ var stdout_file = try std.io.getStdOut;
1345 \\
1346 \\ var stdout_file = try std.io.getStdOut;
1347 \\ var stdout_file = try std.io.getStdOut;
1348 \\}
1349 \\
1350 );
1351
1352 try testCanonical(
1353 \\pub fn main() !void {}
1354 \\pub fn main() var {}
1355 \\pub fn main() i32 {}
1356 \\
1357 );
1358
1359 try testCanonical(
1360 \\const std = @import("std");
1361 \\const std = @import();
1362 \\
1363 );
1364
11011365 try testCanonical(
11021366 \\extern fn puts(s: &const u8) c_int;
11031367 \\
std/zig/tokenizer.zig+39-25
......@@ -5,6 +5,8 @@ pub const Token = struct {
55 id: Id,
66 start: usize,
77 end: usize,
8 line: usize,
9 column: usize,
810
911 const KeywordId = struct {
1012 bytes: []const u8,
......@@ -16,6 +18,7 @@ pub const Token = struct {
1618 KeywordId{.bytes="and", .id = Id.Keyword_and},
1719 KeywordId{.bytes="asm", .id = Id.Keyword_asm},
1820 KeywordId{.bytes="break", .id = Id.Keyword_break},
21 KeywordId{.bytes="catch", .id = Id.Keyword_catch},
1922 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
2023 KeywordId{.bytes="const", .id = Id.Keyword_const},
2124 KeywordId{.bytes="continue", .id = Id.Keyword_continue},
......@@ -28,7 +31,6 @@ pub const Token = struct {
2831 KeywordId{.bytes="false", .id = Id.Keyword_false},
2932 KeywordId{.bytes="fn", .id = Id.Keyword_fn},
3033 KeywordId{.bytes="for", .id = Id.Keyword_for},
31 KeywordId{.bytes="goto", .id = Id.Keyword_goto},
3234 KeywordId{.bytes="if", .id = Id.Keyword_if},
3335 KeywordId{.bytes="inline", .id = Id.Keyword_inline},
3436 KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
......@@ -38,12 +40,14 @@ pub const Token = struct {
3840 KeywordId{.bytes="packed", .id = Id.Keyword_packed},
3941 KeywordId{.bytes="pub", .id = Id.Keyword_pub},
4042 KeywordId{.bytes="return", .id = Id.Keyword_return},
43 KeywordId{.bytes="section", .id = Id.Keyword_section},
4144 KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
4245 KeywordId{.bytes="struct", .id = Id.Keyword_struct},
4346 KeywordId{.bytes="switch", .id = Id.Keyword_switch},
4447 KeywordId{.bytes="test", .id = Id.Keyword_test},
4548 KeywordId{.bytes="this", .id = Id.Keyword_this},
4649 KeywordId{.bytes="true", .id = Id.Keyword_true},
50 KeywordId{.bytes="try", .id = Id.Keyword_try},
4751 KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
4852 KeywordId{.bytes="union", .id = Id.Keyword_union},
4953 KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
......@@ -95,10 +99,12 @@ pub const Token = struct {
9599 AmpersandEqual,
96100 IntegerLiteral,
97101 FloatLiteral,
102 LineComment,
98103 Keyword_align,
99104 Keyword_and,
100105 Keyword_asm,
101106 Keyword_break,
107 Keyword_catch,
102108 Keyword_comptime,
103109 Keyword_const,
104110 Keyword_continue,
......@@ -111,7 +117,6 @@ pub const Token = struct {
111117 Keyword_false,
112118 Keyword_fn,
113119 Keyword_for,
114 Keyword_goto,
115120 Keyword_if,
116121 Keyword_inline,
117122 Keyword_nakedcc,
......@@ -121,12 +126,14 @@ pub const Token = struct {
121126 Keyword_packed,
122127 Keyword_pub,
123128 Keyword_return,
129 Keyword_section,
124130 Keyword_stdcallcc,
125131 Keyword_struct,
126132 Keyword_switch,
127133 Keyword_test,
128134 Keyword_this,
129135 Keyword_true,
136 Keyword_try,
130137 Keyword_undefined,
131138 Keyword_union,
132139 Keyword_unreachable,
......@@ -140,21 +147,19 @@ pub const Token = struct {
140147pub const Tokenizer = struct {
141148 buffer: []const u8,
142149 index: usize,
150 line: usize,
151 column: usize,
143152 pending_invalid_token: ?Token,
144153
145 pub const Location = struct {
146 line: usize,
147 column: usize,
154 pub const LineLocation = struct {
148155 line_start: usize,
149156 line_end: usize,
150157 };
151158
152 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
153 var loc = Location {
154 .line = 0,
155 .column = 0,
159 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) LineLocation {
160 var loc = LineLocation {
156161 .line_start = 0,
157 .line_end = 0,
162 .line_end = self.buffer.len,
158163 };
159164 for (self.buffer) |c, i| {
160165 if (i == token.start) {
......@@ -163,11 +168,7 @@ pub const Tokenizer = struct {
163168 return loc;
164169 }
165170 if (c == '\n') {
166 loc.line += 1;
167 loc.column = 0;
168171 loc.line_start = i + 1;
169 } else {
170 loc.column += 1;
171172 }
172173 }
173174 return loc;
......@@ -182,6 +183,8 @@ pub const Tokenizer = struct {
182183 return Tokenizer {
183184 .buffer = buffer,
184185 .index = 0,
186 .line = 0,
187 .column = 0,
185188 .pending_invalid_token = null,
186189 };
187190 }
......@@ -222,13 +225,21 @@ pub const Tokenizer = struct {
222225 .id = Token.Id.Eof,
223226 .start = self.index,
224227 .end = undefined,
228 .line = self.line,
229 .column = self.column,
225230 };
226 while (self.index < self.buffer.len) : (self.index += 1) {
231 while (self.index < self.buffer.len) {
227232 const c = self.buffer[self.index];
228233 switch (state) {
229234 State.Start => switch (c) {
230 ' ', '\n' => {
235 ' ' => {
236 result.start = self.index + 1;
237 result.column += 1;
238 },
239 '\n' => {
231240 result.start = self.index + 1;
241 result.line += 1;
242 result.column = 0;
232243 },
233244 'c' => {
234245 state = State.C;
......@@ -460,7 +471,7 @@ pub const Tokenizer = struct {
460471
461472 State.Slash => switch (c) {
462473 '/' => {
463 result.id = undefined;
474 result.id = Token.Id.LineComment;
464475 state = State.LineComment;
465476 },
466477 else => {
......@@ -469,14 +480,7 @@ pub const Tokenizer = struct {
469480 },
470481 },
471482 State.LineComment => switch (c) {
472 '\n' => {
473 state = State.Start;
474 result = Token {
475 .id = Token.Id.Eof,
476 .start = self.index + 1,
477 .end = undefined,
478 };
479 },
483 '\n' => break,
480484 else => self.checkLiteralCharacter(),
481485 },
482486 State.Zero => switch (c) {
......@@ -543,6 +547,14 @@ pub const Tokenizer = struct {
543547 else => break,
544548 },
545549 }
550
551 self.index += 1;
552 if (c == '\n') {
553 self.line += 1;
554 self.column = 0;
555 } else {
556 self.column += 1;
557 }
546558 } else if (self.index == self.buffer.len) {
547559 switch (state) {
548560 State.Start,
......@@ -622,6 +634,8 @@ pub const Tokenizer = struct {
622634 .id = Token.Id.Invalid,
623635 .start = self.index,
624636 .end = self.index + invalid_length,
637 .line = self.line,
638 .column = self.column,
625639 };
626640 }
627641
test/behavior.zig+1
......@@ -35,6 +35,7 @@ comptime {
3535 _ = @import("cases/slice.zig");
3636 _ = @import("cases/struct.zig");
3737 _ = @import("cases/struct_contains_slice_of_itself.zig");
38 _ = @import("cases/struct_contains_null_ptr_itself.zig");
3839 _ = @import("cases/switch.zig");
3940 _ = @import("cases/switch_prong_err_enum.zig");
4041 _ = @import("cases/switch_prong_implicit_cast.zig");
test/cases/eval.zig+7
......@@ -388,3 +388,10 @@ test "string literal used as comptime slice is memoized" {
388388 comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node);
389389 comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node);
390390}
391
392test "comptime slice of undefined pointer of length 0" {
393 const slice1 = (&i32)(undefined)[0..0];
394 assert(slice1.len == 0);
395 const slice2 = (&i32)(undefined)[100..100];
396 assert(slice2.len == 0);
397}
test/cases/math.zig+8-1
......@@ -394,4 +394,11 @@ fn test_f128() void {
394394
395395fn should_not_be_zero(x: f128) void {
396396 assert(x != 0.0);
397}
\ No newline at end of file
397}
398
399test "comptime float rem int" {
400 comptime {
401 var x = f32(1) % 2;
402 assert(x == 1.0);
403 }
404}
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/cases/struct_contains_null_ptr_itself.zig created+22
......@@ -0,0 +1,22 @@
1const std = @import("std");
2const assert = std.debug.assert;
3
4test "struct contains null pointer which contains original struct" {
5 var x: ?&NodeLineComment = null;
6 assert(x == null);
7}
8
9pub const Node = struct {
10 id: Id,
11 comment: ?&NodeLineComment,
12
13 pub const Id = enum {
14 Root,
15 LineComment,
16 };
17};
18
19pub const NodeLineComment = struct {
20 base: Node,
21};
22
test/compile_errors.zig+20
......@@ -1,6 +1,26 @@
11const tests = @import("tests.zig");
22
33pub fn addCases(cases: &tests.CompileErrorContext) void {
4 cases.add("comptime slice of undefined pointer non-zero len",
5 \\export fn entry() void {
6 \\ const slice = (&i32)(undefined)[0..1];
7 \\}
8 ,
9 ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer");
10
11 cases.add("type checking function pointers",
12 \\fn a(b: fn (&const u8) void) void {
13 \\ b('a');
14 \\}
15 \\fn c(d: u8) void {
16 \\ @import("std").debug.warn("{c}\n", d);
17 \\}
18 \\export fn entry() void {
19 \\ a(c);
20 \\}
21 ,
22 ".tmp_source.zig:8:7: error: expected type 'fn(&const u8) void', found 'fn(u8) void'");
23
424 cases.add("no else prong on switch on global error set",
525 \\export fn entry() void {
626 \\ foo(error.A);