diff --git a/lib/compiler/aro/aro/Type.zig b/lib/compiler/aro/aro/Type.zig index 3588d46e7c..a2ba4707c4 100644 --- a/lib/compiler/aro/aro/Type.zig +++ b/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; diff --git a/lib/compiler/aro_translate_c.zig b/lib/compiler/aro_translate_c.zig index 1c481bf1a7..dcfd9d8657 100644 --- a/lib/compiler/aro_translate_c.zig +++ b/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)"); +/// 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); diff --git a/test/cases/translate_c/circular_struct_definitions.c b/test/cases/translate_c/circular_struct_definitions.c new file mode 100644 index 0000000000..696066b059 --- /dev/null +++ b/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), +// }; diff --git a/test/cases/translate_c/double_define_struct.c b/test/cases/translate_c/double_define_struct.c new file mode 100644 index 0000000000..e608154d7d --- /dev/null +++ b/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; diff --git a/test/cases/translate_c/field_access_is_grouped_if_necessary.c b/test/cases/translate_c/field_access_is_grouped_if_necessary.c new file mode 100644 index 0000000000..dc46ab5dd6 --- /dev/null +++ b/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; +// } diff --git a/test/cases/translate_c/global_struct_whose_default_name_conflicts_with_global_is_mangled.c b/test/cases/translate_c/global_struct_whose_default_name_conflicts_with_global_is_mangled.c new file mode 100644 index 0000000000..fe06fd88fd --- /dev/null +++ b/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"; diff --git a/test/cases/translate_c/large_packed_struct.c b/test/cases/translate_c/large_packed_struct.c new file mode 100644 index 0000000000..b18ccb3cde --- /dev/null +++ b/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), +// }; diff --git a/test/cases/translate_c/packed_union_nested_unpacked.c b/test/cases/translate_c/packed_union_nested_unpacked.c new file mode 100644 index 0000000000..a2e2c2f5ce --- /dev/null +++ b/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; diff --git a/test/cases/translate_c/packed_union_simple.c b/test/cases/translate_c/packed_union_simple.c new file mode 100644 index 0000000000..f69e3cb865 --- /dev/null +++ b/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; diff --git a/test/cases/translate_c/pointer_to_struct_demoted_opaque_due_to_bit_fields.c b/test/cases/translate_c/pointer_to_struct_demoted_opaque_due_to_bit_fields.c new file mode 100644 index 0000000000..3203687497 --- /dev/null +++ b/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), +// }; diff --git a/test/cases/translate_c/qualified_struct_and_enum.c b/test/cases/translate_c/qualified_struct_and_enum.c new file mode 100644 index 0000000000..19c4214c14 --- /dev/null +++ b/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; diff --git a/test/cases/translate_c/qualified_struct_and_enum_msvc.c b/test/cases/translate_c/qualified_struct_and_enum_msvc.c new file mode 100644 index 0000000000..b2bff8def7 --- /dev/null +++ b/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; diff --git a/test/cases/translate_c/scoped_record.c b/test/cases/translate_c/scoped_record.c new file mode 100644 index 0000000000..fe24000b34 --- /dev/null +++ b/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; +// } +// } + diff --git a/test/cases/translate_c/simple_struct.c b/test/cases/translate_c/simple_struct.c new file mode 100644 index 0000000000..d3e2745ee3 --- /dev/null +++ b/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; diff --git a/test/cases/translate_c/simple_union.c b/test/cases/translate_c/simple_union.c new file mode 100644 index 0000000000..29940a2ab2 --- /dev/null +++ b/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; diff --git a/test/cases/translate_c/struct_in_struct_init_to_zero.c b/test/cases/translate_c/struct_in_struct_init_to_zero.c new file mode 100644 index 0000000000..688a2448d1 --- /dev/null +++ b/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; diff --git a/test/cases/translate_c/struct_with_aligned_fields.c b/test/cases/translate_c/struct_with_aligned_fields.c new file mode 100644 index 0000000000..37a25ff49a --- /dev/null +++ b/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), +// }; diff --git a/test/cases/translate_c/struct_with_invalid_field_alignment.c b/test/cases/translate_c/struct_with_invalid_field_alignment.c new file mode 100644 index 0000000000..6b3fc55b6d --- /dev/null +++ b/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), +// }; +// diff --git a/test/cases/translate_c/type_referenced_struct.c b/test/cases/translate_c/type_referenced_struct.c new file mode 100644 index 0000000000..597d37329e --- /dev/null +++ b/test/cases/translate_c/type_referenced_struct.c @@ -0,0 +1,19 @@ +// When clang uses the -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), +// }; diff --git a/test/cases/translate_c/union_initializer.c b/test/cases/translate_c/union_initializer.c new file mode 100644 index 0000000000..010e493252 --- /dev/null +++ b/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', +// }, +// }; diff --git a/test/cases/translate_c/union_struct_forward_decl.c b/test/cases/translate_c/union_struct_forward_decl.c new file mode 100644 index 0000000000..d2c721804a --- /dev/null +++ b/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), +// }; diff --git a/test/cases/translate_c/unnamed_fields_have_predictable_names.c b/test/cases/translate_c/unnamed_fields_have_predictable_names.c new file mode 100644 index 0000000000..1517bf1327 --- /dev/null +++ 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), +// }; diff --git a/test/cases/translate_c/zero_width_field_alignment.c b/test/cases/translate_c/zero_width_field_alignment.c new file mode 100644 index 0000000000..c50bb668fd --- /dev/null +++ b/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), +// }; diff --git a/test/cases/translate_c/zig_keywords_in_c_code.c b/test/cases/translate_c/zig_keywords_in_c_code.c new file mode 100644 index 0000000000..5ec80ec26e --- /dev/null +++ b/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; diff --git a/test/translate_c.zig b/test/translate_c.zig index 3b201e2c11..890f93a106 100644 --- a/test/translate_c.zig +++ b/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 -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;