mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
C backend: Improve lowering of Zig types to C types
1. Changed Zig pointers to functions to be typedef'd so then we can treat them the same as other types. 2. Distinguished between const slices (zig_L prefix) and mut slices (zig_M prefix). 3. Changed lowering of Zig "const pointers" (e.g. *const u8) to to C "pointers to const" (e.g. const char *) rather than C "const pointers" (e.g. char * const) 4. Ensured that all typedefs are "linked" even if the decl doesn't require any forward declarations 5. Added test that exercises function pointer type rendering 6. Changed .slice_ptr instruction to allocate pointer local rather than a uintptr_t local
This commit is contained in:
parent
91c3206b45
commit
51efd553ae
@ -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();
|
||||
const name = dg.getTypedefName(t) orelse
|
||||
try dg.renderOptionalTypedef(t, child_type);
|
||||
|
||||
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 },
|
||||
);
|
||||
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);
|
||||
if (t.errorUnionPayload().abiSize(target) == 0) {
|
||||
return dg.renderType(w, t.errorUnionSet());
|
||||
}
|
||||
|
||||
var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
|
||||
defer buffer.deinit();
|
||||
const bw = buffer.writer();
|
||||
const name = dg.getTypedefName(t) orelse
|
||||
try dg.renderErrorUnionTypedef(t);
|
||||
|
||||
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);
|
||||
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);
|
||||
const name = dg.getTypedefName(t) orelse
|
||||
try dg.renderStructTypedef(t);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.*;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user