authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-04-22 15:46:18-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:28-07:00
logcf28736f0d9f70c801a3cc30293a98067bc96fa0
tree791ca0f4f1c726ccc900532580024ae49a3dbd6b
parent045c1c15576bdc283be40e0ce22ce0b7379e1bb7

cbe: pass behavior tests


5 files changed, 92 insertions(+), 62 deletions(-)

src/codegen/c.zig+62-55
......@@ -698,17 +698,27 @@ pub const Object = struct {
698698 indent_counter: usize,
699699
700700 const indent_width = 1;
701 const indent_char = ' ';
701702
702703 fn newline(o: *Object) !void {
703704 const bw = &o.code.buffered_writer;
704705 try bw.writeByte('\n');
705 try bw.splatByteAll(' ', o.indent_counter);
706 try bw.splatByteAll(indent_char, o.indent_counter);
706707 }
707708 fn indent(o: *Object) void {
708709 o.indent_counter += indent_width;
709710 }
710 fn outdent(o: *Object) void {
711 fn outdent(o: *Object) !void {
711712 o.indent_counter -= indent_width;
713 const written = o.code.getWritten();
714 switch (written[written.len - 1]) {
715 indent_char => o.code.shrinkRetainingCapacity(written.len - indent_width),
716 '\n' => try o.code.buffered_writer.splatByteAll(indent_char, o.indent_counter),
717 else => {
718 std.debug.print("\"{f}\"\n", .{std.zig.fmtEscapes(written[written.len -| 100..])});
719 unreachable;
720 },
721 }
712722 }
713723};
714724
......@@ -1041,7 +1051,7 @@ pub const DeclGen = struct {
10411051 .error_union => |error_union| switch (ctype.info(ctype_pool)) {
10421052 .basic => switch (error_union.val) {
10431053 .err_name => |err_name| try dg.renderErrorName(writer, err_name),
1044 .payload => try writer.writeAll("0"),
1054 .payload => try writer.writeByte('0'),
10451055 },
10461056 .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable,
10471057 .aggregate => |aggregate| {
......@@ -1197,7 +1207,7 @@ pub const DeclGen = struct {
11971207 .none => "true",
11981208 else => "false",
11991209 }) else switch (opt.val) {
1200 .none => try writer.writeAll("0"),
1210 .none => try writer.writeByte('0'),
12011211 else => |payload| switch (ip.indexToKey(payload)) {
12021212 .undef => |err_ty| try dg.renderUndefValue(
12031213 writer,
......@@ -1528,7 +1538,7 @@ pub const DeclGen = struct {
15281538 try writer.writeByte(')');
15291539 }
15301540 try dg.renderValue(writer, Value.fromInterned(un.val), location);
1531 } else try writer.writeAll("0");
1541 } else try writer.writeByte('0');
15321542 return;
15331543 }
15341544
......@@ -2786,7 +2796,7 @@ pub fn genErrDecls(o: *Object) Error!void {
27862796 try bw.print(" = {d}u,", .{value});
27872797 try o.newline();
27882798 }
2789 o.outdent();
2799 try o.outdent();
27902800 try bw.writeAll("};");
27912801 try o.newline();
27922802 }
......@@ -2895,6 +2905,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
28952905 try bw.print("case {f}: {{", .{
28962906 try o.dg.fmtIntLiteral(try tag_val.intFromEnum(enum_ty, pt), .Other),
28972907 });
2908 o.indent();
28982909 try o.newline();
28992910 try bw.writeAll("static ");
29002911 try o.dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, Const, .none, .complete);
......@@ -2909,16 +2920,15 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
29092920 try o.dg.fmtIntLiteral(try pt.intValue(.usize, tag_name_len), .Other),
29102921 });
29112922 try o.newline();
2912
2923 try o.outdent();
29132924 try bw.writeByte('}');
29142925 try o.newline();
29152926 }
2927 try o.outdent();
29162928 try bw.writeByte('}');
29172929 try o.newline();
2918 try bw.writeAll("while (");
2919 try o.dg.renderValue(bw, Value.true, .Other);
2920 try bw.writeAll(") ");
2921 _ = try airBreakpoint(o, bw);
2930 try airUnreach(o);
2931 try o.outdent();
29222932 try bw.writeByte('}');
29232933 try o.newline();
29242934 },
......@@ -2940,6 +2950,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
29402950 .fmt_ctype_pool_string = fn_name,
29412951 });
29422952 try bw.writeAll(" {");
2953 o.indent();
29432954 try o.newline();
29442955 try bw.writeAll("return ");
29452956 try o.dg.renderNavName(bw, fn_nav_index);
......@@ -2950,6 +2961,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
29502961 }
29512962 try bw.writeAll(");");
29522963 try o.newline();
2964 try o.outdent();
29532965 try bw.writeByte('}');
29542966 try o.newline();
29552967 },
......@@ -3066,7 +3078,10 @@ fn genFunc(f: *Function) !void {
30663078 f.free_locals_map.clearRetainingCapacity();
30673079
30683080 const main_body = f.air.getMainBody();
3069 try genBodyResolveState(f, undefined, &.{}, main_body, false);
3081 o.indent();
3082 try genBodyResolveState(f, undefined, &.{}, main_body, true);
3083 try o.outdent();
3084 try o.code.buffered_writer.writeByte('}');
30703085 try o.newline();
30713086 if (o.dg.expected_block) |_|
30723087 return f.fail("runtime code not allowed in naked function", .{});
......@@ -3285,11 +3300,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
32853300 if (body.len == 0) {
32863301 try bw.writeAll("{}");
32873302 } else {
3288 try bw.writeAll("{");
3303 try bw.writeByte('{');
32893304 f.object.indent();
32903305 try f.object.newline();
32913306 try genBodyInner(f, body);
3292 f.object.outdent();
3307 try f.object.outdent();
32933308 try bw.writeByte('}');
32943309 }
32953310}
......@@ -3367,7 +3382,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
33673382
33683383 .arg => try airArg(f, inst),
33693384
3370 .breakpoint => try airBreakpoint(&f.object, &f.object.code.buffered_writer),
3385 .breakpoint => try airBreakpoint(f),
33713386 .ret_addr => try airRetAddr(f, inst),
33723387 .frame_addr => try airFrameAddress(f, inst),
33733388
......@@ -3621,7 +3636,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
36213636 .ret_safe => return airRet(f, inst, false), // TODO
36223637 .ret_load => return airRet(f, inst, true),
36233638 .trap => return airTrap(f, &f.object.code.buffered_writer),
3624 .unreach => return airUnreach(f),
3639 .unreach => return airUnreach(&f.object),
36253640
36263641 // Instructions which may be `noreturn`.
36273642 .block => res: {
......@@ -4017,18 +4032,14 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
40174032 try f.writeCValueDeref(bw, ret_val)
40184033 else
40194034 try f.writeCValue(bw, ret_val, .Other);
4020 try bw.writeByte(';');
4021 try f.object.newline();
4035 try bw.writeAll(";\n");
40224036 if (is_array) {
40234037 try freeLocal(f, inst, ret_val.new_local, null);
40244038 }
40254039 } else {
40264040 try reap(f, inst, &.{un_op});
40274041 // Not even allowed to return void in a naked function.
4028 if (!f.object.dg.is_naked_fn) {
4029 try bw.writeAll("return;");
4030 try f.object.newline();
4031 }
4042 if (!f.object.dg.is_naked_fn) try bw.writeAll("return;\n");
40324043 }
40334044}
40344045
......@@ -4817,7 +4828,10 @@ fn airCall(
48174828 try f.freeCValue(inst, resolved_arg);
48184829 }
48194830 try bw.writeAll(");");
4820 try f.object.newline();
4831 switch (modifier) {
4832 .always_tail => try bw.writeByte('\n'),
4833 else => try f.object.newline(),
4834 }
48214835
48224836 const result = result: {
48234837 if (result_local == .none or !lowersToArray(ret_ty, pt))
......@@ -4925,8 +4939,6 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
49254939 try die(f, inst, death.toRef());
49264940 }
49274941
4928 try f.object.newline();
4929
49304942 // noreturn blocks have no `br` instructions reaching them, so we don't want a label
49314943 if (f.object.dg.is_naked_fn) {
49324944 if (f.object.dg.expected_block) |expected_block| {
......@@ -4936,7 +4948,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
49364948 }
49374949 } else if (!f.typeOfIndex(inst).isNoReturn(zcu)) {
49384950 // label must be followed by an expression, include an empty one.
4939 try bw.print("zig_block_{d}:;", .{block_id});
4951 try bw.print("\nzig_block_{d}:;", .{block_id});
49404952 try f.object.newline();
49414953 }
49424954
......@@ -5057,14 +5069,12 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
50575069 try a.end(f, bw);
50585070 }
50595071
5060 try bw.print("goto zig_block_{d};", .{block.block_id});
5061 try f.object.newline();
5072 try bw.print("goto zig_block_{d};\n", .{block.block_id});
50625073}
50635074
50645075fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {
50655076 const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
5066 try f.object.code.buffered_writer.print("goto zig_loop_{d};", .{@intFromEnum(repeat.loop_inst)});
5067 try f.object.newline();
5077 try f.object.code.buffered_writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)});
50685078}
50695079
50705080fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
......@@ -5093,8 +5103,7 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
50935103 }
50945104 }
50955105 } else switch_br.cases_len;
5096 try bw.print("goto zig_switch_{d}_dispatch_{d};", .{ @intFromEnum(br.block_inst), target_case_idx });
5097 try f.object.newline();
5106 try bw.print("goto zig_switch_{d}_dispatch_{d};\n", .{ @intFromEnum(br.block_inst), target_case_idx });
50985107 return;
50995108 }
51005109
......@@ -5106,7 +5115,7 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
51065115 try f.writeCValue(bw, cond, .Other);
51075116 try bw.writeByte(';');
51085117 try f.object.newline();
5109 try bw.print("goto zig_switch_{d}_loop;", .{@intFromEnum(br.block_inst)});
5118 try bw.print("goto zig_switch_{d}_loop;\n", .{@intFromEnum(br.block_inst)});
51105119}
51115120
51125121fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
......@@ -5241,13 +5250,13 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
52415250fn airTrap(f: *Function, bw: *std.io.BufferedWriter) !void {
52425251 // Not even allowed to call trap in a naked function.
52435252 if (f.object.dg.is_naked_fn) return;
5244 try bw.writeAll("zig_trap();");
5245 try f.object.newline();
5253 try bw.writeAll("zig_trap();\n");
52465254}
52475255
5248fn airBreakpoint(o: *Object, bw: *std.io.BufferedWriter) !CValue {
5256fn airBreakpoint(f: *Function) !CValue {
5257 const bw = &f.object.code.buffered_writer;
52495258 try bw.writeAll("zig_breakpoint();");
5250 try o.newline();
5259 try f.object.newline();
52515260 return .none;
52525261}
52535262
......@@ -5273,11 +5282,10 @@ fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
52735282 return local;
52745283}
52755284
5276fn airUnreach(f: *Function) !void {
5285fn airUnreach(o: *Object) !void {
52775286 // Not even allowed to call unreachable in a naked function.
5278 if (f.object.dg.is_naked_fn) return;
5279 try f.object.code.buffered_writer.writeAll("zig_unreachable();");
5280 try f.object.newline();
5287 if (o.dg.is_naked_fn) return;
5288 try o.code.buffered_writer.writeAll("zig_unreachable();\n");
52815289}
52825290
52835291fn airLoop(f: *Function, inst: Air.Inst.Index) !void {
......@@ -5407,10 +5415,11 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
54075415 f.object.indent();
54085416 try f.object.newline();
54095417 if (is_dispatch_loop) {
5410 try bw.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });
5418 try bw.print("zig_switch_{d}_dispatch_{d}:;", .{ @intFromEnum(inst), case.idx });
5419 try f.object.newline();
54115420 }
54125421 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
5413 f.object.outdent();
5422 try f.object.outdent();
54145423 try bw.writeByte('}');
54155424 if (f.object.dg.expected_block) |_|
54165425 return f.fail("runtime code not allowed in naked function", .{});
......@@ -5456,7 +5465,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
54565465 try bw.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });
54575466 }
54585467 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
5459 f.object.outdent();
5468 try f.object.outdent();
54605469 try bw.writeByte('}');
54615470 if (f.object.dg.expected_block) |_|
54625471 return f.fail("runtime code not allowed in naked function", .{});
......@@ -5474,14 +5483,10 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
54745483 try genBody(f, else_body);
54755484 if (f.object.dg.expected_block) |_|
54765485 return f.fail("runtime code not allowed in naked function", .{});
5477 } else {
5478 try bw.writeAll("zig_unreachable();");
5479 }
5480 try f.object.newline();
5481
5482 f.object.outdent();
5483 try bw.writeByte('}');
5486 } else try airUnreach(&f.object);
54845487 try f.object.newline();
5488 try f.object.outdent();
5489 try bw.writeAll("}\n");
54855490}
54865491
54875492fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool {
......@@ -6868,7 +6873,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
68686873 try bw.writeAll("NULL");
68696874 try a.end(f, bw);
68706875 }
6871 f.object.outdent();
6876 try f.object.outdent();
68726877 try bw.writeByte('}');
68736878 try f.object.newline();
68746879 } else {
......@@ -8119,6 +8124,7 @@ fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
81198124const StringLiteral = struct {
81208125 len: usize,
81218126 cur_len: usize,
8127 start_count: usize,
81228128 bw: *std.io.BufferedWriter,
81238129
81248130 // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal,
......@@ -8136,6 +8142,7 @@ const StringLiteral = struct {
81368142 return .{
81378143 .cur_len = 0,
81388144 .len = len,
8145 .start_count = bw.count,
81398146 .bw = bw,
81408147 };
81418148 }
......@@ -8175,7 +8182,7 @@ const StringLiteral = struct {
81758182
81768183 pub fn writeChar(sl: *StringLiteral, c: u8) std.io.Writer.Error!void {
81778184 if (sl.len <= max_string_initializer_len) {
8178 if (sl.cur_len == 0 and sl.bw.count > 1)
8185 if (sl.cur_len == 0 and sl.bw.count - sl.start_count > 1)
81798186 try sl.bw.writeAll("\"\"");
81808187
81818188 const count = sl.bw.count;
......@@ -8186,7 +8193,7 @@ const StringLiteral = struct {
81868193
81878194 if (sl.cur_len >= max_literal_len) sl.cur_len = 0;
81888195 } else {
8189 if (sl.bw.count > 1) try sl.bw.writeByte(',');
8196 if (sl.bw.count - sl.start_count > 1) try sl.bw.writeByte(',');
81908197 try sl.bw.print("'\\x{x}'", .{c});
81918198 }
81928199 }
......@@ -8502,7 +8509,7 @@ const Vectorize = struct {
85028509
85038510 pub fn end(self: Vectorize, f: *Function, inst: Air.Inst.Index, bw: *std.io.BufferedWriter) !void {
85048511 if (self.index != .none) {
8505 f.object.outdent();
8512 try f.object.outdent();
85068513 try bw.writeByte('}');
85078514 try f.object.newline();
85088515 try freeLocal(f, inst, self.index.new_local, null);
test/behavior/error.zig+22-2
......@@ -1042,12 +1042,32 @@ test "generic type constructed from inferred error set of unresolved function" {
10421042 _ = bytes;
10431043 return 0;
10441044 }
1045 const T = std.io.Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).@"fn".return_type.?).error_union.error_set, write);
1045 fn Writer(
1046 comptime Context: type,
1047 comptime WriteError: type,
1048 comptime writeFn: fn (context: Context, bytes: []const u8) WriteError!usize,
1049 ) type {
1050 return struct {
1051 context: Context,
1052 comptime {
1053 _ = writeFn;
1054 }
1055 };
1056 }
1057 const T = Writer(void, @typeInfo(@typeInfo(@TypeOf(write)).@"fn".return_type.?).error_union.error_set, write);
10461058 fn writer() T {
10471059 return .{ .context = {} };
10481060 }
1061 fn multiWriter(streams: anytype) MultiWriter(@TypeOf(streams)) {
1062 return .{ .streams = streams };
1063 }
1064 fn MultiWriter(comptime Writers: type) type {
1065 return struct {
1066 streams: Writers,
1067 };
1068 }
10491069 };
1050 _ = std.io.multiWriter(.{S.writer()});
1070 _ = S.multiWriter(.{S.writer()});
10511071}
10521072
10531073test "errorCast to adhoc inferred error set" {
test/behavior/packed-struct.zig+4-1
......@@ -1086,7 +1086,10 @@ test "packed struct used as part of anon decl name" {
10861086 const S = packed struct { a: u0 = 0 };
10871087 var a: u8 = 0;
10881088 _ = &a;
1089 try std.io.null_writer.print("\n{} {}\n", .{ a, S{} });
1089 var buffer: [std.atomic.cache_line]u8 = undefined;
1090 var nw: std.io.Writer.Null = undefined;
1091 var bw = nw.writer().buffered(&buffer);
1092 try bw.print("\n{} {}\n", .{ a, S{} });
10901093}
10911094
10921095test "packed struct acts as a namespace" {
test/behavior/union_with_members.zig+2-2
......@@ -10,8 +10,8 @@ const ET = union(enum) {
1010
1111 pub fn print(a: *const ET, buf: []u8) anyerror!usize {
1212 return switch (a.*) {
13 ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, .lower, fmt.FormatOptions{}),
14 ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, .lower, fmt.FormatOptions{}),
13 ET.SINT => |x| fmt.printInt(buf, x, 10, .lower, fmt.FormatOptions{}),
14 ET.UINT => |x| fmt.printInt(buf, x, 10, .lower, fmt.FormatOptions{}),
1515 };
1616 }
1717};
test/behavior/var_args.zig+2-2
......@@ -218,11 +218,11 @@ test "variadic functions" {
218218 for (std.mem.span(format)) |c| switch (c) {
219219 's' => {
220220 const arg = @cVaArg(ap, [*:0]const u8);
221 list.writer().print("{s}", .{arg}) catch return;
221 list.print("{s}", .{arg}) catch return;
222222 },
223223 'd' => {
224224 const arg = @cVaArg(ap, c_int);
225 list.writer().print("{d}", .{arg}) catch return;
225 list.print("{d}", .{arg}) catch return;
226226 },
227227 else => unreachable,
228228 };