diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c306e0a6b0..b814be4445 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1000,6 +1000,46 @@ pub const DeclGen = struct { return name; } + fn renderTupleTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { + const tuple = t.tupleFields(); + + var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); + defer buffer.deinit(); + const writer = buffer.writer(); + + try buffer.appendSlice("typedef struct {\n"); + { + for (tuple.types) |field_ty, i| { + const val = tuple.values[i]; + if (val.tag() != .unreachable_value) continue; + + var name = std.ArrayList(u8).init(dg.gpa); + defer name.deinit(); + try name.writer().print("field_{d}", .{i}); + + try buffer.append(' '); + try dg.renderTypeAndName(writer, field_ty, .{ .bytes = name.items }, .Mut, Value.initTag(.abi_align_default)); + try buffer.appendSlice(";\n"); + } + } + try buffer.appendSlice("} "); + + const name_start = buffer.items.len; + try writer.print("zig_T_{};\n", .{typeToCIdentifier(t)}); + + const rendered = buffer.toOwnedSlice(); + errdefer dg.typedefs.allocator.free(rendered); + const name = rendered[name_start .. rendered.len - 2]; + + try dg.typedefs.ensureUnusedCapacity(1); + dg.typedefs.putAssumeCapacityNoClobber( + try t.copy(dg.typedefs_arena), + .{ .name = name, .rendered = rendered }, + ); + + return name; + } + fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { const union_ty = t.cast(Type.Payload.Union).?.data; const fqn = try union_ty.getFullyQualifiedName(dg.typedefs.allocator); @@ -1276,7 +1316,9 @@ pub const DeclGen = struct { return w.writeAll(name); }, .Struct => { - const name = dg.getTypedefName(t) orelse + const name = dg.getTypedefName(t) orelse if (t.isTuple()) + try dg.renderTupleTypedef(t) + else try dg.renderStructTypedef(t); return w.writeAll(name); @@ -3116,6 +3158,8 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc var field_name: []const u8 = undefined; var field_val_ty: Type = undefined; + var buf = std.ArrayList(u8).init(f.object.dg.gpa); + defer buf.deinit(); switch (struct_ty.tag()) { .@"struct" => { const fields = struct_ty.structFields(); @@ -3127,6 +3171,14 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc field_name = fields.keys()[index]; field_val_ty = fields.values()[index].ty; }, + .tuple => { + const tuple = struct_ty.tupleFields(); + if (tuple.values[index].tag() != .unreachable_value) return CValue.none; + + try buf.writer().print("field_{d}", .{index}); + field_name = buf.items; + field_val_ty = tuple.types[index]; + }, else => unreachable, } const payload = if (struct_ty.tag() == .union_tagged) "payload." else ""; @@ -3149,9 +3201,18 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const struct_byval = try f.resolveInst(extra.struct_operand); const struct_ty = f.air.typeOf(extra.struct_operand); + var buf = std.ArrayList(u8).init(f.object.dg.gpa); + defer buf.deinit(); const field_name = switch (struct_ty.tag()) { .@"struct" => struct_ty.structFields().keys()[extra.field_index], .@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index], + .tuple => blk: { + const tuple = struct_ty.tupleFields(); + if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none; + + try buf.writer().print("field_{d}", .{extra.field_index}); + break :blk buf.items; + }, else => unreachable, }; const payload = if (struct_ty.tag() == .union_tagged) "payload." else ""; @@ -3652,11 +3713,25 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); - try writer.writeAll(" = "); + try writer.writeAll(" = {"); + switch (vector_ty.zigTypeTag()) { + .Struct => { + const tuple = vector_ty.tupleFields(); + var i: usize = 0; + for (elements) |elem, elem_index| { + if (tuple.values[elem_index].tag() != .unreachable_value) continue; - _ = elements; - _ = local; - return f.fail("TODO: C backend: implement airAggregateInit", .{}); + const value = try f.resolveInst(elem); + if (i != 0) try writer.writeAll(", "); + try f.writeCValue(writer, value); + i += 1; + } + }, + else => |tag| return f.fail("TODO: C backend: implement airAggregateInit for type {s}", .{@tagName(tag)}), + } + try writer.writeAll("};\n"); + + return local; } fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { diff --git a/test/behavior.zig b/test/behavior.zig index f6ff8bd64d..4775767ac3 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -146,6 +146,7 @@ test { { // Tests that pass for stage1, llvm backend, C backend _ = @import("behavior/bugs/421.zig"); + _ = @import("behavior/bugs/3779.zig"); _ = @import("behavior/bugs/9584.zig"); _ = @import("behavior/cast_int.zig"); _ = @import("behavior/eval.zig"); @@ -162,7 +163,6 @@ test { _ = @import("behavior/saturating_arithmetic.zig"); _ = @import("behavior/widening.zig"); _ = @import("behavior/bugs/2114.zig"); - _ = @import("behavior/bugs/3779.zig"); _ = @import("behavior/bugs/10147.zig"); _ = @import("behavior/shuffle.zig"); diff --git a/test/behavior/bugs/3779.zig b/test/behavior/bugs/3779.zig index 51b1725dd9..7dd0914161 100644 --- a/test/behavior/bugs/3779.zig +++ b/test/behavior/bugs/3779.zig @@ -7,9 +7,9 @@ const ptr_tag_name: [*:0]const u8 = tag_name; test "@tagName() returns a string literal" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong - try std.testing.expectEqual(*const [13:0]u8, @TypeOf(tag_name)); - try std.testing.expectEqualStrings("TestEnumValue", tag_name); - try std.testing.expectEqualStrings("TestEnumValue", ptr_tag_name[0..tag_name.len]); + try std.testing.expect(*const [13:0]u8 == @TypeOf(tag_name)); + try std.testing.expect(std.mem.eql(u8, "TestEnumValue", tag_name)); + try std.testing.expect(std.mem.eql(u8, "TestEnumValue", ptr_tag_name[0..tag_name.len])); } const TestError = error{TestErrorCode}; @@ -18,9 +18,9 @@ const ptr_error_name: [*:0]const u8 = error_name; test "@errorName() returns a string literal" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong - try std.testing.expectEqual(*const [13:0]u8, @TypeOf(error_name)); - try std.testing.expectEqualStrings("TestErrorCode", error_name); - try std.testing.expectEqualStrings("TestErrorCode", ptr_error_name[0..error_name.len]); + try std.testing.expect(*const [13:0]u8 == @TypeOf(error_name)); + try std.testing.expect(std.mem.eql(u8, "TestErrorCode", error_name)); + try std.testing.expect(std.mem.eql(u8, "TestErrorCode", ptr_error_name[0..error_name.len])); } const TestType = struct {}; @@ -29,9 +29,9 @@ const ptr_type_name: [*:0]const u8 = type_name; test "@typeName() returns a string literal" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong - try std.testing.expectEqual(*const [type_name.len:0]u8, @TypeOf(type_name)); - try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", type_name); - try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", ptr_type_name[0..type_name.len]); + try std.testing.expect(*const [type_name.len:0]u8 == @TypeOf(type_name)); + try std.testing.expect(std.mem.eql(u8, "behavior.bugs.3779.TestType", type_name)); + try std.testing.expect(std.mem.eql(u8, "behavior.bugs.3779.TestType", ptr_type_name[0..type_name.len])); } const actual_contents = @embedFile("3779_file_to_embed.txt"); @@ -39,10 +39,10 @@ const ptr_actual_contents: [*:0]const u8 = actual_contents; const expected_contents = "hello zig\n"; test "@embedFile() returns a string literal" { - try std.testing.expectEqual(*const [expected_contents.len:0]u8, @TypeOf(actual_contents)); + try std.testing.expect(*const [expected_contents.len:0]u8 == @TypeOf(actual_contents)); try std.testing.expect(std.mem.eql(u8, expected_contents, actual_contents)); - try std.testing.expectEqualStrings(expected_contents, actual_contents); - try std.testing.expectEqualStrings(expected_contents, ptr_actual_contents[0..actual_contents.len]); + try std.testing.expect(std.mem.eql(u8, expected_contents, actual_contents)); + try std.testing.expect(std.mem.eql(u8, expected_contents, ptr_actual_contents[0..actual_contents.len])); } fn testFnForSrc() std.builtin.SourceLocation { @@ -51,9 +51,9 @@ fn testFnForSrc() std.builtin.SourceLocation { test "@src() returns a struct containing 0-terminated string slices" { const src = testFnForSrc(); - try std.testing.expectEqual([:0]const u8, @TypeOf(src.file)); + try std.testing.expect([:0]const u8 == @TypeOf(src.file)); try std.testing.expect(std.mem.endsWith(u8, src.file, "3779.zig")); - try std.testing.expectEqual([:0]const u8, @TypeOf(src.fn_name)); + try std.testing.expect([:0]const u8 == @TypeOf(src.fn_name)); try std.testing.expect(std.mem.endsWith(u8, src.fn_name, "testFnForSrc")); const ptr_src_file: [*:0]const u8 = src.file; diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 6f9522834e..71bf52d71e 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -2,11 +2,9 @@ const builtin = @import("builtin"); const std = @import("std"); const testing = std.testing; const expect = testing.expect; -const expectEqual = testing.expectEqual; test "tuple concatenation" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -18,8 +16,8 @@ test "tuple concatenation" { var x = .{a}; var y = .{b}; var c = x ++ y; - try expectEqual(@as(i32, 1), c[0]); - try expectEqual(@as(i32, 2), c[1]); + try expect(@as(i32, 1) == c[0]); + try expect(@as(i32, 2) == c[1]); } }; try S.doTheTest(); @@ -51,7 +49,6 @@ test "tuple multiplication" { test "more tuple concatenation" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -129,7 +126,6 @@ test "tuple initializer for var" { } test "array-like initializer for tuple types" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO