Commit 51efd553ae
src/codegen/c.zig
@@ -179,6 +179,14 @@ pub const DeclGen = struct {
return error.AnalysisFail;
}
+ fn getTypedefName(dg: *DeclGen, t: Type) ?[]const u8 {
+ if (dg.typedefs.get(t)) |some| {
+ return some.name;
+ } else {
+ return null;
+ }
+ }
+
fn renderDeclValue(
dg: *DeclGen,
writer: anytype,
@@ -200,14 +208,20 @@ pub const DeclGen = struct {
return;
}
- // Determine if we must pointer cast.
assert(decl.has_tv);
- if (ty.eql(decl.ty)) {
- try writer.writeByte('&');
- } else {
- try writer.writeAll("(");
- try dg.renderType(writer, ty);
- try writer.writeAll(")&");
+ // We shouldn't cast C function pointers as this is UB (when you call
+ // them). The analysis until now should ensure that the C function
+ // pointers are compatible. If they are not, then there is a bug
+ // somewhere and we should let the C compiler tell us about it.
+ if (ty.castPtrToFn() == null) {
+ // Determine if we must pointer cast.
+ if (ty.eql(decl.ty)) {
+ try writer.writeByte('&');
+ } else {
+ try writer.writeAll("(");
+ try dg.renderType(writer, ty);
+ try writer.writeAll(")&");
+ }
}
try dg.renderDeclName(decl, writer);
}
@@ -514,7 +528,190 @@ pub const DeclGen = struct {
try w.writeByte(')');
}
+ fn renderPtrToFnTypedef(dg: *DeclGen, t: Type, fn_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
+ var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
+ defer buffer.deinit();
+ const bw = buffer.writer();
+
+ const fn_info = fn_ty.fnInfo();
+
+ try bw.writeAll("typedef ");
+ try dg.renderType(bw, fn_info.return_type);
+ try bw.writeAll(" (*");
+
+ const name_start = buffer.items.len;
+ // TODO: typeToCIdentifier truncates to 128 bytes, we probably don't want to do this
+ try bw.print("zig_F_{s})(", .{typeToCIdentifier(t)});
+ const name_end = buffer.items.len - 2;
+
+ const param_len = fn_info.param_types.len;
+ const is_var_args = fn_info.is_var_args;
+ if (param_len == 0 and !is_var_args)
+ try bw.writeAll("void")
+ else {
+ var index: usize = 0;
+ while (index < param_len) : (index += 1) {
+ if (index > 0) {
+ try bw.writeAll(", ");
+ }
+ try dg.renderType(bw, fn_info.param_types[index]);
+ }
+ }
+ if (is_var_args) {
+ if (param_len != 0) try bw.writeAll(", ");
+ try bw.writeAll("...");
+ }
+ try bw.writeAll(");\n");
+
+ const rendered = buffer.toOwnedSlice();
+ errdefer dg.typedefs.allocator.free(rendered);
+ const name = rendered[name_start..name_end];
+
+ try dg.typedefs.ensureUnusedCapacity(1);
+ dg.typedefs.putAssumeCapacityNoClobber(
+ try t.copy(dg.typedefs_arena),
+ .{ .name = name, .rendered = rendered },
+ );
+
+ return name;
+ }
+
+ fn renderSliceTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
+ var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
+ defer buffer.deinit();
+ const bw = buffer.writer();
+
+ try bw.writeAll("typedef struct { ");
+ const elem_type = t.elemType();
+ try dg.renderType(bw, elem_type);
+ if (t.isConstPtr()) {
+ try bw.writeAll(" const");
+ }
+ if (t.isVolatilePtr()) {
+ try bw.writeAll(" volatile");
+ }
+ try bw.writeAll(" *");
+ try bw.writeAll("ptr; size_t len; } ");
+ const name_index = buffer.items.len;
+ if (t.isConstPtr()) {
+ try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
+ } else {
+ try bw.print("zig_M_{s};\n", .{typeToCIdentifier(elem_type)});
+ }
+
+ const rendered = buffer.toOwnedSlice();
+ errdefer dg.typedefs.allocator.free(rendered);
+ const name = rendered[name_index .. rendered.len - 2];
+
+ try dg.typedefs.ensureUnusedCapacity(1);
+ dg.typedefs.putAssumeCapacityNoClobber(
+ try t.copy(dg.typedefs_arena),
+ .{ .name = name, .rendered = rendered },
+ );
+
+ return name;
+ }
+
+ fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
+ const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere.
+ const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator);
+ defer dg.typedefs.allocator.free(fqn);
+
+ var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
+ defer buffer.deinit();
+
+ try buffer.appendSlice("typedef struct {\n");
+ {
+ var it = struct_obj.fields.iterator();
+ while (it.next()) |entry| {
+ const field_ty = entry.value_ptr.ty;
+ const name: CValue = .{ .bytes = entry.key_ptr.* };
+ try buffer.append(' ');
+ try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut);
+ try buffer.appendSlice(";\n");
+ }
+ }
+ try buffer.appendSlice("} ");
+
+ const name_start = buffer.items.len;
+ try buffer.writer().print("zig_S_{s};\n", .{fmtIdent(fqn)});
+
+ 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 renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
+ const child_type = t.errorUnionPayload();
+ const err_set_type = t.errorUnionSet();
+
+ var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
+ defer buffer.deinit();
+ const bw = buffer.writer();
+
+ try bw.writeAll("typedef struct { ");
+ try dg.renderType(bw, child_type);
+ try bw.writeAll(" payload; uint16_t error; } ");
+ const name_index = buffer.items.len;
+ if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
+ const func = inf_err_set_payload.data.func;
+ try bw.writeAll("zig_E_");
+ try dg.renderDeclName(func.owner_decl, bw);
+ try bw.writeAll(";\n");
+ } else {
+ try bw.print("zig_E_{s}_{s};\n", .{
+ typeToCIdentifier(err_set_type), typeToCIdentifier(child_type),
+ });
+ }
+
+ const rendered = buffer.toOwnedSlice();
+ errdefer dg.typedefs.allocator.free(rendered);
+ const name = rendered[name_index .. rendered.len - 2];
+
+ try dg.typedefs.ensureUnusedCapacity(1);
+ dg.typedefs.putAssumeCapacityNoClobber(
+ try t.copy(dg.typedefs_arena),
+ .{ .name = name, .rendered = rendered },
+ );
+
+ return name;
+ }
+
+ fn renderOptionalTypedef(dg: *DeclGen, t: Type, child_type: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
+ var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
+ defer buffer.deinit();
+ const bw = buffer.writer();
+
+ try bw.writeAll("typedef struct { ");
+ try dg.renderType(bw, child_type);
+ try bw.writeAll(" payload; bool is_null; } ");
+ const name_index = buffer.items.len;
+ try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
+
+ const rendered = buffer.toOwnedSlice();
+ errdefer dg.typedefs.allocator.free(rendered);
+ const name = rendered[name_index .. rendered.len - 2];
+
+ try dg.typedefs.ensureUnusedCapacity(1);
+ dg.typedefs.putAssumeCapacityNoClobber(
+ try t.copy(dg.typedefs_arena),
+ .{ .name = name, .rendered = rendered },
+ );
+
+ return name;
+ }
+
fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {
+ const target = dg.module.getTarget();
+
switch (t.zigTypeTag()) {
.NoReturn => {
try w.writeAll("zig_noreturn void");
@@ -542,7 +739,7 @@ pub const DeclGen = struct {
.c_longlong => try w.writeAll("long long"),
.c_ulonglong => try w.writeAll("unsigned long long"),
.int_signed, .int_unsigned => {
- const info = t.intInfo(dg.module.getTarget());
+ const info = t.intInfo(target);
const sign_prefix = switch (info.signedness) {
.signed => "",
.unsigned => "u",
@@ -554,7 +751,6 @@ pub const DeclGen = struct {
else => unreachable,
}
},
-
.Float => {
switch (t.tag()) {
.f32 => try w.writeAll("float"),
@@ -565,196 +761,71 @@ pub const DeclGen = struct {
else => unreachable,
}
},
-
.Pointer => {
if (t.isSlice()) {
- if (dg.typedefs.get(t)) |some| {
- return w.writeAll(some.name);
- }
+ const name = dg.getTypedefName(t) orelse
+ try dg.renderSliceTypedef(t);
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
- try bw.writeAll("typedef struct { ");
- const elem_type = t.elemType();
- try dg.renderType(bw, elem_type);
- try bw.writeAll(" *");
- // We skip the const qualifier because C's type system
- // would not allow a local mutable variable which is this slice type
- // to be overwritten with a new slice type.
- if (t.isVolatilePtr()) {
- try bw.writeAll("volatile ");
- }
- try bw.writeAll("ptr; size_t len; } ");
- const name_index = buffer.items.len;
- try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
-
- const rendered = buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_index .. rendered.len - 2];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- try w.writeAll(name);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
- return;
+ return w.writeAll(name);
}
+
if (t.castPtrToFn()) |fn_ty| {
- const fn_info = fn_ty.fnInfo();
- try dg.renderType(w, fn_info.return_type);
- try w.writeAll(" (*)(");
- const param_len = fn_info.param_types.len;
- const is_var_args = fn_info.is_var_args;
- if (param_len == 0 and !is_var_args)
- try w.writeAll("void")
- else {
- var index: usize = 0;
- while (index < param_len) : (index += 1) {
- if (index > 0) {
- try w.writeAll(", ");
- }
- try dg.renderType(w, fn_info.param_types[index]);
- }
- }
- if (is_var_args) {
- if (param_len != 0) try w.writeAll(", ");
- try w.writeAll("...");
- }
- try w.writeByte(')');
- return;
+ const name = dg.getTypedefName(t) orelse
+ try dg.renderPtrToFnTypedef(t, fn_ty);
+
+ return w.writeAll(name);
}
try dg.renderType(w, t.elemType());
- try w.writeAll(" *");
if (t.isConstPtr()) {
- try w.writeAll("const ");
+ try w.writeAll(" const");
}
if (t.isVolatilePtr()) {
- try w.writeAll("volatile ");
+ try w.writeAll(" volatile");
}
+ return w.writeAll(" *");
},
.Array => {
+ // We are referencing the array so it will decay to a C pointer.
try dg.renderType(w, t.elemType());
- try w.writeAll(" *");
+ return w.writeAll(" *");
},
.Optional => {
var opt_buf: Type.Payload.ElemType = undefined;
const child_type = t.optionalChild(&opt_buf);
- const target = dg.module.getTarget();
+
if (t.isPtrLikeOptional()) {
return dg.renderType(w, child_type);
- } else if (dg.typedefs.get(t)) |some| {
- return w.writeAll(some.name);
- } else if (child_type.abiSize(target) == 0) {
+ }
+
+ if (child_type.abiSize(target) == 0) {
return w.writeAll("bool");
}
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
- try bw.writeAll("typedef struct { ");
- try dg.renderType(bw, child_type);
- try bw.writeAll(" payload; bool is_null; } ");
- const name_index = buffer.items.len;
- try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
-
- const rendered = buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_index .. rendered.len - 2];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- try w.writeAll(name);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
+ const name = dg.getTypedefName(t) orelse
+ try dg.renderOptionalTypedef(t, child_type);
+
+ return w.writeAll(name);
},
.ErrorSet => {
comptime std.debug.assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2);
- try w.writeAll("uint16_t");
+ return w.writeAll("uint16_t");
},
.ErrorUnion => {
- if (dg.typedefs.get(t)) |some| {
- return w.writeAll(some.name);
- }
- const child_type = t.errorUnionPayload();
- const err_set_type = t.errorUnionSet();
-
- if (!child_type.hasCodeGenBits()) {
- return dg.renderType(w, err_set_type);
- }
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
- const bw = buffer.writer();
-
- try bw.writeAll("typedef struct { ");
- try dg.renderType(bw, child_type);
- try bw.writeAll(" payload; uint16_t error; } ");
- const name_index = buffer.items.len;
- if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
- const func = inf_err_set_payload.data.func;
- try bw.writeAll("zig_E_");
- try dg.renderDeclName(func.owner_decl, bw);
- try bw.writeAll(";\n");
- } else {
- try bw.print("zig_E_{s}_{s};\n", .{
- typeToCIdentifier(err_set_type), typeToCIdentifier(child_type),
- });
+ if (t.errorUnionPayload().abiSize(target) == 0) {
+ return dg.renderType(w, t.errorUnionSet());
}
- const rendered = buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_index .. rendered.len - 2];
+ const name = dg.getTypedefName(t) orelse
+ try dg.renderErrorUnionTypedef(t);
- try dg.typedefs.ensureUnusedCapacity(1);
- try w.writeAll(name);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
+ return w.writeAll(name);
},
.Struct => {
- if (dg.typedefs.get(t)) |some| {
- return w.writeAll(some.name);
- }
- const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere.
- const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator);
- defer dg.typedefs.allocator.free(fqn);
-
- var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
- defer buffer.deinit();
-
- try buffer.appendSlice("typedef struct {\n");
- {
- var it = struct_obj.fields.iterator();
- while (it.next()) |entry| {
- const field_ty = entry.value_ptr.ty;
- const name: CValue = .{ .bytes = entry.key_ptr.* };
- try buffer.append(' ');
- try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut);
- try buffer.appendSlice(";\n");
- }
- }
- try buffer.appendSlice("} ");
-
- const name_start = buffer.items.len;
- try buffer.writer().print("zig_S_{s};\n", .{fmtIdent(fqn)});
+ const name = dg.getTypedefName(t) orelse
+ try dg.renderStructTypedef(t);
- const rendered = buffer.toOwnedSlice();
- errdefer dg.typedefs.allocator.free(rendered);
- const name = rendered[name_start .. rendered.len - 2];
-
- try dg.typedefs.ensureUnusedCapacity(1);
- try w.writeAll(name);
- dg.typedefs.putAssumeCapacityNoClobber(
- try t.copy(dg.typedefs_arena),
- .{ .name = name, .rendered = rendered },
- );
+ return w.writeAll(name);
},
.Enum => {
// For enums, we simply use the integer tag type.
@@ -763,12 +834,17 @@ pub const DeclGen = struct {
try dg.renderType(w, int_tag_ty);
},
- .Union => return dg.fail("TODO: C backend: implement type Union", .{}),
+
+ .Union,
+ .Frame,
+ .AnyFrame,
+ .Vector,
+ .Opaque,
+ => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{
+ @tagName(tag),
+ }),
+
.Fn => unreachable, // This is a function body, not a function pointer.
- .Opaque => return dg.fail("TODO: C backend: implement type Opaque", .{}),
- .Frame => return dg.fail("TODO: C backend: implement type Frame", .{}),
- .AnyFrame => return dg.fail("TODO: C backend: implement type AnyFrame", .{}),
- .Vector => return dg.fail("TODO: C backend: implement type Vector", .{}),
.Null,
.Undefined,
@@ -800,100 +876,14 @@ pub const DeclGen = struct {
render_ty = render_ty.elemType();
}
- // TODO this is duplicated from the code below and does not handle
- // arbitrary nesting of pointers. This renderTypeAndName function
- // needs to be reworked by someone who understands C's insane type syntax. That
- // person might be future me but it is certainly not present me.
- if (render_ty.zigTypeTag() == .Pointer and
- render_ty.childType().zigTypeTag() == .Pointer and
- render_ty.childType().childType().zigTypeTag() == .Fn)
- {
- const ptr2_ty = render_ty.childType();
- const fn_info = ptr2_ty.childType().fnInfo();
- const ret_ty = fn_info.return_type;
- if (ret_ty.zigTypeTag() == .NoReturn) {
- // noreturn attribute is not allowed here.
- try w.writeAll("void");
- } else {
- try dg.renderType(w, ret_ty);
- }
- try w.writeAll(" (*");
- switch (mutability) {
- .Const => try w.writeAll("const "),
- .Mut => {},
- }
- if (!ptr2_ty.ptrIsMutable()) {
- try w.writeAll("*const ");
- } else {
- try w.writeAll("*");
- }
- try dg.writeCValue(w, name);
- try w.writeAll(")(");
- const param_len = fn_info.param_types.len;
- const is_var_args = fn_info.is_var_args;
- if (param_len == 0 and !is_var_args)
- try w.writeAll("void")
- else {
- var index: usize = 0;
- while (index < param_len) : (index += 1) {
- if (index > 0) {
- try w.writeAll(", ");
- }
- try dg.renderType(w, fn_info.param_types[index]);
- }
- }
- if (is_var_args) {
- if (param_len != 0) try w.writeAll(", ");
- try w.writeAll("...");
- }
- try w.writeByte(')');
- return;
- }
+ try dg.renderType(w, render_ty);
- if (render_ty.castPtrToFn()) |fn_ty| {
- const fn_info = fn_ty.fnInfo();
- const ret_ty = fn_info.return_type;
- if (ret_ty.zigTypeTag() == .NoReturn) {
- // noreturn attribute is not allowed here.
- try w.writeAll("void");
- } else {
- try dg.renderType(w, ret_ty);
- }
- try w.writeAll(" (*");
- switch (mutability) {
- .Const => try w.writeAll("const "),
- .Mut => {},
- }
- try dg.writeCValue(w, name);
- try w.writeAll(")(");
- const param_len = fn_info.param_types.len;
- const is_var_args = fn_info.is_var_args;
- if (param_len == 0 and !is_var_args)
- try w.writeAll("void")
- else {
- var index: usize = 0;
- while (index < param_len) : (index += 1) {
- if (index > 0) {
- try w.writeAll(", ");
- }
- try dg.renderType(w, fn_info.param_types[index]);
- }
- }
- if (is_var_args) {
- if (param_len != 0) try w.writeAll(", ");
- try w.writeAll("...");
- }
- try w.writeByte(')');
- } else {
- try dg.renderType(w, render_ty);
-
- const const_prefix = switch (mutability) {
- .Const => "const ",
- .Mut => "",
- };
- try w.print(" {s}", .{const_prefix});
- try dg.writeCValue(w, name);
- }
+ const const_prefix = switch (mutability) {
+ .Const => "const ",
+ .Mut => "",
+ };
+ try w.print(" {s}", .{const_prefix});
+ try dg.writeCValue(w, name);
try w.writeAll(suffix.items);
}
@@ -1224,10 +1214,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
+ const inst_ty = f.air.typeOfIndex(inst);
const ty_op = f.air.instructions.items(.data)[inst].ty_op;
const operand = try f.resolveInst(ty_op.operand);
const writer = f.object.writer();
- const local = try f.allocLocal(Type.initTag(.usize), .Const);
+ const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = ");
try f.writeCValue(writer, operand);
try writer.writeAll(suffix);
src/link/C.zig
@@ -362,7 +362,7 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void
const decl_block = self.decl_table.getPtr(decl).?;
const gpa = self.base.allocator;
- if (decl_block.fwd_decl.items.len != 0) {
+ if (decl_block.typedefs.count() != 0) {
try f.typedefs.ensureUnusedCapacity(gpa, @intCast(u32, decl_block.typedefs.count()));
var it = decl_block.typedefs.iterator();
while (it.next()) |new| {
@@ -371,6 +371,9 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void
try f.err_typedef_buf.appendSlice(gpa, new.value_ptr.rendered);
}
}
+ }
+
+ if (decl_block.fwd_decl.items.len != 0) {
const buf = decl_block.fwd_decl.items;
try f.all_buffers.append(gpa, .{
.iov_base = buf.ptr,
test/behavior/basic.zig
@@ -250,3 +250,45 @@ test "volatile load and store" {
ptr.* += 1;
try expect(ptr.* == 1235);
}
+
+fn fA() []const u8 {
+ return "a";
+}
+fn fB() []const u8 {
+ return "b";
+}
+
+test "call function pointer in struct" {
+ try expect(mem.eql(u8, f3(true), "a"));
+ try expect(mem.eql(u8, f3(false), "b"));
+}
+
+fn f3(x: bool) []const u8 {
+ var wrapper: FnPtrWrapper = .{
+ .fn_ptr = fB,
+ };
+
+ if (x) {
+ wrapper.fn_ptr = fA;
+ }
+
+ return wrapper.fn_ptr();
+}
+
+const FnPtrWrapper = struct {
+ fn_ptr: fn () []const u8,
+};
+
+test "const ptr from var variable" {
+ var x: u64 = undefined;
+ var y: u64 = undefined;
+
+ x = 78;
+ copy(&x, &y);
+
+ try expect(x == y);
+}
+
+fn copy(src: *const u64, dst: *u64) void {
+ dst.* = src.*;
+}