mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 00:26:57 +00:00
aro translate-c: support for record types added
This commit is contained in:
parent
bcb534c295
commit
c9ad1b5199
4
lib/compiler/aro/aro/Type.zig
vendored
4
lib/compiler/aro/aro/Type.zig
vendored
@ -1142,12 +1142,14 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 {
|
||||
};
|
||||
}
|
||||
|
||||
pub const QualHandling = enum { standard, preserve_quals };
|
||||
|
||||
/// Canonicalize a possibly-typeof() type. If the type is not a typeof() type, simply
|
||||
/// return it. Otherwise, determine the actual qualified type.
|
||||
/// The `qual_handling` parameter can be used to return the full set of qualifiers
|
||||
/// added by typeof() operations, which is useful when determining the elemType of
|
||||
/// arrays and pointers.
|
||||
pub fn canonicalize(ty: Type, qual_handling: enum { standard, preserve_quals }) Type {
|
||||
pub fn canonicalize(ty: Type, qual_handling: QualHandling) Type {
|
||||
var cur = ty;
|
||||
if (cur.specifier == .attributed) {
|
||||
cur = cur.data.attributed.base;
|
||||
|
@ -52,18 +52,17 @@ fn getMangle(c: *Context) u32 {
|
||||
return c.mangle_count;
|
||||
}
|
||||
|
||||
/// Convert a clang source location to a file:line:column string
|
||||
fn locStr(c: *Context, loc: TokenIndex) ![]const u8 {
|
||||
_ = c;
|
||||
_ = loc;
|
||||
// const spelling_loc = c.source_manager.getSpellingLoc(loc);
|
||||
// const filename_c = c.source_manager.getFilename(spelling_loc);
|
||||
// const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)");
|
||||
/// Convert an aro TokenIndex to a 'file:line:column' string
|
||||
fn locStr(c: *Context, tok_idx: TokenIndex) ![]const u8 {
|
||||
const token_loc = c.tree.tokens.items(.loc)[tok_idx];
|
||||
const source = c.comp.getSource(token_loc.id);
|
||||
const line_col = source.lineCol(token_loc);
|
||||
const filename = source.path;
|
||||
|
||||
// const line = c.source_manager.getSpellingLineNumber(spelling_loc);
|
||||
// const column = c.source_manager.getSpellingColumnNumber(spelling_loc);
|
||||
// return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column });
|
||||
return "somewhere";
|
||||
const line = source.physicalLine(token_loc);
|
||||
const col = line_col.col;
|
||||
|
||||
return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, col });
|
||||
}
|
||||
|
||||
fn maybeSuppressResult(c: *Context, used: ResultUsed, result: ZigNode) TransError!ZigNode {
|
||||
@ -184,26 +183,30 @@ fn prepopulateGlobalNameTable(c: *Context) !void {
|
||||
const node_data = c.tree.nodes.items(.data);
|
||||
for (c.tree.root_decls) |node| {
|
||||
const data = node_data[@intFromEnum(node)];
|
||||
const decl_name = switch (node_tags[@intFromEnum(node)]) {
|
||||
switch (node_tags[@intFromEnum(node)]) {
|
||||
.typedef => @panic("TODO"),
|
||||
|
||||
.static_assert,
|
||||
.struct_decl_two,
|
||||
.union_decl_two,
|
||||
.struct_decl,
|
||||
.union_decl,
|
||||
=> blk: {
|
||||
const ty = node_types[@intFromEnum(node)];
|
||||
const name_id = ty.data.record.name;
|
||||
break :blk c.mapper.lookup(name_id);
|
||||
},
|
||||
|
||||
.struct_forward_decl,
|
||||
.union_forward_decl,
|
||||
.enum_decl_two,
|
||||
.enum_decl,
|
||||
=> blk: {
|
||||
const ty = node_types[@intFromEnum(node)];
|
||||
const name_id = ty.data.@"enum".name;
|
||||
break :blk c.mapper.lookup(name_id);
|
||||
.enum_forward_decl,
|
||||
=> {
|
||||
const raw_ty = node_types[@intFromEnum(node)];
|
||||
const ty = raw_ty.canonicalize(.standard);
|
||||
const name_id = if (ty.isRecord()) ty.data.record.name else ty.data.@"enum".name;
|
||||
const decl_name = c.mapper.lookup(name_id);
|
||||
const container_prefix = if (ty.is(.@"struct")) "struct" else if (ty.is(.@"union")) "union" else "enum";
|
||||
const prefixed_name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_prefix, decl_name });
|
||||
// `decl_name` and `prefixed_name` are the preferred names for this type.
|
||||
// However, we can name it anything else if necessary, so these are "weak names".
|
||||
try c.weak_global_names.ensureUnusedCapacity(c.gpa, 2);
|
||||
c.weak_global_names.putAssumeCapacity(decl_name, {});
|
||||
c.weak_global_names.putAssumeCapacity(prefixed_name, {});
|
||||
},
|
||||
|
||||
.fn_proto,
|
||||
@ -215,80 +218,256 @@ fn prepopulateGlobalNameTable(c: *Context) !void {
|
||||
.inline_fn_def,
|
||||
.inline_static_fn_def,
|
||||
.@"var",
|
||||
.extern_var,
|
||||
.static_var,
|
||||
.threadlocal_var,
|
||||
.threadlocal_static_var,
|
||||
.extern_var,
|
||||
.threadlocal_extern_var,
|
||||
=> c.tree.tokSlice(data.decl.name),
|
||||
else => unreachable,
|
||||
};
|
||||
try c.global_names.put(c.gpa, decl_name, {});
|
||||
}
|
||||
}
|
||||
|
||||
fn transTopLevelDecls(c: *Context) !void {
|
||||
const node_tags = c.tree.nodes.items(.tag);
|
||||
const node_data = c.tree.nodes.items(.data);
|
||||
for (c.tree.root_decls) |node| {
|
||||
const data = node_data[@intFromEnum(node)];
|
||||
switch (node_tags[@intFromEnum(node)]) {
|
||||
.typedef => {
|
||||
try transTypeDef(c, &c.global_scope.base, node);
|
||||
},
|
||||
|
||||
.static_assert,
|
||||
.struct_decl_two,
|
||||
.union_decl_two,
|
||||
.struct_decl,
|
||||
.union_decl,
|
||||
=> {
|
||||
try transRecordDecl(c, &c.global_scope.base, node);
|
||||
},
|
||||
|
||||
.enum_decl_two => {
|
||||
var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs };
|
||||
var field_count: u8 = 0;
|
||||
if (fields[0] != .none) field_count += 1;
|
||||
if (fields[1] != .none) field_count += 1;
|
||||
try transEnumDecl(c, &c.global_scope.base, node, fields[0..field_count]);
|
||||
},
|
||||
.enum_decl => {
|
||||
const fields = c.tree.data[data.range.start..data.range.end];
|
||||
try transEnumDecl(c, &c.global_scope.base, node, fields);
|
||||
},
|
||||
|
||||
.fn_proto,
|
||||
.static_fn_proto,
|
||||
.inline_fn_proto,
|
||||
.inline_static_fn_proto,
|
||||
.fn_def,
|
||||
.static_fn_def,
|
||||
.inline_fn_def,
|
||||
.inline_static_fn_def,
|
||||
=> {
|
||||
try transFnDecl(c, node);
|
||||
},
|
||||
|
||||
.@"var",
|
||||
.static_var,
|
||||
.threadlocal_var,
|
||||
.threadlocal_static_var,
|
||||
.extern_var,
|
||||
.threadlocal_extern_var,
|
||||
=> {
|
||||
try transVarDecl(c, node, null);
|
||||
const decl_name = c.tree.tokSlice(data.decl.name);
|
||||
try c.global_names.put(c.gpa, decl_name, {});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transTopLevelDecls(c: *Context) !void {
|
||||
for (c.tree.root_decls) |node| {
|
||||
try transDecl(c, &c.global_scope.base, node);
|
||||
}
|
||||
}
|
||||
|
||||
fn transDecl(c: *Context, scope: *Scope, decl: NodeIndex) !void {
|
||||
const node_tags = c.tree.nodes.items(.tag);
|
||||
const node_data = c.tree.nodes.items(.data);
|
||||
const data = node_data[@intFromEnum(decl)];
|
||||
switch (node_tags[@intFromEnum(decl)]) {
|
||||
.typedef => {
|
||||
try transTypeDef(c, scope, decl);
|
||||
},
|
||||
|
||||
.struct_decl_two,
|
||||
.union_decl_two,
|
||||
=> {
|
||||
var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs };
|
||||
var field_count: u2 = 0;
|
||||
if (fields[0] != .none) field_count += 1;
|
||||
if (fields[1] != .none) field_count += 1;
|
||||
try transRecordDecl(c, scope, decl, fields[0..field_count]);
|
||||
},
|
||||
.struct_decl,
|
||||
.union_decl,
|
||||
=> {
|
||||
const fields = c.tree.data[data.range.start..data.range.end];
|
||||
try transRecordDecl(c, scope, decl, fields);
|
||||
},
|
||||
|
||||
.enum_decl_two => {
|
||||
var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs };
|
||||
var field_count: u8 = 0;
|
||||
if (fields[0] != .none) field_count += 1;
|
||||
if (fields[1] != .none) field_count += 1;
|
||||
try transEnumDecl(c, scope, decl, fields[0..field_count]);
|
||||
},
|
||||
.enum_decl => {
|
||||
const fields = c.tree.data[data.range.start..data.range.end];
|
||||
try transEnumDecl(c, scope, decl, fields);
|
||||
},
|
||||
|
||||
.enum_field_decl,
|
||||
.record_field_decl,
|
||||
.indirect_record_field_decl,
|
||||
.struct_forward_decl,
|
||||
.union_forward_decl,
|
||||
.enum_forward_decl,
|
||||
=> return,
|
||||
|
||||
.fn_proto,
|
||||
.static_fn_proto,
|
||||
.inline_fn_proto,
|
||||
.inline_static_fn_proto,
|
||||
.fn_def,
|
||||
.static_fn_def,
|
||||
.inline_fn_def,
|
||||
.inline_static_fn_def,
|
||||
=> {
|
||||
try transFnDecl(c, decl);
|
||||
},
|
||||
|
||||
.@"var",
|
||||
.extern_var,
|
||||
.static_var,
|
||||
.threadlocal_var,
|
||||
.threadlocal_extern_var,
|
||||
.threadlocal_static_var,
|
||||
=> {
|
||||
try transVarDecl(c, decl, null);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void {
|
||||
@panic("TODO");
|
||||
}
|
||||
fn transRecordDecl(_: *Context, _: *Scope, _: NodeIndex) Error!void {
|
||||
@panic("TODO");
|
||||
|
||||
fn mangleWeakGlobalName(c: *Context, want_name: []const u8) ![]const u8 {
|
||||
var cur_name = want_name;
|
||||
|
||||
if (!c.weak_global_names.contains(want_name)) {
|
||||
// This type wasn't noticed by the name detection pass, so nothing has been treating this as
|
||||
// a weak global name. We must mangle it to avoid conflicts with locals.
|
||||
cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() });
|
||||
}
|
||||
|
||||
while (c.global_names.contains(cur_name)) {
|
||||
cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() });
|
||||
}
|
||||
return cur_name;
|
||||
}
|
||||
|
||||
fn transRecordDecl(c: *Context, scope: *Scope, record_node: NodeIndex, field_nodes: []const NodeIndex) Error!void {
|
||||
const node_types = c.tree.nodes.items(.ty);
|
||||
const raw_record_ty = node_types[@intFromEnum(record_node)];
|
||||
const record_decl = raw_record_ty.getRecord().?;
|
||||
if (c.decl_table.get(@intFromPtr(record_decl))) |_|
|
||||
return; // Avoid processing this decl twice
|
||||
const toplevel = scope.id == .root;
|
||||
const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
|
||||
|
||||
const container_kind: ZigTag = if (raw_record_ty.is(.@"union")) .@"union" else .@"struct";
|
||||
const container_kind_name: []const u8 = @tagName(container_kind);
|
||||
|
||||
var is_unnamed = false;
|
||||
var bare_name: []const u8 = c.mapper.lookup(record_decl.name);
|
||||
var name = bare_name;
|
||||
|
||||
if (c.unnamed_typedefs.get(@intFromPtr(record_decl))) |typedef_name| {
|
||||
bare_name = typedef_name;
|
||||
name = typedef_name;
|
||||
} else {
|
||||
if (raw_record_ty.isAnonymousRecord(c.comp)) {
|
||||
bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
|
||||
is_unnamed = true;
|
||||
}
|
||||
name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
|
||||
if (toplevel and !is_unnamed) {
|
||||
name = try mangleWeakGlobalName(c, name);
|
||||
}
|
||||
}
|
||||
if (!toplevel) name = try bs.makeMangledName(c, name);
|
||||
try c.decl_table.putNoClobber(c.gpa, @intFromPtr(record_decl), name);
|
||||
|
||||
const is_pub = toplevel and !is_unnamed;
|
||||
const init_node = blk: {
|
||||
var fields = try std.ArrayList(ast.Payload.Record.Field).initCapacity(c.gpa, record_decl.fields.len);
|
||||
defer fields.deinit();
|
||||
|
||||
// TODO: Add support for flexible array field functions
|
||||
var functions = std.ArrayList(ZigNode).init(c.gpa);
|
||||
defer functions.deinit();
|
||||
|
||||
var unnamed_field_count: u32 = 0;
|
||||
|
||||
// If a record doesn't have any attributes that would affect the alignment and
|
||||
// layout, then we can just use a simple `extern` type. If it does have attributes,
|
||||
// then we need to inspect the layout and assign an `align` value for each field.
|
||||
const has_alignment_attributes = record_decl.field_attributes != null or
|
||||
raw_record_ty.hasAttribute(.@"packed") or
|
||||
raw_record_ty.hasAttribute(.aligned);
|
||||
const head_field_alignment: ?c_uint = headFieldAlignment(record_decl);
|
||||
|
||||
// Iterate over field nodes so that we translate any type decls included in this record decl.
|
||||
// TODO: Move this logic into `fn transType()` instead of handling decl translation here.
|
||||
for (field_nodes) |field_node| {
|
||||
const field_raw_ty = node_types[@intFromEnum(field_node)];
|
||||
if (field_raw_ty.isEnumOrRecord()) try transDecl(c, scope, field_node);
|
||||
}
|
||||
|
||||
for (record_decl.fields, 0..) |field, field_index| {
|
||||
const field_loc = field.name_tok;
|
||||
|
||||
// Demote record to opaque if it contains a bitfield
|
||||
if (!field.isRegularField()) {
|
||||
try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl), {});
|
||||
try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
|
||||
break :blk ZigTag.opaque_literal.init();
|
||||
}
|
||||
|
||||
var field_name = c.mapper.lookup(field.name);
|
||||
if (!field.isNamed()) {
|
||||
field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count});
|
||||
unnamed_field_count += 1;
|
||||
}
|
||||
const field_type = transType(c, scope, field.ty, .preserve_quals, field_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl), {});
|
||||
try warn(c, scope, 0, "{s} demoted to opaque type - unable to translate type of field {s}", .{
|
||||
container_kind_name,
|
||||
field_name,
|
||||
});
|
||||
break :blk ZigTag.opaque_literal.init();
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const field_alignment = if (has_alignment_attributes)
|
||||
alignmentForField(record_decl, head_field_alignment, field_index)
|
||||
else
|
||||
null;
|
||||
|
||||
// C99 introduced designated initializers for structs. Omitted fields are implicitly
|
||||
// initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
|
||||
// values for translated struct fields permits Zig code to comfortably use such an API.
|
||||
const default_value = if (container_kind == .@"struct")
|
||||
try ZigTag.std_mem_zeroes.create(c.arena, field_type)
|
||||
else
|
||||
null;
|
||||
|
||||
fields.appendAssumeCapacity(.{
|
||||
.name = field_name,
|
||||
.type = field_type,
|
||||
.alignment = field_alignment,
|
||||
.default_value = default_value,
|
||||
});
|
||||
}
|
||||
|
||||
const record_payload = try c.arena.create(ast.Payload.Record);
|
||||
record_payload.* = .{
|
||||
.base = .{ .tag = container_kind },
|
||||
.data = .{
|
||||
.layout = .@"extern",
|
||||
.fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items),
|
||||
.functions = try c.arena.dupe(ZigNode, functions.items),
|
||||
.variables = &.{},
|
||||
},
|
||||
};
|
||||
break :blk ZigNode.initPayload(&record_payload.base);
|
||||
};
|
||||
|
||||
const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = ([2]ZigTag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] },
|
||||
.data = .{
|
||||
.name = name,
|
||||
.init = init_node,
|
||||
},
|
||||
};
|
||||
const node = ZigNode.initPayload(&payload.base);
|
||||
if (toplevel) {
|
||||
try addTopLevelDecl(c, name, node);
|
||||
// Only add the alias if the name is available *and* it was caught by
|
||||
// name detection. Don't bother performing a weak mangle, since a
|
||||
// mangled name is of no real use here.
|
||||
if (!is_unnamed and !c.global_names.contains(bare_name) and c.weak_global_names.contains(bare_name))
|
||||
try c.alias_list.append(.{ .alias = bare_name, .name = name });
|
||||
} else {
|
||||
try scope.appendNode(node);
|
||||
if (node.tag() != .pub_var_simple) {
|
||||
try bs.discardVariable(c, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transFnDecl(c: *Context, fn_decl: NodeIndex) Error!void {
|
||||
@ -419,7 +598,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:
|
||||
enum_val_name = try bs.makeMangledName(c, enum_val_name);
|
||||
}
|
||||
|
||||
const enum_const_type_node: ?ZigNode = transType(c, scope, field.ty, field.name_tok) catch |err| switch (err) {
|
||||
const enum_const_type_node: ?ZigNode = transType(c, scope, field.ty, .standard, field.name_tok) catch |err| switch (err) {
|
||||
error.UnsupportedType => null,
|
||||
else => |e| return e,
|
||||
};
|
||||
@ -439,7 +618,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:
|
||||
}
|
||||
}
|
||||
|
||||
break :blk transType(c, scope, ty.data.@"enum".tag_ty, 0) catch |err| switch (err) {
|
||||
break :blk transType(c, scope, ty.data.@"enum".tag_ty, .standard, 0) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
return failDecl(c, 0, name, "unable to translate enum integer type", .{});
|
||||
},
|
||||
@ -472,8 +651,8 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:
|
||||
}
|
||||
}
|
||||
|
||||
fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) TypeError!ZigNode {
|
||||
const ty = raw_ty.canonicalize(.standard);
|
||||
fn transType(c: *Context, scope: *Scope, raw_ty: Type, qual_handling: Type.QualHandling, source_loc: TokenIndex) TypeError!ZigNode {
|
||||
const ty = raw_ty.canonicalize(qual_handling);
|
||||
switch (ty.specifier) {
|
||||
.void => return ZigTag.type.create(c.arena, "anyopaque"),
|
||||
.bool => return ZigTag.type.create(c.arena, "bool"),
|
||||
@ -496,16 +675,152 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) T
|
||||
.long_double => return ZigTag.type.create(c.arena, "c_longdouble"),
|
||||
.float80 => return ZigTag.type.create(c.arena, "f80"),
|
||||
.float128 => return ZigTag.type.create(c.arena, "f128"),
|
||||
.@"enum" => @panic("TODO"),
|
||||
.pointer,
|
||||
.unspecified_variable_len_array,
|
||||
.array,
|
||||
.static_array,
|
||||
.incomplete_array,
|
||||
=> @panic("TODO"),
|
||||
.func,
|
||||
.var_args_func,
|
||||
.old_style_func,
|
||||
=> return transFnType(c, scope, raw_ty, ty, source_loc, .{}),
|
||||
=> return transFnType(c, scope, ty, ty, source_loc, .{}),
|
||||
.@"struct",
|
||||
.@"union",
|
||||
=> {
|
||||
var trans_scope = scope;
|
||||
if (ty.isAnonymousRecord(c.comp)) {
|
||||
const record_decl = ty.data.record;
|
||||
const name_id = c.mapper.lookup(record_decl.name);
|
||||
if (c.weak_global_names.contains(name_id)) trans_scope = &c.global_scope.base;
|
||||
}
|
||||
const name = c.decl_table.get(@intFromPtr(ty.data.record)).?;
|
||||
return ZigTag.identifier.create(c.arena, name);
|
||||
},
|
||||
.attributed,
|
||||
.typeof_type,
|
||||
.typeof_expr,
|
||||
=> unreachable,
|
||||
else => return error.UnsupportedType,
|
||||
}
|
||||
}
|
||||
|
||||
fn zigAlignment(bit_alignment: u29) u32 {
|
||||
return bit_alignment / 8;
|
||||
/// Look ahead through the fields of the record to determine what the alignment of the record
|
||||
/// would be without any align/packed/etc. attributes. This helps us determine whether or not
|
||||
/// the fields with 0 offset need an `align` qualifier. Strictly speaking, we could just
|
||||
/// pedantically assign those fields the same alignment as the parent's pointer alignment,
|
||||
/// but this helps the generated code to be a little less verbose.
|
||||
fn headFieldAlignment(record_decl: *const Type.Record) ?c_uint {
|
||||
const bits_per_byte = 8;
|
||||
const parent_ptr_alignment_bits = record_decl.type_layout.pointer_alignment_bits;
|
||||
const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
|
||||
var max_field_alignment_bits: u64 = 0;
|
||||
for (record_decl.fields) |field| {
|
||||
if (field.ty.getRecord()) |field_record_decl| {
|
||||
const child_record_alignment = field_record_decl.type_layout.field_alignment_bits;
|
||||
if (child_record_alignment > max_field_alignment_bits)
|
||||
max_field_alignment_bits = child_record_alignment;
|
||||
} else {
|
||||
const field_size = field.layout.size_bits;
|
||||
if (field_size > max_field_alignment_bits)
|
||||
max_field_alignment_bits = field_size;
|
||||
}
|
||||
}
|
||||
if (max_field_alignment_bits != parent_ptr_alignment_bits) {
|
||||
return parent_ptr_alignment;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// This function returns a ?c_uint to match Clang's behaviour of using c_uint.
|
||||
/// This can be changed to a u29 after the Clang frontend for translate-c is removed.
|
||||
fn alignmentForField(
|
||||
record_decl: *const Type.Record,
|
||||
head_field_alignment: ?c_uint,
|
||||
field_index: usize,
|
||||
) ?c_uint {
|
||||
const fields = record_decl.fields;
|
||||
assert(fields.len != 0);
|
||||
const field = fields[field_index];
|
||||
|
||||
const bits_per_byte = 8;
|
||||
const parent_ptr_alignment_bits = record_decl.type_layout.pointer_alignment_bits;
|
||||
const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
|
||||
|
||||
// bitfields aren't supported yet. Until support is added, records with bitfields
|
||||
// should be demoted to opaque, and this function shouldn't be called for them.
|
||||
if (!field.isRegularField()) {
|
||||
@panic("TODO: add bitfield support for records");
|
||||
}
|
||||
|
||||
const field_offset_bits: u64 = field.layout.offset_bits;
|
||||
const field_size_bits: u64 = field.layout.size_bits;
|
||||
|
||||
// Fields with zero width always have an alignment of 1
|
||||
if (field_size_bits == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Fields with 0 offset inherit the parent's pointer alignment.
|
||||
if (field_offset_bits == 0) {
|
||||
return head_field_alignment;
|
||||
}
|
||||
|
||||
// Records have a natural alignment when used as a field, and their size is
|
||||
// a multiple of this alignment value. For all other types, the natural alignment
|
||||
// is their size.
|
||||
const field_natural_alignment_bits: u64 = if (field.ty.getRecord()) |record| record.type_layout.field_alignment_bits else field_size_bits;
|
||||
const rem_bits = field_offset_bits % field_natural_alignment_bits;
|
||||
|
||||
// If there's a remainder, then the alignment is smaller than the field's
|
||||
// natural alignment
|
||||
if (rem_bits > 0) {
|
||||
const rem_alignment = rem_bits / bits_per_byte;
|
||||
if (rem_alignment > 0 and std.math.isPowerOfTwo(rem_alignment)) {
|
||||
const actual_alignment = @min(rem_alignment, parent_ptr_alignment);
|
||||
return @as(c_uint, @truncate(actual_alignment));
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// A field may have an offset which positions it to be naturally aligned, but the
|
||||
// parent's pointer alignment determines if this is actually true, so we take the minimum
|
||||
// value.
|
||||
// For example, a float field (4 bytes wide) with a 4 byte offset is positioned to have natural
|
||||
// alignment, but if the parent pointer alignment is 2, then the actual alignment of the
|
||||
// float is 2.
|
||||
const field_natural_alignment: u64 = field_natural_alignment_bits / bits_per_byte;
|
||||
const offset_alignment = field_offset_bits / bits_per_byte;
|
||||
const possible_alignment = @min(parent_ptr_alignment, offset_alignment);
|
||||
if (possible_alignment == field_natural_alignment) {
|
||||
return null;
|
||||
} else if (possible_alignment < field_natural_alignment) {
|
||||
if (std.math.isPowerOfTwo(possible_alignment)) {
|
||||
return possible_alignment;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else { // possible_alignment > field_natural_alignment
|
||||
// Here, the field is positioned be at a higher alignment than it's natural alignment. This means we
|
||||
// need to determine whether it's a specified alignment. We can determine that from the padding preceding
|
||||
// the field.
|
||||
const padding_from_prev_field: u64 = blk: {
|
||||
if (field_offset_bits != 0) {
|
||||
const previous_field = fields[field_index - 1];
|
||||
break :blk (field_offset_bits - previous_field.layout.offset_bits) - previous_field.layout.size_bits;
|
||||
} else {
|
||||
break :blk 0;
|
||||
}
|
||||
};
|
||||
if (padding_from_prev_field < field_natural_alignment_bits) {
|
||||
return null;
|
||||
} else {
|
||||
return possible_alignment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FnProtoContext = struct {
|
||||
@ -536,7 +851,7 @@ fn transFnType(
|
||||
else
|
||||
c.mapper.lookup(param_info.name);
|
||||
|
||||
const type_node = try transType(c, scope, param_ty, param_info.name_tok);
|
||||
const type_node = try transType(c, scope, param_ty, .standard, param_info.name_tok);
|
||||
param_node.* = .{
|
||||
.is_noalias = is_noalias,
|
||||
.name = param_name,
|
||||
@ -551,7 +866,7 @@ fn transFnType(
|
||||
break :blk null;
|
||||
};
|
||||
|
||||
const alignment = if (raw_ty.requestedAlignment(c.comp)) |alignment| zigAlignment(alignment) else null;
|
||||
const alignment: ?c_uint = raw_ty.requestedAlignment(c.comp) orelse null;
|
||||
|
||||
const explicit_callconv = null;
|
||||
// const explicit_callconv = if ((ctx.is_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .C) null else ctx.cc;
|
||||
@ -565,7 +880,7 @@ fn transFnType(
|
||||
// convert primitive anyopaque to actual void (only for return type)
|
||||
break :blk ZigTag.void_type.init();
|
||||
} else {
|
||||
break :blk transType(c, scope, return_ty, source_loc) catch |err| switch (err) {
|
||||
break :blk transType(c, scope, return_ty, .standard, source_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
try warn(c, scope, source_loc, "unsupported function proto return type", .{});
|
||||
return err;
|
||||
@ -642,7 +957,7 @@ fn transExpr(c: *Context, node: NodeIndex, result_used: ResultUsed) TransError!Z
|
||||
// TODO handle other values
|
||||
const int = try transCreateNodeAPInt(c, val);
|
||||
const as_node = try ZigTag.as.create(c.arena, .{
|
||||
.lhs = try transType(c, undefined, ty, undefined),
|
||||
.lhs = try transType(c, undefined, ty, .standard, undefined),
|
||||
.rhs = int,
|
||||
});
|
||||
return maybeSuppressResult(c, result_used, as_node);
|
||||
|
20
test/cases/translate_c/circular_struct_definitions.c
Normal file
20
test/cases/translate_c/circular_struct_definitions.c
Normal file
@ -0,0 +1,20 @@
|
||||
struct Bar;
|
||||
|
||||
struct Foo {
|
||||
struct Bar *next;
|
||||
};
|
||||
|
||||
struct Bar {
|
||||
struct Foo *next;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
//
|
||||
// pub const struct_Bar = extern struct {
|
||||
// next: [*c]struct_Foo = @import("std").mem.zeroes([*c]struct_Foo),
|
||||
// };
|
||||
//
|
||||
// pub const struct_Foo = extern struct {
|
||||
// next: [*c]struct_Bar = @import("std").mem.zeroes([*c]struct_Bar),
|
||||
// };
|
25
test/cases/translate_c/double_define_struct.c
Normal file
25
test/cases/translate_c/double_define_struct.c
Normal file
@ -0,0 +1,25 @@
|
||||
typedef struct Bar Bar;
|
||||
typedef struct Foo Foo;
|
||||
|
||||
struct Foo {
|
||||
Foo *a;
|
||||
};
|
||||
|
||||
struct Bar {
|
||||
Foo *a;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
//
|
||||
// pub const struct_Foo = extern struct {
|
||||
// a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
|
||||
// };
|
||||
//
|
||||
// pub const Foo = struct_Foo;
|
||||
//
|
||||
// pub const struct_Bar = extern struct {
|
||||
// a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
|
||||
// };
|
||||
//
|
||||
// pub const Bar = struct_Bar;
|
@ -0,0 +1,18 @@
|
||||
unsigned long foo(unsigned long x) {
|
||||
return ((union{unsigned long _x}){x})._x;
|
||||
}
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
//
|
||||
// pub export fn foo(arg_x: c_ulong) c_ulong {
|
||||
// var x = arg_x;
|
||||
// _ = &x;
|
||||
// const union_unnamed_1 = extern union {
|
||||
// _x: c_ulong,
|
||||
// };
|
||||
// _ = &union_unnamed_1;
|
||||
// return (union_unnamed_1{
|
||||
// ._x = x,
|
||||
// })._x;
|
||||
// }
|
@ -0,0 +1,15 @@
|
||||
struct foo {
|
||||
int x;
|
||||
};
|
||||
const char *struct_foo = "hello world";
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
//
|
||||
// pub const struct_foo_1 = extern struct {
|
||||
// x: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
//
|
||||
// pub const foo = struct_foo_1;
|
||||
//
|
||||
// pub export var struct_foo: [*c]const u8 = "hello world";
|
20
test/cases/translate_c/large_packed_struct.c
Normal file
20
test/cases/translate_c/large_packed_struct.c
Normal file
@ -0,0 +1,20 @@
|
||||
struct __attribute__((packed)) bar {
|
||||
short a;
|
||||
float b;
|
||||
double c;
|
||||
short x;
|
||||
float y;
|
||||
double z;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// pub const struct_bar = extern struct {
|
||||
// a: c_short align(1) = @import("std").mem.zeroes(c_short),
|
||||
// b: f32 align(1) = @import("std").mem.zeroes(f32),
|
||||
// c: f64 align(1) = @import("std").mem.zeroes(f64),
|
||||
// x: c_short align(1) = @import("std").mem.zeroes(c_short),
|
||||
// y: f32 align(1) = @import("std").mem.zeroes(f32),
|
||||
// z: f64 align(1) = @import("std").mem.zeroes(f64),
|
||||
// };
|
25
test/cases/translate_c/packed_union_nested_unpacked.c
Normal file
25
test/cases/translate_c/packed_union_nested_unpacked.c
Normal file
@ -0,0 +1,25 @@
|
||||
// NOTE: The nested struct is *not* packed/aligned,
|
||||
// even though the parent struct is
|
||||
// this is consistent with GCC docs
|
||||
union Foo{
|
||||
short x;
|
||||
double y;
|
||||
struct {
|
||||
int b;
|
||||
} z;
|
||||
} __attribute__((packed));
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// const struct_unnamed_1 = extern struct {
|
||||
// b: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
//
|
||||
// pub const union_Foo = extern union {
|
||||
// x: c_short align(1),
|
||||
// y: f64 align(1),
|
||||
// z: struct_unnamed_1 align(1),
|
||||
// };
|
||||
//
|
||||
// pub const Foo = union_Foo;
|
14
test/cases/translate_c/packed_union_simple.c
Normal file
14
test/cases/translate_c/packed_union_simple.c
Normal file
@ -0,0 +1,14 @@
|
||||
union Foo {
|
||||
short x;
|
||||
double y;
|
||||
} __attribute__((packed));
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// pub const union_Foo = extern union {
|
||||
// x: c_short align(1),
|
||||
// y: f64 align(1),
|
||||
// };
|
||||
//
|
||||
// pub const Foo = union_Foo;
|
@ -0,0 +1,15 @@
|
||||
struct Foo {
|
||||
unsigned int: 1;
|
||||
};
|
||||
struct Bar {
|
||||
struct Foo *foo;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
//
|
||||
// pub const struct_Foo = opaque {};
|
||||
//
|
||||
// pub const struct_Bar = extern struct {
|
||||
// foo: ?*struct_Foo = @import("std").mem.zeroes(?*struct_Foo),
|
||||
// };
|
25
test/cases/translate_c/qualified_struct_and_enum.c
Normal file
25
test/cases/translate_c/qualified_struct_and_enum.c
Normal file
@ -0,0 +1,25 @@
|
||||
struct Foo {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
enum Bar {
|
||||
BarA,
|
||||
BarB,
|
||||
};
|
||||
void func(struct Foo *a, enum Bar **b);
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
// target=x86_64-linux,x86_64-macos
|
||||
//
|
||||
// pub const struct_Foo = extern struct {
|
||||
// x: c_int = @import("std").mem.zeroes(c_int),
|
||||
// y: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
// pub const BarA: c_int = 0;
|
||||
// pub const BarB: c_int = 1;
|
||||
// pub const enum_Bar = c_uint;
|
||||
// pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
|
||||
//
|
||||
// pub const Foo = struct_Foo;
|
||||
// pub const Bar = enum_Bar;
|
25
test/cases/translate_c/qualified_struct_and_enum_msvc.c
Normal file
25
test/cases/translate_c/qualified_struct_and_enum_msvc.c
Normal file
@ -0,0 +1,25 @@
|
||||
struct Foo {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
enum Bar {
|
||||
BarA,
|
||||
BarB,
|
||||
};
|
||||
void func(struct Foo *a, enum Bar **b);
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
// target=x86_64-windows-msvc
|
||||
//
|
||||
// pub const struct_Foo = extern struct {
|
||||
// x: c_int = @import("std").mem.zeroes(c_int),
|
||||
// y: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
// pub const BarA: c_int = 0;
|
||||
// pub const BarB: c_int = 1;
|
||||
// pub const enum_Bar = c_int;
|
||||
// pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
|
||||
//
|
||||
// pub const Foo = struct_Foo;
|
||||
// pub const Bar = enum_Bar;
|
49
test/cases/translate_c/scoped_record.c
Normal file
49
test/cases/translate_c/scoped_record.c
Normal file
@ -0,0 +1,49 @@
|
||||
void foo() {
|
||||
struct Foo {
|
||||
int A;
|
||||
int B;
|
||||
int C;
|
||||
};
|
||||
struct Foo a = {0};
|
||||
{
|
||||
struct Foo {
|
||||
int A;
|
||||
int B;
|
||||
int C;
|
||||
};
|
||||
struct Foo a = {0};
|
||||
}
|
||||
}
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
//
|
||||
// pub export fn foo() void {
|
||||
// const struct_Foo = extern struct {
|
||||
// A: c_int = @import("std").mem.zeroes(c_int),
|
||||
// B: c_int = @import("std").mem.zeroes(c_int),
|
||||
// C: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
// _ = &struct_Foo;
|
||||
// var a: struct_Foo = struct_Foo{
|
||||
// .A = @as(c_int, 0),
|
||||
// .B = 0,
|
||||
// .C = 0,
|
||||
// };
|
||||
// _ = &a;
|
||||
// {
|
||||
// const struct_Foo_1 = extern struct {
|
||||
// A: c_int = @import("std").mem.zeroes(c_int),
|
||||
// B: c_int = @import("std").mem.zeroes(c_int),
|
||||
// C: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
// _ = &struct_Foo_1;
|
||||
// var a_2: struct_Foo_1 = struct_Foo_1{
|
||||
// .A = @as(c_int, 0),
|
||||
// .B = 0,
|
||||
// .C = 0,
|
||||
// };
|
||||
// _ = &a_2;
|
||||
// }
|
||||
// }
|
||||
|
12
test/cases/translate_c/simple_struct.c
Normal file
12
test/cases/translate_c/simple_struct.c
Normal file
@ -0,0 +1,12 @@
|
||||
struct Foo {
|
||||
int x;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// const struct_Foo = extern struct {
|
||||
// x: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
//
|
||||
// pub const Foo = struct_Foo;
|
12
test/cases/translate_c/simple_union.c
Normal file
12
test/cases/translate_c/simple_union.c
Normal file
@ -0,0 +1,12 @@
|
||||
union Foo {
|
||||
int x;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// pub const union_Foo = extern union {
|
||||
// x: c_int,
|
||||
// };
|
||||
//
|
||||
// pub const Foo = union_Foo;
|
24
test/cases/translate_c/struct_in_struct_init_to_zero.c
Normal file
24
test/cases/translate_c/struct_in_struct_init_to_zero.c
Normal file
@ -0,0 +1,24 @@
|
||||
struct Foo {
|
||||
int a;
|
||||
struct Bar {
|
||||
int a;
|
||||
} b;
|
||||
} a = {};
|
||||
#define PTR void *
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
//
|
||||
// pub const struct_Bar_1 = extern struct {
|
||||
// a: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
// pub const struct_Foo = extern struct {
|
||||
// a: c_int = @import("std").mem.zeroes(c_int),
|
||||
// b: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
|
||||
// };
|
||||
// pub export var a: struct_Foo = struct_Foo{
|
||||
// .a = 0,
|
||||
// .b = @import("std").mem.zeroes(struct_Bar_1),
|
||||
// };
|
||||
//
|
||||
// pub const PTR = ?*anyopaque;
|
10
test/cases/translate_c/struct_with_aligned_fields.c
Normal file
10
test/cases/translate_c/struct_with_aligned_fields.c
Normal file
@ -0,0 +1,10 @@
|
||||
struct foo {
|
||||
__attribute__((aligned(4))) short bar;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// pub const struct_foo = extern struct {
|
||||
// bar: c_short align(4) = @import("std").mem.zeroes(c_short),
|
||||
// };
|
35
test/cases/translate_c/struct_with_invalid_field_alignment.c
Normal file
35
test/cases/translate_c/struct_with_invalid_field_alignment.c
Normal file
@ -0,0 +1,35 @@
|
||||
// The aligned attribute cannot decrease the alignment of a field. The packed attribute is required
|
||||
// for decreasing the alignment. gcc and clang will compile these structs without error
|
||||
// (and possibly without warning), but checking the alignment will reveal a different value than
|
||||
// what was requested. This is consistent with the gcc documentation on type attributes.
|
||||
//
|
||||
// This test is currently broken for the clang frontend. See issue #19307.
|
||||
|
||||
struct foo {
|
||||
__attribute__((aligned(1)))int x;
|
||||
};
|
||||
|
||||
struct bar {
|
||||
__attribute__((aligned(2)))float y;
|
||||
};
|
||||
|
||||
struct baz {
|
||||
__attribute__((aligned(4)))double z;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro
|
||||
// target=x86_64-linux
|
||||
//
|
||||
// pub const struct_foo = extern struct {
|
||||
// x: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
//
|
||||
// pub const struct_bar = extern struct {
|
||||
// y: f32 = @import("std").mem.zeroes(f32),
|
||||
// };
|
||||
//
|
||||
// pub const struct_baz = extern struct {
|
||||
// z: f64 = @import("std").mem.zeroes(f64),
|
||||
// };
|
||||
//
|
19
test/cases/translate_c/type_referenced_struct.c
Normal file
19
test/cases/translate_c/type_referenced_struct.c
Normal file
@ -0,0 +1,19 @@
|
||||
// When clang uses the <arch>-windows-none, triple it behaves as MSVC and
|
||||
// interprets the inner `struct Bar` as an anonymous structure
|
||||
struct Foo {
|
||||
struct Bar{
|
||||
int b;
|
||||
};
|
||||
struct Bar c;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
// target=x86_64-linux-gnu
|
||||
//
|
||||
// pub const struct_Bar_1 = extern struct {
|
||||
// b: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
// pub const struct_Foo = extern struct {
|
||||
// c: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
|
||||
// };
|
22
test/cases/translate_c/union_initializer.c
Normal file
22
test/cases/translate_c/union_initializer.c
Normal file
@ -0,0 +1,22 @@
|
||||
union { int x; char c[4]; }
|
||||
ua = {1},
|
||||
ub = {.c={'a','b','b','a'}};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=clang
|
||||
//
|
||||
// const union_unnamed_1 = extern union {
|
||||
// x: c_int,
|
||||
// c: [4]u8,
|
||||
// };
|
||||
// pub export var ua: union_unnamed_1 = union_unnamed_1{
|
||||
// .x = @as(c_int, 1),
|
||||
// };
|
||||
// pub export var ub: union_unnamed_1 = union_unnamed_1{
|
||||
// .c = [4]u8{
|
||||
// 'a',
|
||||
// 'b',
|
||||
// 'b',
|
||||
// 'a',
|
||||
// },
|
||||
// };
|
37
test/cases/translate_c/union_struct_forward_decl.c
Normal file
37
test/cases/translate_c/union_struct_forward_decl.c
Normal file
@ -0,0 +1,37 @@
|
||||
struct A;
|
||||
union B;
|
||||
enum C;
|
||||
|
||||
struct A {
|
||||
short x;
|
||||
double y;
|
||||
};
|
||||
|
||||
union B {
|
||||
short x;
|
||||
double y;
|
||||
};
|
||||
|
||||
struct Foo {
|
||||
struct A a;
|
||||
union B b;
|
||||
};
|
||||
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// pub const struct_A = extern struct {
|
||||
// x: c_short = @import("std").mem.zeroes(c_short),
|
||||
// y: f64 = @import("std").mem.zeroes(f64),
|
||||
// };
|
||||
//
|
||||
// pub const union_B = extern union {
|
||||
// x: c_short,
|
||||
// y: f64,
|
||||
// };
|
||||
//
|
||||
// pub const struct_Foo = extern struct {
|
||||
// a: struct_A = @import("std").mem.zeroes(struct_A),
|
||||
// b: union_B = @import("std").mem.zeroes(union_B),
|
||||
// };
|
@ -0,0 +1,22 @@
|
||||
struct a {
|
||||
struct { int x; };
|
||||
};
|
||||
struct b {
|
||||
struct { int y; };
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// const struct_unnamed_1 = extern struct {
|
||||
// x: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
// pub const struct_a = extern struct {
|
||||
// unnamed_0: struct_unnamed_1 = @import("std").mem.zeroes(struct_unnamed_1),
|
||||
// };
|
||||
// const struct_unnamed_2 = extern struct {
|
||||
// y: c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
// pub const struct_b = extern struct {
|
||||
// unnamed_0: struct_unnamed_2 = @import("std").mem.zeroes(struct_unnamed_2),
|
||||
// };
|
18
test/cases/translate_c/zero_width_field_alignment.c
Normal file
18
test/cases/translate_c/zero_width_field_alignment.c
Normal file
@ -0,0 +1,18 @@
|
||||
struct __attribute__((packed)) foo {
|
||||
int x;
|
||||
struct {};
|
||||
float y;
|
||||
union {};
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro
|
||||
//
|
||||
// const struct_unnamed_1 = extern struct {};
|
||||
// const union_unnamed_2 = extern union {};
|
||||
// pub const struct_foo = extern struct {
|
||||
// x: c_int align(1) = @import("std").mem.zeroes(c_int),
|
||||
// unnamed_0: struct_unnamed_1 align(1) = @import("std").mem.zeroes(struct_unnamed_1),
|
||||
// y: f32 align(1) = @import("std").mem.zeroes(f32),
|
||||
// unnamed_1: union_unnamed_2 align(1) = @import("std").mem.zeroes(union_unnamed_2),
|
||||
// };
|
12
test/cases/translate_c/zig_keywords_in_c_code.c
Normal file
12
test/cases/translate_c/zig_keywords_in_c_code.c
Normal file
@ -0,0 +1,12 @@
|
||||
struct comptime {
|
||||
int defer;
|
||||
};
|
||||
|
||||
// translate-c
|
||||
// c_frontend=aro,clang
|
||||
//
|
||||
// pub const struct_comptime = extern struct {
|
||||
// @"defer": c_int = @import("std").mem.zeroes(c_int),
|
||||
// };
|
||||
//
|
||||
// pub const @"comptime" = struct_comptime;
|
@ -74,24 +74,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub extern fn main() c_int;
|
||||
});
|
||||
|
||||
cases.add("field access is grouped if necessary",
|
||||
\\unsigned long foo(unsigned long x) {
|
||||
\\ return ((union{unsigned long _x}){x})._x;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo(arg_x: c_ulong) c_ulong {
|
||||
\\ var x = arg_x;
|
||||
\\ _ = &x;
|
||||
\\ const union_unnamed_1 = extern union {
|
||||
\\ _x: c_ulong,
|
||||
\\ };
|
||||
\\ _ = &union_unnamed_1;
|
||||
\\ return (union_unnamed_1{
|
||||
\\ ._x = x,
|
||||
\\ })._x;
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("unnamed child types of typedef receive typedef's name",
|
||||
\\typedef enum {
|
||||
\\ FooA,
|
||||
@ -149,78 +131,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("struct in struct init to zero",
|
||||
\\struct Foo {
|
||||
\\ int a;
|
||||
\\ struct Bar {
|
||||
\\ int a;
|
||||
\\ } b;
|
||||
\\} a = {};
|
||||
\\#define PTR void *
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Bar_1 = extern struct {
|
||||
\\ a: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\};
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ a: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ b: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
|
||||
\\};
|
||||
\\pub export var a: struct_Foo = struct_Foo{
|
||||
\\ .a = 0,
|
||||
\\ .b = @import("std").mem.zeroes(struct_Bar_1),
|
||||
\\};
|
||||
,
|
||||
\\pub const PTR = ?*anyopaque;
|
||||
});
|
||||
|
||||
cases.add("scoped record",
|
||||
\\void foo() {
|
||||
\\ struct Foo {
|
||||
\\ int A;
|
||||
\\ int B;
|
||||
\\ int C;
|
||||
\\ };
|
||||
\\ struct Foo a = {0};
|
||||
\\ {
|
||||
\\ struct Foo {
|
||||
\\ int A;
|
||||
\\ int B;
|
||||
\\ int C;
|
||||
\\ };
|
||||
\\ struct Foo a = {0};
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn foo() void {
|
||||
\\ const struct_Foo = extern struct {
|
||||
\\ A: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ B: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ C: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ };
|
||||
\\ _ = &struct_Foo;
|
||||
\\ var a: struct_Foo = struct_Foo{
|
||||
\\ .A = @as(c_int, 0),
|
||||
\\ .B = 0,
|
||||
\\ .C = 0,
|
||||
\\ };
|
||||
\\ _ = &a;
|
||||
\\ {
|
||||
\\ const struct_Foo_1 = extern struct {
|
||||
\\ A: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ B: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ C: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ };
|
||||
\\ _ = &struct_Foo_1;
|
||||
\\ var a_2: struct_Foo_1 = struct_Foo_1{
|
||||
\\ .A = @as(c_int, 0),
|
||||
\\ .B = 0,
|
||||
\\ .C = 0,
|
||||
\\ };
|
||||
\\ _ = &a_2;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("scoped typedef",
|
||||
\\void foo() {
|
||||
\\ typedef union {
|
||||
@ -466,16 +376,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub const BAR = (@as(c_int, 1) != 0) and (@as(c_int, 2) > @as(c_int, 4));
|
||||
});
|
||||
|
||||
cases.add("struct with aligned fields",
|
||||
\\struct foo {
|
||||
\\ __attribute__((aligned(1))) short bar;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_foo = extern struct {
|
||||
\\ bar: c_short align(1) = @import("std").mem.zeroes(c_short),
|
||||
\\};
|
||||
});
|
||||
|
||||
cases.add("struct with flexible array",
|
||||
\\struct foo { int x; int y[]; };
|
||||
\\struct bar { int x; int y[0]; };
|
||||
@ -661,28 +561,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("union initializer",
|
||||
\\union { int x; char c[4]; }
|
||||
\\ ua = {1},
|
||||
\\ ub = {.c={'a','b','b','a'}};
|
||||
, &[_][]const u8{
|
||||
\\const union_unnamed_1 = extern union {
|
||||
\\ x: c_int,
|
||||
\\ c: [4]u8,
|
||||
\\};
|
||||
\\pub export var ua: union_unnamed_1 = union_unnamed_1{
|
||||
\\ .x = @as(c_int, 1),
|
||||
\\};
|
||||
\\pub export var ub: union_unnamed_1 = union_unnamed_1{
|
||||
\\ .c = [4]u8{
|
||||
\\ 'a',
|
||||
\\ 'b',
|
||||
\\ 'b',
|
||||
\\ 'a',
|
||||
\\ },
|
||||
\\};
|
||||
});
|
||||
|
||||
cases.add("struct initializer - simple",
|
||||
\\typedef struct { int x; } foo;
|
||||
\\struct {double x,y,z;} s0 = {1.2, 1.3};
|
||||
@ -992,21 +870,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\};
|
||||
});
|
||||
|
||||
cases.add("pointer to struct demoted to opaque due to bit fields",
|
||||
\\struct Foo {
|
||||
\\ unsigned int: 1;
|
||||
\\};
|
||||
\\struct Bar {
|
||||
\\ struct Foo *foo;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Foo = opaque {};
|
||||
,
|
||||
\\pub const struct_Bar = extern struct {
|
||||
\\ foo: ?*struct_Foo = @import("std").mem.zeroes(?*struct_Foo),
|
||||
\\};
|
||||
});
|
||||
|
||||
cases.add("macro with left shift",
|
||||
\\#define REDISMODULE_READ (1<<0)
|
||||
, &[_][]const u8{
|
||||
@ -1022,45 +885,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub const FLASH_BANK_SIZE = FLASH_SIZE >> @as(c_int, 1);
|
||||
});
|
||||
|
||||
cases.add("double define struct",
|
||||
\\typedef struct Bar Bar;
|
||||
\\typedef struct Foo Foo;
|
||||
\\
|
||||
\\struct Foo {
|
||||
\\ Foo *a;
|
||||
\\};
|
||||
\\
|
||||
\\struct Bar {
|
||||
\\ Foo *a;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
|
||||
\\};
|
||||
,
|
||||
\\pub const Foo = struct_Foo;
|
||||
,
|
||||
\\pub const struct_Bar = extern struct {
|
||||
\\ a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
|
||||
\\};
|
||||
,
|
||||
\\pub const Bar = struct_Bar;
|
||||
});
|
||||
|
||||
cases.add("simple struct",
|
||||
\\struct Foo {
|
||||
\\ int x;
|
||||
\\ char *y;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\const struct_Foo = extern struct {
|
||||
\\ x: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ y: [*c]u8 = @import("std").mem.zeroes([*c]u8),
|
||||
\\};
|
||||
,
|
||||
\\pub const Foo = struct_Foo;
|
||||
});
|
||||
|
||||
cases.add("self referential struct with function pointer",
|
||||
\\struct Foo {
|
||||
\\ void (*derp)(struct Foo *foo);
|
||||
@ -1099,44 +923,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub const THING2 = THING1;
|
||||
});
|
||||
|
||||
cases.add("circular struct definitions",
|
||||
\\struct Bar;
|
||||
\\
|
||||
\\struct Foo {
|
||||
\\ struct Bar *next;
|
||||
\\};
|
||||
\\
|
||||
\\struct Bar {
|
||||
\\ struct Foo *next;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Bar = extern struct {
|
||||
\\ next: [*c]struct_Foo = @import("std").mem.zeroes([*c]struct_Foo),
|
||||
\\};
|
||||
,
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ next: [*c]struct_Bar = @import("std").mem.zeroes([*c]struct_Bar),
|
||||
\\};
|
||||
});
|
||||
|
||||
cases.add("#define string",
|
||||
\\#define foo "a string"
|
||||
, &[_][]const u8{
|
||||
\\pub const foo = "a string";
|
||||
});
|
||||
|
||||
cases.add("zig keywords in C code",
|
||||
\\struct comptime {
|
||||
\\ int defer;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_comptime = extern struct {
|
||||
\\ @"defer": c_int = @import("std").mem.zeroes(c_int),
|
||||
\\};
|
||||
,
|
||||
\\pub const @"comptime" = struct_comptime;
|
||||
});
|
||||
|
||||
cases.add("macro with parens around negative number",
|
||||
\\#define LUA_GLOBALSINDEX (-10002)
|
||||
, &[_][]const u8{
|
||||
@ -1393,88 +1185,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("simple union",
|
||||
\\union Foo {
|
||||
\\ int x;
|
||||
\\ double y;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const union_Foo = extern union {
|
||||
\\ x: c_int,
|
||||
\\ y: f64,
|
||||
\\};
|
||||
,
|
||||
\\pub const Foo = union_Foo;
|
||||
});
|
||||
|
||||
cases.add("packed union - simple",
|
||||
\\union Foo {
|
||||
\\ char x;
|
||||
\\ double y;
|
||||
\\} __attribute__((packed));
|
||||
, &[_][]const u8{
|
||||
\\pub const union_Foo = extern union {
|
||||
\\ x: u8 align(1),
|
||||
\\ y: f64 align(1),
|
||||
\\};
|
||||
,
|
||||
\\pub const Foo = union_Foo;
|
||||
});
|
||||
|
||||
cases.add("packed union - nested unpacked",
|
||||
\\union Foo{
|
||||
\\ char x;
|
||||
\\ double y;
|
||||
\\ struct {
|
||||
\\ char a;
|
||||
\\ int b;
|
||||
\\ } z;
|
||||
\\} __attribute__((packed));
|
||||
, &[_][]const u8{
|
||||
// NOTE: The nested struct is *not* packed/aligned,
|
||||
// even though the parent struct is
|
||||
// this is consistent with GCC docs
|
||||
\\const struct_unnamed_1 = extern struct {
|
||||
\\ a: u8 = @import("std").mem.zeroes(u8),
|
||||
\\ b: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\};
|
||||
,
|
||||
\\pub const union_Foo = extern union {
|
||||
\\ x: u8 align(1),
|
||||
\\ y: f64 align(1),
|
||||
\\ z: struct_unnamed_1 align(1),
|
||||
\\};
|
||||
,
|
||||
\\pub const Foo = union_Foo;
|
||||
});
|
||||
|
||||
cases.add("packed union - nested packed",
|
||||
\\union Foo{
|
||||
\\ char x;
|
||||
\\ double y;
|
||||
\\ struct {
|
||||
\\ char a;
|
||||
\\ int b;
|
||||
\\ } __attribute__((packed)) z;
|
||||
\\} __attribute__((packed));
|
||||
, &[_][]const u8{
|
||||
// in order for the nested struct to be packed, it must
|
||||
// have an independent packed declaration on
|
||||
// the nested type (see GCC docs for details)
|
||||
\\const struct_unnamed_1 = extern struct {
|
||||
\\ a: u8 align(1) = @import("std").mem.zeroes(u8),
|
||||
\\ b: c_int align(1) = @import("std").mem.zeroes(c_int),
|
||||
\\};
|
||||
,
|
||||
\\pub const union_Foo = extern union {
|
||||
\\ x: u8 align(1),
|
||||
\\ y: f64 align(1),
|
||||
\\ z: struct_unnamed_1 align(1),
|
||||
\\};
|
||||
,
|
||||
\\pub const Foo = union_Foo;
|
||||
});
|
||||
|
||||
cases.add("string literal",
|
||||
\\const char *foo(void) {
|
||||
\\ return "bar";
|
||||
@ -2395,27 +2105,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
if (builtin.os.tag != .windows) {
|
||||
// When clang uses the <arch>-windows-none triple it behaves as MSVC and
|
||||
// interprets the inner `struct Bar` as an anonymous structure
|
||||
cases.add("type referenced struct",
|
||||
\\struct Foo {
|
||||
\\ struct Bar{
|
||||
\\ int b;
|
||||
\\ };
|
||||
\\ struct Bar c;
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Bar_1 = extern struct {
|
||||
\\ b: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\};
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ c: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
|
||||
\\};
|
||||
});
|
||||
}
|
||||
|
||||
cases.add("undefined array global",
|
||||
\\int array[100] = {};
|
||||
, &[_][]const u8{
|
||||
@ -2634,32 +2323,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub const Foo = enum_Foo;
|
||||
});
|
||||
|
||||
cases.add("qualified struct and enum",
|
||||
\\struct Foo {
|
||||
\\ int x;
|
||||
\\ int y;
|
||||
\\};
|
||||
\\enum Bar {
|
||||
\\ BarA,
|
||||
\\ BarB,
|
||||
\\};
|
||||
\\void func(struct Foo *a, enum Bar **b);
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_Foo = extern struct {
|
||||
\\ x: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\ y: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\};
|
||||
\\pub const BarA: c_int = 0;
|
||||
\\pub const BarB: c_int = 1;
|
||||
\\pub const enum_Bar =
|
||||
++ " " ++ default_enum_type ++
|
||||
\\;
|
||||
\\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
|
||||
,
|
||||
\\pub const Foo = struct_Foo;
|
||||
\\pub const Bar = enum_Bar;
|
||||
});
|
||||
|
||||
cases.add("bitwise binary operators, simpler parens",
|
||||
\\int max(int a, int b) {
|
||||
\\ return (a & b) ^ (a | b);
|
||||
@ -3756,24 +3419,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
});
|
||||
}
|
||||
|
||||
cases.add("unnamed fields have predictable names",
|
||||
\\struct a {
|
||||
\\ struct {};
|
||||
\\};
|
||||
\\struct b {
|
||||
\\ struct {};
|
||||
\\};
|
||||
, &[_][]const u8{
|
||||
\\const struct_unnamed_1 = extern struct {};
|
||||
\\pub const struct_a = extern struct {
|
||||
\\ unnamed_0: struct_unnamed_1 = @import("std").mem.zeroes(struct_unnamed_1),
|
||||
\\};
|
||||
\\const struct_unnamed_2 = extern struct {};
|
||||
\\pub const struct_b = extern struct {
|
||||
\\ unnamed_0: struct_unnamed_2 = @import("std").mem.zeroes(struct_unnamed_2),
|
||||
\\};
|
||||
});
|
||||
|
||||
cases.add("integer literal promotion",
|
||||
\\#define GUARANTEED_TO_FIT_1 1024
|
||||
\\#define GUARANTEED_TO_FIT_2 10241024L
|
||||
@ -4283,21 +3928,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub const FOO = @compileError("unable to translate macro: untranslatable usage of arg `x`");
|
||||
});
|
||||
|
||||
cases.add("global struct whose default name conflicts with global is mangled",
|
||||
\\struct foo {
|
||||
\\ int x;
|
||||
\\};
|
||||
\\const char *struct_foo = "hello world";
|
||||
, &[_][]const u8{
|
||||
\\pub const struct_foo_1 = extern struct {
|
||||
\\ x: c_int = @import("std").mem.zeroes(c_int),
|
||||
\\};
|
||||
,
|
||||
\\pub const foo = struct_foo_1;
|
||||
,
|
||||
\\pub export var struct_foo: [*c]const u8 = "hello world";
|
||||
});
|
||||
|
||||
cases.add("unsupport declare statement at the last of a compound statement which belongs to a statement expr",
|
||||
\\void somefunc(void) {
|
||||
\\ int y;
|
||||
|
Loading…
Reference in New Issue
Block a user