Commit c9ad1b5199
Changed files (25)
lib
compiler
aro
aro
test
cases
translate_c
lib/compiler/aro/aro/Type.zig
@@ -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;
lib/compiler/aro_translate_c.zig
@@ -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)");
-
- // 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";
+/// 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 = 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),
+ .threadlocal_static_var,
+ => {
+ const decl_name = c.tree.tokSlice(data.decl.name);
+ try c.global_names.put(c.gpa, decl_name, {});
+ },
else => unreachable,
- };
- try c.global_names.put(c.gpa, decl_name, {});
+ }
}
}
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);
- 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);
- },
+ const data = node_data[@intFromEnum(decl)];
+ switch (node_tags[@intFromEnum(decl)]) {
+ .typedef => {
+ try transTypeDef(c, scope, decl);
+ },
- .static_assert,
- .struct_decl_two,
- .union_decl_two,
- .struct_decl,
- .union_decl,
- => {
- try transRecordDecl(c, &c.global_scope.base, node);
- },
+ .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, &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);
- },
+ .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);
+ },
- .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);
- },
+ .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",
- .static_var,
- .threadlocal_var,
- .threadlocal_static_var,
- .extern_var,
- .threadlocal_extern_var,
- => {
- try transVarDecl(c, node, null);
- },
- else => unreachable,
- }
+ .@"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);
test/cases/translate_c/circular_struct_definitions.c
@@ -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),
+// };
test/cases/translate_c/double_define_struct.c
@@ -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;
test/cases/translate_c/field_access_is_grouped_if_necessary.c
@@ -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;
+// }
test/cases/translate_c/global_struct_whose_default_name_conflicts_with_global_is_mangled.c
@@ -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";
test/cases/translate_c/large_packed_struct.c
@@ -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),
+// };
test/cases/translate_c/packed_union_nested_unpacked.c
@@ -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;
test/cases/translate_c/packed_union_simple.c
@@ -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;
test/cases/translate_c/pointer_to_struct_demoted_opaque_due_to_bit_fields.c
@@ -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),
+// };
test/cases/translate_c/qualified_struct_and_enum.c
@@ -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;
test/cases/translate_c/qualified_struct_and_enum_msvc.c
@@ -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;
test/cases/translate_c/scoped_record.c
@@ -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;
+// }
+// }
+
test/cases/translate_c/simple_struct.c
@@ -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;
test/cases/translate_c/simple_union.c
@@ -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;
test/cases/translate_c/struct_in_struct_init_to_zero.c
@@ -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;
test/cases/translate_c/struct_with_aligned_fields.c
@@ -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),
+// };
test/cases/translate_c/struct_with_invalid_field_alignment.c
@@ -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),
+// };
+//
test/cases/translate_c/type_referenced_struct.c
@@ -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),
+// };
test/cases/translate_c/union_initializer.c
@@ -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',
+// },
+// };
test/cases/translate_c/union_struct_forward_decl.c
@@ -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),
+// };
test/cases/translate_c/unnamed_fields_have_predictable_names.c
@@ -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),
+// };
test/cases/translate_c/zero_width_field_alignment.c
@@ -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),
+// };
test/cases/translate_c/zig_keywords_in_c_code.c
@@ -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;
test/translate_c.zig
@@ -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;