authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-05-01 14:29:50-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-05-01 14:29:50-04:00
log1090b289eca8dadba482f176f19364c172d01f2e
treecb081f933826c09675a97a4d4040dea615728634
parent4d0b660f4bcde2544f3096225ec28f7940162c30
parent3a8dc4e90ddf6b3dc2bdf640c89061c00eee7d45

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


33 files changed, 3540 insertions(+), 1593 deletions(-)

CMakeLists.txt+3
...@@ -415,6 +415,9 @@ set(ZIG_CPP_SOURCES...@@ -415,6 +415,9 @@ set(ZIG_CPP_SOURCES
415415
416set(ZIG_STD_FILES416set(ZIG_STD_FILES
417 "array_list.zig"417 "array_list.zig"
418 "atomic/index.zig"
419 "atomic/stack.zig"
420 "atomic/queue.zig"
418 "base64.zig"421 "base64.zig"
419 "buf_map.zig"422 "buf_map.zig"
420 "buf_set.zig"423 "buf_set.zig"
src/analyze.cpp+1-2
...@@ -1258,7 +1258,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou...@@ -1258,7 +1258,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
1258 }1258 }
12591259
1260 fn_type_id->param_count = fn_proto->params.length;1260 fn_type_id->param_count = fn_proto->params.length;
1261 fn_type_id->param_info = allocate_nonzero<FnTypeParamInfo>(param_count_alloc);1261 fn_type_id->param_info = allocate<FnTypeParamInfo>(param_count_alloc);
1262 fn_type_id->next_param_index = 0;1262 fn_type_id->next_param_index = 0;
1263 fn_type_id->is_var_args = fn_proto->is_var_args;1263 fn_type_id->is_var_args = fn_proto->is_var_args;
1264}1264}
...@@ -6131,4 +6131,3 @@ bool type_can_fail(TypeTableEntry *type_entry) {...@@ -6131,4 +6131,3 @@ bool type_can_fail(TypeTableEntry *type_entry) {
6131bool fn_type_can_fail(FnTypeId *fn_type_id) {6131bool fn_type_can_fail(FnTypeId *fn_type_id) {
6132 return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync;6132 return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync;
6133}6133}
6134
src/ir.cpp+47-27
...@@ -6166,16 +6166,10 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A...@@ -6166,16 +6166,10 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
6166 buf_init_from_buf(&err_set_type->name, type_name);6166 buf_init_from_buf(&err_set_type->name, type_name);
6167 err_set_type->is_copyable = true;6167 err_set_type->is_copyable = true;
6168 err_set_type->data.error_set.err_count = err_count;6168 err_set_type->data.error_set.err_count = err_count;
61696169 err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
6170 if (err_count == 0) {6170 err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
6171 err_set_type->zero_bits = true;6171 irb->codegen->error_di_types.append(&err_set_type->di_type);
6172 err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type;6172 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
6173 } else {
6174 err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
6175 err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
6176 irb->codegen->error_di_types.append(&err_set_type->di_type);
6177 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
6178 }
61796173
6180 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);6174 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
61816175
...@@ -8117,7 +8111,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *...@@ -8117,7 +8111,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *
8117 *errors = reallocate(*errors, old_errors_count, *errors_count);8111 *errors = reallocate(*errors, old_errors_count, *errors_count);
8118}8112}
81198113
8120static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {8114static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) {
8121 assert(instruction_count >= 1);8115 assert(instruction_count >= 1);
8122 IrInstruction *prev_inst = instructions[0];8116 IrInstruction *prev_inst = instructions[0];
8123 if (type_is_invalid(prev_inst->value.type)) {8117 if (type_is_invalid(prev_inst->value.type)) {
...@@ -8164,16 +8158,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8164,16 +8158,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8164 continue;8158 continue;
8165 }8159 }
81668160
8167 if (prev_type->id == TypeTableEntryIdNullLit) {
8168 prev_inst = cur_inst;
8169 continue;
8170 }
8171
8172 if (cur_type->id == TypeTableEntryIdNullLit) {
8173 any_are_null = true;
8174 continue;
8175 }
8176
8177 if (prev_type->id == TypeTableEntryIdErrorSet) {8161 if (prev_type->id == TypeTableEntryIdErrorSet) {
8178 assert(err_set_type != nullptr);8162 assert(err_set_type != nullptr);
8179 if (cur_type->id == TypeTableEntryIdErrorSet) {8163 if (cur_type->id == TypeTableEntryIdErrorSet) {
...@@ -8433,6 +8417,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8433,6 +8417,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8433 }8417 }
8434 }8418 }
84358419
8420 if (prev_type->id == TypeTableEntryIdNullLit) {
8421 prev_inst = cur_inst;
8422 continue;
8423 }
8424
8425 if (cur_type->id == TypeTableEntryIdNullLit) {
8426 any_are_null = true;
8427 continue;
8428 }
8429
8436 if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) {8430 if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) {
8437 continue;8431 continue;
8438 }8432 }
...@@ -8616,6 +8610,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8616,6 +8610,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8616 } else if (err_set_type != nullptr) {8610 } else if (err_set_type != nullptr) {
8617 if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {8611 if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
8618 return err_set_type;8612 return err_set_type;
8613 } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
8614 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type);
8615 } else if (expected_type != nullptr && expected_type->id == TypeTableEntryIdErrorUnion) {
8616 return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.payload_type);
8619 } else {8617 } else {
8620 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||8618 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
8621 prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)8619 prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
...@@ -8627,8 +8625,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8627,8 +8625,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8627 ir_add_error_node(ira, source_node,8625 ir_add_error_node(ira, source_node,
8628 buf_sprintf("unable to make error union out of null literal"));8626 buf_sprintf("unable to make error union out of null literal"));
8629 return ira->codegen->builtin_types.entry_invalid;8627 return ira->codegen->builtin_types.entry_invalid;
8630 } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
8631 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type);
8632 } else {8628 } else {
8633 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);8629 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);
8634 }8630 }
...@@ -10651,7 +10647,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -10651,7 +10647,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
10651 }10647 }
1065210648
10653 IrInstruction *instructions[] = {op1, op2};10649 IrInstruction *instructions[] = {op1, op2};
10654 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2);10650 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);
10655 if (type_is_invalid(resolved_type))10651 if (type_is_invalid(resolved_type))
10656 return resolved_type;10652 return resolved_type;
10657 type_ensure_zero_bits_known(ira->codegen, resolved_type);10653 type_ensure_zero_bits_known(ira->codegen, resolved_type);
...@@ -11041,7 +11037,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp...@@ -11041,7 +11037,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
11041 IrInstruction *op1 = bin_op_instruction->op1->other;11037 IrInstruction *op1 = bin_op_instruction->op1->other;
11042 IrInstruction *op2 = bin_op_instruction->op2->other;11038 IrInstruction *op2 = bin_op_instruction->op2->other;
11043 IrInstruction *instructions[] = {op1, op2};11039 IrInstruction *instructions[] = {op1, op2};
11044 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);11040 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2);
11045 if (type_is_invalid(resolved_type))11041 if (type_is_invalid(resolved_type))
11046 return resolved_type;11042 return resolved_type;
11047 IrBinOp op_id = bin_op_instruction->op_id;11043 IrBinOp op_id = bin_op_instruction->op_id;
...@@ -13010,7 +13006,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP...@@ -13010,7 +13006,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
13010 return first_value->value.type;13006 return first_value->value.type;
13011 }13007 }
1301213008
13013 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node,13009 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr,
13014 new_incoming_values.items, new_incoming_values.length);13010 new_incoming_values.items, new_incoming_values.length);
13015 if (type_is_invalid(resolved_type))13011 if (type_is_invalid(resolved_type))
13016 return resolved_type;13012 return resolved_type;
...@@ -13863,6 +13859,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -13863,6 +13859,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
13863 }13859 }
13864 } else if (child_type->id == TypeTableEntryIdFn) {13860 } else if (child_type->id == TypeTableEntryIdFn) {
13865 if (buf_eql_str(field_name, "ReturnType")) {13861 if (buf_eql_str(field_name, "ReturnType")) {
13862 if (child_type->data.fn.fn_type_id.return_type == nullptr) {
13863 // Return type can only ever be null, if the function is generic
13864 assert(child_type->data.fn.is_generic);
13865
13866 ir_add_error(ira, &field_ptr_instruction->base,
13867 buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name)));
13868 return ira->codegen->builtin_types.entry_invalid;
13869 }
13870
13866 bool ptr_is_const = true;13871 bool ptr_is_const = true;
13867 bool ptr_is_volatile = false;13872 bool ptr_is_volatile = false;
13868 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,13873 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
...@@ -17864,6 +17869,16 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc...@@ -17864,6 +17869,16 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc
1786417869
17865 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);17870 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
17866 out_val->data.x_type = fn_type_id->param_info[arg_index].type;17871 out_val->data.x_type = fn_type_id->param_info[arg_index].type;
17872 if (out_val->data.x_type == nullptr) {
17873 // Args are only unresolved if our function is generic.
17874 assert(fn_type->data.fn.is_generic);
17875
17876 ir_add_error(ira, arg_index_inst,
17877 buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
17878 arg_index, buf_ptr(&fn_type->name)));
17879 return ira->codegen->builtin_types.entry_invalid;
17880 }
17881
17867 return ira->codegen->builtin_types.entry_type;17882 return ira->codegen->builtin_types.entry_type;
17868}17883}
1786917884
...@@ -18169,6 +18184,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr...@@ -18169,6 +18184,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
18169 } else {18184 } else {
18170 if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))18185 if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
18171 return ira->codegen->builtin_types.entry_invalid;18186 return ira->codegen->builtin_types.entry_invalid;
18187 if (ordering == AtomicOrderUnordered) {
18188 ir_add_error(ira, instruction->ordering,
18189 buf_sprintf("@atomicRmw atomic ordering must not be Unordered"));
18190 return ira->codegen->builtin_types.entry_invalid;
18191 }
18172 }18192 }
1817318193
18174 if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)18194 if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
...@@ -18702,7 +18722,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl...@@ -18702,7 +18722,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
18702 } else if (ira->src_implicit_return_type_list.length == 0) {18722 } else if (ira->src_implicit_return_type_list.length == 0) {
18703 return codegen->builtin_types.entry_unreachable;18723 return codegen->builtin_types.entry_unreachable;
18704 } else {18724 } else {
18705 return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items,18725 return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
18706 ira->src_implicit_return_type_list.length);18726 ira->src_implicit_return_type_list.length);
18707 }18727 }
18708}18728}
std/atomic/index.zig created+7
...@@ -0,0 +1,7 @@
1pub const Stack = @import("stack.zig").Stack;
2pub const Queue = @import("queue.zig").Queue;
3
4test "std.atomic" {
5 _ = @import("stack.zig").Stack;
6 _ = @import("queue.zig").Queue;
7}
std/atomic/queue.zig created+120
...@@ -0,0 +1,120 @@
1const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;
3const AtomicRmwOp = builtin.AtomicRmwOp;
4
5/// Many reader, many writer, non-allocating, thread-safe, lock-free
6pub fn Queue(comptime T: type) type {
7 return struct {
8 head: &Node,
9 tail: &Node,
10 root: Node,
11
12 pub const Self = this;
13
14 pub const Node = struct {
15 next: ?&Node,
16 data: T,
17 };
18
19 // TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287
20 pub fn init(self: &Self) void {
21 self.root.next = null;
22 self.head = &self.root;
23 self.tail = &self.root;
24 }
25
26 pub fn put(self: &Self, node: &Node) void {
27 node.next = null;
28
29 const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
30 _ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
31 }
32
33 pub fn get(self: &Self) ?&Node {
34 var head = @atomicLoad(&Node, &self.head, AtomicOrder.Acquire);
35 while (true) {
36 const node = head.next ?? return null;
37 head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? return node;
38 }
39 }
40 };
41}
42
43const std = @import("std");
44const Context = struct {
45 allocator: &std.mem.Allocator,
46 queue: &Queue(i32),
47 put_sum: isize,
48 get_sum: isize,
49 get_count: usize,
50 puts_done: u8, // TODO make this a bool
51};
52const puts_per_thread = 10000;
53const put_thread_count = 3;
54
55test "std.atomic.queue" {
56 var direct_allocator = std.heap.DirectAllocator.init();
57 defer direct_allocator.deinit();
58
59 var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
60 defer direct_allocator.allocator.free(plenty_of_memory);
61
62 var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
63 var a = &fixed_buffer_allocator.allocator;
64
65 var queue: Queue(i32) = undefined;
66 queue.init();
67 var context = Context {
68 .allocator = a,
69 .queue = &queue,
70 .put_sum = 0,
71 .get_sum = 0,
72 .puts_done = 0,
73 .get_count = 0,
74 };
75
76 var putters: [put_thread_count]&std.os.Thread = undefined;
77 for (putters) |*t| {
78 *t = try std.os.spawnThread(&context, startPuts);
79 }
80 var getters: [put_thread_count]&std.os.Thread = undefined;
81 for (getters) |*t| {
82 *t = try std.os.spawnThread(&context, startGets);
83 }
84
85 for (putters) |t| t.wait();
86 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
87 for (getters) |t| t.wait();
88
89 std.debug.assert(context.put_sum == context.get_sum);
90 std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
91}
92
93fn startPuts(ctx: &Context) u8 {
94 var put_count: usize = puts_per_thread;
95 var r = std.rand.DefaultPrng.init(0xdeadbeef);
96 while (put_count != 0) : (put_count -= 1) {
97 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
98 const x = @bitCast(i32, r.random.scalar(u32));
99 const node = ctx.allocator.create(Queue(i32).Node) catch unreachable;
100 node.data = x;
101 ctx.queue.put(node);
102 _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
103 }
104 return 0;
105}
106
107fn startGets(ctx: &Context) u8 {
108 while (true) {
109 while (ctx.queue.get()) |node| {
110 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
111 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
112 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
113 }
114
115 if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
116 break;
117 }
118 }
119 return 0;
120}
std/atomic/stack.zig created+126
...@@ -0,0 +1,126 @@
1const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;
3
4/// Many reader, many writer, non-allocating, thread-safe, lock-free
5pub fn Stack(comptime T: type) type {
6 return struct {
7 root: ?&Node,
8
9 pub const Self = this;
10
11 pub const Node = struct {
12 next: ?&Node,
13 data: T,
14 };
15
16 pub fn init() Self {
17 return Self {
18 .root = null,
19 };
20 }
21
22 /// push operation, but only if you are the first item in the stack. if you did not succeed in
23 /// being the first item in the stack, returns the other item that was there.
24 pub fn pushFirst(self: &Self, node: &Node) ?&Node {
25 node.next = null;
26 return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst);
27 }
28
29 pub fn push(self: &Self, node: &Node) void {
30 var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
31 while (true) {
32 node.next = root;
33 root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break;
34 }
35 }
36
37 pub fn pop(self: &Self) ?&Node {
38 var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
39 while (true) {
40 root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;
41 }
42 }
43
44 pub fn isEmpty(self: &Self) bool {
45 return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null;
46 }
47 };
48}
49
50const std = @import("std");
51const Context = struct {
52 allocator: &std.mem.Allocator,
53 stack: &Stack(i32),
54 put_sum: isize,
55 get_sum: isize,
56 get_count: usize,
57 puts_done: u8, // TODO make this a bool
58};
59const puts_per_thread = 1000;
60const put_thread_count = 3;
61
62test "std.atomic.stack" {
63 var direct_allocator = std.heap.DirectAllocator.init();
64 defer direct_allocator.deinit();
65
66 var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
67 defer direct_allocator.allocator.free(plenty_of_memory);
68
69 var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
70 var a = &fixed_buffer_allocator.allocator;
71
72 var stack = Stack(i32).init();
73 var context = Context {
74 .allocator = a,
75 .stack = &stack,
76 .put_sum = 0,
77 .get_sum = 0,
78 .puts_done = 0,
79 .get_count = 0,
80 };
81
82 var putters: [put_thread_count]&std.os.Thread = undefined;
83 for (putters) |*t| {
84 *t = try std.os.spawnThread(&context, startPuts);
85 }
86 var getters: [put_thread_count]&std.os.Thread = undefined;
87 for (getters) |*t| {
88 *t = try std.os.spawnThread(&context, startGets);
89 }
90
91 for (putters) |t| t.wait();
92 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
93 for (getters) |t| t.wait();
94
95 std.debug.assert(context.put_sum == context.get_sum);
96 std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
97}
98
99fn startPuts(ctx: &Context) u8 {
100 var put_count: usize = puts_per_thread;
101 var r = std.rand.DefaultPrng.init(0xdeadbeef);
102 while (put_count != 0) : (put_count -= 1) {
103 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
104 const x = @bitCast(i32, r.random.scalar(u32));
105 const node = ctx.allocator.create(Stack(i32).Node) catch unreachable;
106 node.data = x;
107 ctx.stack.push(node);
108 _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
109 }
110 return 0;
111}
112
113fn startGets(ctx: &Context) u8 {
114 while (true) {
115 while (ctx.stack.pop()) |node| {
116 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
117 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
118 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
119 }
120
121 if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
122 break;
123 }
124 }
125 return 0;
126}
std/c/darwin.zig+5
...@@ -81,3 +81,8 @@ pub const sockaddr = extern struct {...@@ -81,3 +81,8 @@ pub const sockaddr = extern struct {
81};81};
8282
83pub const sa_family_t = u8;83pub const sa_family_t = u8;
84
85pub const pthread_attr_t = extern struct {
86 __sig: c_long,
87 __opaque: [56]u8,
88};
std/c/index.zig+10
...@@ -53,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void;...@@ -53,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void;
53pub extern "c" fn realloc(&c_void, usize) ?&c_void;53pub extern "c" fn realloc(&c_void, usize) ?&c_void;
54pub extern "c" fn free(&c_void) void;54pub extern "c" fn free(&c_void) void;
55pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;55pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;
56
57pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t,
58 noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void,
59 noalias arg: ?&c_void) c_int;
60pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int;
61pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int;
62pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int;
63pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int;
64
65pub const pthread_t = &@OpaqueType();
std/c/linux.zig+5
...@@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig");...@@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig");
3pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int;3pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int;
4extern "c" fn __errno_location() &c_int;4extern "c" fn __errno_location() &c_int;
5pub const _errno = __errno_location;5pub const _errno = __errno_location;
6
7pub const pthread_attr_t = extern struct {
8 __size: [56]u8,
9 __align: c_long,
10};
std/fmt/errol/index.zig+76-3
...@@ -12,13 +12,79 @@ pub const FloatDecimal = struct {...@@ -12,13 +12,79 @@ pub const FloatDecimal = struct {
12 exp: i32,12 exp: i32,
13};13};
1414
15pub const RoundMode = enum {
16 // Round only the fractional portion (e.g. 1234.23 has precision 2)
17 Decimal,
18 // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5)
19 Scientific,
20};
21
22/// Round a FloatDecimal as returned by errol3 to the specified fractional precision.
23/// All digits after the specified precision should be considered invalid.
24pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void {
25 // The round digit refers to the index which we should look at to determine
26 // whether we need to round to match the specified precision.
27 var round_digit: usize = 0;
28
29 switch (mode) {
30 RoundMode.Decimal => {
31 if (float_decimal.exp >= 0) {
32 round_digit = precision + usize(float_decimal.exp);
33 } else {
34 // if a small negative exp, then adjust we need to offset by the number
35 // of leading zeros that will occur.
36 const min_exp_required = usize(-float_decimal.exp);
37 if (precision > min_exp_required) {
38 round_digit = precision - min_exp_required;
39 }
40 }
41 },
42 RoundMode.Scientific => {
43 round_digit = 1 + precision;
44 },
45 }
46
47 // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05
48 // first, and then to 0.1 in the case of a {.1} single precision.
49
50 // Find the digit which will signify the round point and start rounding backwards.
51 if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) {
52 assert(round_digit >= 0);
53
54 var i = round_digit;
55 while (true) {
56 if (i == 0) {
57 // Rounded all the way past the start. This was of the form 9.999...
58 // Slot the new digit in place and increase the exponent.
59 float_decimal.exp += 1;
60
61 // Re-size the buffer to use the reserved leading byte.
62 const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1);
63 float_decimal.digits = one_before[0..float_decimal.digits.len + 1];
64 float_decimal.digits[0] = '1';
65 return;
66 }
67
68 i -= 1;
69
70 const new_value = (float_decimal.digits[i] - '0' + 1) % 10;
71 float_decimal.digits[i] = new_value + '0';
72
73 // must continue rounding until non-9
74 if (new_value != 0) {
75 return;
76 }
77 }
78 }
79}
80
15/// Corrected Errol3 double to ASCII conversion.81/// Corrected Errol3 double to ASCII conversion.
16pub fn errol3(value: f64, buffer: []u8) FloatDecimal {82pub fn errol3(value: f64, buffer: []u8) FloatDecimal {
17 const bits = @bitCast(u64, value);83 const bits = @bitCast(u64, value);
18 const i = tableLowerBound(bits);84 const i = tableLowerBound(bits);
19 if (i < enum3.len and enum3[i] == bits) {85 if (i < enum3.len and enum3[i] == bits) {
20 const data = enum3_data[i];86 const data = enum3_data[i];
21 const digits = buffer[0..data.str.len];87 const digits = buffer[1..data.str.len + 1];
22 mem.copy(u8, digits, data.str);88 mem.copy(u8, digits, data.str);
23 return FloatDecimal {89 return FloatDecimal {
24 .digits = digits,90 .digits = digits,
...@@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {...@@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
98 }164 }
99165
100 // digit generation166 // digit generation
101 var buf_index: usize = 0;167
168 // We generate digits starting at index 1. If rounding a buffer later then it may be
169 // required to generate a preceeding digit in some cases (9.999) in which case we use
170 // the 0-index for this extra digit.
171 var buf_index: usize = 1;
102 while (true) {172 while (true) {
103 var hdig = u8(math.floor(high.val));173 var hdig = u8(math.floor(high.val));
104 if ((high.val == f64(hdig)) and (high.off < 0))174 if ((high.val == f64(hdig)) and (high.off < 0))
...@@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {...@@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
128 buf_index += 1;198 buf_index += 1;
129199
130 return FloatDecimal {200 return FloatDecimal {
131 .digits = buffer[0..buf_index],201 .digits = buffer[1..buf_index],
132 .exp = exp,202 .exp = exp,
133 };203 };
134}204}
...@@ -189,6 +259,9 @@ fn gethi(in: f64) f64 {...@@ -189,6 +259,9 @@ fn gethi(in: f64) f64 {
189/// Normalize the number by factoring in the error.259/// Normalize the number by factoring in the error.
190/// @hp: The float pair.260/// @hp: The float pair.
191fn hpNormalize(hp: &HP) void {261fn hpNormalize(hp: &HP) void {
262 // Required to avoid segfaults causing buffer overrun during errol3 digit output termination.
263 @setFloatMode(this, @import("builtin").FloatMode.Strict);
264
192 const val = hp.val;265 const val = hp.val;
193266
194 hp.val += hp.off;267 hp.val += hp.off;
std/fmt/index.zig+432-107
...@@ -4,7 +4,7 @@ const debug = std.debug;...@@ -4,7 +4,7 @@ const debug = std.debug;
4const assert = debug.assert;4const assert = debug.assert;
5const mem = std.mem;5const mem = std.mem;
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const errol3 = @import("errol/index.zig").errol3;7const errol = @import("errol/index.zig");
88
9const max_int_digits = 65;9const max_int_digits = 65;
1010
...@@ -22,6 +22,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),...@@ -22,6 +22,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
22 IntegerWidth,22 IntegerWidth,
23 Float,23 Float,
24 FloatWidth,24 FloatWidth,
25 FloatScientific,
26 FloatScientificWidth,
25 Character,27 Character,
26 Buf,28 Buf,
27 BufWidth,29 BufWidth,
...@@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),...@@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
87 's' => {89 's' => {
88 state = State.Buf;90 state = State.Buf;
89 },91 },
92 'e' => {
93 state = State.FloatScientific;
94 },
90 '.' => {95 '.' => {
91 state = State.Float;96 state = State.Float;
92 },97 },
...@@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),...@@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
133 '0' ... '9' => {},138 '0' ... '9' => {},
134 else => @compileError("Unexpected character in format string: " ++ []u8{c}),139 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
135 },140 },
141 State.FloatScientific => switch (c) {
142 '}' => {
143 try formatFloatScientific(args[next_arg], null, context, Errors, output);
144 next_arg += 1;
145 state = State.Start;
146 start_index = i + 1;
147 },
148 '0' ... '9' => {
149 width_start = i;
150 state = State.FloatScientificWidth;
151 },
152 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
153 },
154 State.FloatScientificWidth => switch (c) {
155 '}' => {
156 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
157 try formatFloatScientific(args[next_arg], width, context, Errors, output);
158 next_arg += 1;
159 state = State.Start;
160 start_index = i + 1;
161 },
162 '0' ... '9' => {},
163 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
164 },
136 State.Float => switch (c) {165 State.Float => switch (c) {
137 '}' => {166 '}' => {
138 try formatFloatDecimal(args[next_arg], 0, context, Errors, output);167 try formatFloatDecimal(args[next_arg], null, context, Errors, output);
139 next_arg += 1;168 next_arg += 1;
140 state = State.Start;169 state = State.Start;
141 start_index = i + 1;170 start_index = i + 1;
...@@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@...@@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@
199 return formatInt(value, 10, false, 0, context, Errors, output);228 return formatInt(value, 10, false, 0, context, Errors, output);
200 },229 },
201 builtin.TypeId.Float => {230 builtin.TypeId.Float => {
202 return formatFloat(value, context, Errors, output);231 return formatFloatScientific(value, null, context, Errors, output);
203 },232 },
204 builtin.TypeId.Void => {233 builtin.TypeId.Void => {
205 return output(context, "void");234 return output(context, "void");
...@@ -257,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize,...@@ -257,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize,
257 }286 }
258}287}
259288
260pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {289// Print a float in scientific notation to the specified precision. Null uses full precision.
290// It should be the case that every full precision, printed value can be re-parsed back to the
291// same type unambiguously.
292pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
261 var x = f64(value);293 var x = f64(value);
262294
263 // Errol doesn't handle these special cases.295 // Errol doesn't handle these special cases.
264 if (math.isNan(x)) {
265 return output(context, "NaN");
266 }
267 if (math.signbit(x)) {296 if (math.signbit(x)) {
268 try output(context, "-");297 try output(context, "-");
269 x = -x;298 x = -x;
270 }299 }
300
301 if (math.isNan(x)) {
302 return output(context, "nan");
303 }
271 if (math.isPositiveInf(x)) {304 if (math.isPositiveInf(x)) {
272 return output(context, "Infinity");305 return output(context, "inf");
273 }306 }
274 if (x == 0.0) {307 if (x == 0.0) {
275 return output(context, "0.0");308 try output(context, "0");
309
310 if (maybe_precision) |precision| {
311 if (precision != 0) {
312 try output(context, ".");
313 var i: usize = 0;
314 while (i < precision) : (i += 1) {
315 try output(context, "0");
316 }
317 }
318 } else {
319 try output(context, ".0");
320 }
321
322 try output(context, "e+00");
323 return;
276 }324 }
277325
278 var buffer: [32]u8 = undefined;326 var buffer: [32]u8 = undefined;
279 const float_decimal = errol3(x, buffer[0..]);327 var float_decimal = errol.errol3(x, buffer[0..]);
280 try output(context, float_decimal.digits[0..1]);328
281 try output(context, ".");329 if (maybe_precision) |precision| {
282 if (float_decimal.digits.len > 1) {330 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
283 const num_digits = if (@typeOf(value) == f32)331
284 math.min(usize(9), float_decimal.digits.len)332 try output(context, float_decimal.digits[0..1]);
285 else333
286 float_decimal.digits.len;334 // {e0} case prints no `.`
287 try output(context, float_decimal.digits[1 .. num_digits]);335 if (precision != 0) {
336 try output(context, ".");
337
338 var printed: usize = 0;
339 if (float_decimal.digits.len > 1) {
340 const num_digits = math.min(float_decimal.digits.len, precision + 1);
341 try output(context, float_decimal.digits[1 .. num_digits]);
342 printed += num_digits - 1;
343 }
344
345 while (printed < precision) : (printed += 1) {
346 try output(context, "0");
347 }
348 }
288 } else {349 } else {
289 try output(context, "0");350 try output(context, float_decimal.digits[0..1]);
351 try output(context, ".");
352 if (float_decimal.digits.len > 1) {
353 const num_digits = if (@typeOf(value) == f32)
354 math.min(usize(9), float_decimal.digits.len)
355 else
356 float_decimal.digits.len;
357
358 try output(context, float_decimal.digits[1 .. num_digits]);
359 } else {
360 try output(context, "0");
361 }
290 }362 }
291363
292 if (float_decimal.exp != 1) {364 try output(context, "e");
293 try output(context, "e");365 const exp = float_decimal.exp - 1;
294 try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output);366
367 if (exp >= 0) {
368 try output(context, "+");
369 if (exp > -10 and exp < 10) {
370 try output(context, "0");
371 }
372 try formatInt(exp, 10, false, 0, context, Errors, output);
373 } else {
374 try output(context, "-");
375 if (exp > -10 and exp < 10) {
376 try output(context, "0");
377 }
378 try formatInt(-exp, 10, false, 0, context, Errors, output);
295 }379 }
296}380}
297381
298pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {382// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
383// By default floats are printed at full precision (no rounding).
384pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
299 var x = f64(value);385 var x = f64(value);
300386
301 // Errol doesn't handle these special cases.387 // Errol doesn't handle these special cases.
302 if (math.isNan(x)) {
303 return output(context, "NaN");
304 }
305 if (math.signbit(x)) {388 if (math.signbit(x)) {
306 try output(context, "-");389 try output(context, "-");
307 x = -x;390 x = -x;
308 }391 }
392
393 if (math.isNan(x)) {
394 return output(context, "nan");
395 }
309 if (math.isPositiveInf(x)) {396 if (math.isPositiveInf(x)) {
310 return output(context, "Infinity");397 return output(context, "inf");
311 }398 }
312 if (x == 0.0) {399 if (x == 0.0) {
313 return output(context, "0.0");400 try output(context, "0");
401
402 if (maybe_precision) |precision| {
403 if (precision != 0) {
404 try output(context, ".");
405 var i: usize = 0;
406 while (i < precision) : (i += 1) {
407 try output(context, "0");
408 }
409 } else {
410 try output(context, ".0");
411 }
412 } else {
413 try output(context, "0");
414 }
415
416 return;
314 }417 }
315418
419 // non-special case, use errol3
316 var buffer: [32]u8 = undefined;420 var buffer: [32]u8 = undefined;
317 const float_decimal = errol3(x, buffer[0..]);421 var float_decimal = errol.errol3(x, buffer[0..]);
318422
319 const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1;423 if (maybe_precision) |precision| {
320424 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);
321 try output(context, float_decimal.digits[0 .. num_left_digits]);425
322 try output(context, ".");426 // exp < 0 means the leading is always 0 as errol result is normalized.
323 if (float_decimal.digits.len > 1) {427 var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
324 const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len)428
325 else429 // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
326 float_decimal.digits.len;430 var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
327431
328 const num_right_digits = if (precision != 0)432 if (num_digits_whole > 0) {
329 math.min(precision, (num_valid_digtis-num_left_digits))433 // We may have to zero pad, for instance 1e4 requires zero padding.
330 else434 try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
331 num_valid_digtis - num_left_digits;435
332 try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]);436 var i = num_digits_whole_no_pad;
437 while (i < num_digits_whole) : (i += 1) {
438 try output(context, "0");
439 }
440 } else {
441 try output(context , "0");
442 }
443
444 // {.0} special case doesn't want a trailing '.'
445 if (precision == 0) {
446 return;
447 }
448
449 try output(context, ".");
450
451 // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
452 var printed: usize = 0;
453
454 // Zero-fill until we reach significant digits or run out of precision.
455 if (float_decimal.exp <= 0) {
456 const zero_digit_count = usize(-float_decimal.exp);
457 const zeros_to_print = math.min(zero_digit_count, precision);
458
459 var i: usize = 0;
460 while (i < zeros_to_print) : (i += 1) {
461 try output(context, "0");
462 printed += 1;
463 }
464
465 if (printed >= precision) {
466 return;
467 }
468 }
469
470 // Remaining fractional portion, zero-padding if insufficient.
471 debug.assert(precision >= printed);
472 if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
473 try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
474 return;
475 } else {
476 try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
477 printed += float_decimal.digits.len - num_digits_whole_no_pad;
478
479 while (printed < precision) : (printed += 1) {
480 try output(context, "0");
481 }
482 }
333 } else {483 } else {
334 try output(context, "0");484 // exp < 0 means the leading is always 0 as errol result is normalized.
485 var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
486
487 // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
488 var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
489
490 if (num_digits_whole > 0) {
491 // We may have to zero pad, for instance 1e4 requires zero padding.
492 try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
493
494 var i = num_digits_whole_no_pad;
495 while (i < num_digits_whole) : (i += 1) {
496 try output(context, "0");
497 }
498 } else {
499 try output(context , "0");
500 }
501
502 // Omit `.` if no fractional portion
503 if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) {
504 return;
505 }
506
507 try output(context, ".");
508
509 // Zero-fill until we reach significant digits or run out of precision.
510 if (float_decimal.exp < 0) {
511 const zero_digit_count = usize(-float_decimal.exp);
512
513 var i: usize = 0;
514 while (i < zero_digit_count) : (i += 1) {
515 try output(context, "0");
516 }
517 }
518
519 try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
335 }520 }
336}521}
337522
...@@ -594,70 +779,210 @@ test "fmt.format" {...@@ -594,70 +779,210 @@ test "fmt.format" {
594 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);779 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);
595 assert(mem.startsWith(u8, result, "pointer: Struct@"));780 assert(mem.startsWith(u8, result, "pointer: Struct@"));
596 }781 }
597782 {
598 // TODO get these tests passing in release modes783 var buf1: [32]u8 = undefined;
599 // https://github.com/zig-lang/zig/issues/564784 const value: f32 = 1.34;
600 if (builtin.mode == builtin.Mode.Debug) {785 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
601 {786 assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
602 var buf1: [32]u8 = undefined;787 }
603 const value: f32 = 12.34;788 {
604 const result = try bufPrint(buf1[0..], "f32: {}\n", value);789 var buf1: [32]u8 = undefined;
605 assert(mem.eql(u8, result, "f32: 1.23400001e1\n"));790 const value: f32 = 12.34;
606 }791 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
607 {792 assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
608 var buf1: [32]u8 = undefined;793 }
609 const value: f64 = -12.34e10;794 {
610 const result = try bufPrint(buf1[0..], "f64: {}\n", value);795 var buf1: [32]u8 = undefined;
611 assert(mem.eql(u8, result, "f64: -1.234e11\n"));796 const value: f64 = -12.34e10;
612 }797 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
613 {798 assert(mem.eql(u8, result, "f64: -1.234e+11\n"));
614 var buf1: [32]u8 = undefined;799 }
615 const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);800 {
616 assert(mem.eql(u8, result, "f64: NaN\n"));801 // This fails on release due to a minor rounding difference.
617 }802 // --release-fast outputs 9.999960000000001e-40 vs. the expected.
618 {803 if (builtin.mode == builtin.Mode.Debug) {
619 var buf1: [32]u8 = undefined;
620 const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
621 assert(mem.eql(u8, result, "f64: Infinity\n"));
622 }
623 {
624 var buf1: [32]u8 = undefined;
625 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
626 assert(mem.eql(u8, result, "f64: -Infinity\n"));
627 }
628 {
629 var buf1: [32]u8 = undefined;
630 const value: f32 = 1.1234;
631 const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
632 assert(mem.eql(u8, result, "f32: 1.1\n"));
633 }
634 {
635 var buf1: [32]u8 = undefined;
636 const value: f32 = 1234.567;
637 const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
638 assert(mem.eql(u8, result, "f32: 1234.56\n"));
639 }
640 {
641 var buf1: [32]u8 = undefined;
642 const value: f32 = -11.1234;
643 const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
644 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
645 // -11.12339... is truncated to -11.1233
646 assert(mem.eql(u8, result, "f32: -11.1233\n"));
647 }
648 {
649 var buf1: [32]u8 = undefined;
650 const value: f32 = 91.12345;
651 const result = try bufPrint(buf1[0..], "f32: {.}\n", value);
652 assert(mem.eql(u8, result, "f32: 91.12345\n"));
653 }
654 {
655 var buf1: [32]u8 = undefined;804 var buf1: [32]u8 = undefined;
656 const value: f64 = 91.12345678901235;805 const value: f64 = 9.999960e-40;
657 const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);806 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
658 assert(mem.eql(u8, result, "f64: 91.1234567890\n"));807 assert(mem.eql(u8, result, "f64: 9.99996e-40\n"));
659 }808 }
660809 }
810 {
811 var buf1: [32]u8 = undefined;
812 const value: f64 = 1.409706e-42;
813 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
814 assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));
815 }
816 {
817 var buf1: [32]u8 = undefined;
818 const value: f64 = @bitCast(f32, u32(814313563));
819 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
820 assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));
821 }
822 {
823 var buf1: [32]u8 = undefined;
824 const value: f64 = @bitCast(f32, u32(1006632960));
825 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
826 assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));
827 }
828 {
829 // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
830 // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
831 var buf1: [32]u8 = undefined;
832 const value: f64 = @bitCast(f32, u32(1203982400));
833 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
834 assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));
835 }
836 {
837 var buf1: [32]u8 = undefined;
838 const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
839 assert(mem.eql(u8, result, "f64: nan\n"));
840 }
841 {
842 var buf1: [32]u8 = undefined;
843 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
844 assert(mem.eql(u8, result, "f64: -nan\n"));
845 }
846 {
847 var buf1: [32]u8 = undefined;
848 const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
849 assert(mem.eql(u8, result, "f64: inf\n"));
850 }
851 {
852 var buf1: [32]u8 = undefined;
853 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
854 assert(mem.eql(u8, result, "f64: -inf\n"));
855 }
856 {
857 var buf1: [64]u8 = undefined;
858 const value: f64 = 1.52314e+29;
859 const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
860 assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
861 }
862 {
863 var buf1: [32]u8 = undefined;
864 const value: f32 = 1.1234;
865 const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
866 assert(mem.eql(u8, result, "f32: 1.1\n"));
867 }
868 {
869 var buf1: [32]u8 = undefined;
870 const value: f32 = 1234.567;
871 const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
872 assert(mem.eql(u8, result, "f32: 1234.57\n"));
873 }
874 {
875 var buf1: [32]u8 = undefined;
876 const value: f32 = -11.1234;
877 const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
878 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
879 // -11.12339... is rounded back up to -11.1234
880 assert(mem.eql(u8, result, "f32: -11.1234\n"));
881 }
882 {
883 var buf1: [32]u8 = undefined;
884 const value: f32 = 91.12345;
885 const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
886 assert(mem.eql(u8, result, "f32: 91.12345\n"));
887 }
888 {
889 var buf1: [32]u8 = undefined;
890 const value: f64 = 91.12345678901235;
891 const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
892 assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
893 }
894 {
895 var buf1: [32]u8 = undefined;
896 const value: f64 = 0.0;
897 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
898 assert(mem.eql(u8, result, "f64: 0.00000\n"));
899 }
900 {
901 var buf1: [32]u8 = undefined;
902 const value: f64 = 5.700;
903 const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
904 assert(mem.eql(u8, result, "f64: 6\n"));
905 }
906 {
907 var buf1: [32]u8 = undefined;
908 const value: f64 = 9.999;
909 const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
910 assert(mem.eql(u8, result, "f64: 10.0\n"));
911 }
912 {
913 var buf1: [32]u8 = undefined;
914 const value: f64 = 1.0;
915 const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
916 assert(mem.eql(u8, result, "f64: 1.000\n"));
917 }
918 {
919 var buf1: [32]u8 = undefined;
920 const value: f64 = 0.0003;
921 const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
922 assert(mem.eql(u8, result, "f64: 0.00030000\n"));
923 }
924 {
925 var buf1: [32]u8 = undefined;
926 const value: f64 = 1.40130e-45;
927 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
928 assert(mem.eql(u8, result, "f64: 0.00000\n"));
929 }
930 {
931 var buf1: [32]u8 = undefined;
932 const value: f64 = 9.999960e-40;
933 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
934 assert(mem.eql(u8, result, "f64: 0.00000\n"));
935 }
936 // libc checks
937 {
938 var buf1: [32]u8 = undefined;
939 const value: f64 = f64(@bitCast(f32, u32(916964781)));
940 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
941 assert(mem.eql(u8, result, "f64: 0.00001\n"));
942 }
943 {
944 var buf1: [32]u8 = undefined;
945 const value: f64 = f64(@bitCast(f32, u32(925353389)));
946 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
947 assert(mem.eql(u8, result, "f64: 0.00001\n"));
948 }
949 {
950 var buf1: [32]u8 = undefined;
951 const value: f64 = f64(@bitCast(f32, u32(1036831278)));
952 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
953 assert(mem.eql(u8, result, "f64: 0.10000\n"));
954 }
955 {
956 var buf1: [32]u8 = undefined;
957 const value: f64 = f64(@bitCast(f32, u32(1065353133)));
958 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
959 assert(mem.eql(u8, result, "f64: 1.00000\n"));
960 }
961 {
962 var buf1: [32]u8 = undefined;
963 const value: f64 = f64(@bitCast(f32, u32(1092616192)));
964 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
965 assert(mem.eql(u8, result, "f64: 10.00000\n"));
966 }
967 // libc differences
968 {
969 var buf1: [32]u8 = undefined;
970 // This is 0.015625 exactly according to gdb. We thus round down,
971 // however glibc rounds up for some reason. This occurs for all
972 // floats of the form x.yyyy25 on a precision point.
973 const value: f64 = f64(@bitCast(f32, u32(1015021568)));
974 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
975 assert(mem.eql(u8, result, "f64: 0.01563\n"));
976 }
977 // std-windows-x86_64-Debug-bare test case fails
978 {
979 // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
980 // also rounds to 630 so I'm inclined to believe libc is not
981 // optimal here.
982 var buf1: [32]u8 = undefined;
983 const value: f64 = f64(@bitCast(f32, u32(1518338049)));
984 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
985 assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
661 }986 }
662}987}
663988
std/heap.zig+59-11
...@@ -47,13 +47,6 @@ pub const DirectAllocator = struct {...@@ -47,13 +47,6 @@ pub const DirectAllocator = struct {
4747
48 const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;48 const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
4949
50 //pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122};
51 //pub const want_safety = switch (builtin.mode) {
52 // builtin.Mode.Debug => true,
53 // builtin.Mode.ReleaseSafe => true,
54 // else => false,
55 //};
56
57 pub fn init() DirectAllocator {50 pub fn init() DirectAllocator {
58 return DirectAllocator {51 return DirectAllocator {
59 .allocator = Allocator {52 .allocator = Allocator {
...@@ -98,7 +91,7 @@ pub const DirectAllocator = struct {...@@ -98,7 +91,7 @@ pub const DirectAllocator = struct {
98 const unused_start = addr;91 const unused_start = addr;
99 const unused_len = aligned_addr - 1 - unused_start;92 const unused_len = aligned_addr - 1 - unused_start;
10093
101 var err = p.munmap(@intToPtr(&u8, unused_start), unused_len);94 var err = p.munmap(unused_start, unused_len);
102 debug.assert(p.getErrno(err) == 0);95 debug.assert(p.getErrno(err) == 0);
103 96
104 //It is impossible that there is an unoccupied page at the top of our97 //It is impossible that there is an unoccupied page at the top of our
...@@ -139,7 +132,7 @@ pub const DirectAllocator = struct {...@@ -139,7 +132,7 @@ pub const DirectAllocator = struct {
139 const rem = @rem(new_addr_end, os.page_size);132 const rem = @rem(new_addr_end, os.page_size);
140 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);133 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
141 if (old_addr_end > new_addr_end_rounded) {134 if (old_addr_end > new_addr_end_rounded) {
142 _ = os.posix.munmap(@intToPtr(&u8, new_addr_end_rounded), old_addr_end - new_addr_end_rounded);135 _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
143 }136 }
144 return old_mem[0..new_size];137 return old_mem[0..new_size];
145 }138 }
...@@ -177,7 +170,7 @@ pub const DirectAllocator = struct {...@@ -177,7 +170,7 @@ pub const DirectAllocator = struct {
177170
178 switch (builtin.os) {171 switch (builtin.os) {
179 Os.linux, Os.macosx, Os.ios => {172 Os.linux, Os.macosx, Os.ios => {
180 _ = os.posix.munmap(bytes.ptr, bytes.len);173 _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len);
181 },174 },
182 Os.windows => {175 Os.windows => {
183 const record_addr = @ptrToInt(bytes.ptr) + bytes.len;176 const record_addr = @ptrToInt(bytes.ptr) + bytes.len;
...@@ -298,7 +291,7 @@ pub const FixedBufferAllocator = struct {...@@ -298,7 +291,7 @@ pub const FixedBufferAllocator = struct {
298291
299 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {292 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
300 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);293 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
301 const addr = @ptrToInt(&self.buffer[self.end_index]);294 const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
302 const rem = @rem(addr, alignment);295 const rem = @rem(addr, alignment);
303 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);296 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
304 const adjusted_index = self.end_index + march_forward_bytes;297 const adjusted_index = self.end_index + march_forward_bytes;
...@@ -325,6 +318,54 @@ pub const FixedBufferAllocator = struct {...@@ -325,6 +318,54 @@ pub const FixedBufferAllocator = struct {
325 fn free(allocator: &Allocator, bytes: []u8) void { }318 fn free(allocator: &Allocator, bytes: []u8) void { }
326};319};
327320
321/// lock free
322pub const ThreadSafeFixedBufferAllocator = struct {
323 allocator: Allocator,
324 end_index: usize,
325 buffer: []u8,
326
327 pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
328 return ThreadSafeFixedBufferAllocator {
329 .allocator = Allocator {
330 .allocFn = alloc,
331 .reallocFn = realloc,
332 .freeFn = free,
333 },
334 .buffer = buffer,
335 .end_index = 0,
336 };
337 }
338
339 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
340 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
341 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
342 while (true) {
343 const addr = @ptrToInt(self.buffer.ptr) + end_index;
344 const rem = @rem(addr, alignment);
345 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
346 const adjusted_index = end_index + march_forward_bytes;
347 const new_end_index = adjusted_index + n;
348 if (new_end_index > self.buffer.len) {
349 return error.OutOfMemory;
350 }
351 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index,
352 builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index];
353 }
354 }
355
356 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
357 if (new_size <= old_mem.len) {
358 return old_mem[0..new_size];
359 } else {
360 const result = try alloc(allocator, new_size, alignment);
361 mem.copy(u8, result, old_mem);
362 return result;
363 }
364 }
365
366 fn free(allocator: &Allocator, bytes: []u8) void { }
367};
368
328369
329370
330test "c_allocator" {371test "c_allocator" {
...@@ -363,6 +404,13 @@ test "FixedBufferAllocator" {...@@ -363,6 +404,13 @@ test "FixedBufferAllocator" {
363 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);404 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
364}405}
365406
407test "ThreadSafeFixedBufferAllocator" {
408 var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
409
410 try testAllocator(&fixed_buffer_allocator.allocator);
411 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
412}
413
366fn testAllocator(allocator: &mem.Allocator) !void {414fn testAllocator(allocator: &mem.Allocator) !void {
367 var slice = try allocator.alloc(&i32, 100);415 var slice = try allocator.alloc(&i32, 100);
368416
std/index.zig+2
...@@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap;...@@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap;
8pub const LinkedList = @import("linked_list.zig").LinkedList;8pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
1010
11pub const atomic = @import("atomic/index.zig");
11pub const base64 = @import("base64.zig");12pub const base64 = @import("base64.zig");
12pub const build = @import("build.zig");13pub const build = @import("build.zig");
13pub const c = @import("c/index.zig");14pub const c = @import("c/index.zig");
...@@ -34,6 +35,7 @@ pub const zig = @import("zig/index.zig");...@@ -34,6 +35,7 @@ pub const zig = @import("zig/index.zig");
3435
35test "std" {36test "std" {
36 // run tests from these37 // run tests from these
38 _ = @import("atomic/index.zig");
37 _ = @import("array_list.zig");39 _ = @import("array_list.zig");
38 _ = @import("buf_map.zig");40 _ = @import("buf_map.zig");
39 _ = @import("buf_set.zig");41 _ = @import("buf_set.zig");
std/mem.zig+17-2
...@@ -32,10 +32,25 @@ pub const Allocator = struct {...@@ -32,10 +32,25 @@ pub const Allocator = struct {
32 freeFn: fn (self: &Allocator, old_mem: []u8) void,32 freeFn: fn (self: &Allocator, old_mem: []u8) void,
3333
34 fn create(self: &Allocator, comptime T: type) !&T {34 fn create(self: &Allocator, comptime T: type) !&T {
35 if (@sizeOf(T) == 0) return &{};
35 const slice = try self.alloc(T, 1);36 const slice = try self.alloc(T, 1);
36 return &slice[0];37 return &slice[0];
37 }38 }
3839
40 // TODO once #733 is solved, this will replace create
41 fn construct(self: &Allocator, init: var) t: {
42 // TODO this is a workaround for type getting parsed as Error!&const T
43 const T = @typeOf(init).Child;
44 break :t Error!&T;
45 } {
46 const T = @typeOf(init).Child;
47 if (@sizeOf(T) == 0) return &{};
48 const slice = try self.alloc(T, 1);
49 const ptr = &slice[0];
50 *ptr = *init;
51 return ptr;
52 }
53
39 fn destroy(self: &Allocator, ptr: var) void {54 fn destroy(self: &Allocator, ptr: var) void {
40 self.free(ptr[0..1]);55 self.free(ptr[0..1]);
41 }56 }
...@@ -53,7 +68,7 @@ pub const Allocator = struct {...@@ -53,7 +68,7 @@ pub const Allocator = struct {
53 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;68 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
54 const byte_slice = try self.allocFn(self, byte_count, alignment);69 const byte_slice = try self.allocFn(self, byte_count, alignment);
55 assert(byte_slice.len == byte_count);70 assert(byte_slice.len == byte_count);
56 // This loop should get optimized out in ReleaseFast mode71 // This loop gets optimized out in ReleaseFast mode
57 for (byte_slice) |*byte| {72 for (byte_slice) |*byte| {
58 *byte = undefined;73 *byte = undefined;
59 }74 }
...@@ -80,7 +95,7 @@ pub const Allocator = struct {...@@ -80,7 +95,7 @@ pub const Allocator = struct {
80 const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);95 const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
81 assert(byte_slice.len == byte_count);96 assert(byte_slice.len == byte_count);
82 if (n > old_mem.len) {97 if (n > old_mem.len) {
83 // This loop should get optimized out in ReleaseFast mode98 // This loop gets optimized out in ReleaseFast mode
84 for (byte_slice[old_byte_slice.len..]) |*byte| {99 for (byte_slice[old_byte_slice.len..]) |*byte| {
85 *byte = undefined;100 *byte = undefined;
86 }101 }
std/os/darwin.zig+4-4
...@@ -184,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize {...@@ -184,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize {
184 return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));184 return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));
185}185}
186186
187pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,187pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32,
188 offset: isize) usize188 offset: isize) usize
189{189{
190 const ptr_result = c.mmap(@ptrCast(&c_void, address), length,190 const ptr_result = c.mmap(@ptrCast(&c_void, address), length,
...@@ -193,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,...@@ -193,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
193 return errnoWrap(isize_result);193 return errnoWrap(isize_result);
194}194}
195195
196pub fn munmap(address: &u8, length: usize) usize {196pub fn munmap(address: usize, length: usize) usize {
197 return errnoWrap(c.munmap(@ptrCast(&c_void, address), length));197 return errnoWrap(c.munmap(@intToPtr(&c_void, address), length));
198}198}
199199
200pub fn unlink(path: &const u8) usize {200pub fn unlink(path: &const u8) usize {
...@@ -341,4 +341,4 @@ pub const timeval = c.timeval;...@@ -341,4 +341,4 @@ pub const timeval = c.timeval;
341pub const mach_timebase_info_data = c.mach_timebase_info_data;341pub const mach_timebase_info_data = c.mach_timebase_info_data;
342342
343pub const mach_absolute_time = c.mach_absolute_time;343pub const mach_absolute_time = c.mach_absolute_time;
344pub const mach_timebase_info = c.mach_timebase_info;
\ No newline at end of file
344pub const mach_timebase_info = c.mach_timebase_info;
std/os/index.zig+357-170
...@@ -2,6 +2,11 @@ const std = @import("../index.zig");...@@ -2,6 +2,11 @@ const std = @import("../index.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Os = builtin.Os;3const Os = builtin.Os;
4const is_windows = builtin.os == Os.windows;4const is_windows = builtin.os == Os.windows;
5const is_posix = switch (builtin.os) {
6 builtin.Os.linux,
7 builtin.Os.macosx => true,
8 else => false,
9};
5const os = this;10const os = this;
611
7test "std.os" {12test "std.os" {
...@@ -20,9 +25,10 @@ pub const windows = @import("windows/index.zig");...@@ -20,9 +25,10 @@ pub const windows = @import("windows/index.zig");
20pub const darwin = @import("darwin.zig");25pub const darwin = @import("darwin.zig");
21pub const linux = @import("linux/index.zig");26pub const linux = @import("linux/index.zig");
22pub const zen = @import("zen.zig");27pub const zen = @import("zen.zig");
23pub const posix = switch(builtin.os) {28pub const posix = switch (builtin.os) {
24 Os.linux => linux,29 Os.linux => linux,
25 Os.macosx, Os.ios => darwin,30 Os.macosx,
31 Os.ios => darwin,
26 Os.zen => zen,32 Os.zen => zen,
27 else => @compileError("Unsupported OS"),33 else => @compileError("Unsupported OS"),
28};34};
...@@ -54,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite;...@@ -54,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite;
54pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;60pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
55pub const windowsOpen = windows_util.windowsOpen;61pub const windowsOpen = windows_util.windowsOpen;
56pub const windowsLoadDll = windows_util.windowsLoadDll;62pub const windowsLoadDll = windows_util.windowsLoadDll;
57pub const windowsUnloadDll = windows_util.windowsUnloadDll; 63pub const windowsUnloadDll = windows_util.windowsUnloadDll;
58pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;64pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
5965
60pub const WindowsWaitError = windows_util.WaitError;66pub const WindowsWaitError = windows_util.WaitError;
...@@ -93,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void {...@@ -93,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void {
93 switch (err) {99 switch (err) {
94 posix.EINVAL => unreachable,100 posix.EINVAL => unreachable,
95 posix.EFAULT => unreachable,101 posix.EFAULT => unreachable,
96 posix.EINTR => continue,102 posix.EINTR => continue,
97 posix.ENOSYS => {103 posix.ENOSYS => {
98 const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);104 const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
99 defer close(fd);105 defer close(fd);
100106
101 try posixRead(fd, buf);107 try posixRead(fd, buf);
...@@ -106,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void {...@@ -106,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void {
106 }112 }
107 return;113 return;
108 },114 },
109 Os.macosx, Os.ios => {115 Os.macosx,
110 const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);116 Os.ios => {
117 const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
111 defer close(fd);118 defer close(fd);
112119
113 try posixRead(fd, buf);120 try posixRead(fd, buf);
...@@ -130,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void {...@@ -130,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void {
130 }137 }
131 },138 },
132 Os.zen => {139 Os.zen => {
133 const randomness = []u8 {42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45};140 const randomness = []u8 {
141 42,
142 1,
143 7,
144 12,
145 22,
146 17,
147 99,
148 16,
149 26,
150 87,
151 41,
152 45,
153 };
134 var i: usize = 0;154 var i: usize = 0;
135 while (i < buf.len) : (i += 1) {155 while (i < buf.len) : (i += 1) {
136 if (i > randomness.len) return error.Unknown;156 if (i > randomness.len) return error.Unknown;
...@@ -155,7 +175,9 @@ pub fn abort() noreturn {...@@ -155,7 +175,9 @@ pub fn abort() noreturn {
155 c.abort();175 c.abort();
156 }176 }
157 switch (builtin.os) {177 switch (builtin.os) {
158 Os.linux, Os.macosx, Os.ios => {178 Os.linux,
179 Os.macosx,
180 Os.ios => {
159 _ = posix.raise(posix.SIGABRT);181 _ = posix.raise(posix.SIGABRT);
160 _ = posix.raise(posix.SIGKILL);182 _ = posix.raise(posix.SIGKILL);
161 while (true) {}183 while (true) {}
...@@ -177,7 +199,9 @@ pub fn exit(status: u8) noreturn {...@@ -177,7 +199,9 @@ pub fn exit(status: u8) noreturn {
177 c.exit(status);199 c.exit(status);
178 }200 }
179 switch (builtin.os) {201 switch (builtin.os) {
180 Os.linux, Os.macosx, Os.ios => {202 Os.linux,
203 Os.macosx,
204 Os.ios => {
181 posix.exit(status);205 posix.exit(status);
182 },206 },
183 Os.windows => {207 Os.windows => {
...@@ -226,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void {...@@ -226,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
226 if (err > 0) {250 if (err > 0) {
227 return switch (err) {251 return switch (err) {
228 posix.EINTR => continue,252 posix.EINTR => continue,
229 posix.EINVAL, posix.EFAULT => unreachable,253 posix.EINVAL,
254 posix.EFAULT => unreachable,
230 posix.EAGAIN => error.WouldBlock,255 posix.EAGAIN => error.WouldBlock,
231 posix.EBADF => error.FileClosed,256 posix.EBADF => error.FileClosed,
232 posix.EIO => error.InputOutput,257 posix.EIO => error.InputOutput,
233 posix.EISDIR => error.IsDir,258 posix.EISDIR => error.IsDir,
234 posix.ENOBUFS, posix.ENOMEM => error.SystemResources,259 posix.ENOBUFS,
260 posix.ENOMEM => error.SystemResources,
235 else => unexpectedErrorPosix(err),261 else => unexpectedErrorPosix(err),
236 };262 };
237 }263 }
...@@ -265,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {...@@ -265,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
265 const write_err = posix.getErrno(rc);291 const write_err = posix.getErrno(rc);
266 if (write_err > 0) {292 if (write_err > 0) {
267 return switch (write_err) {293 return switch (write_err) {
268 posix.EINTR => continue,294 posix.EINTR => continue,
269 posix.EINVAL, posix.EFAULT => unreachable,295 posix.EINVAL,
296 posix.EFAULT => unreachable,
270 posix.EAGAIN => PosixWriteError.WouldBlock,297 posix.EAGAIN => PosixWriteError.WouldBlock,
271 posix.EBADF => PosixWriteError.FileClosed,298 posix.EBADF => PosixWriteError.FileClosed,
272 posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,299 posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
273 posix.EDQUOT => PosixWriteError.DiskQuota,300 posix.EDQUOT => PosixWriteError.DiskQuota,
274 posix.EFBIG => PosixWriteError.FileTooBig,301 posix.EFBIG => PosixWriteError.FileTooBig,
275 posix.EIO => PosixWriteError.InputOutput,302 posix.EIO => PosixWriteError.InputOutput,
276 posix.ENOSPC => PosixWriteError.NoSpaceLeft,303 posix.ENOSPC => PosixWriteError.NoSpaceLeft,
277 posix.EPERM => PosixWriteError.AccessDenied,304 posix.EPERM => PosixWriteError.AccessDenied,
278 posix.EPIPE => PosixWriteError.BrokenPipe,305 posix.EPIPE => PosixWriteError.BrokenPipe,
279 else => unexpectedErrorPosix(write_err),306 else => unexpectedErrorPosix(write_err),
280 };307 };
281 }308 }
282 index += rc;309 index += rc;
...@@ -322,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {...@@ -322,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {
322 posix.EFAULT => unreachable,349 posix.EFAULT => unreachable,
323 posix.EINVAL => unreachable,350 posix.EINVAL => unreachable,
324 posix.EACCES => return PosixOpenError.AccessDenied,351 posix.EACCES => return PosixOpenError.AccessDenied,
325 posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig,352 posix.EFBIG,
353 posix.EOVERFLOW => return PosixOpenError.FileTooBig,
326 posix.EISDIR => return PosixOpenError.IsDir,354 posix.EISDIR => return PosixOpenError.IsDir,
327 posix.ELOOP => return PosixOpenError.SymLinkLoop,355 posix.ELOOP => return PosixOpenError.SymLinkLoop,
328 posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,356 posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,
...@@ -347,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void {...@@ -347,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
347 const err = posix.getErrno(posix.dup2(old_fd, new_fd));375 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
348 if (err > 0) {376 if (err > 0) {
349 return switch (err) {377 return switch (err) {
350 posix.EBUSY, posix.EINTR => continue,378 posix.EBUSY,
379 posix.EINTR => continue,
351 posix.EMFILE => error.ProcessFdQuotaExceeded,380 posix.EMFILE => error.ProcessFdQuotaExceeded,
352 posix.EINVAL => unreachable,381 posix.EINVAL => unreachable,
353 else => unexpectedErrorPosix(err),382 else => unexpectedErrorPosix(err),
...@@ -382,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap)...@@ -382,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap)
382411
383pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {412pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
384 for (envp_buf) |env| {413 for (envp_buf) |env| {
385 const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break;414 const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break;
386 allocator.free(env_buf);415 allocator.free(env_buf);
387 }416 }
388 allocator.free(envp_buf);417 allocator.free(envp_buf);
...@@ -393,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {...@@ -393,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
393/// pointers after the args and after the environment variables.422/// pointers after the args and after the environment variables.
394/// `argv[0]` is the executable path.423/// `argv[0]` is the executable path.
395/// This function also uses the PATH environment variable to get the full path to the executable.424/// This function also uses the PATH environment variable to get the full path to the executable.
396pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,425pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void {
397 allocator: &Allocator) !void
398{
399 const argv_buf = try allocator.alloc(?&u8, argv.len + 1);426 const argv_buf = try allocator.alloc(?&u8, argv.len + 1);
400 mem.set(?&u8, argv_buf, null);427 mem.set(?&u8, argv_buf, null);
401 defer {428 defer {
...@@ -434,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,...@@ -434,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
434 while (it.next()) |search_path| {461 while (it.next()) |search_path| {
435 mem.copy(u8, path_buf, search_path);462 mem.copy(u8, path_buf, search_path);
436 path_buf[search_path.len] = '/';463 path_buf[search_path.len] = '/';
437 mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path);464 mem.copy(u8, path_buf[search_path.len + 1..], exe_path);
438 path_buf[search_path.len + exe_path.len + 1] = 0;465 path_buf[search_path.len + exe_path.len + 1] = 0;
439 err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));466 err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
440 assert(err > 0);467 assert(err > 0);
...@@ -466,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {...@@ -466,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
466 assert(err > 0);493 assert(err > 0);
467 return switch (err) {494 return switch (err) {
468 posix.EFAULT => unreachable,495 posix.EFAULT => unreachable,
469 posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources,496 posix.E2BIG,
470 posix.EACCES, posix.EPERM => error.AccessDenied,497 posix.EMFILE,
471 posix.EINVAL, posix.ENOEXEC => error.InvalidExe,498 posix.ENAMETOOLONG,
472 posix.EIO, posix.ELOOP => error.FileSystem,499 posix.ENFILE,
500 posix.ENOMEM => error.SystemResources,
501 posix.EACCES,
502 posix.EPERM => error.AccessDenied,
503 posix.EINVAL,
504 posix.ENOEXEC => error.InvalidExe,
505 posix.EIO,
506 posix.ELOOP => error.FileSystem,
473 posix.EISDIR => error.IsDir,507 posix.EISDIR => error.IsDir,
474 posix.ENOENT => error.FileNotFound,508 posix.ENOENT => error.FileNotFound,
475 posix.ENOTDIR => error.NotDir,509 posix.ENOTDIR => error.NotDir,
...@@ -478,7 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {...@@ -478,7 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
478 };512 };
479}513}
480514
481pub var linux_aux_raw = []usize{0} ** 38;515pub var linux_aux_raw = []usize {0} ** 38;
482pub var posix_environ_raw: []&u8 = undefined;516pub var posix_environ_raw: []&u8 = undefined;
483517
484/// Caller must free result when done.518/// Caller must free result when done.
...@@ -492,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap {...@@ -492,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap {
492526
493 var i: usize = 0;527 var i: usize = 0;
494 while (true) {528 while (true) {
495 if (ptr[i] == 0)529 if (ptr[i] == 0) return result;
496 return result;
497530
498 const key_start = i;531 const key_start = i;
499532
...@@ -531,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {...@@ -531,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
531 var line_i: usize = 0;564 var line_i: usize = 0;
532 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}565 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
533 const this_key = ptr[0..line_i];566 const this_key = ptr[0..line_i];
534 if (!mem.eql(u8, key, this_key))567 if (!mem.eql(u8, key, this_key)) continue;
535 continue;
536568
537 var end_i: usize = line_i;569 var end_i: usize = line_i;
538 while (ptr[end_i] != 0) : (end_i += 1) {}570 while (ptr[end_i] != 0) : (end_i += 1) {}
...@@ -685,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -685,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
685 const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));717 const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
686 if (err > 0) {718 if (err > 0) {
687 return switch (err) {719 return switch (err) {
688 posix.EFAULT, posix.EINVAL => unreachable,720 posix.EFAULT,
689 posix.EACCES, posix.EPERM => error.AccessDenied,721 posix.EINVAL => unreachable,
722 posix.EACCES,
723 posix.EPERM => error.AccessDenied,
690 posix.EDQUOT => error.DiskQuota,724 posix.EDQUOT => error.DiskQuota,
691 posix.EEXIST => error.PathAlreadyExists,725 posix.EEXIST => error.PathAlreadyExists,
692 posix.EIO => error.FileSystem,726 posix.EIO => error.FileSystem,
...@@ -703,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -703,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
703}737}
704738
705// here we replace the standard +/ with -_ so that it can be used in a file name739// here we replace the standard +/ with -_ so that it can be used in a file name
706const b64_fs_encoder = base64.Base64Encoder.init(740const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
707 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
708 base64.standard_pad_char);
709741
710pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {742pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {
711 if (symLink(allocator, existing_path, new_path)) {743 if (symLink(allocator, existing_path, new_path)) {
...@@ -724,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -724,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
724 tmp_path[dirname.len] = os.path.sep;756 tmp_path[dirname.len] = os.path.sep;
725 while (true) {757 while (true) {
726 try getRandomBytes(rand_buf[0..]);758 try getRandomBytes(rand_buf[0..]);
727 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);759 b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
728760
729 if (symLink(allocator, existing_path, tmp_path)) {761 if (symLink(allocator, existing_path, tmp_path)) {
730 return rename(allocator, tmp_path, new_path);762 return rename(allocator, tmp_path, new_path);
...@@ -733,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -733,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
733 else => return err, // TODO zig should know this set does not include PathAlreadyExists765 else => return err, // TODO zig should know this set does not include PathAlreadyExists
734 }766 }
735 }767 }
736
737}768}
738769
739pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {770pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {
...@@ -756,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {...@@ -756,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {
756 return switch (err) {787 return switch (err) {
757 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,788 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
758 windows.ERROR.ACCESS_DENIED => error.AccessDenied,789 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
759 windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,790 windows.ERROR.FILENAME_EXCED_RANGE,
791 windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
760 else => unexpectedErrorWindows(err),792 else => unexpectedErrorWindows(err),
761 };793 };
762 }794 }
...@@ -772,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {...@@ -772,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {
772 const err = posix.getErrno(posix.unlink(buf.ptr));804 const err = posix.getErrno(posix.unlink(buf.ptr));
773 if (err > 0) {805 if (err > 0) {
774 return switch (err) {806 return switch (err) {
775 posix.EACCES, posix.EPERM => error.AccessDenied,807 posix.EACCES,
808 posix.EPERM => error.AccessDenied,
776 posix.EBUSY => error.FileBusy,809 posix.EBUSY => error.FileBusy,
777 posix.EFAULT, posix.EINVAL => unreachable,810 posix.EFAULT,
811 posix.EINVAL => unreachable,
778 posix.EIO => error.FileSystem,812 posix.EIO => error.FileSystem,
779 posix.EISDIR => error.IsDir,813 posix.EISDIR => error.IsDir,
780 posix.ELOOP => error.SymLinkLoop,814 posix.ELOOP => error.SymLinkLoop,
...@@ -852,7 +886,7 @@ pub const AtomicFile = struct {...@@ -852,7 +886,7 @@ pub const AtomicFile = struct {
852886
853 while (true) {887 while (true) {
854 try getRandomBytes(rand_buf[0..]);888 try getRandomBytes(rand_buf[0..]);
855 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);889 b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
856890
857 const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {891 const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
858 error.PathAlreadyExists => continue,892 error.PathAlreadyExists => continue,
...@@ -903,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)...@@ -903,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
903 new_buf[new_path.len] = 0;937 new_buf[new_path.len] = 0;
904938
905 if (is_windows) {939 if (is_windows) {
906 const flags = windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH;940 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
907 if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {941 if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {
908 const err = windows.GetLastError();942 const err = windows.GetLastError();
909 return switch (err) {943 return switch (err) {
...@@ -914,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)...@@ -914,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
914 const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));948 const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
915 if (err > 0) {949 if (err > 0) {
916 return switch (err) {950 return switch (err) {
917 posix.EACCES, posix.EPERM => error.AccessDenied,951 posix.EACCES,
952 posix.EPERM => error.AccessDenied,
918 posix.EBUSY => error.FileBusy,953 posix.EBUSY => error.FileBusy,
919 posix.EDQUOT => error.DiskQuota,954 posix.EDQUOT => error.DiskQuota,
920 posix.EFAULT, posix.EINVAL => unreachable,955 posix.EFAULT,
956 posix.EINVAL => unreachable,
921 posix.EISDIR => error.IsDir,957 posix.EISDIR => error.IsDir,
922 posix.ELOOP => error.SymLinkLoop,958 posix.ELOOP => error.SymLinkLoop,
923 posix.EMLINK => error.LinkQuotaExceeded,959 posix.EMLINK => error.LinkQuotaExceeded,
...@@ -926,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)...@@ -926,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
926 posix.ENOTDIR => error.NotDir,962 posix.ENOTDIR => error.NotDir,
927 posix.ENOMEM => error.SystemResources,963 posix.ENOMEM => error.SystemResources,
928 posix.ENOSPC => error.NoSpaceLeft,964 posix.ENOSPC => error.NoSpaceLeft,
929 posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists,965 posix.EEXIST,
966 posix.ENOTEMPTY => error.PathAlreadyExists,
930 posix.EROFS => error.ReadOnlyFileSystem,967 posix.EROFS => error.ReadOnlyFileSystem,
931 posix.EXDEV => error.RenameAcrossMountPoints,968 posix.EXDEV => error.RenameAcrossMountPoints,
932 else => unexpectedErrorPosix(err),969 else => unexpectedErrorPosix(err),
...@@ -964,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {...@@ -964,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {
964 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));1001 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
965 if (err > 0) {1002 if (err > 0) {
966 return switch (err) {1003 return switch (err) {
967 posix.EACCES, posix.EPERM => error.AccessDenied,1004 posix.EACCES,
1005 posix.EPERM => error.AccessDenied,
968 posix.EDQUOT => error.DiskQuota,1006 posix.EDQUOT => error.DiskQuota,
969 posix.EEXIST => error.PathAlreadyExists,1007 posix.EEXIST => error.PathAlreadyExists,
970 posix.EFAULT => unreachable,1008 posix.EFAULT => unreachable,
...@@ -994,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {...@@ -994,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {
994 // TODO stat the file and return an error if it's not a directory1032 // TODO stat the file and return an error if it's not a directory
995 // this is important because otherwise a dangling symlink1033 // this is important because otherwise a dangling symlink
996 // could cause an infinite loop1034 // could cause an infinite loop
997 if (end_index == resolved_path.len)1035 if (end_index == resolved_path.len) return;
998 return;
999 } else if (err == error.FileNotFound) {1036 } else if (err == error.FileNotFound) {
1000 // march end_index backward until next path component1037 // march end_index backward until next path component
1001 while (true) {1038 while (true) {
1002 end_index -= 1;1039 end_index -= 1;
1003 if (os.path.isSep(resolved_path[end_index]))1040 if (os.path.isSep(resolved_path[end_index])) break;
1004 break;
1005 }1041 }
1006 continue;1042 continue;
1007 } else {1043 } else {
1008 return err;1044 return err;
1009 }1045 }
1010 };1046 };
1011 if (end_index == resolved_path.len)1047 if (end_index == resolved_path.len) return;
1012 return;
1013 // march end_index forward until next path component1048 // march end_index forward until next path component
1014 while (true) {1049 while (true) {
1015 end_index += 1;1050 end_index += 1;
1016 if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index]))1051 if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break;
1017 break;
1018 }1052 }
1019 }1053 }
1020}1054}
...@@ -1031,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {...@@ -1031,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
1031 const err = posix.getErrno(posix.rmdir(path_buf.ptr));1065 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
1032 if (err > 0) {1066 if (err > 0) {
1033 return switch (err) {1067 return switch (err) {
1034 posix.EACCES, posix.EPERM => error.AccessDenied,1068 posix.EACCES,
1069 posix.EPERM => error.AccessDenied,
1035 posix.EBUSY => error.FileBusy,1070 posix.EBUSY => error.FileBusy,
1036 posix.EFAULT, posix.EINVAL => unreachable,1071 posix.EFAULT,
1072 posix.EINVAL => unreachable,
1037 posix.ELOOP => error.SymLinkLoop,1073 posix.ELOOP => error.SymLinkLoop,
1038 posix.ENAMETOOLONG => error.NameTooLong,1074 posix.ENAMETOOLONG => error.NameTooLong,
1039 posix.ENOENT => error.FileNotFound,1075 posix.ENOENT => error.FileNotFound,
1040 posix.ENOMEM => error.SystemResources,1076 posix.ENOMEM => error.SystemResources,
1041 posix.ENOTDIR => error.NotDir,1077 posix.ENOTDIR => error.NotDir,
1042 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,1078 posix.EEXIST,
1079 posix.ENOTEMPTY => error.DirNotEmpty,
1043 posix.EROFS => error.ReadOnlyFileSystem,1080 posix.EROFS => error.ReadOnlyFileSystem,
1044 else => unexpectedErrorPosix(err),1081 else => unexpectedErrorPosix(err),
1045 };1082 };
...@@ -1049,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {...@@ -1049,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
1049/// Whether ::full_path describes a symlink, file, or directory, this function1086/// Whether ::full_path describes a symlink, file, or directory, this function
1050/// removes it. If it cannot be removed because it is a non-empty directory,1087/// removes it. If it cannot be removed because it is a non-empty directory,
1051/// this function recursively removes its entries and then tries again.1088/// this function recursively removes its entries and then tries again.
1052// TODO non-recursive implementation1089/// TODO non-recursive implementation
1053const DeleteTreeError = error {1090const DeleteTreeError = error {
1054 OutOfMemory,1091 OutOfMemory,
1055 AccessDenied,1092 AccessDenied,
...@@ -1091,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!...@@ -1091,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
1091 error.NotDir,1128 error.NotDir,
1092 error.FileSystem,1129 error.FileSystem,
1093 error.FileBusy,1130 error.FileBusy,
1094 error.Unexpected1131 error.Unexpected => return err,
1095 => return err,
1096 }1132 }
1097 {1133 {
1098 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {1134 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
...@@ -1116,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!...@@ -1116,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
1116 error.SystemResources,1152 error.SystemResources,
1117 error.NoSpaceLeft,1153 error.NoSpaceLeft,
1118 error.PathAlreadyExists,1154 error.PathAlreadyExists,
1119 error.Unexpected1155 error.Unexpected => return err,
1120 => return err,
1121 };1156 };
1122 defer dir.close();1157 defer dir.close();
11231158
...@@ -1147,7 +1182,8 @@ pub const Dir = struct {...@@ -1147,7 +1182,8 @@ pub const Dir = struct {
1147 end_index: usize,1182 end_index: usize,
11481183
1149 const darwin_seek_t = switch (builtin.os) {1184 const darwin_seek_t = switch (builtin.os) {
1150 Os.macosx, Os.ios => i64,1185 Os.macosx,
1186 Os.ios => i64,
1151 else => void,1187 else => void,
1152 };1188 };
11531189
...@@ -1171,12 +1207,14 @@ pub const Dir = struct {...@@ -1171,12 +1207,14 @@ pub const Dir = struct {
1171 pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {1207 pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
1172 const fd = switch (builtin.os) {1208 const fd = switch (builtin.os) {
1173 Os.windows => @compileError("TODO support Dir.open for windows"),1209 Os.windows => @compileError("TODO support Dir.open for windows"),
1174 Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),1210 Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
1175 Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),1211 Os.macosx,
1212 Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
1176 else => @compileError("Dir.open is not supported for this platform"),1213 else => @compileError("Dir.open is not supported for this platform"),
1177 };1214 };
1178 const darwin_seek_init = switch (builtin.os) {1215 const darwin_seek_init = switch (builtin.os) {
1179 Os.macosx, Os.ios => 0,1216 Os.macosx,
1217 Os.ios => 0,
1180 else => {},1218 else => {},
1181 };1219 };
1182 return Dir {1220 return Dir {
...@@ -1199,7 +1237,8 @@ pub const Dir = struct {...@@ -1199,7 +1237,8 @@ pub const Dir = struct {
1199 pub fn next(self: &Dir) !?Entry {1237 pub fn next(self: &Dir) !?Entry {
1200 switch (builtin.os) {1238 switch (builtin.os) {
1201 Os.linux => return self.nextLinux(),1239 Os.linux => return self.nextLinux(),
1202 Os.macosx, Os.ios => return self.nextDarwin(),1240 Os.macosx,
1241 Os.ios => return self.nextDarwin(),
1203 Os.windows => return self.nextWindows(),1242 Os.windows => return self.nextWindows(),
1204 else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),1243 else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
1205 }1244 }
...@@ -1213,12 +1252,13 @@ pub const Dir = struct {...@@ -1213,12 +1252,13 @@ pub const Dir = struct {
1213 }1252 }
12141253
1215 while (true) {1254 while (true) {
1216 const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len,1255 const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek);
1217 &self.darwin_seek);
1218 const err = posix.getErrno(result);1256 const err = posix.getErrno(result);
1219 if (err > 0) {1257 if (err > 0) {
1220 switch (err) {1258 switch (err) {
1221 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1259 posix.EBADF,
1260 posix.EFAULT,
1261 posix.ENOTDIR => unreachable,
1222 posix.EINVAL => {1262 posix.EINVAL => {
1223 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);1263 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
1224 continue;1264 continue;
...@@ -1226,14 +1266,13 @@ pub const Dir = struct {...@@ -1226,14 +1266,13 @@ pub const Dir = struct {
1226 else => return unexpectedErrorPosix(err),1266 else => return unexpectedErrorPosix(err),
1227 }1267 }
1228 }1268 }
1229 if (result == 0)1269 if (result == 0) return null;
1230 return null;
1231 self.index = 0;1270 self.index = 0;
1232 self.end_index = result;1271 self.end_index = result;
1233 break;1272 break;
1234 }1273 }
1235 }1274 }
1236 const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);1275 const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
1237 const next_index = self.index + darwin_entry.d_reclen;1276 const next_index = self.index + darwin_entry.d_reclen;
1238 self.index = next_index;1277 self.index = next_index;
12391278
...@@ -1278,7 +1317,9 @@ pub const Dir = struct {...@@ -1278,7 +1317,9 @@ pub const Dir = struct {
1278 const err = posix.getErrno(result);1317 const err = posix.getErrno(result);
1279 if (err > 0) {1318 if (err > 0) {
1280 switch (err) {1319 switch (err) {
1281 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1320 posix.EBADF,
1321 posix.EFAULT,
1322 posix.ENOTDIR => unreachable,
1282 posix.EINVAL => {1323 posix.EINVAL => {
1283 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);1324 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
1284 continue;1325 continue;
...@@ -1286,14 +1327,13 @@ pub const Dir = struct {...@@ -1286,14 +1327,13 @@ pub const Dir = struct {
1286 else => return unexpectedErrorPosix(err),1327 else => return unexpectedErrorPosix(err),
1287 }1328 }
1288 }1329 }
1289 if (result == 0)1330 if (result == 0) return null;
1290 return null;
1291 self.index = 0;1331 self.index = 0;
1292 self.end_index = result;1332 self.end_index = result;
1293 break;1333 break;
1294 }1334 }
1295 }1335 }
1296 const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);1336 const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
1297 const next_index = self.index + linux_entry.d_reclen;1337 const next_index = self.index + linux_entry.d_reclen;
1298 self.index = next_index;1338 self.index = next_index;
12991339
...@@ -1362,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {...@@ -1362,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
1362 if (err > 0) {1402 if (err > 0) {
1363 return switch (err) {1403 return switch (err) {
1364 posix.EACCES => error.AccessDenied,1404 posix.EACCES => error.AccessDenied,
1365 posix.EFAULT, posix.EINVAL => unreachable,1405 posix.EFAULT,
1406 posix.EINVAL => unreachable,
1366 posix.EIO => error.FileSystem,1407 posix.EIO => error.FileSystem,
1367 posix.ELOOP => error.SymLinkLoop,1408 posix.ELOOP => error.SymLinkLoop,
1368 posix.ENAMETOOLONG => error.NameTooLong,1409 posix.ENAMETOOLONG => error.NameTooLong,
...@@ -1455,8 +1496,7 @@ pub const ArgIteratorPosix = struct {...@@ -1455,8 +1496,7 @@ pub const ArgIteratorPosix = struct {
1455 }1496 }
14561497
1457 pub fn next(self: &ArgIteratorPosix) ?[]const u8 {1498 pub fn next(self: &ArgIteratorPosix) ?[]const u8 {
1458 if (self.index == self.count)1499 if (self.index == self.count) return null;
1459 return null;
14601500
1461 const s = raw[self.index];1501 const s = raw[self.index];
1462 self.index += 1;1502 self.index += 1;
...@@ -1464,8 +1504,7 @@ pub const ArgIteratorPosix = struct {...@@ -1464,8 +1504,7 @@ pub const ArgIteratorPosix = struct {
1464 }1504 }
14651505
1466 pub fn skip(self: &ArgIteratorPosix) bool {1506 pub fn skip(self: &ArgIteratorPosix) bool {
1467 if (self.index == self.count)1507 if (self.index == self.count) return false;
1468 return false;
14691508
1470 self.index += 1;1509 self.index += 1;
1471 return true;1510 return true;
...@@ -1483,7 +1522,9 @@ pub const ArgIteratorWindows = struct {...@@ -1483,7 +1522,9 @@ pub const ArgIteratorWindows = struct {
1483 quote_count: usize,1522 quote_count: usize,
1484 seen_quote_count: usize,1523 seen_quote_count: usize,
14851524
1486 pub const NextError = error{OutOfMemory};1525 pub const NextError = error {
1526 OutOfMemory,
1527 };
14871528
1488 pub fn init() ArgIteratorWindows {1529 pub fn init() ArgIteratorWindows {
1489 return initWithCmdLine(windows.GetCommandLineA());1530 return initWithCmdLine(windows.GetCommandLineA());
...@@ -1506,7 +1547,8 @@ pub const ArgIteratorWindows = struct {...@@ -1506,7 +1547,8 @@ pub const ArgIteratorWindows = struct {
1506 const byte = self.cmd_line[self.index];1547 const byte = self.cmd_line[self.index];
1507 switch (byte) {1548 switch (byte) {
1508 0 => return null,1549 0 => return null,
1509 ' ', '\t' => continue,1550 ' ',
1551 '\t' => continue,
1510 else => break,1552 else => break,
1511 }1553 }
1512 }1554 }
...@@ -1520,7 +1562,8 @@ pub const ArgIteratorWindows = struct {...@@ -1520,7 +1562,8 @@ pub const ArgIteratorWindows = struct {
1520 const byte = self.cmd_line[self.index];1562 const byte = self.cmd_line[self.index];
1521 switch (byte) {1563 switch (byte) {
1522 0 => return false,1564 0 => return false,
1523 ' ', '\t' => continue,1565 ' ',
1566 '\t' => continue,
1524 else => break,1567 else => break,
1525 }1568 }
1526 }1569 }
...@@ -1539,7 +1582,8 @@ pub const ArgIteratorWindows = struct {...@@ -1539,7 +1582,8 @@ pub const ArgIteratorWindows = struct {
1539 '\\' => {1582 '\\' => {
1540 backslash_count += 1;1583 backslash_count += 1;
1541 },1584 },
1542 ' ', '\t' => {1585 ' ',
1586 '\t' => {
1543 if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {1587 if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
1544 return true;1588 return true;
1545 }1589 }
...@@ -1579,7 +1623,8 @@ pub const ArgIteratorWindows = struct {...@@ -1579,7 +1623,8 @@ pub const ArgIteratorWindows = struct {
1579 '\\' => {1623 '\\' => {
1580 backslash_count += 1;1624 backslash_count += 1;
1581 },1625 },
1582 ' ', '\t' => {1626 ' ',
1627 '\t' => {
1583 try self.emitBackslashes(&buf, backslash_count);1628 try self.emitBackslashes(&buf, backslash_count);
1584 backslash_count = 0;1629 backslash_count = 0;
1585 if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {1630 if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
...@@ -1623,7 +1668,6 @@ pub const ArgIteratorWindows = struct {...@@ -1623,7 +1668,6 @@ pub const ArgIteratorWindows = struct {
1623 }1668 }
1624 }1669 }
1625 }1670 }
1626
1627};1671};
16281672
1629pub const ArgIterator = struct {1673pub const ArgIterator = struct {
...@@ -1638,7 +1682,7 @@ pub const ArgIterator = struct {...@@ -1638,7 +1682,7 @@ pub const ArgIterator = struct {
1638 }1682 }
16391683
1640 pub const NextError = ArgIteratorWindows.NextError;1684 pub const NextError = ArgIteratorWindows.NextError;
1641 1685
1642 /// You must free the returned memory when done.1686 /// You must free the returned memory when done.
1643 pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {1687 pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {
1644 if (builtin.os == Os.windows) {1688 if (builtin.os == Os.windows) {
...@@ -1713,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void {...@@ -1713,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void {
1713}1757}
17141758
1715test "windows arg parsing" {1759test "windows arg parsing" {
1716 testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});1760 testWindowsCmdLine(c"a b\tc d", [][]const u8 {
1717 testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});1761 "a",
1718 testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{"a\\\\\\b", "de fg", "h"});1762 "b",
1719 testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{"a\\\"b", "c", "d"});1763 "c",
1720 testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{"a\\\\b c", "d", "e"});1764 "d",
1721 testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{"a", "b", "c", "\"d", "f"});1765 });
17221766 testWindowsCmdLine(c"\"abc\" d e", [][]const u8 {
1723 testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"",1767 "abc",
1724 [][]const u8{".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help"});1768 "d",
1769 "e",
1770 });
1771 testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 {
1772 "a\\\\\\b",
1773 "de fg",
1774 "h",
1775 });
1776 testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 {
1777 "a\\\"b",
1778 "c",
1779 "d",
1780 });
1781 testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 {
1782 "a\\\\b c",
1783 "d",
1784 "e",
1785 });
1786 testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 {
1787 "a",
1788 "b",
1789 "c",
1790 "\"d",
1791 "f",
1792 });
1793
1794 testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 {
1795 ".\\..\\zig-cache\\build",
1796 "bin\\zig.exe",
1797 ".\\..",
1798 ".\\..\\zig-cache",
1799 "--help",
1800 });
1725}1801}
17261802
1727fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void {1803fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void {
...@@ -1768,7 +1844,8 @@ pub fn openSelfExe() !os.File {...@@ -1768,7 +1844,8 @@ pub fn openSelfExe() !os.File {
1768 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);1844 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1769 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);1845 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
1770 },1846 },
1771 Os.macosx, Os.ios => {1847 Os.macosx,
1848 Os.ios => {
1772 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;1849 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
1773 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);1850 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1774 const self_exe_path = try selfExePath(&fixed_allocator.allocator);1851 const self_exe_path = try selfExePath(&fixed_allocator.allocator);
...@@ -1780,8 +1857,10 @@ pub fn openSelfExe() !os.File {...@@ -1780,8 +1857,10 @@ pub fn openSelfExe() !os.File {
17801857
1781test "openSelfExe" {1858test "openSelfExe" {
1782 switch (builtin.os) {1859 switch (builtin.os) {
1783 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),1860 Os.linux,
1784 else => return, // Unsupported OS.1861 Os.macosx,
1862 Os.ios => (try openSelfExe()).close(),
1863 else => return, // Unsupported OS.
1785 }1864 }
1786}1865}
17871866
...@@ -1818,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {...@@ -1818,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {
1818 try out_path.resize(new_len);1897 try out_path.resize(new_len);
1819 }1898 }
1820 },1899 },
1821 Os.macosx, Os.ios => {1900 Os.macosx,
1901 Os.ios => {
1822 var u32_len: u32 = 0;1902 var u32_len: u32 = 0;
1823 const ret1 = c._NSGetExecutablePath(undefined, &u32_len);1903 const ret1 = c._NSGetExecutablePath(undefined, &u32_len);
1824 assert(ret1 != 0);1904 assert(ret1 != 0);
...@@ -1846,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {...@@ -1846,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {
1846 const dir = path.dirname(full_exe_path);1926 const dir = path.dirname(full_exe_path);
1847 return allocator.shrink(u8, full_exe_path, dir.len);1927 return allocator.shrink(u8, full_exe_path, dir.len);
1848 },1928 },
1849 Os.windows, Os.macosx, Os.ios => {1929 Os.windows,
1930 Os.macosx,
1931 Os.ios => {
1850 const self_exe_path = try selfExePath(allocator);1932 const self_exe_path = try selfExePath(allocator);
1851 errdefer allocator.free(self_exe_path);1933 errdefer allocator.free(self_exe_path);
1852 const dirname = os.path.dirname(self_exe_path);1934 const dirname = os.path.dirname(self_exe_path);
...@@ -1903,7 +1985,8 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {...@@ -1903,7 +1985,8 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {
1903 posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,1985 posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,
1904 posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,1986 posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,
1905 posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,1987 posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,
1906 posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources,1988 posix.ENOBUFS,
1989 posix.ENOMEM => return PosixSocketError.SystemResources,
1907 posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,1990 posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,
1908 else => return unexpectedErrorPosix(err),1991 else => return unexpectedErrorPosix(err),
1909 }1992 }
...@@ -1934,7 +2017,7 @@ pub const PosixBindError = error {...@@ -1934,7 +2017,7 @@ pub const PosixBindError = error {
19342017
1935 /// A nonexistent interface was requested or the requested address was not local.2018 /// A nonexistent interface was requested or the requested address was not local.
1936 AddressNotAvailable,2019 AddressNotAvailable,
1937 2020
1938 /// addr points outside the user's accessible address space.2021 /// addr points outside the user's accessible address space.
1939 PageFault,2022 PageFault,
19402023
...@@ -2023,7 +2106,7 @@ pub const PosixAcceptError = error {...@@ -2023,7 +2106,7 @@ pub const PosixAcceptError = error {
2023 FileDescriptorClosed,2106 FileDescriptorClosed,
20242107
2025 ConnectionAborted,2108 ConnectionAborted,
2026 2109
2027 /// The addr argument is not in a writable part of the user address space.2110 /// The addr argument is not in a writable part of the user address space.
2028 PageFault,2111 PageFault,
20292112
...@@ -2036,7 +2119,7 @@ pub const PosixAcceptError = error {...@@ -2036,7 +2119,7 @@ pub const PosixAcceptError = error {
20362119
2037 /// The system-wide limit on the total number of open files has been reached.2120 /// The system-wide limit on the total number of open files has been reached.
2038 SystemFdQuotaExceeded,2121 SystemFdQuotaExceeded,
2039 2122
2040 /// Not enough free memory. This often means that the memory allocation is limited2123 /// Not enough free memory. This often means that the memory allocation is limited
2041 /// by the socket buffer limits, not by the system memory.2124 /// by the socket buffer limits, not by the system memory.
2042 SystemResources,2125 SystemResources,
...@@ -2072,7 +2155,8 @@ pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!...@@ -2072,7 +2155,8 @@ pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!
2072 posix.EINVAL => return PosixAcceptError.InvalidSyscall,2155 posix.EINVAL => return PosixAcceptError.InvalidSyscall,
2073 posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,2156 posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,
2074 posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,2157 posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,
2075 posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources,2158 posix.ENOBUFS,
2159 posix.ENOMEM => return PosixAcceptError.SystemResources,
2076 posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,2160 posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,
2077 posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,2161 posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,
2078 posix.EPROTO => return PosixAcceptError.ProtocolFailure,2162 posix.EPROTO => return PosixAcceptError.ProtocolFailure,
...@@ -2283,7 +2367,8 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn...@@ -2283,7 +2367,8 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn
2283 const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));2367 const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
2284 const err = posix.getErrno(rc);2368 const err = posix.getErrno(rc);
2285 switch (err) {2369 switch (err) {
2286 0, posix.EINPROGRESS => return,2370 0,
2371 posix.EINPROGRESS => return,
2287 else => return unexpectedErrorPosix(err),2372 else => return unexpectedErrorPosix(err),
22882373
2289 posix.EACCES => return PosixConnectError.PermissionDenied,2374 posix.EACCES => return PosixConnectError.PermissionDenied,
...@@ -2343,24 +2428,58 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {...@@ -2343,24 +2428,58 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
2343}2428}
23442429
2345pub const Thread = struct {2430pub const Thread = struct {
2346 pid: i32,2431 data: Data,
2347 allocator: ?&mem.Allocator,2432
2348 stack: []u8,2433 pub const use_pthreads = is_posix and builtin.link_libc;
2434 const Data = if (use_pthreads) struct {
2435 handle: c.pthread_t,
2436 stack_addr: usize,
2437 stack_len: usize,
2438 } else switch (builtin.os) {
2439 builtin.Os.linux => struct {
2440 pid: i32,
2441 stack_addr: usize,
2442 stack_len: usize,
2443 },
2444 builtin.Os.windows => struct {
2445 handle: windows.HANDLE,
2446 alloc_start: &c_void,
2447 heap_handle: windows.HANDLE,
2448 },
2449 else => @compileError("Unsupported OS"),
2450 };
23492451
2350 pub fn wait(self: &const Thread) void {2452 pub fn wait(self: &const Thread) void {
2351 while (true) {2453 if (use_pthreads) {
2352 const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst);2454 const err = c.pthread_join(self.data.handle, null);
2353 if (pid_value == 0) break;2455 switch (err) {
2354 const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);2456 0 => {},
2355 switch (linux.getErrno(rc)) {2457 posix.EINVAL => unreachable,
2356 0 => continue,2458 posix.ESRCH => unreachable,
2357 posix.EINTR => continue,2459 posix.EDEADLK => unreachable,
2358 posix.EAGAIN => continue,
2359 else => unreachable,2460 else => unreachable,
2360 }2461 }
2361 }2462 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
2362 if (self.allocator) |a| {2463 } else switch (builtin.os) {
2363 a.free(self.stack);2464 builtin.Os.linux => {
2465 while (true) {
2466 const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst);
2467 if (pid_value == 0) break;
2468 const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null);
2469 switch (linux.getErrno(rc)) {
2470 0 => continue,
2471 posix.EINTR => continue,
2472 posix.EAGAIN => continue,
2473 else => unreachable,
2474 }
2475 }
2476 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
2477 },
2478 builtin.Os.windows => {
2479 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
2480 assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0);
2481 },
2482 else => @compileError("Unsupported OS"),
2364 }2483 }
2365 }2484 }
2366};2485};
...@@ -2385,38 +2504,91 @@ pub const SpawnThreadError = error {...@@ -2385,38 +2504,91 @@ pub const SpawnThreadError = error {
2385 /// be copied.2504 /// be copied.
2386 SystemResources,2505 SystemResources,
23872506
2507 /// Not enough userland memory to spawn the thread.
2508 OutOfMemory,
2509
2388 Unexpected,2510 Unexpected,
2389};2511};
23902512
2391pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory};
2392
2393/// caller must call wait on the returned thread2513/// caller must call wait on the returned thread
2394/// fn startFn(@typeOf(context)) T2514/// fn startFn(@typeOf(context)) T
2395/// where T is u8, noreturn, void, or !void2515/// where T is u8, noreturn, void, or !void
2396pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread {2516/// caller must call wait on the returned thread
2517pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread {
2397 // TODO compile-time call graph analysis to determine stack upper bound2518 // TODO compile-time call graph analysis to determine stack upper bound
2398 // https://github.com/zig-lang/zig/issues/1572519 // https://github.com/zig-lang/zig/issues/157
2399 const default_stack_size = 8 * 1024 * 1024;2520 const default_stack_size = 8 * 1024 * 1024;
2400 const stack_bytes = try allocator.alloc(u8, default_stack_size);
2401 const thread = try spawnThread(stack_bytes, context, startFn);
2402 thread.allocator = allocator;
2403 return thread;
2404}
24052521
2406/// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end
2407/// fn startFn(@typeOf(context)) T
2408/// where T is u8, noreturn, void, or !void
2409/// caller must call wait on the returned thread
2410pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThreadError!&Thread {
2411 const Context = @typeOf(context);2522 const Context = @typeOf(context);
2412 comptime assert(@ArgType(@typeOf(startFn), 0) == Context);2523 comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
24132524
2414 var stack_end: usize = @ptrToInt(stack.ptr) + stack.len;2525 if (builtin.os == builtin.Os.windows) {
2526 const WinThread = struct {
2527 const OuterContext = struct {
2528 thread: Thread,
2529 inner: Context,
2530 };
2531 extern fn threadMain(arg: windows.LPVOID) windows.DWORD {
2532 if (@sizeOf(Context) == 0) {
2533 return startFn({});
2534 } else {
2535 return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg)));
2536 }
2537 }
2538 };
2539
2540 const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory;
2541 const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
2542 const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory;
2543 errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
2544 const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count];
2545 const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
2546 outer_context.inner = context;
2547 outer_context.thread.data.heap_handle = heap_handle;
2548 outer_context.thread.data.alloc_start = bytes_ptr;
2549
2550 const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner);
2551 outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? {
2552 const err = windows.GetLastError();
2553 return switch (err) {
2554 else => os.unexpectedErrorWindows(err),
2555 };
2556 };
2557 return &outer_context.thread;
2558 }
2559
2560 const MainFuncs = struct {
2561 extern fn linuxThreadMain(ctx_addr: usize) u8 {
2562 if (@sizeOf(Context) == 0) {
2563 return startFn({});
2564 } else {
2565 return startFn(*@intToPtr(&const Context, ctx_addr));
2566 }
2567 }
2568 extern fn posixThreadMain(ctx: ?&c_void) ?&c_void {
2569 if (@sizeOf(Context) == 0) {
2570 _ = startFn({});
2571 return null;
2572 } else {
2573 _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx)));
2574 return null;
2575 }
2576 }
2577 };
2578
2579 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
2580
2581 const mmap_len = default_stack_size;
2582 const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
2583 if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
2584 errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
2585
2586 var stack_end: usize = stack_addr + mmap_len;
2415 var arg: usize = undefined;2587 var arg: usize = undefined;
2416 if (@sizeOf(Context) != 0) {2588 if (@sizeOf(Context) != 0) {
2417 stack_end -= @sizeOf(Context);2589 stack_end -= @sizeOf(Context);
2418 stack_end -= stack_end % @alignOf(Context);2590 stack_end -= stack_end % @alignOf(Context);
2419 assert(stack_end >= @ptrToInt(stack.ptr));2591 assert(stack_end >= stack_addr);
2420 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));2592 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));
2421 *context_ptr = context;2593 *context_ptr = context;
2422 arg = stack_end;2594 arg = stack_end;
...@@ -2424,36 +2596,51 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread...@@ -2424,36 +2596,51 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread
24242596
2425 stack_end -= @sizeOf(Thread);2597 stack_end -= @sizeOf(Thread);
2426 stack_end -= stack_end % @alignOf(Thread);2598 stack_end -= stack_end % @alignOf(Thread);
2427 assert(stack_end >= @ptrToInt(stack.ptr));2599 assert(stack_end >= stack_addr);
2428 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));2600 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
2429 thread_ptr.stack = stack;
2430 thread_ptr.allocator = null;
24312601
2432 const threadMain = struct {2602 thread_ptr.data.stack_addr = stack_addr;
2433 extern fn threadMain(ctx_addr: usize) u8 {2603 thread_ptr.data.stack_len = mmap_len;
2434 if (@sizeOf(Context) == 0) {
2435 return startFn({});
2436 } else {
2437 return startFn(*@intToPtr(&const Context, ctx_addr));
2438 }
2439 }
2440 }.threadMain;
24412604
2442 const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND2605 if (builtin.os == builtin.Os.windows) {
2443 | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS2606 // use windows API directly
2444 | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;2607 @compileError("TODO support spawnThread for Windows");
2445 const newtls: usize = 0;2608 } else if (Thread.use_pthreads) {
2446 const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid);2609 // use pthreads
2447 const err = posix.getErrno(rc);2610 var attr: c.pthread_attr_t = undefined;
2448 switch (err) {2611 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
2449 0 => return thread_ptr,2612 defer assert(c.pthread_attr_destroy(&attr) == 0);
2450 posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,2613
2451 posix.EINVAL => unreachable,2614 // align to page
2452 posix.ENOMEM => return SpawnThreadError.SystemResources,2615 stack_end -= stack_end % os.page_size;
2453 posix.ENOSPC => unreachable,2616 assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0);
2454 posix.EPERM => unreachable,2617
2455 posix.EUSERS => unreachable,2618 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
2456 else => return unexpectedErrorPosix(err),2619 switch (err) {
2620 0 => return thread_ptr,
2621 posix.EAGAIN => return SpawnThreadError.SystemResources,
2622 posix.EPERM => unreachable,
2623 posix.EINVAL => unreachable,
2624 else => return unexpectedErrorPosix(usize(err)),
2625 }
2626 } else if (builtin.os == builtin.Os.linux) {
2627 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
2628 const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
2629 const newtls: usize = 0;
2630 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid);
2631 const err = posix.getErrno(rc);
2632 switch (err) {
2633 0 => return thread_ptr,
2634 posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
2635 posix.EINVAL => unreachable,
2636 posix.ENOMEM => return SpawnThreadError.SystemResources,
2637 posix.ENOSPC => unreachable,
2638 posix.EPERM => unreachable,
2639 posix.EUSERS => unreachable,
2640 else => return unexpectedErrorPosix(err),
2641 }
2642 } else {
2643 @compileError("Unsupported OS");
2457 }2644 }
2458}2645}
24592646
std/os/linux/index.zig+3-3
...@@ -706,13 +706,13 @@ pub fn umount2(special: &const u8, flags: u32) usize {...@@ -706,13 +706,13 @@ pub fn umount2(special: &const u8, flags: u32) usize {
706 return syscall2(SYS_umount2, @ptrToInt(special), flags);706 return syscall2(SYS_umount2, @ptrToInt(special), flags);
707}707}
708708
709pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {709pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
710 return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),710 return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
711 @bitCast(usize, offset));711 @bitCast(usize, offset));
712}712}
713713
714pub fn munmap(address: &u8, length: usize) usize {714pub fn munmap(address: usize, length: usize) usize {
715 return syscall2(SYS_munmap, @ptrToInt(address), length);715 return syscall2(SYS_munmap, address, length);
716}716}
717717
718pub fn read(fd: i32, buf: &u8, count: usize) usize {718pub fn read(fd: i32, buf: &u8, count: usize) usize {
std/os/test.zig+4-16
...@@ -44,24 +44,12 @@ test "access file" {...@@ -44,24 +44,12 @@ test "access file" {
44}44}
4545
46test "spawn threads" {46test "spawn threads" {
47 if (builtin.os != builtin.Os.linux) {
48 // TODO implement threads on macos and windows
49 return;
50 }
51
52 var direct_allocator = std.heap.DirectAllocator.init();
53 defer direct_allocator.deinit();
54
55 var shared_ctx: i32 = 1;47 var shared_ctx: i32 = 1;
5648
57 const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1);49 const thread1 = try std.os.spawnThread({}, start1);
58 const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2);50 const thread2 = try std.os.spawnThread(&shared_ctx, start2);
5951 const thread3 = try std.os.spawnThread(&shared_ctx, start2);
60 var stack1: [1024]u8 = undefined;52 const thread4 = try std.os.spawnThread(&shared_ctx, start2);
61 var stack2: [1024]u8 = undefined;
62
63 const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2);
64 const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2);
6553
66 thread1.wait();54 thread1.wait();
67 thread2.wait();55 thread2.wait();
std/os/time.zig+1-1
...@@ -281,7 +281,7 @@ test "os.time.Timer" {...@@ -281,7 +281,7 @@ test "os.time.Timer" {
281 debug.assert(time_0 > 0 and time_0 < margin);281 debug.assert(time_0 > 0 and time_0 < margin);
282 282
283 const time_1 = timer.lap();283 const time_1 = timer.lap();
284 debug.assert(time_1 > time_0);284 debug.assert(time_1 >= time_0);
285 285
286 timer.reset();286 timer.reset();
287 debug.assert(timer.read() < time_1);287 debug.assert(timer.read() < time_1);
std/os/windows/index.zig+6
...@@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp...@@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp
28pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,28pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,
29 dwFlags: DWORD) BOOLEAN;29 dwFlags: DWORD) BOOLEAN;
3030
31
32pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
33
31pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;34pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
3235
33pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;36pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
...@@ -318,6 +321,9 @@ pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;...@@ -318,6 +321,9 @@ pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
318pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;321pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
319pub const HEAP_NO_SERIALIZE = 0x00000001;322pub const HEAP_NO_SERIALIZE = 0x00000001;
320323
324pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD;
325pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
326
321test "import" {327test "import" {
322 _ = @import("util.zig");328 _ = @import("util.zig");
323}329}
std/special/bootstrap_lib.zig+2-1
...@@ -1,9 +1,10 @@...@@ -1,9 +1,10 @@
1// This file is included in the compilation unit when exporting a library on windows.1// This file is included in the compilation unit when exporting a library on windows.
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");
45
5comptime {6comptime {
6 @export("_DllMainCRTStartup", _DllMainCRTStartup);7 @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong);
7}8}
89
9stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD,10stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD,
std/special/compiler_rt/index.zig+17-4
...@@ -32,10 +32,6 @@ comptime {...@@ -32,10 +32,6 @@ comptime {
32 @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);32 @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);
3333
34 @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);34 @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);
35 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
36
37 @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
38 @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
3935
40 @export("__udivsi3", __udivsi3, linkage);36 @export("__udivsi3", __udivsi3, linkage);
41 @export("__udivdi3", __udivdi3, linkage);37 @export("__udivdi3", __udivdi3, linkage);
...@@ -62,9 +58,16 @@ comptime {...@@ -62,9 +58,16 @@ comptime {
62 @export("__chkstk", __chkstk, strong_linkage);58 @export("__chkstk", __chkstk, strong_linkage);
63 @export("___chkstk_ms", ___chkstk_ms, linkage);59 @export("___chkstk_ms", ___chkstk_ms, linkage);
64 }60 }
61 @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage);
62 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);
63 @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage);
65 },64 },
66 else => {},65 else => {},
67 }66 }
67 } else {
68 @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
69 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
70 @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
68 }71 }
69}72}
7073
...@@ -83,6 +86,16 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn...@@ -83,6 +86,16 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
83 }86 }
84}87}
8588
89pub fn setXmm0(comptime T: type, value: T) void {
90 comptime assert(builtin.arch == builtin.Arch.x86_64);
91 const aligned_value: T align(16) = value;
92 asm volatile (
93 \\movaps (%[ptr]), %%xmm0
94 :
95 : [ptr] "r" (&aligned_value)
96 : "xmm0");
97}
98
86extern fn __udivdi3(a: u64, b: u64) u64 {99extern fn __udivdi3(a: u64, b: u64) u64 {
87 @setRuntimeSafety(is_test);100 @setRuntimeSafety(is_test);
88 return __udivmoddi4(a, b, null);101 return __udivmoddi4(a, b, null);
std/special/compiler_rt/udivmodti4.zig+6
...@@ -1,11 +1,17 @@...@@ -1,11 +1,17 @@
1const udivmod = @import("udivmod.zig").udivmod;1const udivmod = @import("udivmod.zig").udivmod;
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const compiler_rt = @import("index.zig");
34
4pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 {5pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 {
5 @setRuntimeSafety(builtin.is_test);6 @setRuntimeSafety(builtin.is_test);
6 return udivmod(u128, a, b, maybe_rem);7 return udivmod(u128, a, b, maybe_rem);
7}8}
89
10pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void {
11 @setRuntimeSafety(builtin.is_test);
12 compiler_rt.setXmm0(u128, udivmod(u128, *a, *b, maybe_rem));
13}
14
9test "import udivmodti4" {15test "import udivmodti4" {
10 _ = @import("udivmodti4_test.zig");16 _ = @import("udivmodti4_test.zig");
11}17}
std/special/compiler_rt/udivti3.zig+7-2
...@@ -1,7 +1,12 @@...@@ -1,7 +1,12 @@
1const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;1const udivmodti4 = @import("udivmodti4.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub extern fn __udivti3(a: u128, b: u128) u128 {4pub extern fn __udivti3(a: u128, b: u128) u128 {
5 @setRuntimeSafety(builtin.is_test);5 @setRuntimeSafety(builtin.is_test);
6 return __udivmodti4(a, b, null);6 return udivmodti4.__udivmodti4(a, b, null);
7}
8
9pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void {
10 @setRuntimeSafety(builtin.is_test);
11 udivmodti4.__udivmodti4_windows_x86_64(a, b, null);
7}12}
std/special/compiler_rt/umodti3.zig+8-2
...@@ -1,9 +1,15 @@...@@ -1,9 +1,15 @@
1const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;1const udivmodti4 = @import("udivmodti4.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const compiler_rt = @import("index.zig");
34
4pub extern fn __umodti3(a: u128, b: u128) u128 {5pub extern fn __umodti3(a: u128, b: u128) u128 {
5 @setRuntimeSafety(builtin.is_test);6 @setRuntimeSafety(builtin.is_test);
6 var r: u128 = undefined;7 var r: u128 = undefined;
7 _ = __udivmodti4(a, b, &r);8 _ = udivmodti4.__udivmodti4(a, b, &r);
8 return r;9 return r;
9}10}
11
12pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void {
13 @setRuntimeSafety(builtin.is_test);
14 compiler_rt.setXmm0(u128, __umodti3(*a, *b));
15}
std/unicode.zig+151-12
...@@ -1,6 +1,16 @@...@@ -1,6 +1,16 @@
1const std = @import("./index.zig");1const std = @import("./index.zig");
2const debug = std.debug;2const debug = std.debug;
33
4/// Returns how many bytes the UTF-8 representation would require
5/// for the given codepoint.
6pub fn utf8CodepointSequenceLength(c: u32) !u3 {
7 if (c < 0x80) return u3(1);
8 if (c < 0x800) return u3(2);
9 if (c < 0x10000) return u3(3);
10 if (c < 0x110000) return u3(4);
11 return error.CodepointTooLarge;
12}
13
4/// Given the first byte of a UTF-8 codepoint,14/// Given the first byte of a UTF-8 codepoint,
5/// returns a number 1-4 indicating the total length of the codepoint in bytes.15/// returns a number 1-4 indicating the total length of the codepoint in bytes.
6/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.16/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.
...@@ -12,11 +22,47 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {...@@ -12,11 +22,47 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {
12 return error.Utf8InvalidStartByte;22 return error.Utf8InvalidStartByte;
13}23}
1424
25/// Encodes the given codepoint into a UTF-8 byte sequence.
26/// c: the codepoint.
27/// out: the out buffer to write to. Must have a len >= utf8CodepointSequenceLength(c).
28/// Errors: if c cannot be encoded in UTF-8.
29/// Returns: the number of bytes written to out.
30pub fn utf8Encode(c: u32, out: []u8) !u3 {
31 const length = try utf8CodepointSequenceLength(c);
32 debug.assert(out.len >= length);
33 switch (length) {
34 // The pattern for each is the same
35 // - Increasing the initial shift by 6 each time
36 // - Each time after the first shorten the shifted
37 // value to a max of 0b111111 (63)
38 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range
39 2 => {
40 out[0] = u8(0b11000000 | (c >> 6));
41 out[1] = u8(0b10000000 | (c & 0b111111));
42 },
43 3 => {
44 if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf;
45 out[0] = u8(0b11100000 | (c >> 12));
46 out[1] = u8(0b10000000 | ((c >> 6) & 0b111111));
47 out[2] = u8(0b10000000 | (c & 0b111111));
48 },
49 4 => {
50 out[0] = u8(0b11110000 | (c >> 18));
51 out[1] = u8(0b10000000 | ((c >> 12) & 0b111111));
52 out[2] = u8(0b10000000 | ((c >> 6) & 0b111111));
53 out[3] = u8(0b10000000 | (c & 0b111111));
54 },
55 else => unreachable,
56 }
57 return length;
58}
59
60const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error;
15/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.61/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
16/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.62/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
17/// If you already know the length at comptime, you can call one of63/// If you already know the length at comptime, you can call one of
18/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.64/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
19pub fn utf8Decode(bytes: []const u8) !u32 {65pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 {
20 return switch (bytes.len) {66 return switch (bytes.len) {
21 1 => u32(bytes[0]),67 1 => u32(bytes[0]),
22 2 => utf8Decode2(bytes),68 2 => utf8Decode2(bytes),
...@@ -25,7 +71,12 @@ pub fn utf8Decode(bytes: []const u8) !u32 {...@@ -25,7 +71,12 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
25 else => unreachable,71 else => unreachable,
26 };72 };
27}73}
28pub fn utf8Decode2(bytes: []const u8) !u32 {74
75const Utf8Decode2Error = error{
76 Utf8ExpectedContinuation,
77 Utf8OverlongEncoding,
78};
79pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 {
29 debug.assert(bytes.len == 2);80 debug.assert(bytes.len == 2);
30 debug.assert(bytes[0] & 0b11100000 == 0b11000000);81 debug.assert(bytes[0] & 0b11100000 == 0b11000000);
31 var value: u32 = bytes[0] & 0b00011111;82 var value: u32 = bytes[0] & 0b00011111;
...@@ -38,7 +89,13 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {...@@ -38,7 +89,13 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
3889
39 return value;90 return value;
40}91}
41pub fn utf8Decode3(bytes: []const u8) !u32 {92
93const Utf8Decode3Error = error{
94 Utf8ExpectedContinuation,
95 Utf8OverlongEncoding,
96 Utf8EncodesSurrogateHalf,
97};
98pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 {
42 debug.assert(bytes.len == 3);99 debug.assert(bytes.len == 3);
43 debug.assert(bytes[0] & 0b11110000 == 0b11100000);100 debug.assert(bytes[0] & 0b11110000 == 0b11100000);
44 var value: u32 = bytes[0] & 0b00001111;101 var value: u32 = bytes[0] & 0b00001111;
...@@ -56,7 +113,13 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {...@@ -56,7 +113,13 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
56113
57 return value;114 return value;
58}115}
59pub fn utf8Decode4(bytes: []const u8) !u32 {116
117const Utf8Decode4Error = error{
118 Utf8ExpectedContinuation,
119 Utf8OverlongEncoding,
120 Utf8CodepointTooLarge,
121};
122pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 {
60 debug.assert(bytes.len == 4);123 debug.assert(bytes.len == 4);
61 debug.assert(bytes[0] & 0b11111000 == 0b11110000);124 debug.assert(bytes[0] & 0b11111000 == 0b11110000);
62 var value: u32 = bytes[0] & 0b00000111;125 var value: u32 = bytes[0] & 0b00000111;
...@@ -158,19 +221,67 @@ const Utf8Iterator = struct {...@@ -158,19 +221,67 @@ const Utf8Iterator = struct {
158 pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {221 pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {
159 const slice = it.nextCodepointSlice() ?? return null;222 const slice = it.nextCodepointSlice() ?? return null;
160223
161 const r = switch (slice.len) {224 switch (slice.len) {
162 1 => u32(slice[0]),225 1 => return u32(slice[0]),
163 2 => utf8Decode2(slice),226 2 => return utf8Decode2(slice) catch unreachable,
164 3 => utf8Decode3(slice),227 3 => return utf8Decode3(slice) catch unreachable,
165 4 => utf8Decode4(slice),228 4 => return utf8Decode4(slice) catch unreachable,
166 else => unreachable,229 else => unreachable,
167 };230 }
168
169 return r catch unreachable;
170 }231 }
171};232};
172233
234test "utf8 encode" {
235 comptime testUtf8Encode() catch unreachable;
236 try testUtf8Encode();
237}
238fn testUtf8Encode() !void {
239 // A few taken from wikipedia a few taken elsewhere
240 var array: [4]u8 = undefined;
241 debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3);
242 debug.assert(array[0] == 0b11100010);
243 debug.assert(array[1] == 0b10000010);
244 debug.assert(array[2] == 0b10101100);
245
246 debug.assert((try utf8Encode(try utf8Decode("$"), array[0..])) == 1);
247 debug.assert(array[0] == 0b00100100);
248
249 debug.assert((try utf8Encode(try utf8Decode("¢"), array[0..])) == 2);
250 debug.assert(array[0] == 0b11000010);
251 debug.assert(array[1] == 0b10100010);
252
253 debug.assert((try utf8Encode(try utf8Decode("𐍈"), array[0..])) == 4);
254 debug.assert(array[0] == 0b11110000);
255 debug.assert(array[1] == 0b10010000);
256 debug.assert(array[2] == 0b10001101);
257 debug.assert(array[3] == 0b10001000);
258}
259
260test "utf8 encode error" {
261 comptime testUtf8EncodeError();
262 testUtf8EncodeError();
263}
264fn testUtf8EncodeError() void {
265 var array: [4]u8 = undefined;
266 testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf);
267 testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf);
268 testErrorEncode(0x110000, array[0..], error.CodepointTooLarge);
269 testErrorEncode(0xffffffff, array[0..], error.CodepointTooLarge);
270}
271
272fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
273 if (utf8Encode(codePoint, array)) |_| {
274 unreachable;
275 } else |err| {
276 debug.assert(err == expectedErr);
277 }
278}
279
173test "utf8 iterator on ascii" {280test "utf8 iterator on ascii" {
281 comptime testUtf8IteratorOnAscii();
282 testUtf8IteratorOnAscii();
283}
284fn testUtf8IteratorOnAscii() void {
174 const s = Utf8View.initComptime("abc");285 const s = Utf8View.initComptime("abc");
175286
176 var it1 = s.iterator();287 var it1 = s.iterator();
...@@ -187,6 +298,10 @@ test "utf8 iterator on ascii" {...@@ -187,6 +298,10 @@ test "utf8 iterator on ascii" {
187}298}
188299
189test "utf8 view bad" {300test "utf8 view bad" {
301 comptime testUtf8ViewBad();
302 testUtf8ViewBad();
303}
304fn testUtf8ViewBad() void {
190 // Compile-time error.305 // Compile-time error.
191 // const s3 = Utf8View.initComptime("\xfe\xf2");306 // const s3 = Utf8View.initComptime("\xfe\xf2");
192307
...@@ -195,6 +310,10 @@ test "utf8 view bad" {...@@ -195,6 +310,10 @@ test "utf8 view bad" {
195}310}
196311
197test "utf8 view ok" {312test "utf8 view ok" {
313 comptime testUtf8ViewOk();
314 testUtf8ViewOk();
315}
316fn testUtf8ViewOk() void {
198 const s = Utf8View.initComptime("東京市");317 const s = Utf8View.initComptime("東京市");
199318
200 var it1 = s.iterator();319 var it1 = s.iterator();
...@@ -211,6 +330,10 @@ test "utf8 view ok" {...@@ -211,6 +330,10 @@ test "utf8 view ok" {
211}330}
212331
213test "bad utf8 slice" {332test "bad utf8 slice" {
333 comptime testBadUtf8Slice();
334 testBadUtf8Slice();
335}
336fn testBadUtf8Slice() void {
214 debug.assert(utf8ValidateSlice("abc"));337 debug.assert(utf8ValidateSlice("abc"));
215 debug.assert(!utf8ValidateSlice("abc\xc0"));338 debug.assert(!utf8ValidateSlice("abc\xc0"));
216 debug.assert(!utf8ValidateSlice("abc\xc0abc"));339 debug.assert(!utf8ValidateSlice("abc\xc0abc"));
...@@ -218,6 +341,10 @@ test "bad utf8 slice" {...@@ -218,6 +341,10 @@ test "bad utf8 slice" {
218}341}
219342
220test "valid utf8" {343test "valid utf8" {
344 comptime testValidUtf8();
345 testValidUtf8();
346}
347fn testValidUtf8() void {
221 testValid("\x00", 0x0);348 testValid("\x00", 0x0);
222 testValid("\x20", 0x20);349 testValid("\x20", 0x20);
223 testValid("\x7f", 0x7f);350 testValid("\x7f", 0x7f);
...@@ -233,6 +360,10 @@ test "valid utf8" {...@@ -233,6 +360,10 @@ test "valid utf8" {
233}360}
234361
235test "invalid utf8 continuation bytes" {362test "invalid utf8 continuation bytes" {
363 comptime testInvalidUtf8ContinuationBytes();
364 testInvalidUtf8ContinuationBytes();
365}
366fn testInvalidUtf8ContinuationBytes() void {
236 // unexpected continuation367 // unexpected continuation
237 testError("\x80", error.Utf8InvalidStartByte);368 testError("\x80", error.Utf8InvalidStartByte);
238 testError("\xbf", error.Utf8InvalidStartByte);369 testError("\xbf", error.Utf8InvalidStartByte);
...@@ -261,6 +392,10 @@ test "invalid utf8 continuation bytes" {...@@ -261,6 +392,10 @@ test "invalid utf8 continuation bytes" {
261}392}
262393
263test "overlong utf8 codepoint" {394test "overlong utf8 codepoint" {
395 comptime testOverlongUtf8Codepoint();
396 testOverlongUtf8Codepoint();
397}
398fn testOverlongUtf8Codepoint() void {
264 testError("\xc0\x80", error.Utf8OverlongEncoding);399 testError("\xc0\x80", error.Utf8OverlongEncoding);
265 testError("\xc1\xbf", error.Utf8OverlongEncoding);400 testError("\xc1\xbf", error.Utf8OverlongEncoding);
266 testError("\xe0\x80\x80", error.Utf8OverlongEncoding);401 testError("\xe0\x80\x80", error.Utf8OverlongEncoding);
...@@ -270,6 +405,10 @@ test "overlong utf8 codepoint" {...@@ -270,6 +405,10 @@ test "overlong utf8 codepoint" {
270}405}
271406
272test "misc invalid utf8" {407test "misc invalid utf8" {
408 comptime testMiscInvalidUtf8();
409 testMiscInvalidUtf8();
410}
411fn testMiscInvalidUtf8() void {
273 // codepoint out of bounds412 // codepoint out of bounds
274 testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);413 testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);
275 testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);414 testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);
std/zig/ast.zig+112-10
...@@ -6,6 +6,7 @@ const mem = std.mem;...@@ -6,6 +6,7 @@ const mem = std.mem;
66
7pub const Node = struct {7pub const Node = struct {
8 id: Id,8 id: Id,
9 same_line_comment: ?&Token,
910
10 pub const Id = enum {11 pub const Id = enum {
11 // Top level12 // Top level
...@@ -34,6 +35,7 @@ pub const Node = struct {...@@ -34,6 +35,7 @@ pub const Node = struct {
34 VarType,35 VarType,
35 ErrorType,36 ErrorType,
36 FnProto,37 FnProto,
38 PromiseType,
3739
38 // Primary expressions40 // Primary expressions
39 IntegerLiteral,41 IntegerLiteral,
...@@ -57,6 +59,7 @@ pub const Node = struct {...@@ -57,6 +59,7 @@ pub const Node = struct {
5759
58 // Misc60 // Misc
59 LineComment,61 LineComment,
62 DocComment,
60 SwitchCase,63 SwitchCase,
61 SwitchElse,64 SwitchElse,
62 Else,65 Else,
...@@ -66,6 +69,7 @@ pub const Node = struct {...@@ -66,6 +69,7 @@ pub const Node = struct {
66 StructField,69 StructField,
67 UnionTag,70 UnionTag,
68 EnumTag,71 EnumTag,
72 ErrorTag,
69 AsmInput,73 AsmInput,
70 AsmOutput,74 AsmOutput,
71 AsyncAttribute,75 AsyncAttribute,
...@@ -73,6 +77,13 @@ pub const Node = struct {...@@ -73,6 +77,13 @@ pub const Node = struct {
73 FieldInitializer,77 FieldInitializer,
74 };78 };
7579
80 pub fn cast(base: &Node, comptime T: type) ?&T {
81 if (base.id == comptime typeToId(T)) {
82 return @fieldParentPtr(T, "base", base);
83 }
84 return null;
85 }
86
76 pub fn iterate(base: &Node, index: usize) ?&Node {87 pub fn iterate(base: &Node, index: usize) ?&Node {
77 comptime var i = 0;88 comptime var i = 0;
78 inline while (i < @memberCount(Id)) : (i += 1) {89 inline while (i < @memberCount(Id)) : (i += 1) {
...@@ -118,6 +129,7 @@ pub const Node = struct {...@@ -118,6 +129,7 @@ pub const Node = struct {
118129
119 pub const Root = struct {130 pub const Root = struct {
120 base: Node,131 base: Node,
132 doc_comments: ?&DocComment,
121 decls: ArrayList(&Node),133 decls: ArrayList(&Node),
122 eof_token: Token,134 eof_token: Token,
123135
...@@ -139,7 +151,7 @@ pub const Node = struct {...@@ -139,7 +151,7 @@ pub const Node = struct {
139151
140 pub const VarDecl = struct {152 pub const VarDecl = struct {
141 base: Node,153 base: Node,
142 comments: ?&LineComment,154 doc_comments: ?&DocComment,
143 visib_token: ?Token,155 visib_token: ?Token,
144 name_token: Token,156 name_token: Token,
145 eq_token: Token,157 eq_token: Token,
...@@ -188,6 +200,7 @@ pub const Node = struct {...@@ -188,6 +200,7 @@ pub const Node = struct {
188200
189 pub const Use = struct {201 pub const Use = struct {
190 base: Node,202 base: Node,
203 doc_comments: ?&DocComment,
191 visib_token: ?Token,204 visib_token: ?Token,
192 expr: &Node,205 expr: &Node,
193 semicolon_token: Token,206 semicolon_token: Token,
...@@ -258,7 +271,7 @@ pub const Node = struct {...@@ -258,7 +271,7 @@ pub const Node = struct {
258271
259 const InitArg = union(enum) {272 const InitArg = union(enum) {
260 None,273 None,
261 Enum,274 Enum: ?&Node,
262 Type: &Node,275 Type: &Node,
263 };276 };
264277
...@@ -291,6 +304,7 @@ pub const Node = struct {...@@ -291,6 +304,7 @@ pub const Node = struct {
291304
292 pub const StructField = struct {305 pub const StructField = struct {
293 base: Node,306 base: Node,
307 doc_comments: ?&DocComment,
294 visib_token: ?Token,308 visib_token: ?Token,
295 name_token: Token,309 name_token: Token,
296 type_expr: &Node,310 type_expr: &Node,
...@@ -316,8 +330,10 @@ pub const Node = struct {...@@ -316,8 +330,10 @@ pub const Node = struct {
316330
317 pub const UnionTag = struct {331 pub const UnionTag = struct {
318 base: Node,332 base: Node,
333 doc_comments: ?&DocComment,
319 name_token: Token,334 name_token: Token,
320 type_expr: ?&Node,335 type_expr: ?&Node,
336 value_expr: ?&Node,
321337
322 pub fn iterate(self: &UnionTag, index: usize) ?&Node {338 pub fn iterate(self: &UnionTag, index: usize) ?&Node {
323 var i = index;339 var i = index;
...@@ -327,6 +343,11 @@ pub const Node = struct {...@@ -327,6 +343,11 @@ pub const Node = struct {
327 i -= 1;343 i -= 1;
328 }344 }
329345
346 if (self.value_expr) |value_expr| {
347 if (i < 1) return value_expr;
348 i -= 1;
349 }
350
330 return null;351 return null;
331 }352 }
332353
...@@ -335,6 +356,9 @@ pub const Node = struct {...@@ -335,6 +356,9 @@ pub const Node = struct {
335 }356 }
336357
337 pub fn lastToken(self: &UnionTag) Token {358 pub fn lastToken(self: &UnionTag) Token {
359 if (self.value_expr) |value_expr| {
360 return value_expr.lastToken();
361 }
338 if (self.type_expr) |type_expr| {362 if (self.type_expr) |type_expr| {
339 return type_expr.lastToken();363 return type_expr.lastToken();
340 }364 }
...@@ -345,6 +369,7 @@ pub const Node = struct {...@@ -345,6 +369,7 @@ pub const Node = struct {
345369
346 pub const EnumTag = struct {370 pub const EnumTag = struct {
347 base: Node,371 base: Node,
372 doc_comments: ?&DocComment,
348 name_token: Token,373 name_token: Token,
349 value: ?&Node,374 value: ?&Node,
350375
...@@ -372,6 +397,31 @@ pub const Node = struct {...@@ -372,6 +397,31 @@ pub const Node = struct {
372 }397 }
373 };398 };
374399
400 pub const ErrorTag = struct {
401 base: Node,
402 doc_comments: ?&DocComment,
403 name_token: Token,
404
405 pub fn iterate(self: &ErrorTag, index: usize) ?&Node {
406 var i = index;
407
408 if (self.doc_comments) |comments| {
409 if (i < 1) return &comments.base;
410 i -= 1;
411 }
412
413 return null;
414 }
415
416 pub fn firstToken(self: &ErrorTag) Token {
417 return self.name_token;
418 }
419
420 pub fn lastToken(self: &ErrorTag) Token {
421 return self.name_token;
422 }
423 };
424
375 pub const Identifier = struct {425 pub const Identifier = struct {
376 base: Node,426 base: Node,
377 token: Token,427 token: Token,
...@@ -421,7 +471,7 @@ pub const Node = struct {...@@ -421,7 +471,7 @@ pub const Node = struct {
421471
422 pub const FnProto = struct {472 pub const FnProto = struct {
423 base: Node,473 base: Node,
424 comments: ?&LineComment,474 doc_comments: ?&DocComment,
425 visib_token: ?Token,475 visib_token: ?Token,
426 fn_token: Token,476 fn_token: Token,
427 name_token: ?Token,477 name_token: ?Token,
...@@ -494,6 +544,37 @@ pub const Node = struct {...@@ -494,6 +544,37 @@ pub const Node = struct {
494 }544 }
495 };545 };
496546
547 pub const PromiseType = struct {
548 base: Node,
549 promise_token: Token,
550 result: ?Result,
551
552 pub const Result = struct {
553 arrow_token: Token,
554 return_type: &Node,
555 };
556
557 pub fn iterate(self: &PromiseType, index: usize) ?&Node {
558 var i = index;
559
560 if (self.result) |result| {
561 if (i < 1) return result.return_type;
562 i -= 1;
563 }
564
565 return null;
566 }
567
568 pub fn firstToken(self: &PromiseType) Token {
569 return self.promise_token;
570 }
571
572 pub fn lastToken(self: &PromiseType) Token {
573 if (self.result) |result| return result.return_type.lastToken();
574 return self.promise_token;
575 }
576 };
577
497 pub const ParamDecl = struct {578 pub const ParamDecl = struct {
498 base: Node,579 base: Node,
499 comptime_token: ?Token,580 comptime_token: ?Token,
...@@ -584,6 +665,7 @@ pub const Node = struct {...@@ -584,6 +665,7 @@ pub const Node = struct {
584665
585 pub const Comptime = struct {666 pub const Comptime = struct {
586 base: Node,667 base: Node,
668 doc_comments: ?&DocComment,
587 comptime_token: Token,669 comptime_token: Token,
588 expr: &Node,670 expr: &Node,
589671
...@@ -718,7 +800,8 @@ pub const Node = struct {...@@ -718,7 +800,8 @@ pub const Node = struct {
718 base: Node,800 base: Node,
719 switch_token: Token,801 switch_token: Token,
720 expr: &Node,802 expr: &Node,
721 cases: ArrayList(&SwitchCase),803 /// these can be SwitchCase nodes or LineComment nodes
804 cases: ArrayList(&Node),
722 rbrace: Token,805 rbrace: Token,
723806
724 pub fn iterate(self: &Switch, index: usize) ?&Node {807 pub fn iterate(self: &Switch, index: usize) ?&Node {
...@@ -727,7 +810,7 @@ pub const Node = struct {...@@ -727,7 +810,7 @@ pub const Node = struct {
727 if (i < 1) return self.expr;810 if (i < 1) return self.expr;
728 i -= 1;811 i -= 1;
729812
730 if (i < self.cases.len) return &self.cases.at(i).base;813 if (i < self.cases.len) return self.cases.at(i);
731 i -= self.cases.len;814 i -= self.cases.len;
732815
733 return null;816 return null;
...@@ -1186,7 +1269,7 @@ pub const Node = struct {...@@ -1186,7 +1269,7 @@ pub const Node = struct {
1186 ArrayAccess: &Node,1269 ArrayAccess: &Node,
1187 Slice: SliceRange,1270 Slice: SliceRange,
1188 ArrayInitializer: ArrayList(&Node),1271 ArrayInitializer: ArrayList(&Node),
1189 StructInitializer: ArrayList(&FieldInitializer),1272 StructInitializer: ArrayList(&Node),
1190 };1273 };
11911274
1192 const CallInfo = struct {1275 const CallInfo = struct {
...@@ -1228,7 +1311,7 @@ pub const Node = struct {...@@ -1228,7 +1311,7 @@ pub const Node = struct {
1228 i -= exprs.len;1311 i -= exprs.len;
1229 },1312 },
1230 Op.StructInitializer => |fields| {1313 Op.StructInitializer => |fields| {
1231 if (i < fields.len) return &fields.at(i).base;1314 if (i < fields.len) return fields.at(i);
1232 i -= fields.len;1315 i -= fields.len;
1233 },1316 },
1234 }1317 }
...@@ -1337,6 +1420,7 @@ pub const Node = struct {...@@ -1337,6 +1420,7 @@ pub const Node = struct {
13371420
1338 pub const Suspend = struct {1421 pub const Suspend = struct {
1339 base: Node,1422 base: Node,
1423 label: ?Token,
1340 suspend_token: Token,1424 suspend_token: Token,
1341 payload: ?&Node,1425 payload: ?&Node,
1342 body: ?&Node,1426 body: ?&Node,
...@@ -1358,6 +1442,7 @@ pub const Node = struct {...@@ -1358,6 +1442,7 @@ pub const Node = struct {
1358 }1442 }
13591443
1360 pub fn firstToken(self: &Suspend) Token {1444 pub fn firstToken(self: &Suspend) Token {
1445 if (self.label) |label| return label;
1361 return self.suspend_token;1446 return self.suspend_token;
1362 }1447 }
13631448
...@@ -1715,24 +1800,41 @@ pub const Node = struct {...@@ -1715,24 +1800,41 @@ pub const Node = struct {
17151800
1716 pub const LineComment = struct {1801 pub const LineComment = struct {
1717 base: Node,1802 base: Node,
1718 lines: ArrayList(Token),1803 token: Token,
17191804
1720 pub fn iterate(self: &LineComment, index: usize) ?&Node {1805 pub fn iterate(self: &LineComment, index: usize) ?&Node {
1721 return null;1806 return null;
1722 }1807 }
17231808
1724 pub fn firstToken(self: &LineComment) Token {1809 pub fn firstToken(self: &LineComment) Token {
1725 return self.lines.at(0);1810 return self.token;
1726 }1811 }
17271812
1728 pub fn lastToken(self: &LineComment) Token {1813 pub fn lastToken(self: &LineComment) Token {
1814 return self.token;
1815 }
1816 };
1817
1818 pub const DocComment = struct {
1819 base: Node,
1820 lines: ArrayList(Token),
1821
1822 pub fn iterate(self: &DocComment, index: usize) ?&Node {
1823 return null;
1824 }
1825
1826 pub fn firstToken(self: &DocComment) Token {
1827 return self.lines.at(0);
1828 }
1829
1830 pub fn lastToken(self: &DocComment) Token {
1729 return self.lines.at(self.lines.len - 1);1831 return self.lines.at(self.lines.len - 1);
1730 }1832 }
1731 };1833 };
17321834
1733 pub const TestDecl = struct {1835 pub const TestDecl = struct {
1734 base: Node,1836 base: Node,
1735 comments: ?&LineComment,1837 doc_comments: ?&DocComment,
1736 test_token: Token,1838 test_token: Token,
1737 name: &Node,1839 name: &Node,
1738 body_node: &Node,1840 body_node: &Node,
std/zig/parser.zig+607-1195
...@@ -55,6 +55,7 @@ pub const Parser = struct {...@@ -55,6 +55,7 @@ pub const Parser = struct {
55 visib_token: ?Token,55 visib_token: ?Token,
56 extern_export_inline_token: ?Token,56 extern_export_inline_token: ?Token,
57 lib_name: ?&ast.Node,57 lib_name: ?&ast.Node,
58 comments: ?&ast.Node.DocComment,
58 };59 };
5960
60 const VarDeclCtx = struct {61 const VarDeclCtx = struct {
...@@ -64,18 +65,19 @@ pub const Parser = struct {...@@ -64,18 +65,19 @@ pub const Parser = struct {
64 extern_export_token: ?Token,65 extern_export_token: ?Token,
65 lib_name: ?&ast.Node,66 lib_name: ?&ast.Node,
66 list: &ArrayList(&ast.Node),67 list: &ArrayList(&ast.Node),
67 comments: ?&ast.Node.LineComment,68 comments: ?&ast.Node.DocComment,
68 };69 };
6970
70 const TopLevelExternOrFieldCtx = struct {71 const TopLevelExternOrFieldCtx = struct {
71 visib_token: Token,72 visib_token: Token,
72 container_decl: &ast.Node.ContainerDecl,73 container_decl: &ast.Node.ContainerDecl,
74 comments: ?&ast.Node.DocComment,
73 };75 };
7476
75 const ExternTypeCtx = struct {77 const ExternTypeCtx = struct {
76 opt_ctx: OptionalCtx,78 opt_ctx: OptionalCtx,
77 extern_token: Token,79 extern_token: Token,
78 comments: ?&ast.Node.LineComment,80 comments: ?&ast.Node.DocComment,
79 };81 };
8082
81 const ContainerKindCtx = struct {83 const ContainerKindCtx = struct {
...@@ -182,6 +184,11 @@ pub const Parser = struct {...@@ -182,6 +184,11 @@ pub const Parser = struct {
182 }184 }
183 };185 };
184186
187 const AddCommentsCtx = struct {
188 node_ptr: &&ast.Node,
189 comments: ?&ast.Node.DocComment,
190 };
191
185 const State = union(enum) {192 const State = union(enum) {
186 TopLevel,193 TopLevel,
187 TopLevelExtern: TopLevelDeclCtx,194 TopLevelExtern: TopLevelDeclCtx,
...@@ -221,6 +228,8 @@ pub const Parser = struct {...@@ -221,6 +228,8 @@ pub const Parser = struct {
221 Statement: &ast.Node.Block,228 Statement: &ast.Node.Block,
222 ComptimeStatement: ComptimeStatementCtx,229 ComptimeStatement: ComptimeStatementCtx,
223 Semicolon: &&ast.Node,230 Semicolon: &&ast.Node,
231 LookForSameLineComment: &&ast.Node,
232 LookForSameLineCommentDirect: &ast.Node,
224233
225 AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),234 AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),
226 AsmOutputReturnOrType: &ast.Node.AsmOutput,235 AsmOutputReturnOrType: &ast.Node.AsmOutput,
...@@ -229,13 +238,14 @@ pub const Parser = struct {...@@ -229,13 +238,14 @@ pub const Parser = struct {
229238
230 ExprListItemOrEnd: ExprListCtx,239 ExprListItemOrEnd: ExprListCtx,
231 ExprListCommaOrEnd: ExprListCtx,240 ExprListCommaOrEnd: ExprListCtx,
232 FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer),241 FieldInitListItemOrEnd: ListSave(&ast.Node),
233 FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer),242 FieldInitListCommaOrEnd: ListSave(&ast.Node),
234 FieldListCommaOrEnd: &ast.Node.ContainerDecl,243 FieldListCommaOrEnd: &ast.Node.ContainerDecl,
235 IdentifierListItemOrEnd: ListSave(&ast.Node),244 FieldInitValue: OptionalCtx,
236 IdentifierListCommaOrEnd: ListSave(&ast.Node),245 ErrorTagListItemOrEnd: ListSave(&ast.Node),
237 SwitchCaseOrEnd: ListSave(&ast.Node.SwitchCase),246 ErrorTagListCommaOrEnd: ListSave(&ast.Node),
238 SwitchCaseCommaOrEnd: ListSave(&ast.Node.SwitchCase),247 SwitchCaseOrEnd: ListSave(&ast.Node),
248 SwitchCaseCommaOrEnd: ListSave(&ast.Node),
239 SwitchCaseFirstItem: &ArrayList(&ast.Node),249 SwitchCaseFirstItem: &ArrayList(&ast.Node),
240 SwitchCaseItem: &ArrayList(&ast.Node),250 SwitchCaseItem: &ArrayList(&ast.Node),
241 SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),251 SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
...@@ -290,6 +300,7 @@ pub const Parser = struct {...@@ -290,6 +300,7 @@ pub const Parser = struct {
290 ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx,300 ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx,
291 StringLiteral: OptionalCtx,301 StringLiteral: OptionalCtx,
292 Identifier: OptionalCtx,302 Identifier: OptionalCtx,
303 ErrorTag: &&ast.Node,
293304
294305
295 IfToken: @TagType(Token.Id),306 IfToken: @TagType(Token.Id),
...@@ -314,6 +325,7 @@ pub const Parser = struct {...@@ -314,6 +325,7 @@ pub const Parser = struct {
314 ast.Node.Root {325 ast.Node.Root {
315 .base = undefined,326 .base = undefined,
316 .decls = ArrayList(&ast.Node).init(arena),327 .decls = ArrayList(&ast.Node).init(arena),
328 .doc_comments = null,
317 // initialized when we get the eof token329 // initialized when we get the eof token
318 .eof_token = undefined,330 .eof_token = undefined,
319 }331 }
...@@ -339,31 +351,38 @@ pub const Parser = struct {...@@ -339,31 +351,38 @@ pub const Parser = struct {
339351
340 switch (state) {352 switch (state) {
341 State.TopLevel => {353 State.TopLevel => {
342 const comments = try self.eatComments(arena);354 while (try self.eatLineComment(arena)) |line_comment| {
355 try root_node.decls.append(&line_comment.base);
356 }
357
358 const comments = try self.eatDocComments(arena);
343 const token = self.getNextToken();359 const token = self.getNextToken();
344 switch (token.id) {360 switch (token.id) {
345 Token.Id.Keyword_test => {361 Token.Id.Keyword_test => {
346 stack.append(State.TopLevel) catch unreachable;362 stack.append(State.TopLevel) catch unreachable;
347363
348 const block = try self.createNode(arena, ast.Node.Block,364 const block = try arena.construct(ast.Node.Block {
349 ast.Node.Block {365 .base = ast.Node {
350 .base = undefined,366 .id = ast.Node.Id.Block,
351 .label = null,367 .same_line_comment = null,
352 .lbrace = undefined,368 },
353 .statements = ArrayList(&ast.Node).init(arena),369 .label = null,
354 .rbrace = undefined,370 .lbrace = undefined,
355 }371 .statements = ArrayList(&ast.Node).init(arena),
356 );372 .rbrace = undefined,
357 const test_node = try self.createAttachNode(arena, &root_node.decls, ast.Node.TestDecl,373 });
358 ast.Node.TestDecl {374 const test_node = try arena.construct(ast.Node.TestDecl {
359 .base = undefined,375 .base = ast.Node {
360 .comments = comments,376 .id = ast.Node.Id.TestDecl,
361 .test_token = token,377 .same_line_comment = null,
362 .name = undefined,378 },
363 .body_node = &block.base,379 .doc_comments = comments,
364 }380 .test_token = token,
365 );381 .name = undefined,
366 stack.append(State { .Block = block }) catch unreachable;382 .body_node = &block.base,
383 });
384 try root_node.decls.append(&test_node.base);
385 try stack.append(State { .Block = block });
367 try stack.append(State {386 try stack.append(State {
368 .ExpectTokenSave = ExpectTokenSave {387 .ExpectTokenSave = ExpectTokenSave {
369 .id = Token.Id.LBrace,388 .id = Token.Id.LBrace,
...@@ -375,7 +394,11 @@ pub const Parser = struct {...@@ -375,7 +394,11 @@ pub const Parser = struct {
375 },394 },
376 Token.Id.Eof => {395 Token.Id.Eof => {
377 root_node.eof_token = token;396 root_node.eof_token = token;
378 return Tree {.root_node = root_node, .arena_allocator = arena_allocator};397 root_node.doc_comments = comments;
398 return Tree {
399 .root_node = root_node,
400 .arena_allocator = arena_allocator,
401 };
379 },402 },
380 Token.Id.Keyword_pub => {403 Token.Id.Keyword_pub => {
381 stack.append(State.TopLevel) catch unreachable;404 stack.append(State.TopLevel) catch unreachable;
...@@ -385,6 +408,7 @@ pub const Parser = struct {...@@ -385,6 +408,7 @@ pub const Parser = struct {
385 .visib_token = token,408 .visib_token = token,
386 .extern_export_inline_token = null,409 .extern_export_inline_token = null,
387 .lib_name = null,410 .lib_name = null,
411 .comments = comments,
388 }412 }
389 });413 });
390 continue;414 continue;
...@@ -404,6 +428,7 @@ pub const Parser = struct {...@@ -404,6 +428,7 @@ pub const Parser = struct {
404 .base = undefined,428 .base = undefined,
405 .comptime_token = token,429 .comptime_token = token,
406 .expr = &block.base,430 .expr = &block.base,
431 .doc_comments = comments,
407 }432 }
408 );433 );
409 stack.append(State.TopLevel) catch unreachable;434 stack.append(State.TopLevel) catch unreachable;
...@@ -425,6 +450,7 @@ pub const Parser = struct {...@@ -425,6 +450,7 @@ pub const Parser = struct {
425 .visib_token = null,450 .visib_token = null,
426 .extern_export_inline_token = null,451 .extern_export_inline_token = null,
427 .lib_name = null,452 .lib_name = null,
453 .comments = comments,
428 }454 }
429 });455 });
430 continue;456 continue;
...@@ -441,6 +467,7 @@ pub const Parser = struct {...@@ -441,6 +467,7 @@ pub const Parser = struct {
441 .visib_token = ctx.visib_token,467 .visib_token = ctx.visib_token,
442 .extern_export_inline_token = token,468 .extern_export_inline_token = token,
443 .lib_name = null,469 .lib_name = null,
470 .comments = ctx.comments,
444 },471 },
445 }) catch unreachable;472 }) catch unreachable;
446 continue;473 continue;
...@@ -452,6 +479,7 @@ pub const Parser = struct {...@@ -452,6 +479,7 @@ pub const Parser = struct {
452 .visib_token = ctx.visib_token,479 .visib_token = ctx.visib_token,
453 .extern_export_inline_token = token,480 .extern_export_inline_token = token,
454 .lib_name = null,481 .lib_name = null,
482 .comments = ctx.comments,
455 },483 },
456 }) catch unreachable;484 }) catch unreachable;
457 continue;485 continue;
...@@ -478,12 +506,12 @@ pub const Parser = struct {...@@ -478,12 +506,12 @@ pub const Parser = struct {
478 .visib_token = ctx.visib_token,506 .visib_token = ctx.visib_token,
479 .extern_export_inline_token = ctx.extern_export_inline_token,507 .extern_export_inline_token = ctx.extern_export_inline_token,
480 .lib_name = lib_name,508 .lib_name = lib_name,
509 .comments = ctx.comments,
481 },510 },
482 }) catch unreachable;511 }) catch unreachable;
483 continue;512 continue;
484 },513 },
485 State.TopLevelDecl => |ctx| {514 State.TopLevelDecl => |ctx| {
486 const comments = try self.eatComments(arena);
487 const token = self.getNextToken();515 const token = self.getNextToken();
488 switch (token.id) {516 switch (token.id) {
489 Token.Id.Keyword_use => {517 Token.Id.Keyword_use => {
...@@ -497,6 +525,7 @@ pub const Parser = struct {...@@ -497,6 +525,7 @@ pub const Parser = struct {
497 .visib_token = ctx.visib_token,525 .visib_token = ctx.visib_token,
498 .expr = undefined,526 .expr = undefined,
499 .semicolon_token = undefined,527 .semicolon_token = undefined,
528 .doc_comments = ctx.comments,
500 }529 }
501 );530 );
502 stack.append(State {531 stack.append(State {
...@@ -515,9 +544,9 @@ pub const Parser = struct {...@@ -515,9 +544,9 @@ pub const Parser = struct {
515 }544 }
516 }545 }
517546
518 stack.append(State {547 try stack.append(State {
519 .VarDecl = VarDeclCtx {548 .VarDecl = VarDeclCtx {
520 .comments = comments,549 .comments = ctx.comments,
521 .visib_token = ctx.visib_token,550 .visib_token = ctx.visib_token,
522 .lib_name = ctx.lib_name,551 .lib_name = ctx.lib_name,
523 .comptime_token = null,552 .comptime_token = null,
...@@ -525,29 +554,31 @@ pub const Parser = struct {...@@ -525,29 +554,31 @@ pub const Parser = struct {
525 .mut_token = token,554 .mut_token = token,
526 .list = ctx.decls555 .list = ctx.decls
527 }556 }
528 }) catch unreachable;557 });
529 continue;558 continue;
530 },559 },
531 Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,560 Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
532 Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {561 Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
533 const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.Node.FnProto,562 const fn_proto = try arena.construct(ast.Node.FnProto {
534 ast.Node.FnProto {563 .base = ast.Node {
535 .base = undefined,564 .id = ast.Node.Id.FnProto,
536 .comments = comments,565 .same_line_comment = null,
537 .visib_token = ctx.visib_token,566 },
538 .name_token = null,567 .doc_comments = ctx.comments,
539 .fn_token = undefined,568 .visib_token = ctx.visib_token,
540 .params = ArrayList(&ast.Node).init(arena),569 .name_token = null,
541 .return_type = undefined,570 .fn_token = undefined,
542 .var_args_token = null,571 .params = ArrayList(&ast.Node).init(arena),
543 .extern_export_inline_token = ctx.extern_export_inline_token,572 .return_type = undefined,
544 .cc_token = null,573 .var_args_token = null,
545 .async_attr = null,574 .extern_export_inline_token = ctx.extern_export_inline_token,
546 .body_node = null,575 .cc_token = null,
547 .lib_name = ctx.lib_name,576 .async_attr = null,
548 .align_expr = null,577 .body_node = null,
549 }578 .lib_name = ctx.lib_name,
550 );579 .align_expr = null,
580 });
581 try ctx.decls.append(&fn_proto.base);
551 stack.append(State { .FnDef = fn_proto }) catch unreachable;582 stack.append(State { .FnDef = fn_proto }) catch unreachable;
552 try stack.append(State { .FnProto = fn_proto });583 try stack.append(State { .FnProto = fn_proto });
553584
...@@ -597,14 +628,18 @@ pub const Parser = struct {...@@ -597,14 +628,18 @@ pub const Parser = struct {
597 State.TopLevelExternOrField => |ctx| {628 State.TopLevelExternOrField => |ctx| {
598 if (self.eatToken(Token.Id.Identifier)) |identifier| {629 if (self.eatToken(Token.Id.Identifier)) |identifier| {
599 std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);630 std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
600 const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField,631 const node = try arena.construct(ast.Node.StructField {
601 ast.Node.StructField {632 .base = ast.Node {
602 .base = undefined,633 .id = ast.Node.Id.StructField,
603 .visib_token = ctx.visib_token,634 .same_line_comment = null,
604 .name_token = identifier,635 },
605 .type_expr = undefined,636 .doc_comments = ctx.comments,
606 }637 .visib_token = ctx.visib_token,
607 );638 .name_token = identifier,
639 .type_expr = undefined,
640 });
641 const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
642 *node_ptr = &node.base;
608643
609 stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;644 stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
610 try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } });645 try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
...@@ -619,11 +654,21 @@ pub const Parser = struct {...@@ -619,11 +654,21 @@ pub const Parser = struct {
619 .visib_token = ctx.visib_token,654 .visib_token = ctx.visib_token,
620 .extern_export_inline_token = null,655 .extern_export_inline_token = null,
621 .lib_name = null,656 .lib_name = null,
657 .comments = ctx.comments,
622 }658 }
623 });659 });
624 continue;660 continue;
625 },661 },
626662
663 State.FieldInitValue => |ctx| {
664 const eq_tok = self.getNextToken();
665 if (eq_tok.id != Token.Id.Equal) {
666 self.putBackToken(eq_tok);
667 continue;
668 }
669 stack.append(State { .Expression = ctx }) catch unreachable;
670 continue;
671 },
627672
628 State.ContainerKind => |ctx| {673 State.ContainerKind => |ctx| {
629 const token = self.getNextToken();674 const token = self.getNextToken();
...@@ -670,7 +715,16 @@ pub const Parser = struct {...@@ -670,7 +715,16 @@ pub const Parser = struct {
670 const init_arg_token = self.getNextToken();715 const init_arg_token = self.getNextToken();
671 switch (init_arg_token.id) {716 switch (init_arg_token.id) {
672 Token.Id.Keyword_enum => {717 Token.Id.Keyword_enum => {
673 container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg.Enum;718 container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null};
719 const lparen_tok = self.getNextToken();
720 if (lparen_tok.id == Token.Id.LParen) {
721 try stack.append(State { .ExpectToken = Token.Id.RParen } );
722 try stack.append(State { .Expression = OptionalCtx {
723 .RequiredNull = &container_decl.init_arg_expr.Enum,
724 } });
725 } else {
726 self.putBackToken(lparen_tok);
727 }
674 },728 },
675 else => {729 else => {
676 self.putBackToken(init_arg_token);730 self.putBackToken(init_arg_token);
...@@ -680,22 +734,32 @@ pub const Parser = struct {...@@ -680,22 +734,32 @@ pub const Parser = struct {
680 }734 }
681 continue;735 continue;
682 },736 },
737
683 State.ContainerDecl => |container_decl| {738 State.ContainerDecl => |container_decl| {
739 while (try self.eatLineComment(arena)) |line_comment| {
740 try container_decl.fields_and_decls.append(&line_comment.base);
741 }
742
743 const comments = try self.eatDocComments(arena);
684 const token = self.getNextToken();744 const token = self.getNextToken();
685 switch (token.id) {745 switch (token.id) {
686 Token.Id.Identifier => {746 Token.Id.Identifier => {
687 switch (container_decl.kind) {747 switch (container_decl.kind) {
688 ast.Node.ContainerDecl.Kind.Struct => {748 ast.Node.ContainerDecl.Kind.Struct => {
689 const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField,749 const node = try arena.construct(ast.Node.StructField {
690 ast.Node.StructField {750 .base = ast.Node {
691 .base = undefined,751 .id = ast.Node.Id.StructField,
692 .visib_token = null,752 .same_line_comment = null,
693 .name_token = token,753 },
694 .type_expr = undefined,754 .doc_comments = comments,
695 }755 .visib_token = null,
696 );756 .name_token = token,
757 .type_expr = undefined,
758 });
759 const node_ptr = try container_decl.fields_and_decls.addOne();
760 *node_ptr = &node.base;
697761
698 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;762 try stack.append(State { .FieldListCommaOrEnd = container_decl });
699 try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });763 try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
700 try stack.append(State { .ExpectToken = Token.Id.Colon });764 try stack.append(State { .ExpectToken = Token.Id.Colon });
701 continue;765 continue;
...@@ -706,10 +770,13 @@ pub const Parser = struct {...@@ -706,10 +770,13 @@ pub const Parser = struct {
706 .base = undefined,770 .base = undefined,
707 .name_token = token,771 .name_token = token,
708 .type_expr = null,772 .type_expr = null,
773 .value_expr = null,
774 .doc_comments = comments,
709 }775 }
710 );776 );
711777
712 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;778 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
779 try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
713 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });780 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
714 try stack.append(State { .IfToken = Token.Id.Colon });781 try stack.append(State { .IfToken = Token.Id.Colon });
715 continue;782 continue;
...@@ -720,6 +787,7 @@ pub const Parser = struct {...@@ -720,6 +787,7 @@ pub const Parser = struct {
720 .base = undefined,787 .base = undefined,
721 .name_token = token,788 .name_token = token,
722 .value = null,789 .value = null,
790 .doc_comments = comments,
723 }791 }
724 );792 );
725793
...@@ -737,6 +805,7 @@ pub const Parser = struct {...@@ -737,6 +805,7 @@ pub const Parser = struct {
737 .TopLevelExternOrField = TopLevelExternOrFieldCtx {805 .TopLevelExternOrField = TopLevelExternOrFieldCtx {
738 .visib_token = token,806 .visib_token = token,
739 .container_decl = container_decl,807 .container_decl = container_decl,
808 .comments = comments,
740 }809 }
741 });810 });
742 continue;811 continue;
...@@ -749,6 +818,7 @@ pub const Parser = struct {...@@ -749,6 +818,7 @@ pub const Parser = struct {
749 .visib_token = token,818 .visib_token = token,
750 .extern_export_inline_token = null,819 .extern_export_inline_token = null,
751 .lib_name = null,820 .lib_name = null,
821 .comments = comments,
752 }822 }
753 });823 });
754 continue;824 continue;
...@@ -763,11 +833,15 @@ pub const Parser = struct {...@@ -763,11 +833,15 @@ pub const Parser = struct {
763 .visib_token = token,833 .visib_token = token,
764 .extern_export_inline_token = null,834 .extern_export_inline_token = null,
765 .lib_name = null,835 .lib_name = null,
836 .comments = comments,
766 }837 }
767 });838 });
768 continue;839 continue;
769 },840 },
770 Token.Id.RBrace => {841 Token.Id.RBrace => {
842 if (comments != null) {
843 return self.parseError(token, "doc comments must be attached to a node");
844 }
771 container_decl.rbrace_token = token;845 container_decl.rbrace_token = token;
772 continue;846 continue;
773 },847 },
...@@ -780,6 +854,7 @@ pub const Parser = struct {...@@ -780,6 +854,7 @@ pub const Parser = struct {
780 .visib_token = null,854 .visib_token = null,
781 .extern_export_inline_token = null,855 .extern_export_inline_token = null,
782 .lib_name = null,856 .lib_name = null,
857 .comments = comments,
783 }858 }
784 });859 });
785 continue;860 continue;
...@@ -789,26 +864,29 @@ pub const Parser = struct {...@@ -789,26 +864,29 @@ pub const Parser = struct {
789864
790865
791 State.VarDecl => |ctx| {866 State.VarDecl => |ctx| {
792 const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl,867 const var_decl = try arena.construct(ast.Node.VarDecl {
793 ast.Node.VarDecl {868 .base = ast.Node {
794 .base = undefined,869 .id = ast.Node.Id.VarDecl,
795 .comments = ctx.comments,870 .same_line_comment = null,
796 .visib_token = ctx.visib_token,871 },
797 .mut_token = ctx.mut_token,872 .doc_comments = ctx.comments,
798 .comptime_token = ctx.comptime_token,873 .visib_token = ctx.visib_token,
799 .extern_export_token = ctx.extern_export_token,874 .mut_token = ctx.mut_token,
800 .type_node = null,875 .comptime_token = ctx.comptime_token,
801 .align_node = null,876 .extern_export_token = ctx.extern_export_token,
802 .init_node = null,877 .type_node = null,
803 .lib_name = ctx.lib_name,878 .align_node = null,
804 // initialized later879 .init_node = null,
805 .name_token = undefined,880 .lib_name = ctx.lib_name,
806 .eq_token = undefined,881 // initialized later
807 .semicolon_token = undefined,882 .name_token = undefined,
808 }883 .eq_token = undefined,
809 );884 .semicolon_token = undefined,
885 });
886 try ctx.list.append(&var_decl.base);
810887
811 stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;888 try stack.append(State { .LookForSameLineCommentDirect = &var_decl.base });
889 try stack.append(State { .VarDeclAlign = var_decl });
812 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });890 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
813 try stack.append(State { .IfToken = Token.Id.Colon });891 try stack.append(State { .IfToken = Token.Id.Colon });
814 try stack.append(State {892 try stack.append(State {
...@@ -820,7 +898,7 @@ pub const Parser = struct {...@@ -820,7 +898,7 @@ pub const Parser = struct {
820 continue;898 continue;
821 },899 },
822 State.VarDeclAlign => |var_decl| {900 State.VarDeclAlign => |var_decl| {
823 stack.append(State { .VarDeclEq = var_decl }) catch unreachable;901 try stack.append(State { .VarDeclEq = var_decl });
824902
825 const next_token = self.getNextToken();903 const next_token = self.getNextToken();
826 if (next_token.id == Token.Id.Keyword_align) {904 if (next_token.id == Token.Id.Keyword_align) {
...@@ -1048,6 +1126,22 @@ pub const Parser = struct {...@@ -1048,6 +1126,22 @@ pub const Parser = struct {
1048 }) catch unreachable;1126 }) catch unreachable;
1049 continue;1127 continue;
1050 },1128 },
1129 Token.Id.Keyword_suspend => {
1130 const node = try arena.construct(ast.Node.Suspend {
1131 .base = ast.Node {
1132 .id = ast.Node.Id.Suspend,
1133 .same_line_comment = null,
1134 },
1135 .label = ctx.label,
1136 .suspend_token = token,
1137 .payload = null,
1138 .body = null,
1139 });
1140 ctx.opt_ctx.store(&node.base);
1141 stack.append(State { .SuspendBody = node }) catch unreachable;
1142 try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
1143 continue;
1144 },
1051 Token.Id.Keyword_inline => {1145 Token.Id.Keyword_inline => {
1052 stack.append(State {1146 stack.append(State {
1053 .Inline = InlineCtx {1147 .Inline = InlineCtx {
...@@ -1185,13 +1279,20 @@ pub const Parser = struct {...@@ -1185,13 +1279,20 @@ pub const Parser = struct {
1185 else => {1279 else => {
1186 self.putBackToken(token);1280 self.putBackToken(token);
1187 stack.append(State { .Block = block }) catch unreachable;1281 stack.append(State { .Block = block }) catch unreachable;
1282
1283 var any_comments = false;
1284 while (try self.eatLineComment(arena)) |line_comment| {
1285 try block.statements.append(&line_comment.base);
1286 any_comments = true;
1287 }
1288 if (any_comments) continue;
1289
1188 try stack.append(State { .Statement = block });1290 try stack.append(State { .Statement = block });
1189 continue;1291 continue;
1190 },1292 },
1191 }1293 }
1192 },1294 },
1193 State.Statement => |block| {1295 State.Statement => |block| {
1194 const comments = try self.eatComments(arena);
1195 const token = self.getNextToken();1296 const token = self.getNextToken();
1196 switch (token.id) {1297 switch (token.id) {
1197 Token.Id.Keyword_comptime => {1298 Token.Id.Keyword_comptime => {
...@@ -1206,7 +1307,7 @@ pub const Parser = struct {...@@ -1206,7 +1307,7 @@ pub const Parser = struct {
1206 Token.Id.Keyword_var, Token.Id.Keyword_const => {1307 Token.Id.Keyword_var, Token.Id.Keyword_const => {
1207 stack.append(State {1308 stack.append(State {
1208 .VarDecl = VarDeclCtx {1309 .VarDecl = VarDeclCtx {
1209 .comments = comments,1310 .comments = null,
1210 .visib_token = null,1311 .visib_token = null,
1211 .comptime_token = null,1312 .comptime_token = null,
1212 .extern_export_token = null,1313 .extern_export_token = null,
...@@ -1218,19 +1319,23 @@ pub const Parser = struct {...@@ -1218,19 +1319,23 @@ pub const Parser = struct {
1218 continue;1319 continue;
1219 },1320 },
1220 Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {1321 Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
1221 const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer,1322 const node = try arena.construct(ast.Node.Defer {
1222 ast.Node.Defer {1323 .base = ast.Node {
1223 .base = undefined,1324 .id = ast.Node.Id.Defer,
1224 .defer_token = token,1325 .same_line_comment = null,
1225 .kind = switch (token.id) {1326 },
1226 Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,1327 .defer_token = token,
1227 Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,1328 .kind = switch (token.id) {
1228 else => unreachable,1329 Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
1229 },1330 Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
1230 .expr = undefined,1331 else => unreachable,
1231 }1332 },
1232 );1333 .expr = undefined,
1233 stack.append(State { .Semicolon = &&node.base }) catch unreachable;1334 });
1335 const node_ptr = try block.statements.addOne();
1336 *node_ptr = &node.base;
1337
1338 stack.append(State { .Semicolon = node_ptr }) catch unreachable;
1234 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });1339 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
1235 continue;1340 continue;
1236 },1341 },
...@@ -1249,21 +1354,21 @@ pub const Parser = struct {...@@ -1249,21 +1354,21 @@ pub const Parser = struct {
1249 },1354 },
1250 else => {1355 else => {
1251 self.putBackToken(token);1356 self.putBackToken(token);
1252 const statememt = try block.statements.addOne();1357 const statement = try block.statements.addOne();
1253 stack.append(State { .Semicolon = statememt }) catch unreachable;1358 stack.append(State { .LookForSameLineComment = statement }) catch unreachable;
1254 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } });1359 try stack.append(State { .Semicolon = statement });
1360 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
1255 continue;1361 continue;
1256 }1362 }
1257 }1363 }
1258 },1364 },
1259 State.ComptimeStatement => |ctx| {1365 State.ComptimeStatement => |ctx| {
1260 const comments = try self.eatComments(arena);
1261 const token = self.getNextToken();1366 const token = self.getNextToken();
1262 switch (token.id) {1367 switch (token.id) {
1263 Token.Id.Keyword_var, Token.Id.Keyword_const => {1368 Token.Id.Keyword_var, Token.Id.Keyword_const => {
1264 stack.append(State {1369 stack.append(State {
1265 .VarDecl = VarDeclCtx {1370 .VarDecl = VarDeclCtx {
1266 .comments = comments,1371 .comments = null,
1267 .visib_token = null,1372 .visib_token = null,
1268 .comptime_token = ctx.comptime_token,1373 .comptime_token = ctx.comptime_token,
1269 .extern_export_token = null,1374 .extern_export_token = null,
...@@ -1293,6 +1398,16 @@ pub const Parser = struct {...@@ -1293,6 +1398,16 @@ pub const Parser = struct {
1293 continue;1398 continue;
1294 },1399 },
12951400
1401 State.LookForSameLineComment => |node_ptr| {
1402 try self.lookForSameLineComment(arena, *node_ptr);
1403 continue;
1404 },
1405
1406 State.LookForSameLineCommentDirect => |node| {
1407 try self.lookForSameLineComment(arena, node);
1408 continue;
1409 },
1410
12961411
1297 State.AsmOutputItems => |items| {1412 State.AsmOutputItems => |items| {
1298 const lbracket = self.getNextToken();1413 const lbracket = self.getNextToken();
...@@ -1395,20 +1510,25 @@ pub const Parser = struct {...@@ -1395,20 +1510,25 @@ pub const Parser = struct {
1395 }1510 }
1396 },1511 },
1397 State.FieldInitListItemOrEnd => |list_state| {1512 State.FieldInitListItemOrEnd => |list_state| {
1513 while (try self.eatLineComment(arena)) |line_comment| {
1514 try list_state.list.append(&line_comment.base);
1515 }
1516
1398 if (self.eatToken(Token.Id.RBrace)) |rbrace| {1517 if (self.eatToken(Token.Id.RBrace)) |rbrace| {
1399 *list_state.ptr = rbrace;1518 *list_state.ptr = rbrace;
1400 continue;1519 continue;
1401 }1520 }
14021521
1403 const node = try self.createNode(arena, ast.Node.FieldInitializer,1522 const node = try arena.construct(ast.Node.FieldInitializer {
1404 ast.Node.FieldInitializer {1523 .base = ast.Node {
1405 .base = undefined,1524 .id = ast.Node.Id.FieldInitializer,
1406 .period_token = undefined,1525 .same_line_comment = null,
1407 .name_token = undefined,1526 },
1408 .expr = undefined,1527 .period_token = undefined,
1409 }1528 .name_token = undefined,
1410 );1529 .expr = undefined,
1411 try list_state.list.append(node);1530 });
1531 try list_state.list.append(&node.base);
14121532
1413 stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;1533 stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
1414 try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } });1534 try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } });
...@@ -1440,60 +1560,78 @@ pub const Parser = struct {...@@ -1440,60 +1560,78 @@ pub const Parser = struct {
1440 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {1560 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
1441 container_decl.rbrace_token = end;1561 container_decl.rbrace_token = end;
1442 continue;1562 continue;
1443 } else {
1444 stack.append(State { .ContainerDecl = container_decl }) catch unreachable;
1445 continue;
1446 }1563 }
1564
1565 try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]);
1566 try stack.append(State { .ContainerDecl = container_decl });
1567 continue;
1447 },1568 },
1448 State.IdentifierListItemOrEnd => |list_state| {1569 State.ErrorTagListItemOrEnd => |list_state| {
1570 while (try self.eatLineComment(arena)) |line_comment| {
1571 try list_state.list.append(&line_comment.base);
1572 }
1573
1449 if (self.eatToken(Token.Id.RBrace)) |rbrace| {1574 if (self.eatToken(Token.Id.RBrace)) |rbrace| {
1450 *list_state.ptr = rbrace;1575 *list_state.ptr = rbrace;
1451 continue;1576 continue;
1452 }1577 }
14531578
1454 stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable;1579 const node_ptr = try list_state.list.addOne();
1455 try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } });1580
1581 try stack.append(State { .ErrorTagListCommaOrEnd = list_state });
1582 try stack.append(State { .ErrorTag = node_ptr });
1456 continue;1583 continue;
1457 },1584 },
1458 State.IdentifierListCommaOrEnd => |list_state| {1585 State.ErrorTagListCommaOrEnd => |list_state| {
1459 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {1586 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
1460 *list_state.ptr = end;1587 *list_state.ptr = end;
1461 continue;1588 continue;
1462 } else {1589 } else {
1463 stack.append(State { .IdentifierListItemOrEnd = list_state }) catch unreachable;1590 stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
1464 continue;1591 continue;
1465 }1592 }
1466 },1593 },
1467 State.SwitchCaseOrEnd => |list_state| {1594 State.SwitchCaseOrEnd => |list_state| {
1595 while (try self.eatLineComment(arena)) |line_comment| {
1596 try list_state.list.append(&line_comment.base);
1597 }
1598
1468 if (self.eatToken(Token.Id.RBrace)) |rbrace| {1599 if (self.eatToken(Token.Id.RBrace)) |rbrace| {
1469 *list_state.ptr = rbrace;1600 *list_state.ptr = rbrace;
1470 continue;1601 continue;
1471 }1602 }
14721603
1473 const node = try self.createNode(arena, ast.Node.SwitchCase,1604 const comments = try self.eatDocComments(arena);
1474 ast.Node.SwitchCase {1605 const node = try arena.construct(ast.Node.SwitchCase {
1475 .base = undefined,1606 .base = ast.Node {
1476 .items = ArrayList(&ast.Node).init(arena),1607 .id = ast.Node.Id.SwitchCase,
1477 .payload = null,1608 .same_line_comment = null,
1478 .expr = undefined,1609 },
1479 }1610 .items = ArrayList(&ast.Node).init(arena),
1480 );1611 .payload = null,
1481 try list_state.list.append(node);1612 .expr = undefined,
1482 stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;1613 });
1614 try list_state.list.append(&node.base);
1615 try stack.append(State { .SwitchCaseCommaOrEnd = list_state });
1483 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });1616 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
1484 try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });1617 try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
1485 try stack.append(State { .SwitchCaseFirstItem = &node.items });1618 try stack.append(State { .SwitchCaseFirstItem = &node.items });
1619
1486 continue;1620 continue;
1487 },1621 },
1622
1488 State.SwitchCaseCommaOrEnd => |list_state| {1623 State.SwitchCaseCommaOrEnd => |list_state| {
1489 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {1624 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
1490 *list_state.ptr = end;1625 *list_state.ptr = end;
1491 continue;1626 continue;
1492 } else {
1493 stack.append(State { .SwitchCaseOrEnd = list_state }) catch unreachable;
1494 continue;
1495 }1627 }
1628
1629 const node = list_state.list.toSlice()[list_state.list.len - 1];
1630 try self.lookForSameLineComment(arena, node);
1631 try stack.append(State { .SwitchCaseOrEnd = list_state });
1632 continue;
1496 },1633 },
1634
1497 State.SwitchCaseFirstItem => |case_items| {1635 State.SwitchCaseFirstItem => |case_items| {
1498 const token = self.getNextToken();1636 const token = self.getNextToken();
1499 if (token.id == Token.Id.Keyword_else) {1637 if (token.id == Token.Id.Keyword_else) {
...@@ -1576,24 +1714,26 @@ pub const Parser = struct {...@@ -1576,24 +1714,26 @@ pub const Parser = struct {
15761714
1577 State.ExternType => |ctx| {1715 State.ExternType => |ctx| {
1578 if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| {1716 if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| {
1579 const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto,1717 const fn_proto = try arena.construct(ast.Node.FnProto {
1580 ast.Node.FnProto {1718 .base = ast.Node {
1581 .base = undefined,1719 .id = ast.Node.Id.FnProto,
1582 .comments = ctx.comments,1720 .same_line_comment = null,
1583 .visib_token = null,1721 },
1584 .name_token = null,1722 .doc_comments = ctx.comments,
1585 .fn_token = fn_token,1723 .visib_token = null,
1586 .params = ArrayList(&ast.Node).init(arena),1724 .name_token = null,
1587 .return_type = undefined,1725 .fn_token = fn_token,
1588 .var_args_token = null,1726 .params = ArrayList(&ast.Node).init(arena),
1589 .extern_export_inline_token = ctx.extern_token,1727 .return_type = undefined,
1590 .cc_token = null,1728 .var_args_token = null,
1591 .async_attr = null,1729 .extern_export_inline_token = ctx.extern_token,
1592 .body_node = null,1730 .cc_token = null,
1593 .lib_name = null,1731 .async_attr = null,
1594 .align_expr = null,1732 .body_node = null,
1595 }1733 .lib_name = null,
1596 );1734 .align_expr = null,
1735 });
1736 ctx.opt_ctx.store(&fn_proto.base);
1597 stack.append(State { .FnProto = fn_proto }) catch unreachable;1737 stack.append(State { .FnProto = fn_proto }) catch unreachable;
1598 continue;1738 continue;
1599 }1739 }
...@@ -2210,7 +2350,7 @@ pub const Parser = struct {...@@ -2210,7 +2350,7 @@ pub const Parser = struct {
2210 .base = undefined,2350 .base = undefined,
2211 .lhs = lhs,2351 .lhs = lhs,
2212 .op = ast.Node.SuffixOp.Op {2352 .op = ast.Node.SuffixOp.Op {
2213 .StructInitializer = ArrayList(&ast.Node.FieldInitializer).init(arena),2353 .StructInitializer = ArrayList(&ast.Node).init(arena),
2214 },2354 },
2215 .rtoken = undefined,2355 .rtoken = undefined,
2216 }2356 }
...@@ -2218,7 +2358,7 @@ pub const Parser = struct {...@@ -2218,7 +2358,7 @@ pub const Parser = struct {
2218 stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;2358 stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2219 try stack.append(State { .IfToken = Token.Id.LBrace });2359 try stack.append(State { .IfToken = Token.Id.LBrace });
2220 try stack.append(State {2360 try stack.append(State {
2221 .FieldInitListItemOrEnd = ListSave(&ast.Node.FieldInitializer) {2361 .FieldInitListItemOrEnd = ListSave(&ast.Node) {
2222 .list = &node.op.StructInitializer,2362 .list = &node.op.StructInitializer,
2223 .ptr = &node.rtoken,2363 .ptr = &node.rtoken,
2224 }2364 }
...@@ -2443,6 +2583,29 @@ pub const Parser = struct {...@@ -2443,6 +2583,29 @@ pub const Parser = struct {
2443 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token);2583 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token);
2444 continue;2584 continue;
2445 },2585 },
2586 Token.Id.Keyword_promise => {
2587 const node = try arena.construct(ast.Node.PromiseType {
2588 .base = ast.Node {
2589 .id = ast.Node.Id.PromiseType,
2590 .same_line_comment = null,
2591 },
2592 .promise_token = token,
2593 .result = null,
2594 });
2595 opt_ctx.store(&node.base);
2596 const next_token = self.getNextToken();
2597 if (next_token.id != Token.Id.Arrow) {
2598 self.putBackToken(next_token);
2599 continue;
2600 }
2601 node.result = ast.Node.PromiseType.Result {
2602 .arrow_token = next_token,
2603 .return_type = undefined,
2604 };
2605 const return_type_ptr = &((??node.result).return_type);
2606 try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
2607 continue;
2608 },
2446 Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {2609 Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
2447 opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable);2610 opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
2448 continue;2611 continue;
...@@ -2546,46 +2709,50 @@ pub const Parser = struct {...@@ -2546,46 +2709,50 @@ pub const Parser = struct {
2546 continue;2709 continue;
2547 },2710 },
2548 Token.Id.Keyword_fn => {2711 Token.Id.Keyword_fn => {
2549 const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,2712 const fn_proto = try arena.construct(ast.Node.FnProto {
2550 ast.Node.FnProto {2713 .base = ast.Node {
2551 .base = undefined,2714 .id = ast.Node.Id.FnProto,
2552 .comments = null,2715 .same_line_comment = null,
2553 .visib_token = null,2716 },
2554 .name_token = null,2717 .doc_comments = null,
2555 .fn_token = token,2718 .visib_token = null,
2556 .params = ArrayList(&ast.Node).init(arena),2719 .name_token = null,
2557 .return_type = undefined,2720 .fn_token = token,
2558 .var_args_token = null,2721 .params = ArrayList(&ast.Node).init(arena),
2559 .extern_export_inline_token = null,2722 .return_type = undefined,
2560 .cc_token = null,2723 .var_args_token = null,
2561 .async_attr = null,2724 .extern_export_inline_token = null,
2562 .body_node = null,2725 .cc_token = null,
2563 .lib_name = null,2726 .async_attr = null,
2564 .align_expr = null,2727 .body_node = null,
2565 }2728 .lib_name = null,
2566 );2729 .align_expr = null,
2730 });
2731 opt_ctx.store(&fn_proto.base);
2567 stack.append(State { .FnProto = fn_proto }) catch unreachable;2732 stack.append(State { .FnProto = fn_proto }) catch unreachable;
2568 continue;2733 continue;
2569 },2734 },
2570 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {2735 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
2571 const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,2736 const fn_proto = try arena.construct(ast.Node.FnProto {
2572 ast.Node.FnProto {2737 .base = ast.Node {
2573 .base = undefined,2738 .id = ast.Node.Id.FnProto,
2574 .comments = null,2739 .same_line_comment = null,
2575 .visib_token = null,2740 },
2576 .name_token = null,2741 .doc_comments = null,
2577 .fn_token = undefined,2742 .visib_token = null,
2578 .params = ArrayList(&ast.Node).init(arena),2743 .name_token = null,
2579 .return_type = undefined,2744 .fn_token = undefined,
2580 .var_args_token = null,2745 .params = ArrayList(&ast.Node).init(arena),
2581 .extern_export_inline_token = null,2746 .return_type = undefined,
2582 .cc_token = token,2747 .var_args_token = null,
2583 .async_attr = null,2748 .extern_export_inline_token = null,
2584 .body_node = null,2749 .cc_token = token,
2585 .lib_name = null,2750 .async_attr = null,
2586 .align_expr = null,2751 .body_node = null,
2587 }2752 .lib_name = null,
2588 );2753 .align_expr = null,
2754 });
2755 opt_ctx.store(&fn_proto.base);
2589 stack.append(State { .FnProto = fn_proto }) catch unreachable;2756 stack.append(State { .FnProto = fn_proto }) catch unreachable;
2590 try stack.append(State {2757 try stack.append(State {
2591 .ExpectTokenSave = ExpectTokenSave {2758 .ExpectTokenSave = ExpectTokenSave {
...@@ -2659,17 +2826,19 @@ pub const Parser = struct {...@@ -2659,17 +2826,19 @@ pub const Parser = struct {
2659 continue;2826 continue;
2660 }2827 }
26612828
2662 const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ErrorSetDecl,2829 const node = try arena.construct(ast.Node.ErrorSetDecl {
2663 ast.Node.ErrorSetDecl {2830 .base = ast.Node {
2664 .base = undefined,2831 .id = ast.Node.Id.ErrorSetDecl,
2665 .error_token = ctx.error_token,2832 .same_line_comment = null,
2666 .decls = ArrayList(&ast.Node).init(arena),2833 },
2667 .rbrace_token = undefined,2834 .error_token = ctx.error_token,
2668 }2835 .decls = ArrayList(&ast.Node).init(arena),
2669 );2836 .rbrace_token = undefined,
2837 });
2838 ctx.opt_ctx.store(&node.base);
26702839
2671 stack.append(State {2840 stack.append(State {
2672 .IdentifierListItemOrEnd = ListSave(&ast.Node) {2841 .ErrorTagListItemOrEnd = ListSave(&ast.Node) {
2673 .list = &node.decls,2842 .list = &node.decls,
2674 .ptr = &node.rbrace_token,2843 .ptr = &node.rbrace_token,
2675 }2844 }
...@@ -2689,6 +2858,7 @@ pub const Parser = struct {...@@ -2689,6 +2858,7 @@ pub const Parser = struct {
2689 }2858 }
2690 );2859 );
2691 },2860 },
2861
2692 State.Identifier => |opt_ctx| {2862 State.Identifier => |opt_ctx| {
2693 if (self.eatToken(Token.Id.Identifier)) |ident_token| {2863 if (self.eatToken(Token.Id.Identifier)) |ident_token| {
2694 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);2864 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
...@@ -2701,6 +2871,25 @@ pub const Parser = struct {...@@ -2701,6 +2871,25 @@ pub const Parser = struct {
2701 }2871 }
2702 },2872 },
27032873
2874 State.ErrorTag => |node_ptr| {
2875 const comments = try self.eatDocComments(arena);
2876 const ident_token = self.getNextToken();
2877 if (ident_token.id != Token.Id.Identifier) {
2878 return self.parseError(ident_token, "expected {}, found {}",
2879 @tagName(Token.Id.Identifier), @tagName(ident_token.id));
2880 }
2881
2882 const node = try arena.construct(ast.Node.ErrorTag {
2883 .base = ast.Node {
2884 .id = ast.Node.Id.ErrorTag,
2885 .same_line_comment = null,
2886 },
2887 .doc_comments = comments,
2888 .name_token = ident_token,
2889 });
2890 *node_ptr = &node.base;
2891 continue;
2892 },
27042893
2705 State.ExpectToken => |token_id| {2894 State.ExpectToken => |token_id| {
2706 _ = try self.expectToken(token_id);2895 _ = try self.expectToken(token_id);
...@@ -2739,21 +2928,21 @@ pub const Parser = struct {...@@ -2739,21 +2928,21 @@ pub const Parser = struct {
2739 }2928 }
2740 }2929 }
27412930
2742 fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment {2931 fn eatDocComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment {
2743 var result: ?&ast.Node.LineComment = null;2932 var result: ?&ast.Node.DocComment = null;
2744 while (true) {2933 while (true) {
2745 if (self.eatToken(Token.Id.LineComment)) |line_comment| {2934 if (self.eatToken(Token.Id.DocComment)) |line_comment| {
2746 const node = blk: {2935 const node = blk: {
2747 if (result) |comment_node| {2936 if (result) |comment_node| {
2748 break :blk comment_node;2937 break :blk comment_node;
2749 } else {2938 } else {
2750 const comment_node = try arena.create(ast.Node.LineComment);2939 const comment_node = try arena.construct(ast.Node.DocComment {
2751 *comment_node = ast.Node.LineComment {
2752 .base = ast.Node {2940 .base = ast.Node {
2753 .id = ast.Node.Id.LineComment,2941 .id = ast.Node.Id.DocComment,
2942 .same_line_comment = null,
2754 },2943 },
2755 .lines = ArrayList(Token).init(arena),2944 .lines = ArrayList(Token).init(arena),
2756 };2945 });
2757 result = comment_node;2946 result = comment_node;
2758 break :blk comment_node;2947 break :blk comment_node;
2759 }2948 }
...@@ -2766,6 +2955,17 @@ pub const Parser = struct {...@@ -2766,6 +2955,17 @@ pub const Parser = struct {
2766 return result;2955 return result;
2767 }2956 }
27682957
2958 fn eatLineComment(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment {
2959 const token = self.eatToken(Token.Id.LineComment) ?? return null;
2960 return try arena.construct(ast.Node.LineComment {
2961 .base = ast.Node {
2962 .id = ast.Node.Id.LineComment,
2963 .same_line_comment = null,
2964 },
2965 .token = token,
2966 });
2967 }
2968
2769 fn requireSemiColon(node: &const ast.Node) bool {2969 fn requireSemiColon(node: &const ast.Node) bool {
2770 var n = node;2970 var n = node;
2771 while (true) {2971 while (true) {
...@@ -2783,6 +2983,7 @@ pub const Parser = struct {...@@ -2783,6 +2983,7 @@ pub const Parser = struct {
2783 ast.Node.Id.SwitchCase,2983 ast.Node.Id.SwitchCase,
2784 ast.Node.Id.SwitchElse,2984 ast.Node.Id.SwitchElse,
2785 ast.Node.Id.FieldInitializer,2985 ast.Node.Id.FieldInitializer,
2986 ast.Node.Id.DocComment,
2786 ast.Node.Id.LineComment,2987 ast.Node.Id.LineComment,
2787 ast.Node.Id.TestDecl => return false,2988 ast.Node.Id.TestDecl => return false,
2788 ast.Node.Id.While => {2989 ast.Node.Id.While => {
...@@ -2838,6 +3039,25 @@ pub const Parser = struct {...@@ -2838,6 +3039,25 @@ pub const Parser = struct {
2838 }3039 }
2839 }3040 }
28403041
3042 fn lookForSameLineComment(self: &Parser, arena: &mem.Allocator, node: &ast.Node) !void {
3043 const node_last_token = node.lastToken();
3044
3045 const line_comment_token = self.getNextToken();
3046 if (line_comment_token.id != Token.Id.DocComment and line_comment_token.id != Token.Id.LineComment) {
3047 self.putBackToken(line_comment_token);
3048 return;
3049 }
3050
3051 const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token);
3052 const different_line = offset_loc.line != 0;
3053 if (different_line) {
3054 self.putBackToken(line_comment_token);
3055 return;
3056 }
3057
3058 node.same_line_comment = try arena.construct(line_comment_token);
3059 }
3060
2841 fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {3061 fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {
2842 switch (token.id) {3062 switch (token.id) {
2843 Token.Id.StringLiteral => {3063 Token.Id.StringLiteral => {
...@@ -2875,6 +3095,7 @@ pub const Parser = struct {...@@ -2875,6 +3095,7 @@ pub const Parser = struct {
2875 const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend,3095 const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend,
2876 ast.Node.Suspend {3096 ast.Node.Suspend {
2877 .base = undefined,3097 .base = undefined,
3098 .label = null,
2878 .suspend_token = *token,3099 .suspend_token = *token,
2879 .payload = null,3100 .payload = null,
2880 .body = null,3101 .body = null,
...@@ -2928,18 +3149,20 @@ pub const Parser = struct {...@@ -2928,18 +3149,20 @@ pub const Parser = struct {
2928 return true;3149 return true;
2929 },3150 },
2930 Token.Id.Keyword_switch => {3151 Token.Id.Keyword_switch => {
2931 const node = try self.createToCtxNode(arena, ctx, ast.Node.Switch,3152 const node = try arena.construct(ast.Node.Switch {
2932 ast.Node.Switch {3153 .base = ast.Node {
2933 .base = undefined,3154 .id = ast.Node.Id.Switch,
2934 .switch_token = *token,3155 .same_line_comment = null,
2935 .expr = undefined,3156 },
2936 .cases = ArrayList(&ast.Node.SwitchCase).init(arena),3157 .switch_token = *token,
2937 .rbrace = undefined,3158 .expr = undefined,
2938 }3159 .cases = ArrayList(&ast.Node).init(arena),
2939 );3160 .rbrace = undefined,
3161 });
3162 ctx.store(&node.base);
29403163
2941 stack.append(State {3164 stack.append(State {
2942 .SwitchCaseOrEnd = ListSave(&ast.Node.SwitchCase) {3165 .SwitchCaseOrEnd = ListSave(&ast.Node) {
2943 .list = &node.cases,3166 .list = &node.cases,
2944 .ptr = &node.rbrace,3167 .ptr = &node.rbrace,
2945 },3168 },
...@@ -2956,6 +3179,7 @@ pub const Parser = struct {...@@ -2956,6 +3179,7 @@ pub const Parser = struct {
2956 .base = undefined,3179 .base = undefined,
2957 .comptime_token = *token,3180 .comptime_token = *token,
2958 .expr = undefined,3181 .expr = undefined,
3182 .doc_comments = null,
2959 }3183 }
2960 );3184 );
2961 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });3185 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
...@@ -3096,7 +3320,10 @@ pub const Parser = struct {...@@ -3096,7 +3320,10 @@ pub const Parser = struct {
3096 *node = *init_to;3320 *node = *init_to;
3097 node.base = blk: {3321 node.base = blk: {
3098 const id = ast.Node.typeToId(T);3322 const id = ast.Node.typeToId(T);
3099 break :blk ast.Node {.id = id};3323 break :blk ast.Node {
3324 .id = id,
3325 .same_line_comment = null,
3326 };
3100 };3327 };
31013328
3102 return node;3329 return node;
...@@ -3229,9 +3456,9 @@ pub const Parser = struct {...@@ -3229,9 +3456,9 @@ pub const Parser = struct {
3229 Expression: &ast.Node,3456 Expression: &ast.Node,
3230 VarDecl: &ast.Node.VarDecl,3457 VarDecl: &ast.Node.VarDecl,
3231 Statement: &ast.Node,3458 Statement: &ast.Node,
3232 FieldInitializer: &ast.Node.FieldInitializer,
3233 PrintIndent,3459 PrintIndent,
3234 Indent: usize,3460 Indent: usize,
3461 PrintSameLineComment: ?&Token,
3235 };3462 };
32363463
3237 pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {3464 pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
...@@ -3266,6 +3493,7 @@ pub const Parser = struct {...@@ -3266,6 +3493,7 @@ pub const Parser = struct {
3266 while (stack.popOrNull()) |state| {3493 while (stack.popOrNull()) |state| {
3267 switch (state) {3494 switch (state) {
3268 RenderState.TopLevelDecl => |decl| {3495 RenderState.TopLevelDecl => |decl| {
3496 try stack.append(RenderState { .PrintSameLineComment = decl.same_line_comment } );
3269 switch (decl.id) {3497 switch (decl.id) {
3270 ast.Node.Id.FnProto => {3498 ast.Node.Id.FnProto => {
3271 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);3499 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
...@@ -3291,6 +3519,7 @@ pub const Parser = struct {...@@ -3291,6 +3519,7 @@ pub const Parser = struct {
3291 },3519 },
3292 ast.Node.Id.VarDecl => {3520 ast.Node.Id.VarDecl => {
3293 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);3521 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
3522 try self.renderComments(stream, var_decl, indent);
3294 try stack.append(RenderState { .VarDecl = var_decl});3523 try stack.append(RenderState { .VarDecl = var_decl});
3295 },3524 },
3296 ast.Node.Id.TestDecl => {3525 ast.Node.Id.TestDecl => {
...@@ -3303,16 +3532,26 @@ pub const Parser = struct {...@@ -3303,16 +3532,26 @@ pub const Parser = struct {
3303 },3532 },
3304 ast.Node.Id.StructField => {3533 ast.Node.Id.StructField => {
3305 const field = @fieldParentPtr(ast.Node.StructField, "base", decl);3534 const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
3535 try self.renderComments(stream, field, indent);
3306 if (field.visib_token) |visib_token| {3536 if (field.visib_token) |visib_token| {
3307 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));3537 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
3308 }3538 }
3309 try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token));3539 try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token));
3540 try stack.append(RenderState { .Text = "," });
3310 try stack.append(RenderState { .Expression = field.type_expr});3541 try stack.append(RenderState { .Expression = field.type_expr});
3311 },3542 },
3312 ast.Node.Id.UnionTag => {3543 ast.Node.Id.UnionTag => {
3313 const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);3544 const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
3545 try self.renderComments(stream, tag, indent);
3314 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));3546 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
33153547
3548 try stack.append(RenderState { .Text = "," });
3549
3550 if (tag.value_expr) |value_expr| {
3551 try stack.append(RenderState { .Expression = value_expr });
3552 try stack.append(RenderState { .Text = " = " });
3553 }
3554
3316 if (tag.type_expr) |type_expr| {3555 if (tag.type_expr) |type_expr| {
3317 try stream.print(": ");3556 try stream.print(": ");
3318 try stack.append(RenderState { .Expression = type_expr});3557 try stack.append(RenderState { .Expression = type_expr});
...@@ -3320,35 +3559,40 @@ pub const Parser = struct {...@@ -3320,35 +3559,40 @@ pub const Parser = struct {
3320 },3559 },
3321 ast.Node.Id.EnumTag => {3560 ast.Node.Id.EnumTag => {
3322 const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);3561 const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
3562 try self.renderComments(stream, tag, indent);
3323 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));3563 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
33243564
3565 try stack.append(RenderState { .Text = "," });
3325 if (tag.value) |value| {3566 if (tag.value) |value| {
3326 try stream.print(" = ");3567 try stream.print(" = ");
3327 try stack.append(RenderState { .Expression = value});3568 try stack.append(RenderState { .Expression = value});
3328 }3569 }
3329 },3570 },
3571 ast.Node.Id.ErrorTag => {
3572 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl);
3573 try self.renderComments(stream, tag, indent);
3574 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
3575 },
3330 ast.Node.Id.Comptime => {3576 ast.Node.Id.Comptime => {
3331 if (requireSemiColon(decl)) {3577 if (requireSemiColon(decl)) {
3332 try stack.append(RenderState { .Text = ";" });3578 try stack.append(RenderState { .Text = ";" });
3333 }3579 }
3334 try stack.append(RenderState { .Expression = decl });3580 try stack.append(RenderState { .Expression = decl });
3335 },3581 },
3582 ast.Node.Id.LineComment => {
3583 const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl);
3584 try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token));
3585 },
3336 else => unreachable,3586 else => unreachable,
3337 }3587 }
3338 },3588 },
33393589
3340 RenderState.FieldInitializer => |field_init| {
3341 //TODO try self.renderComments(stream, field_init, indent);
3342 try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));
3343 try stream.print(" = ");
3344 try stack.append(RenderState { .Expression = field_init.expr });
3345 },
3346
3347 RenderState.VarDecl => |var_decl| {3590 RenderState.VarDecl => |var_decl| {
3348 try stack.append(RenderState { .Text = ";" });3591 try stack.append(RenderState { .Text = ";" });
3349 if (var_decl.init_node) |init_node| {3592 if (var_decl.init_node) |init_node| {
3350 try stack.append(RenderState { .Expression = init_node });3593 try stack.append(RenderState { .Expression = init_node });
3351 try stack.append(RenderState { .Text = " = " });3594 const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
3595 try stack.append(RenderState { .Text = text });
3352 }3596 }
3353 if (var_decl.align_node) |align_node| {3597 if (var_decl.align_node) |align_node| {
3354 try stack.append(RenderState { .Text = ")" });3598 try stack.append(RenderState { .Text = ")" });
...@@ -3385,7 +3629,6 @@ pub const Parser = struct {...@@ -3385,7 +3629,6 @@ pub const Parser = struct {
33853629
3386 RenderState.ParamDecl => |base| {3630 RenderState.ParamDecl => |base| {
3387 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);3631 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
3388 // TODO try self.renderComments(stream, param_decl, indent);
3389 if (param_decl.comptime_token) |comptime_token| {3632 if (param_decl.comptime_token) |comptime_token| {
3390 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));3633 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
3391 }3634 }
...@@ -3467,6 +3710,9 @@ pub const Parser = struct {...@@ -3467,6 +3710,9 @@ pub const Parser = struct {
3467 },3710 },
3468 ast.Node.Id.Suspend => {3711 ast.Node.Id.Suspend => {
3469 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);3712 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
3713 if (suspend_node.label) |label| {
3714 try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
3715 }
3470 try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));3716 try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));
34713717
3472 if (suspend_node.body) |body| {3718 if (suspend_node.body) |body| {
...@@ -3635,19 +3881,41 @@ pub const Parser = struct {...@@ -3635,19 +3881,41 @@ pub const Parser = struct {
3635 try stack.append(RenderState { .Expression = suffix_op.lhs });3881 try stack.append(RenderState { .Expression = suffix_op.lhs });
3636 continue;3882 continue;
3637 }3883 }
3884 if (field_inits.len == 1) {
3885 const field_init = field_inits.at(0);
3886
3887 try stack.append(RenderState { .Text = " }" });
3888 try stack.append(RenderState { .Expression = field_init });
3889 try stack.append(RenderState { .Text = "{ " });
3890 try stack.append(RenderState { .Expression = suffix_op.lhs });
3891 continue;
3892 }
3638 try stack.append(RenderState { .Text = "}"});3893 try stack.append(RenderState { .Text = "}"});
3639 try stack.append(RenderState.PrintIndent);3894 try stack.append(RenderState.PrintIndent);
3640 try stack.append(RenderState { .Indent = indent });3895 try stack.append(RenderState { .Indent = indent });
3896 try stack.append(RenderState { .Text = "\n" });
3641 var i = field_inits.len;3897 var i = field_inits.len;
3642 while (i != 0) {3898 while (i != 0) {
3643 i -= 1;3899 i -= 1;
3644 const field_init = field_inits.at(i);3900 const field_init = field_inits.at(i);
3645 try stack.append(RenderState { .Text = ",\n" });3901 if (field_init.id != ast.Node.Id.LineComment) {
3646 try stack.append(RenderState { .FieldInitializer = field_init });3902 try stack.append(RenderState { .Text = "," });
3903 }
3904 try stack.append(RenderState { .Expression = field_init });
3647 try stack.append(RenderState.PrintIndent);3905 try stack.append(RenderState.PrintIndent);
3906 if (i != 0) {
3907 try stack.append(RenderState { .Text = blk: {
3908 const prev_node = field_inits.at(i - 1);
3909 const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, field_init.firstToken());
3910 if (loc.line >= 2) {
3911 break :blk "\n\n";
3912 }
3913 break :blk "\n";
3914 }});
3915 }
3648 }3916 }
3649 try stack.append(RenderState { .Indent = indent + indent_delta });3917 try stack.append(RenderState { .Indent = indent + indent_delta });
3650 try stack.append(RenderState { .Text = " {\n"});3918 try stack.append(RenderState { .Text = "{\n"});
3651 try stack.append(RenderState { .Expression = suffix_op.lhs });3919 try stack.append(RenderState { .Expression = suffix_op.lhs });
3652 },3920 },
3653 ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| {3921 ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| {
...@@ -3656,6 +3924,16 @@ pub const Parser = struct {...@@ -3656,6 +3924,16 @@ pub const Parser = struct {
3656 try stack.append(RenderState { .Expression = suffix_op.lhs });3924 try stack.append(RenderState { .Expression = suffix_op.lhs });
3657 continue;3925 continue;
3658 }3926 }
3927 if (exprs.len == 1) {
3928 const expr = exprs.at(0);
3929
3930 try stack.append(RenderState { .Text = "}" });
3931 try stack.append(RenderState { .Expression = expr });
3932 try stack.append(RenderState { .Text = "{" });
3933 try stack.append(RenderState { .Expression = suffix_op.lhs });
3934 continue;
3935 }
3936
3659 try stack.append(RenderState { .Text = "}"});3937 try stack.append(RenderState { .Text = "}"});
3660 try stack.append(RenderState.PrintIndent);3938 try stack.append(RenderState.PrintIndent);
3661 try stack.append(RenderState { .Indent = indent });3939 try stack.append(RenderState { .Indent = indent });
...@@ -3668,7 +3946,7 @@ pub const Parser = struct {...@@ -3668,7 +3946,7 @@ pub const Parser = struct {
3668 try stack.append(RenderState.PrintIndent);3946 try stack.append(RenderState.PrintIndent);
3669 }3947 }
3670 try stack.append(RenderState { .Indent = indent + indent_delta });3948 try stack.append(RenderState { .Indent = indent + indent_delta });
3671 try stack.append(RenderState { .Text = " {\n"});3949 try stack.append(RenderState { .Text = "{\n"});
3672 try stack.append(RenderState { .Expression = suffix_op.lhs });3950 try stack.append(RenderState { .Expression = suffix_op.lhs });
3673 },3951 },
3674 }3952 }
...@@ -3802,45 +4080,49 @@ pub const Parser = struct {...@@ -3802,45 +4080,49 @@ pub const Parser = struct {
3802 ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),4080 ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
3803 }4081 }
38044082
3805 try stack.append(RenderState { .Text = "}"});
3806 try stack.append(RenderState.PrintIndent);
3807 try stack.append(RenderState { .Indent = indent });
3808 try stack.append(RenderState { .Text = "\n"});
3809
3810 const fields_and_decls = container_decl.fields_and_decls.toSliceConst();4083 const fields_and_decls = container_decl.fields_and_decls.toSliceConst();
3811 var i = fields_and_decls.len;4084 if (fields_and_decls.len == 0) {
3812 while (i != 0) {4085 try stack.append(RenderState { .Text = "{}"});
3813 i -= 1;4086 } else {
3814 const node = fields_and_decls[i];4087 try stack.append(RenderState { .Text = "}"});
3815 switch (node.id) {
3816 ast.Node.Id.StructField,
3817 ast.Node.Id.UnionTag,
3818 ast.Node.Id.EnumTag => {
3819 try stack.append(RenderState { .Text = "," });
3820 },
3821 else => { }
3822 }
3823 try stack.append(RenderState { .TopLevelDecl = node});
3824 try stack.append(RenderState.PrintIndent);4088 try stack.append(RenderState.PrintIndent);
3825 try stack.append(RenderState {4089 try stack.append(RenderState { .Indent = indent });
3826 .Text = blk: {4090 try stack.append(RenderState { .Text = "\n"});
3827 if (i != 0) {4091
3828 const prev_node = fields_and_decls[i - 1];4092 var i = fields_and_decls.len;
3829 const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());4093 while (i != 0) {
3830 if (loc.line >= 2) {4094 i -= 1;
3831 break :blk "\n\n";4095 const node = fields_and_decls[i];
4096 try stack.append(RenderState { .TopLevelDecl = node});
4097 try stack.append(RenderState.PrintIndent);
4098 try stack.append(RenderState {
4099 .Text = blk: {
4100 if (i != 0) {
4101 const prev_node = fields_and_decls[i - 1];
4102 const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
4103 if (loc.line >= 2) {
4104 break :blk "\n\n";
4105 }
3832 }4106 }
3833 }4107 break :blk "\n";
3834 break :blk "\n";4108 },
3835 },4109 });
3836 });4110 }
4111 try stack.append(RenderState { .Indent = indent + indent_delta});
4112 try stack.append(RenderState { .Text = "{"});
3837 }4113 }
3838 try stack.append(RenderState { .Indent = indent + indent_delta});
3839 try stack.append(RenderState { .Text = "{"});
38404114
3841 switch (container_decl.init_arg_expr) {4115 switch (container_decl.init_arg_expr) {
3842 ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),4116 ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
3843 ast.Node.ContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}),4117 ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| {
4118 if (enum_tag_type) |expr| {
4119 try stack.append(RenderState { .Text = ")) "});
4120 try stack.append(RenderState { .Expression = expr});
4121 try stack.append(RenderState { .Text = "(enum("});
4122 } else {
4123 try stack.append(RenderState { .Text = "(enum) "});
4124 }
4125 },
3844 ast.Node.ContainerDecl.InitArg.Type => |type_expr| {4126 ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
3845 try stack.append(RenderState { .Text = ") "});4127 try stack.append(RenderState { .Text = ") "});
3846 try stack.append(RenderState { .Expression = type_expr});4128 try stack.append(RenderState { .Expression = type_expr});
...@@ -3850,20 +4132,47 @@ pub const Parser = struct {...@@ -3850,20 +4132,47 @@ pub const Parser = struct {
3850 },4132 },
3851 ast.Node.Id.ErrorSetDecl => {4133 ast.Node.Id.ErrorSetDecl => {
3852 const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);4134 const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
3853 try stream.print("error ");4135
4136 const decls = err_set_decl.decls.toSliceConst();
4137 if (decls.len == 0) {
4138 try stream.write("error{}");
4139 continue;
4140 }
4141
4142 if (decls.len == 1) blk: {
4143 const node = decls[0];
4144
4145 // if there are any doc comments or same line comments
4146 // don't try to put it all on one line
4147 if (node.same_line_comment != null) break :blk;
4148 if (node.cast(ast.Node.ErrorTag)) |tag| {
4149 if (tag.doc_comments != null) break :blk;
4150 } else {
4151 break :blk;
4152 }
4153
4154
4155 try stream.write("error{");
4156 try stack.append(RenderState { .Text = "}" });
4157 try stack.append(RenderState { .TopLevelDecl = node });
4158 continue;
4159 }
4160
4161 try stream.write("error{");
38544162
3855 try stack.append(RenderState { .Text = "}"});4163 try stack.append(RenderState { .Text = "}"});
3856 try stack.append(RenderState.PrintIndent);4164 try stack.append(RenderState.PrintIndent);
3857 try stack.append(RenderState { .Indent = indent });4165 try stack.append(RenderState { .Indent = indent });
3858 try stack.append(RenderState { .Text = "\n"});4166 try stack.append(RenderState { .Text = "\n"});
38594167
3860 const decls = err_set_decl.decls.toSliceConst();
3861 var i = decls.len;4168 var i = decls.len;
3862 while (i != 0) {4169 while (i != 0) {
3863 i -= 1;4170 i -= 1;
3864 const node = decls[i];4171 const node = decls[i];
3865 try stack.append(RenderState { .Text = "," });4172 if (node.id != ast.Node.Id.LineComment) {
3866 try stack.append(RenderState { .Expression = node });4173 try stack.append(RenderState { .Text = "," });
4174 }
4175 try stack.append(RenderState { .TopLevelDecl = node });
3867 try stack.append(RenderState.PrintIndent);4176 try stack.append(RenderState.PrintIndent);
3868 try stack.append(RenderState {4177 try stack.append(RenderState {
3869 .Text = blk: {4178 .Text = blk: {
...@@ -3879,7 +4188,6 @@ pub const Parser = struct {...@@ -3879,7 +4188,6 @@ pub const Parser = struct {
3879 });4188 });
3880 }4189 }
3881 try stack.append(RenderState { .Indent = indent + indent_delta});4190 try stack.append(RenderState { .Indent = indent + indent_delta});
3882 try stack.append(RenderState { .Text = "{"});
3883 },4191 },
3884 ast.Node.Id.MultilineStringLiteral => {4192 ast.Node.Id.MultilineStringLiteral => {
3885 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);4193 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
...@@ -3891,7 +4199,7 @@ pub const Parser = struct {...@@ -3891,7 +4199,7 @@ pub const Parser = struct {
3891 try stream.writeByteNTimes(' ', indent + indent_delta);4199 try stream.writeByteNTimes(' ', indent + indent_delta);
3892 try stream.print("{}", self.tokenizer.getTokenSlice(t));4200 try stream.print("{}", self.tokenizer.getTokenSlice(t));
3893 }4201 }
3894 try stream.writeByteNTimes(' ', indent + indent_delta);4202 try stream.writeByteNTimes(' ', indent);
3895 },4203 },
3896 ast.Node.Id.UndefinedLiteral => {4204 ast.Node.Id.UndefinedLiteral => {
3897 const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);4205 const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
...@@ -3974,7 +4282,19 @@ pub const Parser = struct {...@@ -3974,7 +4282,19 @@ pub const Parser = struct {
3974 try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });4282 try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
3975 }4283 }
3976 },4284 },
3977 ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),4285 ast.Node.Id.PromiseType => {
4286 const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base);
4287 try stream.write(self.tokenizer.getTokenSlice(promise_type.promise_token));
4288 if (promise_type.result) |result| {
4289 try stream.write(self.tokenizer.getTokenSlice(result.arrow_token));
4290 try stack.append(RenderState { .Expression = result.return_type});
4291 }
4292 },
4293 ast.Node.Id.LineComment => {
4294 const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base);
4295 try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token));
4296 },
4297 ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes
3978 ast.Node.Id.Switch => {4298 ast.Node.Id.Switch => {
3979 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);4299 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
3980 try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));4300 try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
...@@ -3989,8 +4309,7 @@ pub const Parser = struct {...@@ -3989,8 +4309,7 @@ pub const Parser = struct {
3989 while (i != 0) {4309 while (i != 0) {
3990 i -= 1;4310 i -= 1;
3991 const node = cases[i];4311 const node = cases[i];
3992 try stack.append(RenderState { .Text = ","});4312 try stack.append(RenderState { .Expression = node});
3993 try stack.append(RenderState { .Expression = &node.base});
3994 try stack.append(RenderState.PrintIndent);4313 try stack.append(RenderState.PrintIndent);
3995 try stack.append(RenderState {4314 try stack.append(RenderState {
3996 .Text = blk: {4315 .Text = blk: {
...@@ -4012,6 +4331,8 @@ pub const Parser = struct {...@@ -4012,6 +4331,8 @@ pub const Parser = struct {
4012 ast.Node.Id.SwitchCase => {4331 ast.Node.Id.SwitchCase => {
4013 const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);4332 const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
40144333
4334 try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment });
4335 try stack.append(RenderState { .Text = "," });
4015 try stack.append(RenderState { .Expression = switch_case.expr });4336 try stack.append(RenderState { .Expression = switch_case.expr });
4016 if (switch_case.payload) |payload| {4337 if (switch_case.payload) |payload| {
4017 try stack.append(RenderState { .Text = " " });4338 try stack.append(RenderState { .Text = " " });
...@@ -4321,6 +4642,7 @@ pub const Parser = struct {...@@ -4321,6 +4642,7 @@ pub const Parser = struct {
4321 ast.Node.Id.StructField,4642 ast.Node.Id.StructField,
4322 ast.Node.Id.UnionTag,4643 ast.Node.Id.UnionTag,
4323 ast.Node.Id.EnumTag,4644 ast.Node.Id.EnumTag,
4645 ast.Node.Id.ErrorTag,
4324 ast.Node.Id.Root,4646 ast.Node.Id.Root,
4325 ast.Node.Id.VarDecl,4647 ast.Node.Id.VarDecl,
4326 ast.Node.Id.Use,4648 ast.Node.Id.Use,
...@@ -4328,10 +4650,10 @@ pub const Parser = struct {...@@ -4328,10 +4650,10 @@ pub const Parser = struct {
4328 ast.Node.Id.ParamDecl => unreachable,4650 ast.Node.Id.ParamDecl => unreachable,
4329 },4651 },
4330 RenderState.Statement => |base| {4652 RenderState.Statement => |base| {
4653 try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } );
4331 switch (base.id) {4654 switch (base.id) {
4332 ast.Node.Id.VarDecl => {4655 ast.Node.Id.VarDecl => {
4333 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);4656 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
4334 try self.renderComments(stream, var_decl, indent);
4335 try stack.append(RenderState { .VarDecl = var_decl});4657 try stack.append(RenderState { .VarDecl = var_decl});
4336 },4658 },
4337 else => {4659 else => {
...@@ -4344,12 +4666,16 @@ pub const Parser = struct {...@@ -4344,12 +4666,16 @@ pub const Parser = struct {
4344 },4666 },
4345 RenderState.Indent => |new_indent| indent = new_indent,4667 RenderState.Indent => |new_indent| indent = new_indent,
4346 RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),4668 RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
4669 RenderState.PrintSameLineComment => |maybe_comment| blk: {
4670 const comment_token = maybe_comment ?? break :blk;
4671 try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token));
4672 },
4347 }4673 }
4348 }4674 }
4349 }4675 }
43504676
4351 fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void {4677 fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void {
4352 const comment = node.comments ?? return;4678 const comment = node.doc_comments ?? return;
4353 for (comment.lines.toSliceConst()) |line_token| {4679 for (comment.lines.toSliceConst()) |line_token| {
4354 try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));4680 try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
4355 try stream.writeByteNTimes(' ', indent);4681 try stream.writeByteNTimes(' ', indent);
...@@ -4373,920 +4699,6 @@ pub const Parser = struct {...@@ -4373,920 +4699,6 @@ pub const Parser = struct {
43734699
4374};4700};
43754701
4376var fixed_buffer_mem: [100 * 1024]u8 = undefined;4702test "std.zig.parser" {
43774703 _ = @import("parser_test.zig");
4378fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
4379 var tokenizer = Tokenizer.init(source);
4380 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
4381 defer parser.deinit();
4382
4383 var tree = try parser.parse();
4384 defer tree.deinit();
4385
4386 var buffer = try std.Buffer.initSize(allocator, 0);
4387 errdefer buffer.deinit();
4388
4389 var buffer_out_stream = io.BufferOutStream.init(&buffer);
4390 try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
4391 return buffer.toOwnedSlice();
4392}
4393
4394fn testCanonical(source: []const u8) !void {
4395 const needed_alloc_count = x: {
4396 // Try it once with unlimited memory, make sure it works
4397 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
4398 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
4399 const result_source = try testParse(source, &failing_allocator.allocator);
4400 if (!mem.eql(u8, result_source, source)) {
4401 warn("\n====== expected this output: =========\n");
4402 warn("{}", source);
4403 warn("\n======== instead found this: =========\n");
4404 warn("{}", result_source);
4405 warn("\n======================================\n");
4406 return error.TestFailed;
4407 }
4408 failing_allocator.allocator.free(result_source);
4409 break :x failing_allocator.index;
4410 };
4411
4412 var fail_index: usize = 0;
4413 while (fail_index < needed_alloc_count) : (fail_index += 1) {
4414 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
4415 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
4416 if (testParse(source, &failing_allocator.allocator)) |_| {
4417 return error.NondeterministicMemoryUsage;
4418 } else |err| switch (err) {
4419 error.OutOfMemory => {
4420 if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
4421 warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
4422 fail_index, needed_alloc_count,
4423 failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
4424 failing_allocator.index, failing_allocator.deallocations);
4425 return error.MemoryLeakDetected;
4426 }
4427 },
4428 error.ParseError => @panic("test failed"),
4429 }
4430 }
4431}
4432
4433test "zig fmt: preserve top level comments" {
4434 try testCanonical(
4435 \\// top level comment
4436 \\test "hi" {}
4437 \\
4438 );
4439}
4440
4441test "zig fmt: get stdout or fail" {
4442 try testCanonical(
4443 \\const std = @import("std");
4444 \\
4445 \\pub fn main() !void {
4446 \\ // If this program is run without stdout attached, exit with an error.
4447 \\ // another comment
4448 \\ var stdout_file = try std.io.getStdOut;
4449 \\}
4450 \\
4451 );
4452}
4453
4454test "zig fmt: preserve spacing" {
4455 try testCanonical(
4456 \\const std = @import("std");
4457 \\
4458 \\pub fn main() !void {
4459 \\ var stdout_file = try std.io.getStdOut;
4460 \\ var stdout_file = try std.io.getStdOut;
4461 \\
4462 \\ var stdout_file = try std.io.getStdOut;
4463 \\ var stdout_file = try std.io.getStdOut;
4464 \\}
4465 \\
4466 );
4467}
4468
4469test "zig fmt: return types" {
4470 try testCanonical(
4471 \\pub fn main() !void {}
4472 \\pub fn main() var {}
4473 \\pub fn main() i32 {}
4474 \\
4475 );
4476}
4477
4478test "zig fmt: imports" {
4479 try testCanonical(
4480 \\const std = @import("std");
4481 \\const std = @import();
4482 \\
4483 );
4484}
4485
4486test "zig fmt: global declarations" {
4487 try testCanonical(
4488 \\const a = b;
4489 \\pub const a = b;
4490 \\var a = b;
4491 \\pub var a = b;
4492 \\const a: i32 = b;
4493 \\pub const a: i32 = b;
4494 \\var a: i32 = b;
4495 \\pub var a: i32 = b;
4496 \\extern const a: i32 = b;
4497 \\pub extern const a: i32 = b;
4498 \\extern var a: i32 = b;
4499 \\pub extern var a: i32 = b;
4500 \\extern "a" const a: i32 = b;
4501 \\pub extern "a" const a: i32 = b;
4502 \\extern "a" var a: i32 = b;
4503 \\pub extern "a" var a: i32 = b;
4504 \\
4505 );
4506}
4507
4508test "zig fmt: extern declaration" {
4509 try testCanonical(
4510 \\extern var foo: c_int;
4511 \\
4512 );
4513}
4514
4515test "zig fmt: alignment" {
4516 try testCanonical(
4517 \\var foo: c_int align(1);
4518 \\
4519 );
4520}
4521
4522test "zig fmt: C main" {
4523 try testCanonical(
4524 \\fn main(argc: c_int, argv: &&u8) c_int {
4525 \\ const a = b;
4526 \\}
4527 \\
4528 );
4529}
4530
4531test "zig fmt: return" {
4532 try testCanonical(
4533 \\fn foo(argc: c_int, argv: &&u8) c_int {
4534 \\ return 0;
4535 \\}
4536 \\
4537 \\fn bar() void {
4538 \\ return;
4539 \\}
4540 \\
4541 );
4542}
4543
4544test "zig fmt: pointer attributes" {
4545 try testCanonical(
4546 \\extern fn f1(s: &align(&u8) u8) c_int;
4547 \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
4548 \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
4549 \\extern fn f4(s: &align(1) const volatile u8) c_int;
4550 \\
4551 );
4552}
4553
4554test "zig fmt: slice attributes" {
4555 try testCanonical(
4556 \\extern fn f1(s: &align(&u8) u8) c_int;
4557 \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
4558 \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
4559 \\extern fn f4(s: &align(1) const volatile u8) c_int;
4560 \\
4561 );
4562}
4563
4564test "zig fmt: test declaration" {
4565 try testCanonical(
4566 \\test "test name" {
4567 \\ const a = 1;
4568 \\ var b = 1;
4569 \\}
4570 \\
4571 );
4572}
4573
4574test "zig fmt: infix operators" {
4575 try testCanonical(
4576 \\test "infix operators" {
4577 \\ var i = undefined;
4578 \\ i = 2;
4579 \\ i *= 2;
4580 \\ i |= 2;
4581 \\ i ^= 2;
4582 \\ i <<= 2;
4583 \\ i >>= 2;
4584 \\ i &= 2;
4585 \\ i *= 2;
4586 \\ i *%= 2;
4587 \\ i -= 2;
4588 \\ i -%= 2;
4589 \\ i += 2;
4590 \\ i +%= 2;
4591 \\ i /= 2;
4592 \\ i %= 2;
4593 \\ _ = i == i;
4594 \\ _ = i != i;
4595 \\ _ = i != i;
4596 \\ _ = i.i;
4597 \\ _ = i || i;
4598 \\ _ = i!i;
4599 \\ _ = i ** i;
4600 \\ _ = i ++ i;
4601 \\ _ = i ?? i;
4602 \\ _ = i % i;
4603 \\ _ = i / i;
4604 \\ _ = i *% i;
4605 \\ _ = i * i;
4606 \\ _ = i -% i;
4607 \\ _ = i - i;
4608 \\ _ = i +% i;
4609 \\ _ = i + i;
4610 \\ _ = i << i;
4611 \\ _ = i >> i;
4612 \\ _ = i & i;
4613 \\ _ = i ^ i;
4614 \\ _ = i | i;
4615 \\ _ = i >= i;
4616 \\ _ = i <= i;
4617 \\ _ = i > i;
4618 \\ _ = i < i;
4619 \\ _ = i and i;
4620 \\ _ = i or i;
4621 \\}
4622 \\
4623 );
4624}
4625
4626test "zig fmt: precedence" {
4627 try testCanonical(
4628 \\test "precedence" {
4629 \\ a!b();
4630 \\ (a!b)();
4631 \\ !a!b;
4632 \\ !(a!b);
4633 \\ !a{};
4634 \\ !(a{});
4635 \\ a + b{};
4636 \\ (a + b){};
4637 \\ a << b + c;
4638 \\ (a << b) + c;
4639 \\ a & b << c;
4640 \\ (a & b) << c;
4641 \\ a ^ b & c;
4642 \\ (a ^ b) & c;
4643 \\ a | b ^ c;
4644 \\ (a | b) ^ c;
4645 \\ a == b | c;
4646 \\ (a == b) | c;
4647 \\ a and b == c;
4648 \\ (a and b) == c;
4649 \\ a or b and c;
4650 \\ (a or b) and c;
4651 \\ (a or b) and c;
4652 \\}
4653 \\
4654 );
4655}
4656
4657test "zig fmt: prefix operators" {
4658 try testCanonical(
4659 \\test "prefix operators" {
4660 \\ try return --%~??!*&0;
4661 \\}
4662 \\
4663 );
4664}
4665
4666test "zig fmt: call expression" {
4667 try testCanonical(
4668 \\test "test calls" {
4669 \\ a();
4670 \\ a(1);
4671 \\ a(1, 2);
4672 \\ a(1, 2) + a(1, 2);
4673 \\}
4674 \\
4675 );
4676}
4677
4678test "zig fmt: var args" {
4679 try testCanonical(
4680 \\fn print(args: ...) void {}
4681 \\
4682 );
4683}
4684
4685test "zig fmt: var type" {
4686 try testCanonical(
4687 \\fn print(args: var) var {}
4688 \\const Var = var;
4689 \\const i: var = 0;
4690 \\
4691 );
4692}
4693
4694test "zig fmt: functions" {
4695 try testCanonical(
4696 \\extern fn puts(s: &const u8) c_int;
4697 \\extern "c" fn puts(s: &const u8) c_int;
4698 \\export fn puts(s: &const u8) c_int;
4699 \\inline fn puts(s: &const u8) c_int;
4700 \\pub extern fn puts(s: &const u8) c_int;
4701 \\pub extern "c" fn puts(s: &const u8) c_int;
4702 \\pub export fn puts(s: &const u8) c_int;
4703 \\pub inline fn puts(s: &const u8) c_int;
4704 \\pub extern fn puts(s: &const u8) align(2 + 2) c_int;
4705 \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int;
4706 \\pub export fn puts(s: &const u8) align(2 + 2) c_int;
4707 \\pub inline fn puts(s: &const u8) align(2 + 2) c_int;
4708 \\
4709 );
4710}
4711
4712test "zig fmt: multiline string" {
4713 try testCanonical(
4714 \\const s =
4715 \\ \\ something
4716 \\ \\ something else
4717 \\ ;
4718 \\
4719 );
4720}
4721
4722test "zig fmt: values" {
4723 try testCanonical(
4724 \\test "values" {
4725 \\ 1;
4726 \\ 1.0;
4727 \\ "string";
4728 \\ c"cstring";
4729 \\ 'c';
4730 \\ true;
4731 \\ false;
4732 \\ null;
4733 \\ undefined;
4734 \\ error;
4735 \\ this;
4736 \\ unreachable;
4737 \\}
4738 \\
4739 );
4740}
4741
4742test "zig fmt: indexing" {
4743 try testCanonical(
4744 \\test "test index" {
4745 \\ a[0];
4746 \\ a[0 + 5];
4747 \\ a[0..];
4748 \\ a[0..5];
4749 \\ a[a[0]];
4750 \\ a[a[0..]];
4751 \\ a[a[0..5]];
4752 \\ a[a[0]..];
4753 \\ a[a[0..5]..];
4754 \\ a[a[0]..a[0]];
4755 \\ a[a[0..5]..a[0]];
4756 \\ a[a[0..5]..a[0..5]];
4757 \\}
4758 \\
4759 );
4760}
4761
4762test "zig fmt: struct declaration" {
4763 try testCanonical(
4764 \\const S = struct {
4765 \\ const Self = this;
4766 \\ f1: u8,
4767 \\ pub f3: u8,
4768 \\
4769 \\ fn method(self: &Self) Self {
4770 \\ return *self;
4771 \\ }
4772 \\
4773 \\ f2: u8,
4774 \\};
4775 \\
4776 \\const Ps = packed struct {
4777 \\ a: u8,
4778 \\ pub b: u8,
4779 \\
4780 \\ c: u8,
4781 \\};
4782 \\
4783 \\const Es = extern struct {
4784 \\ a: u8,
4785 \\ pub b: u8,
4786 \\
4787 \\ c: u8,
4788 \\};
4789 \\
4790 );
4791}
4792
4793test "zig fmt: enum declaration" {
4794 try testCanonical(
4795 \\const E = enum {
4796 \\ Ok,
4797 \\ SomethingElse = 0,
4798 \\};
4799 \\
4800 \\const E2 = enum(u8) {
4801 \\ Ok,
4802 \\ SomethingElse = 255,
4803 \\ SomethingThird,
4804 \\};
4805 \\
4806 \\const Ee = extern enum {
4807 \\ Ok,
4808 \\ SomethingElse,
4809 \\ SomethingThird,
4810 \\};
4811 \\
4812 \\const Ep = packed enum {
4813 \\ Ok,
4814 \\ SomethingElse,
4815 \\ SomethingThird,
4816 \\};
4817 \\
4818 );
4819}
4820
4821test "zig fmt: union declaration" {
4822 try testCanonical(
4823 \\const U = union {
4824 \\ Int: u8,
4825 \\ Float: f32,
4826 \\ None,
4827 \\ Bool: bool,
4828 \\};
4829 \\
4830 \\const Ue = union(enum) {
4831 \\ Int: u8,
4832 \\ Float: f32,
4833 \\ None,
4834 \\ Bool: bool,
4835 \\};
4836 \\
4837 \\const E = enum {
4838 \\ Int,
4839 \\ Float,
4840 \\ None,
4841 \\ Bool,
4842 \\};
4843 \\
4844 \\const Ue2 = union(E) {
4845 \\ Int: u8,
4846 \\ Float: f32,
4847 \\ None,
4848 \\ Bool: bool,
4849 \\};
4850 \\
4851 \\const Eu = extern union {
4852 \\ Int: u8,
4853 \\ Float: f32,
4854 \\ None,
4855 \\ Bool: bool,
4856 \\};
4857 \\
4858 );
4859}
4860
4861test "zig fmt: error set declaration" {
4862 try testCanonical(
4863 \\const E = error {
4864 \\ A,
4865 \\ B,
4866 \\
4867 \\ C,
4868 \\};
4869 \\
4870 );
4871}
4872
4873test "zig fmt: arrays" {
4874 try testCanonical(
4875 \\test "test array" {
4876 \\ const a: [2]u8 = [2]u8 {
4877 \\ 1,
4878 \\ 2,
4879 \\ };
4880 \\ const a: [2]u8 = []u8 {
4881 \\ 1,
4882 \\ 2,
4883 \\ };
4884 \\ const a: [0]u8 = []u8{};
4885 \\}
4886 \\
4887 );
4888}
4889
4890test "zig fmt: container initializers" {
4891 try testCanonical(
4892 \\const a1 = []u8{};
4893 \\const a2 = []u8 {
4894 \\ 1,
4895 \\ 2,
4896 \\ 3,
4897 \\ 4,
4898 \\};
4899 \\const s1 = S{};
4900 \\const s2 = S {
4901 \\ .a = 1,
4902 \\ .b = 2,
4903 \\};
4904 \\
4905 );
4906}
4907
4908test "zig fmt: catch" {
4909 try testCanonical(
4910 \\test "catch" {
4911 \\ const a: error!u8 = 0;
4912 \\ _ = a catch return;
4913 \\ _ = a catch |err| return;
4914 \\}
4915 \\
4916 );
4917}
4918
4919test "zig fmt: blocks" {
4920 try testCanonical(
4921 \\test "blocks" {
4922 \\ {
4923 \\ const a = 0;
4924 \\ const b = 0;
4925 \\ }
4926 \\
4927 \\ blk: {
4928 \\ const a = 0;
4929 \\ const b = 0;
4930 \\ }
4931 \\
4932 \\ const r = blk: {
4933 \\ const a = 0;
4934 \\ const b = 0;
4935 \\ };
4936 \\}
4937 \\
4938 );
4939}
4940
4941test "zig fmt: switch" {
4942 try testCanonical(
4943 \\test "switch" {
4944 \\ switch (0) {
4945 \\ 0 => {},
4946 \\ 1 => unreachable,
4947 \\ 2,
4948 \\ 3 => {},
4949 \\ 4 ... 7 => {},
4950 \\ 1 + 4 * 3 + 22 => {},
4951 \\ else => {
4952 \\ const a = 1;
4953 \\ const b = a;
4954 \\ },
4955 \\ }
4956 \\
4957 \\ const res = switch (0) {
4958 \\ 0 => 0,
4959 \\ 1 => 2,
4960 \\ 1 => a = 4,
4961 \\ else => 4,
4962 \\ };
4963 \\
4964 \\ const Union = union(enum) {
4965 \\ Int: i64,
4966 \\ Float: f64,
4967 \\ };
4968 \\
4969 \\ const u = Union {
4970 \\ .Int = 0,
4971 \\ };
4972 \\ switch (u) {
4973 \\ Union.Int => |int| {},
4974 \\ Union.Float => |*float| unreachable,
4975 \\ }
4976 \\}
4977 \\
4978 );
4979}
4980
4981test "zig fmt: while" {
4982 try testCanonical(
4983 \\test "while" {
4984 \\ while (10 < 1) {
4985 \\ unreachable;
4986 \\ }
4987 \\
4988 \\ while (10 < 1)
4989 \\ unreachable;
4990 \\
4991 \\ var i: usize = 0;
4992 \\ while (i < 10) : (i += 1) {
4993 \\ continue;
4994 \\ }
4995 \\
4996 \\ i = 0;
4997 \\ while (i < 10) : (i += 1)
4998 \\ continue;
4999 \\
5000 \\ i = 0;
5001 \\ var j: usize = 0;
5002 \\ while (i < 10) : ({
5003 \\ i += 1;
5004 \\ j += 1;
5005 \\ }) {
5006 \\ continue;
5007 \\ }
5008 \\
5009 \\ var a: ?u8 = 2;
5010 \\ while (a) |v| : (a = null) {
5011 \\ continue;
5012 \\ }
5013 \\
5014 \\ while (a) |v| : (a = null)
5015 \\ unreachable;
5016 \\
5017 \\ label: while (10 < 0) {
5018 \\ unreachable;
5019 \\ }
5020 \\
5021 \\ const res = while (0 < 10) {
5022 \\ break 7;
5023 \\ } else {
5024 \\ unreachable;
5025 \\ };
5026 \\
5027 \\ const res = while (0 < 10)
5028 \\ break 7
5029 \\ else
5030 \\ unreachable;
5031 \\
5032 \\ var a: error!u8 = 0;
5033 \\ while (a) |v| {
5034 \\ a = error.Err;
5035 \\ } else |err| {
5036 \\ i = 1;
5037 \\ }
5038 \\
5039 \\ comptime var k: usize = 0;
5040 \\ inline while (i < 10) : (i += 1)
5041 \\ j += 2;
5042 \\}
5043 \\
5044 );
5045}
5046
5047test "zig fmt: for" {
5048 try testCanonical(
5049 \\test "for" {
5050 \\ const a = []u8 {
5051 \\ 1,
5052 \\ 2,
5053 \\ 3,
5054 \\ };
5055 \\ for (a) |v| {
5056 \\ continue;
5057 \\ }
5058 \\
5059 \\ for (a) |v|
5060 \\ continue;
5061 \\
5062 \\ for (a) |*v|
5063 \\ continue;
5064 \\
5065 \\ for (a) |v, i| {
5066 \\ continue;
5067 \\ }
5068 \\
5069 \\ for (a) |v, i|
5070 \\ continue;
5071 \\
5072 \\ const res = for (a) |v, i| {
5073 \\ break v;
5074 \\ } else {
5075 \\ unreachable;
5076 \\ };
5077 \\
5078 \\ var num: usize = 0;
5079 \\ inline for (a) |v, i| {
5080 \\ num += v;
5081 \\ num += i;
5082 \\ }
5083 \\}
5084 \\
5085 );
5086}
5087
5088test "zig fmt: if" {
5089 try testCanonical(
5090 \\test "if" {
5091 \\ if (10 < 0) {
5092 \\ unreachable;
5093 \\ }
5094 \\
5095 \\ if (10 < 0) unreachable;
5096 \\
5097 \\ if (10 < 0) {
5098 \\ unreachable;
5099 \\ } else {
5100 \\ const a = 20;
5101 \\ }
5102 \\
5103 \\ if (10 < 0) {
5104 \\ unreachable;
5105 \\ } else if (5 < 0) {
5106 \\ unreachable;
5107 \\ } else {
5108 \\ const a = 20;
5109 \\ }
5110 \\
5111 \\ const is_world_broken = if (10 < 0) true else false;
5112 \\ const some_number = 1 + if (10 < 0) 2 else 3;
5113 \\
5114 \\ const a: ?u8 = 10;
5115 \\ const b: ?u8 = null;
5116 \\ if (a) |v| {
5117 \\ const some = v;
5118 \\ } else if (b) |*v| {
5119 \\ unreachable;
5120 \\ } else {
5121 \\ const some = 10;
5122 \\ }
5123 \\
5124 \\ const non_null_a = if (a) |v| v else 0;
5125 \\
5126 \\ const a_err: error!u8 = 0;
5127 \\ if (a_err) |v| {
5128 \\ const p = v;
5129 \\ } else |err| {
5130 \\ unreachable;
5131 \\ }
5132 \\}
5133 \\
5134 );
5135}
5136
5137test "zig fmt: defer" {
5138 try testCanonical(
5139 \\test "defer" {
5140 \\ var i: usize = 0;
5141 \\ defer i = 1;
5142 \\ defer {
5143 \\ i += 2;
5144 \\ i *= i;
5145 \\ }
5146 \\
5147 \\ errdefer i += 3;
5148 \\ errdefer {
5149 \\ i += 2;
5150 \\ i /= i;
5151 \\ }
5152 \\}
5153 \\
5154 );
5155}
5156
5157test "zig fmt: comptime" {
5158 try testCanonical(
5159 \\fn a() u8 {
5160 \\ return 5;
5161 \\}
5162 \\
5163 \\fn b(comptime i: u8) u8 {
5164 \\ return i;
5165 \\}
5166 \\
5167 \\const av = comptime a();
5168 \\const av2 = comptime blk: {
5169 \\ var res = a();
5170 \\ res *= b(2);
5171 \\ break :blk res;
5172 \\};
5173 \\
5174 \\comptime {
5175 \\ _ = a();
5176 \\}
5177 \\
5178 \\test "comptime" {
5179 \\ const av3 = comptime a();
5180 \\ const av4 = comptime blk: {
5181 \\ var res = a();
5182 \\ res *= a();
5183 \\ break :blk res;
5184 \\ };
5185 \\
5186 \\ comptime var i = 0;
5187 \\ comptime {
5188 \\ i = a();
5189 \\ i += b(i);
5190 \\ }
5191 \\}
5192 \\
5193 );
5194}
5195
5196test "zig fmt: fn type" {
5197 try testCanonical(
5198 \\fn a(i: u8) u8 {
5199 \\ return i + 1;
5200 \\}
5201 \\
5202 \\const a: fn(u8) u8 = undefined;
5203 \\const b: extern fn(u8) u8 = undefined;
5204 \\const c: nakedcc fn(u8) u8 = undefined;
5205 \\const ap: fn(u8) u8 = a;
5206 \\
5207 );
5208}
5209
5210test "zig fmt: inline asm" {
5211 try testCanonical(
5212 \\pub fn syscall1(number: usize, arg1: usize) usize {
5213 \\ return asm volatile ("syscall"
5214 \\ : [ret] "={rax}" (-> usize)
5215 \\ : [number] "{rax}" (number),
5216 \\ [arg1] "{rdi}" (arg1)
5217 \\ : "rcx", "r11");
5218 \\}
5219 \\
5220 );
5221}
5222
5223test "zig fmt: coroutines" {
5224 try testCanonical(
5225 \\async fn simpleAsyncFn() void {
5226 \\ const a = async a.b();
5227 \\ x += 1;
5228 \\ suspend;
5229 \\ x += 1;
5230 \\ suspend |p| {}
5231 \\ const p = async simpleAsyncFn() catch unreachable;
5232 \\ await p;
5233 \\}
5234 \\
5235 \\test "coroutine suspend, resume, cancel" {
5236 \\ const p = try async<std.debug.global_allocator> testAsyncSeq();
5237 \\ resume p;
5238 \\ cancel p;
5239 \\}
5240 \\
5241 );
5242}
5243
5244test "zig fmt: Block after if" {
5245 try testCanonical(
5246 \\test "Block after if" {
5247 \\ if (true) {
5248 \\ const a = 0;
5249 \\ }
5250 \\
5251 \\ {
5252 \\ const a = 0;
5253 \\ }
5254 \\}
5255 \\
5256 );
5257}
5258
5259test "zig fmt: use" {
5260 try testCanonical(
5261 \\use @import("std");
5262 \\pub use @import("std");
5263 \\
5264 );
5265}
5266
5267test "zig fmt: string identifier" {
5268 try testCanonical(
5269 \\const @"a b" = @"c d".@"e f";
5270 \\fn @"g h"() void {}
5271 \\
5272 );
5273}
5274
5275test "zig fmt: error return" {
5276 try testCanonical(
5277 \\fn err() error {
5278 \\ call();
5279 \\ return error.InvalidArgs;
5280 \\}
5281 \\
5282 );
5283}
5284
5285test "zig fmt: struct literals with fields on each line" {
5286 try testCanonical(
5287 \\var self = BufSet {
5288 \\ .hash_map = BufSetHashMap.init(a),
5289 \\};
5290 \\
5291 );
5292}4704}
std/zig/parser_test.zig created+1139
...@@ -0,0 +1,1139 @@
1test "zig fmt: line comments in struct initializer" {
2 try testCanonical(
3 \\fn foo() void {
4 \\ return Self{
5 \\ .a = b,
6 \\
7 \\ // Initialize these two fields to buffer_size so that
8 \\ // in `readFn` we treat the state as being able to read
9 \\ .start_index = buffer_size,
10 \\ .end_index = buffer_size,
11 \\
12 \\ // middle
13 \\
14 \\ .a = b,
15 \\
16 \\ // end
17 \\ };
18 \\}
19 \\
20 );
21}
22
23//TODO
24//test "zig fmt: same-line comptime" {
25// try testCanonical(
26// \\test "" {
27// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
28// \\}
29// \\
30// );
31//}
32
33
34//TODO
35//test "zig fmt: number literals" {
36// try testCanonical(
37// \\pub const f64_true_min = 4.94065645841246544177e-324;
38// \\
39// );
40//}
41
42test "zig fmt: doc comments before struct field" {
43 try testCanonical(
44 \\pub const Allocator = struct {
45 \\ /// Allocate byte_count bytes and return them in a slice, with the
46 \\ /// slice's pointer aligned at least to alignment bytes.
47 \\ allocFn: fn() void,
48 \\};
49 \\
50 );
51}
52
53test "zig fmt: error set declaration" {
54 try testCanonical(
55 \\const E = error{
56 \\ A,
57 \\ B,
58 \\
59 \\ C,
60 \\};
61 \\
62 \\const Error = error{
63 \\ /// no more memory
64 \\ OutOfMemory,
65 \\};
66 \\
67 \\const Error = error{
68 \\ /// no more memory
69 \\ OutOfMemory,
70 \\
71 \\ /// another
72 \\ Another,
73 \\
74 \\ // end
75 \\};
76 \\
77 \\const Error = error{OutOfMemory};
78 \\const Error = error{};
79 \\
80 );
81}
82
83test "zig fmt: union(enum(u32)) with assigned enum values" {
84 try testCanonical(
85 \\const MultipleChoice = union(enum(u32)) {
86 \\ A = 20,
87 \\ B = 40,
88 \\ C = 60,
89 \\ D = 1000,
90 \\};
91 \\
92 );
93}
94
95test "zig fmt: labeled suspend" {
96 try testCanonical(
97 \\fn foo() void {
98 \\ s: suspend |p| {
99 \\ break :s;
100 \\ }
101 \\}
102 \\
103 );
104}
105
106test "zig fmt: comments before error set decl" {
107 try testCanonical(
108 \\const UnexpectedError = error{
109 \\ /// The Operating System returned an undocumented error code.
110 \\ Unexpected,
111 \\ // another
112 \\ Another,
113 \\
114 \\ // in between
115 \\
116 \\ // at end
117 \\};
118 \\
119 );
120}
121
122test "zig fmt: comments before switch prong" {
123 try testCanonical(
124 \\test "" {
125 \\ switch (err) {
126 \\ error.PathAlreadyExists => continue,
127 \\
128 \\ // comment 1
129 \\
130 \\ // comment 2
131 \\ else => return err,
132 \\ // at end
133 \\ }
134 \\}
135 \\
136 );
137}
138
139test "zig fmt: same-line comment after switch prong" {
140 try testCanonical(
141 \\test "" {
142 \\ switch (err) {
143 \\ error.PathAlreadyExists => {}, // comment 2
144 \\ else => return err, // comment 1
145 \\ }
146 \\}
147 \\
148 );
149}
150
151test "zig fmt: comments before var decl in struct" {
152 try testCanonical(
153 \\pub const vfs_cap_data = extern struct {
154 \\ // All of these are mandated as little endian
155 \\ // when on disk.
156 \\ const Data = struct {
157 \\ permitted: u32,
158 \\ inheritable: u32,
159 \\ };
160 \\
161 \\ // in between
162 \\
163 \\ /// All of these are mandated as little endian
164 \\ /// when on disk.
165 \\ const Data = struct {
166 \\ permitted: u32,
167 \\ inheritable: u32,
168 \\ };
169 \\
170 \\ // at end
171 \\};
172 \\
173 );
174}
175
176test "zig fmt: same-line comment after var decl in struct" {
177 try testCanonical(
178 \\pub const vfs_cap_data = extern struct {
179 \\ const Data = struct {}; // when on disk.
180 \\};
181 \\
182 );
183}
184
185test "zig fmt: same-line comment after field decl" {
186 try testCanonical(
187 \\pub const dirent = extern struct {
188 \\ d_name: u8,
189 \\ d_name: u8, // comment 1
190 \\ d_name: u8,
191 \\ d_name: u8, // comment 2
192 \\ d_name: u8,
193 \\};
194 \\
195 );
196}
197
198test "zig fmt: array literal with 1 item on 1 line" {
199 try testCanonical(
200 \\var s = []const u64{0} ** 25;
201 \\
202 );
203}
204
205test "zig fmt: same-line comment after a statement" {
206 try testCanonical(
207 \\test "" {
208 \\ a = b;
209 \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
210 \\ a = b;
211 \\}
212 \\
213 );
214}
215
216test "zig fmt: comments before global variables" {
217 try testCanonical(
218 \\/// Foo copies keys and values before they go into the map, and
219 \\/// frees them when they get removed.
220 \\pub const Foo = struct {};
221 \\
222 );
223}
224
225test "zig fmt: comments in statements" {
226 try testCanonical(
227 \\test "std" {
228 \\ // statement comment
229 \\ _ = @import("foo/bar.zig");
230 \\
231 \\ // middle
232 \\ // middle2
233 \\
234 \\ // end
235 \\}
236 \\
237 );
238}
239
240test "zig fmt: comments before test decl" {
241 try testCanonical(
242 \\/// top level doc comment
243 \\test "hi" {}
244 \\
245 \\// top level normal comment
246 \\test "hi" {}
247 \\
248 \\// middle
249 \\
250 \\// end
251 \\
252 );
253}
254
255test "zig fmt: preserve spacing" {
256 try testCanonical(
257 \\const std = @import("std");
258 \\
259 \\pub fn main() !void {
260 \\ var stdout_file = try std.io.getStdOut;
261 \\ var stdout_file = try std.io.getStdOut;
262 \\
263 \\ var stdout_file = try std.io.getStdOut;
264 \\ var stdout_file = try std.io.getStdOut;
265 \\}
266 \\
267 );
268}
269
270test "zig fmt: return types" {
271 try testCanonical(
272 \\pub fn main() !void {}
273 \\pub fn main() var {}
274 \\pub fn main() i32 {}
275 \\
276 );
277}
278
279test "zig fmt: imports" {
280 try testCanonical(
281 \\const std = @import("std");
282 \\const std = @import();
283 \\
284 );
285}
286
287test "zig fmt: global declarations" {
288 try testCanonical(
289 \\const a = b;
290 \\pub const a = b;
291 \\var a = b;
292 \\pub var a = b;
293 \\const a: i32 = b;
294 \\pub const a: i32 = b;
295 \\var a: i32 = b;
296 \\pub var a: i32 = b;
297 \\extern const a: i32 = b;
298 \\pub extern const a: i32 = b;
299 \\extern var a: i32 = b;
300 \\pub extern var a: i32 = b;
301 \\extern "a" const a: i32 = b;
302 \\pub extern "a" const a: i32 = b;
303 \\extern "a" var a: i32 = b;
304 \\pub extern "a" var a: i32 = b;
305 \\
306 );
307}
308
309test "zig fmt: extern declaration" {
310 try testCanonical(
311 \\extern var foo: c_int;
312 \\
313 );
314}
315
316test "zig fmt: alignment" {
317 try testCanonical(
318 \\var foo: c_int align(1);
319 \\
320 );
321}
322
323test "zig fmt: C main" {
324 try testCanonical(
325 \\fn main(argc: c_int, argv: &&u8) c_int {
326 \\ const a = b;
327 \\}
328 \\
329 );
330}
331
332test "zig fmt: return" {
333 try testCanonical(
334 \\fn foo(argc: c_int, argv: &&u8) c_int {
335 \\ return 0;
336 \\}
337 \\
338 \\fn bar() void {
339 \\ return;
340 \\}
341 \\
342 );
343}
344
345test "zig fmt: pointer attributes" {
346 try testCanonical(
347 \\extern fn f1(s: &align(&u8) u8) c_int;
348 \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
349 \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
350 \\extern fn f4(s: &align(1) const volatile u8) c_int;
351 \\
352 );
353}
354
355test "zig fmt: slice attributes" {
356 try testCanonical(
357 \\extern fn f1(s: &align(&u8) u8) c_int;
358 \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
359 \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
360 \\extern fn f4(s: &align(1) const volatile u8) c_int;
361 \\
362 );
363}
364
365test "zig fmt: test declaration" {
366 try testCanonical(
367 \\test "test name" {
368 \\ const a = 1;
369 \\ var b = 1;
370 \\}
371 \\
372 );
373}
374
375test "zig fmt: infix operators" {
376 try testCanonical(
377 \\test "infix operators" {
378 \\ var i = undefined;
379 \\ i = 2;
380 \\ i *= 2;
381 \\ i |= 2;
382 \\ i ^= 2;
383 \\ i <<= 2;
384 \\ i >>= 2;
385 \\ i &= 2;
386 \\ i *= 2;
387 \\ i *%= 2;
388 \\ i -= 2;
389 \\ i -%= 2;
390 \\ i += 2;
391 \\ i +%= 2;
392 \\ i /= 2;
393 \\ i %= 2;
394 \\ _ = i == i;
395 \\ _ = i != i;
396 \\ _ = i != i;
397 \\ _ = i.i;
398 \\ _ = i || i;
399 \\ _ = i!i;
400 \\ _ = i ** i;
401 \\ _ = i ++ i;
402 \\ _ = i ?? i;
403 \\ _ = i % i;
404 \\ _ = i / i;
405 \\ _ = i *% i;
406 \\ _ = i * i;
407 \\ _ = i -% i;
408 \\ _ = i - i;
409 \\ _ = i +% i;
410 \\ _ = i + i;
411 \\ _ = i << i;
412 \\ _ = i >> i;
413 \\ _ = i & i;
414 \\ _ = i ^ i;
415 \\ _ = i | i;
416 \\ _ = i >= i;
417 \\ _ = i <= i;
418 \\ _ = i > i;
419 \\ _ = i < i;
420 \\ _ = i and i;
421 \\ _ = i or i;
422 \\}
423 \\
424 );
425}
426
427test "zig fmt: precedence" {
428 try testCanonical(
429 \\test "precedence" {
430 \\ a!b();
431 \\ (a!b)();
432 \\ !a!b;
433 \\ !(a!b);
434 \\ !a{};
435 \\ !(a{});
436 \\ a + b{};
437 \\ (a + b){};
438 \\ a << b + c;
439 \\ (a << b) + c;
440 \\ a & b << c;
441 \\ (a & b) << c;
442 \\ a ^ b & c;
443 \\ (a ^ b) & c;
444 \\ a | b ^ c;
445 \\ (a | b) ^ c;
446 \\ a == b | c;
447 \\ (a == b) | c;
448 \\ a and b == c;
449 \\ (a and b) == c;
450 \\ a or b and c;
451 \\ (a or b) and c;
452 \\ (a or b) and c;
453 \\}
454 \\
455 );
456}
457
458test "zig fmt: prefix operators" {
459 try testCanonical(
460 \\test "prefix operators" {
461 \\ try return --%~??!*&0;
462 \\}
463 \\
464 );
465}
466
467test "zig fmt: call expression" {
468 try testCanonical(
469 \\test "test calls" {
470 \\ a();
471 \\ a(1);
472 \\ a(1, 2);
473 \\ a(1, 2) + a(1, 2);
474 \\}
475 \\
476 );
477}
478
479test "zig fmt: var args" {
480 try testCanonical(
481 \\fn print(args: ...) void {}
482 \\
483 );
484}
485
486test "zig fmt: var type" {
487 try testCanonical(
488 \\fn print(args: var) var {}
489 \\const Var = var;
490 \\const i: var = 0;
491 \\
492 );
493}
494
495test "zig fmt: functions" {
496 try testCanonical(
497 \\extern fn puts(s: &const u8) c_int;
498 \\extern "c" fn puts(s: &const u8) c_int;
499 \\export fn puts(s: &const u8) c_int;
500 \\inline fn puts(s: &const u8) c_int;
501 \\pub extern fn puts(s: &const u8) c_int;
502 \\pub extern "c" fn puts(s: &const u8) c_int;
503 \\pub export fn puts(s: &const u8) c_int;
504 \\pub inline fn puts(s: &const u8) c_int;
505 \\pub extern fn puts(s: &const u8) align(2 + 2) c_int;
506 \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int;
507 \\pub export fn puts(s: &const u8) align(2 + 2) c_int;
508 \\pub inline fn puts(s: &const u8) align(2 + 2) c_int;
509 \\
510 );
511}
512
513test "zig fmt: multiline string" {
514 try testCanonical(
515 \\test "" {
516 \\ const s1 =
517 \\ \\one
518 \\ \\two)
519 \\ \\three
520 \\ ;
521 \\ const s2 =
522 \\ c\\one
523 \\ c\\two)
524 \\ c\\three
525 \\ ;
526 \\}
527 \\
528 );
529}
530
531test "zig fmt: values" {
532 try testCanonical(
533 \\test "values" {
534 \\ 1;
535 \\ 1.0;
536 \\ "string";
537 \\ c"cstring";
538 \\ 'c';
539 \\ true;
540 \\ false;
541 \\ null;
542 \\ undefined;
543 \\ error;
544 \\ this;
545 \\ unreachable;
546 \\}
547 \\
548 );
549}
550
551test "zig fmt: indexing" {
552 try testCanonical(
553 \\test "test index" {
554 \\ a[0];
555 \\ a[0 + 5];
556 \\ a[0..];
557 \\ a[0..5];
558 \\ a[a[0]];
559 \\ a[a[0..]];
560 \\ a[a[0..5]];
561 \\ a[a[0]..];
562 \\ a[a[0..5]..];
563 \\ a[a[0]..a[0]];
564 \\ a[a[0..5]..a[0]];
565 \\ a[a[0..5]..a[0..5]];
566 \\}
567 \\
568 );
569}
570
571test "zig fmt: struct declaration" {
572 try testCanonical(
573 \\const S = struct {
574 \\ const Self = this;
575 \\ f1: u8,
576 \\ pub f3: u8,
577 \\
578 \\ fn method(self: &Self) Self {
579 \\ return *self;
580 \\ }
581 \\
582 \\ f2: u8,
583 \\};
584 \\
585 \\const Ps = packed struct {
586 \\ a: u8,
587 \\ pub b: u8,
588 \\
589 \\ c: u8,
590 \\};
591 \\
592 \\const Es = extern struct {
593 \\ a: u8,
594 \\ pub b: u8,
595 \\
596 \\ c: u8,
597 \\};
598 \\
599 );
600}
601
602test "zig fmt: enum declaration" {
603 try testCanonical(
604 \\const E = enum {
605 \\ Ok,
606 \\ SomethingElse = 0,
607 \\};
608 \\
609 \\const E2 = enum(u8) {
610 \\ Ok,
611 \\ SomethingElse = 255,
612 \\ SomethingThird,
613 \\};
614 \\
615 \\const Ee = extern enum {
616 \\ Ok,
617 \\ SomethingElse,
618 \\ SomethingThird,
619 \\};
620 \\
621 \\const Ep = packed enum {
622 \\ Ok,
623 \\ SomethingElse,
624 \\ SomethingThird,
625 \\};
626 \\
627 );
628}
629
630test "zig fmt: union declaration" {
631 try testCanonical(
632 \\const U = union {
633 \\ Int: u8,
634 \\ Float: f32,
635 \\ None,
636 \\ Bool: bool,
637 \\};
638 \\
639 \\const Ue = union(enum) {
640 \\ Int: u8,
641 \\ Float: f32,
642 \\ None,
643 \\ Bool: bool,
644 \\};
645 \\
646 \\const E = enum {
647 \\ Int,
648 \\ Float,
649 \\ None,
650 \\ Bool,
651 \\};
652 \\
653 \\const Ue2 = union(E) {
654 \\ Int: u8,
655 \\ Float: f32,
656 \\ None,
657 \\ Bool: bool,
658 \\};
659 \\
660 \\const Eu = extern union {
661 \\ Int: u8,
662 \\ Float: f32,
663 \\ None,
664 \\ Bool: bool,
665 \\};
666 \\
667 );
668}
669
670test "zig fmt: arrays" {
671 try testCanonical(
672 \\test "test array" {
673 \\ const a: [2]u8 = [2]u8{
674 \\ 1,
675 \\ 2,
676 \\ };
677 \\ const a: [2]u8 = []u8{
678 \\ 1,
679 \\ 2,
680 \\ };
681 \\ const a: [0]u8 = []u8{};
682 \\}
683 \\
684 );
685}
686
687test "zig fmt: container initializers" {
688 try testCanonical(
689 \\const a0 = []u8{};
690 \\const a1 = []u8{1};
691 \\const a2 = []u8{
692 \\ 1,
693 \\ 2,
694 \\ 3,
695 \\ 4,
696 \\};
697 \\const s0 = S{};
698 \\const s1 = S{ .a = 1 };
699 \\const s2 = S{
700 \\ .a = 1,
701 \\ .b = 2,
702 \\};
703 \\
704 );
705}
706
707test "zig fmt: catch" {
708 try testCanonical(
709 \\test "catch" {
710 \\ const a: error!u8 = 0;
711 \\ _ = a catch return;
712 \\ _ = a catch |err| return;
713 \\}
714 \\
715 );
716}
717
718test "zig fmt: blocks" {
719 try testCanonical(
720 \\test "blocks" {
721 \\ {
722 \\ const a = 0;
723 \\ const b = 0;
724 \\ }
725 \\
726 \\ blk: {
727 \\ const a = 0;
728 \\ const b = 0;
729 \\ }
730 \\
731 \\ const r = blk: {
732 \\ const a = 0;
733 \\ const b = 0;
734 \\ };
735 \\}
736 \\
737 );
738}
739
740test "zig fmt: switch" {
741 try testCanonical(
742 \\test "switch" {
743 \\ switch (0) {
744 \\ 0 => {},
745 \\ 1 => unreachable,
746 \\ 2,
747 \\ 3 => {},
748 \\ 4 ... 7 => {},
749 \\ 1 + 4 * 3 + 22 => {},
750 \\ else => {
751 \\ const a = 1;
752 \\ const b = a;
753 \\ },
754 \\ }
755 \\
756 \\ const res = switch (0) {
757 \\ 0 => 0,
758 \\ 1 => 2,
759 \\ 1 => a = 4,
760 \\ else => 4,
761 \\ };
762 \\
763 \\ const Union = union(enum) {
764 \\ Int: i64,
765 \\ Float: f64,
766 \\ };
767 \\
768 \\ switch (u) {
769 \\ Union.Int => |int| {},
770 \\ Union.Float => |*float| unreachable,
771 \\ }
772 \\}
773 \\
774 );
775}
776
777test "zig fmt: while" {
778 try testCanonical(
779 \\test "while" {
780 \\ while (10 < 1) {
781 \\ unreachable;
782 \\ }
783 \\
784 \\ while (10 < 1)
785 \\ unreachable;
786 \\
787 \\ var i: usize = 0;
788 \\ while (i < 10) : (i += 1) {
789 \\ continue;
790 \\ }
791 \\
792 \\ i = 0;
793 \\ while (i < 10) : (i += 1)
794 \\ continue;
795 \\
796 \\ i = 0;
797 \\ var j: usize = 0;
798 \\ while (i < 10) : ({
799 \\ i += 1;
800 \\ j += 1;
801 \\ }) {
802 \\ continue;
803 \\ }
804 \\
805 \\ var a: ?u8 = 2;
806 \\ while (a) |v| : (a = null) {
807 \\ continue;
808 \\ }
809 \\
810 \\ while (a) |v| : (a = null)
811 \\ unreachable;
812 \\
813 \\ label: while (10 < 0) {
814 \\ unreachable;
815 \\ }
816 \\
817 \\ const res = while (0 < 10) {
818 \\ break 7;
819 \\ } else {
820 \\ unreachable;
821 \\ };
822 \\
823 \\ const res = while (0 < 10)
824 \\ break 7
825 \\ else
826 \\ unreachable;
827 \\
828 \\ var a: error!u8 = 0;
829 \\ while (a) |v| {
830 \\ a = error.Err;
831 \\ } else |err| {
832 \\ i = 1;
833 \\ }
834 \\
835 \\ comptime var k: usize = 0;
836 \\ inline while (i < 10) : (i += 1)
837 \\ j += 2;
838 \\}
839 \\
840 );
841}
842
843test "zig fmt: for" {
844 try testCanonical(
845 \\test "for" {
846 \\ for (a) |v| {
847 \\ continue;
848 \\ }
849 \\
850 \\ for (a) |v|
851 \\ continue;
852 \\
853 \\ for (a) |*v|
854 \\ continue;
855 \\
856 \\ for (a) |v, i| {
857 \\ continue;
858 \\ }
859 \\
860 \\ for (a) |v, i|
861 \\ continue;
862 \\
863 \\ const res = for (a) |v, i| {
864 \\ break v;
865 \\ } else {
866 \\ unreachable;
867 \\ };
868 \\
869 \\ var num: usize = 0;
870 \\ inline for (a) |v, i| {
871 \\ num += v;
872 \\ num += i;
873 \\ }
874 \\}
875 \\
876 );
877}
878
879test "zig fmt: if" {
880 try testCanonical(
881 \\test "if" {
882 \\ if (10 < 0) {
883 \\ unreachable;
884 \\ }
885 \\
886 \\ if (10 < 0) unreachable;
887 \\
888 \\ if (10 < 0) {
889 \\ unreachable;
890 \\ } else {
891 \\ const a = 20;
892 \\ }
893 \\
894 \\ if (10 < 0) {
895 \\ unreachable;
896 \\ } else if (5 < 0) {
897 \\ unreachable;
898 \\ } else {
899 \\ const a = 20;
900 \\ }
901 \\
902 \\ const is_world_broken = if (10 < 0) true else false;
903 \\ const some_number = 1 + if (10 < 0) 2 else 3;
904 \\
905 \\ const a: ?u8 = 10;
906 \\ const b: ?u8 = null;
907 \\ if (a) |v| {
908 \\ const some = v;
909 \\ } else if (b) |*v| {
910 \\ unreachable;
911 \\ } else {
912 \\ const some = 10;
913 \\ }
914 \\
915 \\ const non_null_a = if (a) |v| v else 0;
916 \\
917 \\ const a_err: error!u8 = 0;
918 \\ if (a_err) |v| {
919 \\ const p = v;
920 \\ } else |err| {
921 \\ unreachable;
922 \\ }
923 \\}
924 \\
925 );
926}
927
928test "zig fmt: defer" {
929 try testCanonical(
930 \\test "defer" {
931 \\ var i: usize = 0;
932 \\ defer i = 1;
933 \\ defer {
934 \\ i += 2;
935 \\ i *= i;
936 \\ }
937 \\
938 \\ errdefer i += 3;
939 \\ errdefer {
940 \\ i += 2;
941 \\ i /= i;
942 \\ }
943 \\}
944 \\
945 );
946}
947
948test "zig fmt: comptime" {
949 try testCanonical(
950 \\fn a() u8 {
951 \\ return 5;
952 \\}
953 \\
954 \\fn b(comptime i: u8) u8 {
955 \\ return i;
956 \\}
957 \\
958 \\const av = comptime a();
959 \\const av2 = comptime blk: {
960 \\ var res = a();
961 \\ res *= b(2);
962 \\ break :blk res;
963 \\};
964 \\
965 \\comptime {
966 \\ _ = a();
967 \\}
968 \\
969 \\test "comptime" {
970 \\ const av3 = comptime a();
971 \\ const av4 = comptime blk: {
972 \\ var res = a();
973 \\ res *= a();
974 \\ break :blk res;
975 \\ };
976 \\
977 \\ comptime var i = 0;
978 \\ comptime {
979 \\ i = a();
980 \\ i += b(i);
981 \\ }
982 \\}
983 \\
984 );
985}
986
987test "zig fmt: fn type" {
988 try testCanonical(
989 \\fn a(i: u8) u8 {
990 \\ return i + 1;
991 \\}
992 \\
993 \\const a: fn(u8) u8 = undefined;
994 \\const b: extern fn(u8) u8 = undefined;
995 \\const c: nakedcc fn(u8) u8 = undefined;
996 \\const ap: fn(u8) u8 = a;
997 \\
998 );
999}
1000
1001test "zig fmt: inline asm" {
1002 try testCanonical(
1003 \\pub fn syscall1(number: usize, arg1: usize) usize {
1004 \\ return asm volatile ("syscall"
1005 \\ : [ret] "={rax}" (-> usize)
1006 \\ : [number] "{rax}" (number),
1007 \\ [arg1] "{rdi}" (arg1)
1008 \\ : "rcx", "r11");
1009 \\}
1010 \\
1011 );
1012}
1013
1014test "zig fmt: coroutines" {
1015 try testCanonical(
1016 \\async fn simpleAsyncFn() void {
1017 \\ const a = async a.b();
1018 \\ x += 1;
1019 \\ suspend;
1020 \\ x += 1;
1021 \\ suspend |p| {}
1022 \\ const p: promise->void = async simpleAsyncFn() catch unreachable;
1023 \\ await p;
1024 \\}
1025 \\
1026 \\test "coroutine suspend, resume, cancel" {
1027 \\ const p: promise = try async<std.debug.global_allocator> testAsyncSeq();
1028 \\ resume p;
1029 \\ cancel p;
1030 \\}
1031 \\
1032 );
1033}
1034
1035test "zig fmt: Block after if" {
1036 try testCanonical(
1037 \\test "Block after if" {
1038 \\ if (true) {
1039 \\ const a = 0;
1040 \\ }
1041 \\
1042 \\ {
1043 \\ const a = 0;
1044 \\ }
1045 \\}
1046 \\
1047 );
1048}
1049
1050test "zig fmt: use" {
1051 try testCanonical(
1052 \\use @import("std");
1053 \\pub use @import("std");
1054 \\
1055 );
1056}
1057
1058test "zig fmt: string identifier" {
1059 try testCanonical(
1060 \\const @"a b" = @"c d".@"e f";
1061 \\fn @"g h"() void {}
1062 \\
1063 );
1064}
1065
1066test "zig fmt: error return" {
1067 try testCanonical(
1068 \\fn err() error {
1069 \\ call();
1070 \\ return error.InvalidArgs;
1071 \\}
1072 \\
1073 );
1074}
1075
1076const std = @import("std");
1077const mem = std.mem;
1078const warn = std.debug.warn;
1079const Tokenizer = std.zig.Tokenizer;
1080const Parser = std.zig.Parser;
1081const io = std.io;
1082
1083var fixed_buffer_mem: [100 * 1024]u8 = undefined;
1084
1085fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
1086 var tokenizer = Tokenizer.init(source);
1087 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
1088 defer parser.deinit();
1089
1090 var tree = try parser.parse();
1091 defer tree.deinit();
1092
1093 var buffer = try std.Buffer.initSize(allocator, 0);
1094 errdefer buffer.deinit();
1095
1096 var buffer_out_stream = io.BufferOutStream.init(&buffer);
1097 try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
1098 return buffer.toOwnedSlice();
1099}
1100
1101fn testCanonical(source: []const u8) !void {
1102 const needed_alloc_count = x: {
1103 // Try it once with unlimited memory, make sure it works
1104 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1105 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
1106 const result_source = try testParse(source, &failing_allocator.allocator);
1107 if (!mem.eql(u8, result_source, source)) {
1108 warn("\n====== expected this output: =========\n");
1109 warn("{}", source);
1110 warn("\n======== instead found this: =========\n");
1111 warn("{}", result_source);
1112 warn("\n======================================\n");
1113 return error.TestFailed;
1114 }
1115 failing_allocator.allocator.free(result_source);
1116 break :x failing_allocator.index;
1117 };
1118
1119 var fail_index: usize = 0;
1120 while (fail_index < needed_alloc_count) : (fail_index += 1) {
1121 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1122 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
1123 if (testParse(source, &failing_allocator.allocator)) |_| {
1124 return error.NondeterministicMemoryUsage;
1125 } else |err| switch (err) {
1126 error.OutOfMemory => {
1127 if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
1128 warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
1129 fail_index, needed_alloc_count,
1130 failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
1131 failing_allocator.index, failing_allocator.deallocations);
1132 return error.MemoryLeakDetected;
1133 }
1134 },
1135 error.ParseError => @panic("test failed"),
1136 }
1137 }
1138}
1139
std/zig/tokenizer.zig+116-21
...@@ -40,6 +40,7 @@ pub const Token = struct {...@@ -40,6 +40,7 @@ pub const Token = struct {
40 KeywordId{.bytes="null", .id = Id.Keyword_null},40 KeywordId{.bytes="null", .id = Id.Keyword_null},
41 KeywordId{.bytes="or", .id = Id.Keyword_or},41 KeywordId{.bytes="or", .id = Id.Keyword_or},
42 KeywordId{.bytes="packed", .id = Id.Keyword_packed},42 KeywordId{.bytes="packed", .id = Id.Keyword_packed},
43 KeywordId{.bytes="promise", .id = Id.Keyword_promise},
43 KeywordId{.bytes="pub", .id = Id.Keyword_pub},44 KeywordId{.bytes="pub", .id = Id.Keyword_pub},
44 KeywordId{.bytes="resume", .id = Id.Keyword_resume},45 KeywordId{.bytes="resume", .id = Id.Keyword_resume},
45 KeywordId{.bytes="return", .id = Id.Keyword_return},46 KeywordId{.bytes="return", .id = Id.Keyword_return},
...@@ -137,6 +138,7 @@ pub const Token = struct {...@@ -137,6 +138,7 @@ pub const Token = struct {
137 IntegerLiteral,138 IntegerLiteral,
138 FloatLiteral,139 FloatLiteral,
139 LineComment,140 LineComment,
141 DocComment,
140 Keyword_align,142 Keyword_align,
141 Keyword_and,143 Keyword_and,
142 Keyword_asm,144 Keyword_asm,
...@@ -165,6 +167,7 @@ pub const Token = struct {...@@ -165,6 +167,7 @@ pub const Token = struct {
165 Keyword_null,167 Keyword_null,
166 Keyword_or,168 Keyword_or,
167 Keyword_packed,169 Keyword_packed,
170 Keyword_promise,
168 Keyword_pub,171 Keyword_pub,
169 Keyword_resume,172 Keyword_resume,
170 Keyword_return,173 Keyword_return,
...@@ -257,7 +260,10 @@ pub const Tokenizer = struct {...@@ -257,7 +260,10 @@ pub const Tokenizer = struct {
257 Asterisk,260 Asterisk,
258 AsteriskPercent,261 AsteriskPercent,
259 Slash,262 Slash,
263 LineCommentStart,
260 LineComment,264 LineComment,
265 DocCommentStart,
266 DocComment,
261 Zero,267 Zero,
262 IntegerLiteral,268 IntegerLiteral,
263 IntegerLiteralWithRadix,269 IntegerLiteralWithRadix,
...@@ -822,8 +828,8 @@ pub const Tokenizer = struct {...@@ -822,8 +828,8 @@ pub const Tokenizer = struct {
822828
823 State.Slash => switch (c) {829 State.Slash => switch (c) {
824 '/' => {830 '/' => {
831 state = State.LineCommentStart;
825 result.id = Token.Id.LineComment;832 result.id = Token.Id.LineComment;
826 state = State.LineComment;
827 },833 },
828 '=' => {834 '=' => {
829 result.id = Token.Id.SlashEqual;835 result.id = Token.Id.SlashEqual;
...@@ -835,7 +841,31 @@ pub const Tokenizer = struct {...@@ -835,7 +841,31 @@ pub const Tokenizer = struct {
835 break;841 break;
836 },842 },
837 },843 },
838 State.LineComment => switch (c) {844 State.LineCommentStart => switch (c) {
845 '/' => {
846 state = State.DocCommentStart;
847 },
848 '\n' => break,
849 else => {
850 state = State.LineComment;
851 self.checkLiteralCharacter();
852 },
853 },
854 State.DocCommentStart => switch (c) {
855 '/' => {
856 state = State.LineComment;
857 },
858 '\n' => {
859 result.id = Token.Id.DocComment;
860 break;
861 },
862 else => {
863 state = State.DocComment;
864 result.id = Token.Id.DocComment;
865 self.checkLiteralCharacter();
866 },
867 },
868 State.LineComment, State.DocComment => switch (c) {
839 '\n' => break,869 '\n' => break,
840 else => self.checkLiteralCharacter(),870 else => self.checkLiteralCharacter(),
841 },871 },
...@@ -920,8 +950,12 @@ pub const Tokenizer = struct {...@@ -920,8 +950,12 @@ pub const Tokenizer = struct {
920 result.id = id;950 result.id = id;
921 }951 }
922 },952 },
953 State.LineCommentStart,
923 State.LineComment => {954 State.LineComment => {
924 result.id = Token.Id.Eof;955 result.id = Token.Id.LineComment;
956 },
957 State.DocComment, State.DocCommentStart => {
958 result.id = Token.Id.DocComment;
925 },959 },
926960
927 State.NumberDot,961 State.NumberDot,
...@@ -1092,41 +1126,77 @@ test "tokenizer - invalid literal/comment characters" {...@@ -1092,41 +1126,77 @@ test "tokenizer - invalid literal/comment characters" {
1092 Token.Id.Invalid,1126 Token.Id.Invalid,
1093 });1127 });
1094 testTokenize("//\x00", []Token.Id {1128 testTokenize("//\x00", []Token.Id {
1129 Token.Id.LineComment,
1095 Token.Id.Invalid,1130 Token.Id.Invalid,
1096 });1131 });
1097 testTokenize("//\x1f", []Token.Id {1132 testTokenize("//\x1f", []Token.Id {
1133 Token.Id.LineComment,
1098 Token.Id.Invalid,1134 Token.Id.Invalid,
1099 });1135 });
1100 testTokenize("//\x7f", []Token.Id {1136 testTokenize("//\x7f", []Token.Id {
1137 Token.Id.LineComment,
1101 Token.Id.Invalid,1138 Token.Id.Invalid,
1102 });1139 });
1103}1140}
11041141
1105test "tokenizer - utf8" {1142test "tokenizer - utf8" {
1106 testTokenize("//\xc2\x80", []Token.Id{});1143 testTokenize("//\xc2\x80", []Token.Id{Token.Id.LineComment});
1107 testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{});1144 testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{Token.Id.LineComment});
1108}1145}
11091146
1110test "tokenizer - invalid utf8" {1147test "tokenizer - invalid utf8" {
1111 testTokenize("//\x80", []Token.Id{Token.Id.Invalid});1148 testTokenize("//\x80", []Token.Id{
1112 testTokenize("//\xbf", []Token.Id{Token.Id.Invalid});1149 Token.Id.LineComment,
1113 testTokenize("//\xf8", []Token.Id{Token.Id.Invalid});1150 Token.Id.Invalid,
1114 testTokenize("//\xff", []Token.Id{Token.Id.Invalid});1151 });
1115 testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid});1152 testTokenize("//\xbf", []Token.Id{
1116 testTokenize("//\xe0", []Token.Id{Token.Id.Invalid});1153 Token.Id.LineComment,
1117 testTokenize("//\xf0", []Token.Id{Token.Id.Invalid});1154 Token.Id.Invalid,
1118 testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid});1155 });
1156 testTokenize("//\xf8", []Token.Id{
1157 Token.Id.LineComment,
1158 Token.Id.Invalid,
1159 });
1160 testTokenize("//\xff", []Token.Id{
1161 Token.Id.LineComment,
1162 Token.Id.Invalid,
1163 });
1164 testTokenize("//\xc2\xc0", []Token.Id{
1165 Token.Id.LineComment,
1166 Token.Id.Invalid,
1167 });
1168 testTokenize("//\xe0", []Token.Id{
1169 Token.Id.LineComment,
1170 Token.Id.Invalid,
1171 });
1172 testTokenize("//\xf0", []Token.Id{
1173 Token.Id.LineComment,
1174 Token.Id.Invalid,
1175 });
1176 testTokenize("//\xf0\x90\x80\xc0", []Token.Id{
1177 Token.Id.LineComment,
1178 Token.Id.Invalid,
1179 });
1119}1180}
11201181
1121test "tokenizer - illegal unicode codepoints" {1182test "tokenizer - illegal unicode codepoints" {
1122 // unicode newline characters.U+0085, U+2028, U+20291183 // unicode newline characters.U+0085, U+2028, U+2029
1123 testTokenize("//\xc2\x84", []Token.Id{});1184 testTokenize("//\xc2\x84", []Token.Id{Token.Id.LineComment});
1124 testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid});1185 testTokenize("//\xc2\x85", []Token.Id{
1125 testTokenize("//\xc2\x86", []Token.Id{});1186 Token.Id.LineComment,
1126 testTokenize("//\xe2\x80\xa7", []Token.Id{});1187 Token.Id.Invalid,
1127 testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid});1188 });
1128 testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid});1189 testTokenize("//\xc2\x86", []Token.Id{Token.Id.LineComment});
1129 testTokenize("//\xe2\x80\xaa", []Token.Id{});1190 testTokenize("//\xe2\x80\xa7", []Token.Id{Token.Id.LineComment});
1191 testTokenize("//\xe2\x80\xa8", []Token.Id{
1192 Token.Id.LineComment,
1193 Token.Id.Invalid,
1194 });
1195 testTokenize("//\xe2\x80\xa9", []Token.Id{
1196 Token.Id.LineComment,
1197 Token.Id.Invalid,
1198 });
1199 testTokenize("//\xe2\x80\xaa", []Token.Id{Token.Id.LineComment});
1130}1200}
11311201
1132test "tokenizer - string identifier and builtin fns" {1202test "tokenizer - string identifier and builtin fns" {
...@@ -1153,11 +1223,36 @@ test "tokenizer - pipe and then invalid" {...@@ -1153,11 +1223,36 @@ test "tokenizer - pipe and then invalid" {
1153 });1223 });
1154}1224}
11551225
1226test "tokenizer - line comment and doc comment" {
1227 testTokenize("//", []Token.Id{Token.Id.LineComment});
1228 testTokenize("// a / b", []Token.Id{Token.Id.LineComment});
1229 testTokenize("// /", []Token.Id{Token.Id.LineComment});
1230 testTokenize("/// a", []Token.Id{Token.Id.DocComment});
1231 testTokenize("///", []Token.Id{Token.Id.DocComment});
1232 testTokenize("////", []Token.Id{Token.Id.LineComment});
1233}
1234
1235test "tokenizer - line comment followed by identifier" {
1236 testTokenize(
1237 \\ Unexpected,
1238 \\ // another
1239 \\ Another,
1240 , []Token.Id{
1241 Token.Id.Identifier,
1242 Token.Id.Comma,
1243 Token.Id.LineComment,
1244 Token.Id.Identifier,
1245 Token.Id.Comma,
1246 });
1247}
1248
1156fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {1249fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
1157 var tokenizer = Tokenizer.init(source);1250 var tokenizer = Tokenizer.init(source);
1158 for (expected_tokens) |expected_token_id| {1251 for (expected_tokens) |expected_token_id| {
1159 const token = tokenizer.next();1252 const token = tokenizer.next();
1160 std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));1253 if (@TagType(Token.Id)(token.id) != @TagType(Token.Id)(expected_token_id)) {
1254 std.debug.panic("expected {}, found {}\n", @tagName(@TagType(Token.Id)(expected_token_id)), @tagName(@TagType(Token.Id)(token.id)));
1255 }
1161 switch (expected_token_id) {1256 switch (expected_token_id) {
1162 Token.Id.StringLiteral => |expected_kind| {1257 Token.Id.StringLiteral => |expected_kind| {
1163 std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });1258 std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });
test/cases/error.zig+66
...@@ -175,3 +175,69 @@ fn baz_1() !i32 {...@@ -175,3 +175,69 @@ fn baz_1() !i32 {
175fn quux_1() !i32 {175fn quux_1() !i32 {
176 return error.C;176 return error.C;
177}177}
178
179
180test "error: fn returning empty error set can be passed as fn returning any error" {
181 entry();
182 comptime entry();
183}
184
185fn entry() void {
186 foo2(bar2);
187}
188
189fn foo2(f: fn()error!void) void {
190 const x = f();
191}
192
193fn bar2() (error{}!void) { }
194
195
196test "error: Zero sized error set returned with value payload crash" {
197 _ = foo3(0);
198 _ = comptime foo3(0);
199}
200
201const Error = error{};
202fn foo3(b: usize) Error!usize {
203 return b;
204}
205
206
207test "error: Infer error set from literals" {
208 _ = nullLiteral("n") catch |err| handleErrors(err);
209 _ = floatLiteral("n") catch |err| handleErrors(err);
210 _ = intLiteral("n") catch |err| handleErrors(err);
211 _ = comptime nullLiteral("n") catch |err| handleErrors(err);
212 _ = comptime floatLiteral("n") catch |err| handleErrors(err);
213 _ = comptime intLiteral("n") catch |err| handleErrors(err);
214}
215
216fn handleErrors(err: var) noreturn {
217 switch (err) {
218 error.T => {}
219 }
220
221 unreachable;
222}
223
224fn nullLiteral(str: []const u8) !?i64 {
225 if (str[0] == 'n')
226 return null;
227
228 return error.T;
229}
230
231fn floatLiteral(str: []const u8) !?f64 {
232 if (str[0] == 'n')
233 return 1.0;
234
235 return error.T;
236}
237
238fn intLiteral(str: []const u8) !?i64 {
239 if (str[0] == 'n')
240 return 1;
241
242 return error.T;
243}
test/cases/eval.zig+7
...@@ -529,3 +529,10 @@ test "comptime shlWithOverflow" {...@@ -529,3 +529,10 @@ test "comptime shlWithOverflow" {
529529
530 assert(ct_shifted == rt_shifted);530 assert(ct_shifted == rt_shifted);
531}531}
532
533test "runtime 128 bit integer division" {
534 var a: u128 = 152313999999999991610955792383;
535 var b: u128 = 10000000000000000000;
536 var c = a / b;
537 assert(c == 15231399999);
538}
test/compile_errors.zig+17
...@@ -3209,4 +3209,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -3209,4 +3209,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
3209 \\}3209 \\}
3210 ,3210 ,
3211 ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset");3211 ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset");
3212
3213 cases.add("getting return type of generic function",
3214 \\fn generic(a: var) void {}
3215 \\comptime {
3216 \\ _ = @typeOf(generic).ReturnType;
3217 \\}
3218 ,
3219 ".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic");
3220
3221 cases.add("getting @ArgType of generic function",
3222 \\fn generic(a: var) void {}
3223 \\comptime {
3224 \\ _ = @ArgType(@typeOf(generic), 0);
3225 \\}
3226 ,
3227 ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic");
3228
3212}3229}