authorgravatar for february.cozzocrea@gmail.comfebruary cozzocrea <february.cozzocrea@gmail.com> 2024-03-02 17:14:41-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-09 13:46:50-07:00
logc9ad1b51993e45942e38079325dcb7b5ce41e877
treecd45690414db3cca09af08c918a415f1d752a533
parentbcb534c295d5cc6fd63caa570cc08e6b148a507c

aro translate-c: support for record types added


25 files changed, 881 insertions(+), 460 deletions(-)

lib/compiler/aro/aro/Type.zig+3-1
...@@ -1142,12 +1142,14 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 {...@@ -1142,12 +1142,14 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 {
1142 };1142 };
1143}1143}
11441144
1145pub const QualHandling = enum { standard, preserve_quals };
1146
1145/// Canonicalize a possibly-typeof() type. If the type is not a typeof() type, simply1147/// Canonicalize a possibly-typeof() type. If the type is not a typeof() type, simply
1146/// return it. Otherwise, determine the actual qualified type.1148/// return it. Otherwise, determine the actual qualified type.
1147/// The `qual_handling` parameter can be used to return the full set of qualifiers1149/// The `qual_handling` parameter can be used to return the full set of qualifiers
1148/// added by typeof() operations, which is useful when determining the elemType of1150/// added by typeof() operations, which is useful when determining the elemType of
1149/// arrays and pointers.1151/// arrays and pointers.
1150pub fn canonicalize(ty: Type, qual_handling: enum { standard, preserve_quals }) Type {1152pub fn canonicalize(ty: Type, qual_handling: QualHandling) Type {
1151 var cur = ty;1153 var cur = ty;
1152 if (cur.specifier == .attributed) {1154 if (cur.specifier == .attributed) {
1153 cur = cur.data.attributed.base;1155 cur = cur.data.attributed.base;
lib/compiler/aro_translate_c.zig+404-89
...@@ -52,18 +52,17 @@ fn getMangle(c: *Context) u32 {...@@ -52,18 +52,17 @@ fn getMangle(c: *Context) u32 {
52 return c.mangle_count;52 return c.mangle_count;
53}53}
5454
55/// Convert a clang source location to a file:line:column string55/// Convert an aro TokenIndex to a 'file:line:column' string
56fn locStr(c: *Context, loc: TokenIndex) ![]const u8 {56fn locStr(c: *Context, tok_idx: TokenIndex) ![]const u8 {
57 _ = c;57 const token_loc = c.tree.tokens.items(.loc)[tok_idx];
58 _ = loc;58 const source = c.comp.getSource(token_loc.id);
59 // const spelling_loc = c.source_manager.getSpellingLoc(loc);59 const line_col = source.lineCol(token_loc);
60 // const filename_c = c.source_manager.getFilename(spelling_loc);60 const filename = source.path;
61 // const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)");61
6262 const line = source.physicalLine(token_loc);
63 // const line = c.source_manager.getSpellingLineNumber(spelling_loc);63 const col = line_col.col;
64 // const column = c.source_manager.getSpellingColumnNumber(spelling_loc);64
65 // return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column });65 return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, col });
66 return "somewhere";
67}66}
6867
69fn maybeSuppressResult(c: *Context, used: ResultUsed, result: ZigNode) TransError!ZigNode {68fn maybeSuppressResult(c: *Context, used: ResultUsed, result: ZigNode) TransError!ZigNode {
...@@ -184,26 +183,30 @@ fn prepopulateGlobalNameTable(c: *Context) !void {...@@ -184,26 +183,30 @@ fn prepopulateGlobalNameTable(c: *Context) !void {
184 const node_data = c.tree.nodes.items(.data);183 const node_data = c.tree.nodes.items(.data);
185 for (c.tree.root_decls) |node| {184 for (c.tree.root_decls) |node| {
186 const data = node_data[@intFromEnum(node)];185 const data = node_data[@intFromEnum(node)];
187 const decl_name = switch (node_tags[@intFromEnum(node)]) {186 switch (node_tags[@intFromEnum(node)]) {
188 .typedef => @panic("TODO"),187 .typedef => @panic("TODO"),
189188
190 .static_assert,
191 .struct_decl_two,189 .struct_decl_two,
192 .union_decl_two,190 .union_decl_two,
193 .struct_decl,191 .struct_decl,
194 .union_decl,192 .union_decl,
195 => blk: {193 .struct_forward_decl,
196 const ty = node_types[@intFromEnum(node)];194 .union_forward_decl,
197 const name_id = ty.data.record.name;
198 break :blk c.mapper.lookup(name_id);
199 },
200
201 .enum_decl_two,195 .enum_decl_two,
202 .enum_decl,196 .enum_decl,
203 => blk: {197 .enum_forward_decl,
204 const ty = node_types[@intFromEnum(node)];198 => {
205 const name_id = ty.data.@"enum".name;199 const raw_ty = node_types[@intFromEnum(node)];
206 break :blk c.mapper.lookup(name_id);200 const ty = raw_ty.canonicalize(.standard);
201 const name_id = if (ty.isRecord()) ty.data.record.name else ty.data.@"enum".name;
202 const decl_name = c.mapper.lookup(name_id);
203 const container_prefix = if (ty.is(.@"struct")) "struct" else if (ty.is(.@"union")) "union" else "enum";
204 const prefixed_name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_prefix, decl_name });
205 // `decl_name` and `prefixed_name` are the preferred names for this type.
206 // However, we can name it anything else if necessary, so these are "weak names".
207 try c.weak_global_names.ensureUnusedCapacity(c.gpa, 2);
208 c.weak_global_names.putAssumeCapacity(decl_name, {});
209 c.weak_global_names.putAssumeCapacity(prefixed_name, {});
207 },210 },
208211
209 .fn_proto,212 .fn_proto,
...@@ -215,80 +218,256 @@ fn prepopulateGlobalNameTable(c: *Context) !void {...@@ -215,80 +218,256 @@ fn prepopulateGlobalNameTable(c: *Context) !void {
215 .inline_fn_def,218 .inline_fn_def,
216 .inline_static_fn_def,219 .inline_static_fn_def,
217 .@"var",220 .@"var",
221 .extern_var,
218 .static_var,222 .static_var,
219 .threadlocal_var,223 .threadlocal_var,
220 .threadlocal_static_var,
221 .extern_var,
222 .threadlocal_extern_var,224 .threadlocal_extern_var,
223 => c.tree.tokSlice(data.decl.name),225 .threadlocal_static_var,
226 => {
227 const decl_name = c.tree.tokSlice(data.decl.name);
228 try c.global_names.put(c.gpa, decl_name, {});
229 },
224 else => unreachable,230 else => unreachable,
225 };231 }
226 try c.global_names.put(c.gpa, decl_name, {});
227 }232 }
228}233}
229234
230fn transTopLevelDecls(c: *Context) !void {235fn transTopLevelDecls(c: *Context) !void {
236 for (c.tree.root_decls) |node| {
237 try transDecl(c, &c.global_scope.base, node);
238 }
239}
240
241fn transDecl(c: *Context, scope: *Scope, decl: NodeIndex) !void {
231 const node_tags = c.tree.nodes.items(.tag);242 const node_tags = c.tree.nodes.items(.tag);
232 const node_data = c.tree.nodes.items(.data);243 const node_data = c.tree.nodes.items(.data);
233 for (c.tree.root_decls) |node| {244 const data = node_data[@intFromEnum(decl)];
234 const data = node_data[@intFromEnum(node)];245 switch (node_tags[@intFromEnum(decl)]) {
235 switch (node_tags[@intFromEnum(node)]) {246 .typedef => {
236 .typedef => {247 try transTypeDef(c, scope, decl);
237 try transTypeDef(c, &c.global_scope.base, node);248 },
238 },
239249
240 .static_assert,250 .struct_decl_two,
241 .struct_decl_two,251 .union_decl_two,
242 .union_decl_two,252 => {
243 .struct_decl,253 var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs };
244 .union_decl,254 var field_count: u2 = 0;
245 => {255 if (fields[0] != .none) field_count += 1;
246 try transRecordDecl(c, &c.global_scope.base, node);256 if (fields[1] != .none) field_count += 1;
247 },257 try transRecordDecl(c, scope, decl, fields[0..field_count]);
258 },
259 .struct_decl,
260 .union_decl,
261 => {
262 const fields = c.tree.data[data.range.start..data.range.end];
263 try transRecordDecl(c, scope, decl, fields);
264 },
248265
249 .enum_decl_two => {266 .enum_decl_two => {
250 var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs };267 var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs };
251 var field_count: u8 = 0;268 var field_count: u8 = 0;
252 if (fields[0] != .none) field_count += 1;269 if (fields[0] != .none) field_count += 1;
253 if (fields[1] != .none) field_count += 1;270 if (fields[1] != .none) field_count += 1;
254 try transEnumDecl(c, &c.global_scope.base, node, fields[0..field_count]);271 try transEnumDecl(c, scope, decl, fields[0..field_count]);
255 },272 },
256 .enum_decl => {273 .enum_decl => {
257 const fields = c.tree.data[data.range.start..data.range.end];274 const fields = c.tree.data[data.range.start..data.range.end];
258 try transEnumDecl(c, &c.global_scope.base, node, fields);275 try transEnumDecl(c, scope, decl, fields);
259 },276 },
260277
261 .fn_proto,278 .enum_field_decl,
262 .static_fn_proto,279 .record_field_decl,
263 .inline_fn_proto,280 .indirect_record_field_decl,
264 .inline_static_fn_proto,281 .struct_forward_decl,
265 .fn_def,282 .union_forward_decl,
266 .static_fn_def,283 .enum_forward_decl,
267 .inline_fn_def,284 => return,
268 .inline_static_fn_def,285
269 => {286 .fn_proto,
270 try transFnDecl(c, node);287 .static_fn_proto,
271 },288 .inline_fn_proto,
289 .inline_static_fn_proto,
290 .fn_def,
291 .static_fn_def,
292 .inline_fn_def,
293 .inline_static_fn_def,
294 => {
295 try transFnDecl(c, decl);
296 },
272297
273 .@"var",298 .@"var",
274 .static_var,299 .extern_var,
275 .threadlocal_var,300 .static_var,
276 .threadlocal_static_var,301 .threadlocal_var,
277 .extern_var,302 .threadlocal_extern_var,
278 .threadlocal_extern_var,303 .threadlocal_static_var,
279 => {304 => {
280 try transVarDecl(c, node, null);305 try transVarDecl(c, decl, null);
281 },306 },
282 else => unreachable,307 else => unreachable,
283 }
284 }308 }
285}309}
286310
287fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void {311fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void {
288 @panic("TODO");312 @panic("TODO");
289}313}
290fn transRecordDecl(_: *Context, _: *Scope, _: NodeIndex) Error!void {314
291 @panic("TODO");315fn mangleWeakGlobalName(c: *Context, want_name: []const u8) ![]const u8 {
316 var cur_name = want_name;
317
318 if (!c.weak_global_names.contains(want_name)) {
319 // This type wasn't noticed by the name detection pass, so nothing has been treating this as
320 // a weak global name. We must mangle it to avoid conflicts with locals.
321 cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() });
322 }
323
324 while (c.global_names.contains(cur_name)) {
325 cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() });
326 }
327 return cur_name;
328}
329
330fn transRecordDecl(c: *Context, scope: *Scope, record_node: NodeIndex, field_nodes: []const NodeIndex) Error!void {
331 const node_types = c.tree.nodes.items(.ty);
332 const raw_record_ty = node_types[@intFromEnum(record_node)];
333 const record_decl = raw_record_ty.getRecord().?;
334 if (c.decl_table.get(@intFromPtr(record_decl))) |_|
335 return; // Avoid processing this decl twice
336 const toplevel = scope.id == .root;
337 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
338
339 const container_kind: ZigTag = if (raw_record_ty.is(.@"union")) .@"union" else .@"struct";
340 const container_kind_name: []const u8 = @tagName(container_kind);
341
342 var is_unnamed = false;
343 var bare_name: []const u8 = c.mapper.lookup(record_decl.name);
344 var name = bare_name;
345
346 if (c.unnamed_typedefs.get(@intFromPtr(record_decl))) |typedef_name| {
347 bare_name = typedef_name;
348 name = typedef_name;
349 } else {
350 if (raw_record_ty.isAnonymousRecord(c.comp)) {
351 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
352 is_unnamed = true;
353 }
354 name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
355 if (toplevel and !is_unnamed) {
356 name = try mangleWeakGlobalName(c, name);
357 }
358 }
359 if (!toplevel) name = try bs.makeMangledName(c, name);
360 try c.decl_table.putNoClobber(c.gpa, @intFromPtr(record_decl), name);
361
362 const is_pub = toplevel and !is_unnamed;
363 const init_node = blk: {
364 var fields = try std.ArrayList(ast.Payload.Record.Field).initCapacity(c.gpa, record_decl.fields.len);
365 defer fields.deinit();
366
367 // TODO: Add support for flexible array field functions
368 var functions = std.ArrayList(ZigNode).init(c.gpa);
369 defer functions.deinit();
370
371 var unnamed_field_count: u32 = 0;
372
373 // If a record doesn't have any attributes that would affect the alignment and
374 // layout, then we can just use a simple `extern` type. If it does have attributes,
375 // then we need to inspect the layout and assign an `align` value for each field.
376 const has_alignment_attributes = record_decl.field_attributes != null or
377 raw_record_ty.hasAttribute(.@"packed") or
378 raw_record_ty.hasAttribute(.aligned);
379 const head_field_alignment: ?c_uint = headFieldAlignment(record_decl);
380
381 // Iterate over field nodes so that we translate any type decls included in this record decl.
382 // TODO: Move this logic into `fn transType()` instead of handling decl translation here.
383 for (field_nodes) |field_node| {
384 const field_raw_ty = node_types[@intFromEnum(field_node)];
385 if (field_raw_ty.isEnumOrRecord()) try transDecl(c, scope, field_node);
386 }
387
388 for (record_decl.fields, 0..) |field, field_index| {
389 const field_loc = field.name_tok;
390
391 // Demote record to opaque if it contains a bitfield
392 if (!field.isRegularField()) {
393 try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl), {});
394 try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
395 break :blk ZigTag.opaque_literal.init();
396 }
397
398 var field_name = c.mapper.lookup(field.name);
399 if (!field.isNamed()) {
400 field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count});
401 unnamed_field_count += 1;
402 }
403 const field_type = transType(c, scope, field.ty, .preserve_quals, field_loc) catch |err| switch (err) {
404 error.UnsupportedType => {
405 try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl), {});
406 try warn(c, scope, 0, "{s} demoted to opaque type - unable to translate type of field {s}", .{
407 container_kind_name,
408 field_name,
409 });
410 break :blk ZigTag.opaque_literal.init();
411 },
412 else => |e| return e,
413 };
414
415 const field_alignment = if (has_alignment_attributes)
416 alignmentForField(record_decl, head_field_alignment, field_index)
417 else
418 null;
419
420 // C99 introduced designated initializers for structs. Omitted fields are implicitly
421 // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
422 // values for translated struct fields permits Zig code to comfortably use such an API.
423 const default_value = if (container_kind == .@"struct")
424 try ZigTag.std_mem_zeroes.create(c.arena, field_type)
425 else
426 null;
427
428 fields.appendAssumeCapacity(.{
429 .name = field_name,
430 .type = field_type,
431 .alignment = field_alignment,
432 .default_value = default_value,
433 });
434 }
435
436 const record_payload = try c.arena.create(ast.Payload.Record);
437 record_payload.* = .{
438 .base = .{ .tag = container_kind },
439 .data = .{
440 .layout = .@"extern",
441 .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items),
442 .functions = try c.arena.dupe(ZigNode, functions.items),
443 .variables = &.{},
444 },
445 };
446 break :blk ZigNode.initPayload(&record_payload.base);
447 };
448
449 const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
450 payload.* = .{
451 .base = .{ .tag = ([2]ZigTag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] },
452 .data = .{
453 .name = name,
454 .init = init_node,
455 },
456 };
457 const node = ZigNode.initPayload(&payload.base);
458 if (toplevel) {
459 try addTopLevelDecl(c, name, node);
460 // Only add the alias if the name is available *and* it was caught by
461 // name detection. Don't bother performing a weak mangle, since a
462 // mangled name is of no real use here.
463 if (!is_unnamed and !c.global_names.contains(bare_name) and c.weak_global_names.contains(bare_name))
464 try c.alias_list.append(.{ .alias = bare_name, .name = name });
465 } else {
466 try scope.appendNode(node);
467 if (node.tag() != .pub_var_simple) {
468 try bs.discardVariable(c, name);
469 }
470 }
292}471}
293472
294fn transFnDecl(c: *Context, fn_decl: NodeIndex) Error!void {473fn transFnDecl(c: *Context, fn_decl: NodeIndex) Error!void {
...@@ -419,7 +598,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:...@@ -419,7 +598,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:
419 enum_val_name = try bs.makeMangledName(c, enum_val_name);598 enum_val_name = try bs.makeMangledName(c, enum_val_name);
420 }599 }
421600
422 const enum_const_type_node: ?ZigNode = transType(c, scope, field.ty, field.name_tok) catch |err| switch (err) {601 const enum_const_type_node: ?ZigNode = transType(c, scope, field.ty, .standard, field.name_tok) catch |err| switch (err) {
423 error.UnsupportedType => null,602 error.UnsupportedType => null,
424 else => |e| return e,603 else => |e| return e,
425 };604 };
...@@ -439,7 +618,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:...@@ -439,7 +618,7 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:
439 }618 }
440 }619 }
441620
442 break :blk transType(c, scope, ty.data.@"enum".tag_ty, 0) catch |err| switch (err) {621 break :blk transType(c, scope, ty.data.@"enum".tag_ty, .standard, 0) catch |err| switch (err) {
443 error.UnsupportedType => {622 error.UnsupportedType => {
444 return failDecl(c, 0, name, "unable to translate enum integer type", .{});623 return failDecl(c, 0, name, "unable to translate enum integer type", .{});
445 },624 },
...@@ -472,8 +651,8 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:...@@ -472,8 +651,8 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes:
472 }651 }
473}652}
474653
475fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) TypeError!ZigNode {654fn transType(c: *Context, scope: *Scope, raw_ty: Type, qual_handling: Type.QualHandling, source_loc: TokenIndex) TypeError!ZigNode {
476 const ty = raw_ty.canonicalize(.standard);655 const ty = raw_ty.canonicalize(qual_handling);
477 switch (ty.specifier) {656 switch (ty.specifier) {
478 .void => return ZigTag.type.create(c.arena, "anyopaque"),657 .void => return ZigTag.type.create(c.arena, "anyopaque"),
479 .bool => return ZigTag.type.create(c.arena, "bool"),658 .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...@@ -496,16 +675,152 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) T
496 .long_double => return ZigTag.type.create(c.arena, "c_longdouble"),675 .long_double => return ZigTag.type.create(c.arena, "c_longdouble"),
497 .float80 => return ZigTag.type.create(c.arena, "f80"),676 .float80 => return ZigTag.type.create(c.arena, "f80"),
498 .float128 => return ZigTag.type.create(c.arena, "f128"),677 .float128 => return ZigTag.type.create(c.arena, "f128"),
678 .@"enum" => @panic("TODO"),
679 .pointer,
680 .unspecified_variable_len_array,
681 .array,
682 .static_array,
683 .incomplete_array,
684 => @panic("TODO"),
499 .func,685 .func,
500 .var_args_func,686 .var_args_func,
501 .old_style_func,687 .old_style_func,
502 => return transFnType(c, scope, raw_ty, ty, source_loc, .{}),688 => return transFnType(c, scope, ty, ty, source_loc, .{}),
689 .@"struct",
690 .@"union",
691 => {
692 var trans_scope = scope;
693 if (ty.isAnonymousRecord(c.comp)) {
694 const record_decl = ty.data.record;
695 const name_id = c.mapper.lookup(record_decl.name);
696 if (c.weak_global_names.contains(name_id)) trans_scope = &c.global_scope.base;
697 }
698 const name = c.decl_table.get(@intFromPtr(ty.data.record)).?;
699 return ZigTag.identifier.create(c.arena, name);
700 },
701 .attributed,
702 .typeof_type,
703 .typeof_expr,
704 => unreachable,
503 else => return error.UnsupportedType,705 else => return error.UnsupportedType,
504 }706 }
505}707}
506708
507fn zigAlignment(bit_alignment: u29) u32 {709/// Look ahead through the fields of the record to determine what the alignment of the record
508 return bit_alignment / 8;710/// would be without any align/packed/etc. attributes. This helps us determine whether or not
711/// the fields with 0 offset need an `align` qualifier. Strictly speaking, we could just
712/// pedantically assign those fields the same alignment as the parent's pointer alignment,
713/// but this helps the generated code to be a little less verbose.
714fn headFieldAlignment(record_decl: *const Type.Record) ?c_uint {
715 const bits_per_byte = 8;
716 const parent_ptr_alignment_bits = record_decl.type_layout.pointer_alignment_bits;
717 const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
718 var max_field_alignment_bits: u64 = 0;
719 for (record_decl.fields) |field| {
720 if (field.ty.getRecord()) |field_record_decl| {
721 const child_record_alignment = field_record_decl.type_layout.field_alignment_bits;
722 if (child_record_alignment > max_field_alignment_bits)
723 max_field_alignment_bits = child_record_alignment;
724 } else {
725 const field_size = field.layout.size_bits;
726 if (field_size > max_field_alignment_bits)
727 max_field_alignment_bits = field_size;
728 }
729 }
730 if (max_field_alignment_bits != parent_ptr_alignment_bits) {
731 return parent_ptr_alignment;
732 } else {
733 return null;
734 }
735}
736
737/// This function returns a ?c_uint to match Clang's behaviour of using c_uint.
738/// This can be changed to a u29 after the Clang frontend for translate-c is removed.
739fn alignmentForField(
740 record_decl: *const Type.Record,
741 head_field_alignment: ?c_uint,
742 field_index: usize,
743) ?c_uint {
744 const fields = record_decl.fields;
745 assert(fields.len != 0);
746 const field = fields[field_index];
747
748 const bits_per_byte = 8;
749 const parent_ptr_alignment_bits = record_decl.type_layout.pointer_alignment_bits;
750 const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
751
752 // bitfields aren't supported yet. Until support is added, records with bitfields
753 // should be demoted to opaque, and this function shouldn't be called for them.
754 if (!field.isRegularField()) {
755 @panic("TODO: add bitfield support for records");
756 }
757
758 const field_offset_bits: u64 = field.layout.offset_bits;
759 const field_size_bits: u64 = field.layout.size_bits;
760
761 // Fields with zero width always have an alignment of 1
762 if (field_size_bits == 0) {
763 return 1;
764 }
765
766 // Fields with 0 offset inherit the parent's pointer alignment.
767 if (field_offset_bits == 0) {
768 return head_field_alignment;
769 }
770
771 // Records have a natural alignment when used as a field, and their size is
772 // a multiple of this alignment value. For all other types, the natural alignment
773 // is their size.
774 const field_natural_alignment_bits: u64 = if (field.ty.getRecord()) |record| record.type_layout.field_alignment_bits else field_size_bits;
775 const rem_bits = field_offset_bits % field_natural_alignment_bits;
776
777 // If there's a remainder, then the alignment is smaller than the field's
778 // natural alignment
779 if (rem_bits > 0) {
780 const rem_alignment = rem_bits / bits_per_byte;
781 if (rem_alignment > 0 and std.math.isPowerOfTwo(rem_alignment)) {
782 const actual_alignment = @min(rem_alignment, parent_ptr_alignment);
783 return @as(c_uint, @truncate(actual_alignment));
784 } else {
785 return 1;
786 }
787 }
788
789 // A field may have an offset which positions it to be naturally aligned, but the
790 // parent's pointer alignment determines if this is actually true, so we take the minimum
791 // value.
792 // For example, a float field (4 bytes wide) with a 4 byte offset is positioned to have natural
793 // alignment, but if the parent pointer alignment is 2, then the actual alignment of the
794 // float is 2.
795 const field_natural_alignment: u64 = field_natural_alignment_bits / bits_per_byte;
796 const offset_alignment = field_offset_bits / bits_per_byte;
797 const possible_alignment = @min(parent_ptr_alignment, offset_alignment);
798 if (possible_alignment == field_natural_alignment) {
799 return null;
800 } else if (possible_alignment < field_natural_alignment) {
801 if (std.math.isPowerOfTwo(possible_alignment)) {
802 return possible_alignment;
803 } else {
804 return 1;
805 }
806 } else { // possible_alignment > field_natural_alignment
807 // Here, the field is positioned be at a higher alignment than it's natural alignment. This means we
808 // need to determine whether it's a specified alignment. We can determine that from the padding preceding
809 // the field.
810 const padding_from_prev_field: u64 = blk: {
811 if (field_offset_bits != 0) {
812 const previous_field = fields[field_index - 1];
813 break :blk (field_offset_bits - previous_field.layout.offset_bits) - previous_field.layout.size_bits;
814 } else {
815 break :blk 0;
816 }
817 };
818 if (padding_from_prev_field < field_natural_alignment_bits) {
819 return null;
820 } else {
821 return possible_alignment;
822 }
823 }
509}824}
510825
511const FnProtoContext = struct {826const FnProtoContext = struct {
...@@ -536,7 +851,7 @@ fn transFnType(...@@ -536,7 +851,7 @@ fn transFnType(
536 else851 else
537 c.mapper.lookup(param_info.name);852 c.mapper.lookup(param_info.name);
538853
539 const type_node = try transType(c, scope, param_ty, param_info.name_tok);854 const type_node = try transType(c, scope, param_ty, .standard, param_info.name_tok);
540 param_node.* = .{855 param_node.* = .{
541 .is_noalias = is_noalias,856 .is_noalias = is_noalias,
542 .name = param_name,857 .name = param_name,
...@@ -551,7 +866,7 @@ fn transFnType(...@@ -551,7 +866,7 @@ fn transFnType(
551 break :blk null;866 break :blk null;
552 };867 };
553868
554 const alignment = if (raw_ty.requestedAlignment(c.comp)) |alignment| zigAlignment(alignment) else null;869 const alignment: ?c_uint = raw_ty.requestedAlignment(c.comp) orelse null;
555870
556 const explicit_callconv = null;871 const explicit_callconv = null;
557 // const explicit_callconv = if ((ctx.is_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .C) null else ctx.cc;872 // 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(...@@ -565,7 +880,7 @@ fn transFnType(
565 // convert primitive anyopaque to actual void (only for return type)880 // convert primitive anyopaque to actual void (only for return type)
566 break :blk ZigTag.void_type.init();881 break :blk ZigTag.void_type.init();
567 } else {882 } else {
568 break :blk transType(c, scope, return_ty, source_loc) catch |err| switch (err) {883 break :blk transType(c, scope, return_ty, .standard, source_loc) catch |err| switch (err) {
569 error.UnsupportedType => {884 error.UnsupportedType => {
570 try warn(c, scope, source_loc, "unsupported function proto return type", .{});885 try warn(c, scope, source_loc, "unsupported function proto return type", .{});
571 return err;886 return err;
...@@ -642,7 +957,7 @@ fn transExpr(c: *Context, node: NodeIndex, result_used: ResultUsed) TransError!Z...@@ -642,7 +957,7 @@ fn transExpr(c: *Context, node: NodeIndex, result_used: ResultUsed) TransError!Z
642 // TODO handle other values957 // TODO handle other values
643 const int = try transCreateNodeAPInt(c, val);958 const int = try transCreateNodeAPInt(c, val);
644 const as_node = try ZigTag.as.create(c.arena, .{959 const as_node = try ZigTag.as.create(c.arena, .{
645 .lhs = try transType(c, undefined, ty, undefined),960 .lhs = try transType(c, undefined, ty, .standard, undefined),
646 .rhs = int,961 .rhs = int,
647 });962 });
648 return maybeSuppressResult(c, result_used, as_node);963 return maybeSuppressResult(c, result_used, as_node);
test/cases/translate_c/circular_struct_definitions.c created+20
...@@ -0,0 +1,20 @@
1struct Bar;
2
3struct Foo {
4 struct Bar *next;
5};
6
7struct Bar {
8 struct Foo *next;
9};
10
11// translate-c
12// c_frontend=clang
13//
14// pub const struct_Bar = extern struct {
15// next: [*c]struct_Foo = @import("std").mem.zeroes([*c]struct_Foo),
16// };
17//
18// pub const struct_Foo = extern struct {
19// next: [*c]struct_Bar = @import("std").mem.zeroes([*c]struct_Bar),
20// };
test/cases/translate_c/double_define_struct.c created+25
...@@ -0,0 +1,25 @@
1typedef struct Bar Bar;
2typedef struct Foo Foo;
3
4struct Foo {
5 Foo *a;
6};
7
8struct Bar {
9 Foo *a;
10};
11
12// translate-c
13// c_frontend=clang
14//
15// pub const struct_Foo = extern struct {
16// a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
17// };
18//
19// pub const Foo = struct_Foo;
20//
21// pub const struct_Bar = extern struct {
22// a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
23// };
24//
25// pub const Bar = struct_Bar;
test/cases/translate_c/field_access_is_grouped_if_necessary.c created+18
...@@ -0,0 +1,18 @@
1unsigned long foo(unsigned long x) {
2 return ((union{unsigned long _x}){x})._x;
3}
4
5// translate-c
6// c_frontend=clang
7//
8// pub export fn foo(arg_x: c_ulong) c_ulong {
9// var x = arg_x;
10// _ = &x;
11// const union_unnamed_1 = extern union {
12// _x: c_ulong,
13// };
14// _ = &union_unnamed_1;
15// return (union_unnamed_1{
16// ._x = x,
17// })._x;
18// }
test/cases/translate_c/global_struct_whose_default_name_conflicts_with_global_is_mangled.c created+15
...@@ -0,0 +1,15 @@
1struct foo {
2 int x;
3};
4const char *struct_foo = "hello world";
5
6// translate-c
7// c_frontend=clang
8//
9// pub const struct_foo_1 = extern struct {
10// x: c_int = @import("std").mem.zeroes(c_int),
11// };
12//
13// pub const foo = struct_foo_1;
14//
15// pub export var struct_foo: [*c]const u8 = "hello world";
test/cases/translate_c/large_packed_struct.c created+20
...@@ -0,0 +1,20 @@
1struct __attribute__((packed)) bar {
2 short a;
3 float b;
4 double c;
5 short x;
6 float y;
7 double z;
8};
9
10// translate-c
11// c_frontend=aro,clang
12//
13// pub const struct_bar = extern struct {
14// a: c_short align(1) = @import("std").mem.zeroes(c_short),
15// b: f32 align(1) = @import("std").mem.zeroes(f32),
16// c: f64 align(1) = @import("std").mem.zeroes(f64),
17// x: c_short align(1) = @import("std").mem.zeroes(c_short),
18// y: f32 align(1) = @import("std").mem.zeroes(f32),
19// z: f64 align(1) = @import("std").mem.zeroes(f64),
20// };
test/cases/translate_c/packed_union_nested_unpacked.c created+25
...@@ -0,0 +1,25 @@
1// NOTE: The nested struct is *not* packed/aligned,
2// even though the parent struct is
3// this is consistent with GCC docs
4union Foo{
5 short x;
6 double y;
7 struct {
8 int b;
9 } z;
10} __attribute__((packed));
11
12// translate-c
13// c_frontend=aro,clang
14//
15// const struct_unnamed_1 = extern struct {
16// b: c_int = @import("std").mem.zeroes(c_int),
17// };
18//
19// pub const union_Foo = extern union {
20// x: c_short align(1),
21// y: f64 align(1),
22// z: struct_unnamed_1 align(1),
23// };
24//
25// pub const Foo = union_Foo;
test/cases/translate_c/packed_union_simple.c created+14
...@@ -0,0 +1,14 @@
1union Foo {
2 short x;
3 double y;
4} __attribute__((packed));
5
6// translate-c
7// c_frontend=aro,clang
8//
9// pub const union_Foo = extern union {
10// x: c_short align(1),
11// y: f64 align(1),
12// };
13//
14// pub const Foo = union_Foo;
test/cases/translate_c/pointer_to_struct_demoted_opaque_due_to_bit_fields.c created+15
...@@ -0,0 +1,15 @@
1struct Foo {
2 unsigned int: 1;
3};
4struct Bar {
5 struct Foo *foo;
6};
7
8// translate-c
9// c_frontend=clang
10//
11// pub const struct_Foo = opaque {};
12//
13// pub const struct_Bar = extern struct {
14// foo: ?*struct_Foo = @import("std").mem.zeroes(?*struct_Foo),
15// };
test/cases/translate_c/qualified_struct_and_enum.c created+25
...@@ -0,0 +1,25 @@
1struct Foo {
2 int x;
3 int y;
4};
5enum Bar {
6 BarA,
7 BarB,
8};
9void func(struct Foo *a, enum Bar **b);
10
11// translate-c
12// c_frontend=clang
13// target=x86_64-linux,x86_64-macos
14//
15// pub const struct_Foo = extern struct {
16// x: c_int = @import("std").mem.zeroes(c_int),
17// y: c_int = @import("std").mem.zeroes(c_int),
18// };
19// pub const BarA: c_int = 0;
20// pub const BarB: c_int = 1;
21// pub const enum_Bar = c_uint;
22// pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
23//
24// pub const Foo = struct_Foo;
25// pub const Bar = enum_Bar;
test/cases/translate_c/qualified_struct_and_enum_msvc.c created+25
...@@ -0,0 +1,25 @@
1struct Foo {
2 int x;
3 int y;
4};
5enum Bar {
6 BarA,
7 BarB,
8};
9void func(struct Foo *a, enum Bar **b);
10
11// translate-c
12// c_frontend=clang
13// target=x86_64-windows-msvc
14//
15// pub const struct_Foo = extern struct {
16// x: c_int = @import("std").mem.zeroes(c_int),
17// y: c_int = @import("std").mem.zeroes(c_int),
18// };
19// pub const BarA: c_int = 0;
20// pub const BarB: c_int = 1;
21// pub const enum_Bar = c_int;
22// pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
23//
24// pub const Foo = struct_Foo;
25// pub const Bar = enum_Bar;
test/cases/translate_c/scoped_record.c created+49
...@@ -0,0 +1,49 @@
1void foo() {
2 struct Foo {
3 int A;
4 int B;
5 int C;
6 };
7 struct Foo a = {0};
8 {
9 struct Foo {
10 int A;
11 int B;
12 int C;
13 };
14 struct Foo a = {0};
15 }
16}
17
18// translate-c
19// c_frontend=clang
20//
21// pub export fn foo() void {
22// const struct_Foo = extern struct {
23// A: c_int = @import("std").mem.zeroes(c_int),
24// B: c_int = @import("std").mem.zeroes(c_int),
25// C: c_int = @import("std").mem.zeroes(c_int),
26// };
27// _ = &struct_Foo;
28// var a: struct_Foo = struct_Foo{
29// .A = @as(c_int, 0),
30// .B = 0,
31// .C = 0,
32// };
33// _ = &a;
34// {
35// const struct_Foo_1 = extern struct {
36// A: c_int = @import("std").mem.zeroes(c_int),
37// B: c_int = @import("std").mem.zeroes(c_int),
38// C: c_int = @import("std").mem.zeroes(c_int),
39// };
40// _ = &struct_Foo_1;
41// var a_2: struct_Foo_1 = struct_Foo_1{
42// .A = @as(c_int, 0),
43// .B = 0,
44// .C = 0,
45// };
46// _ = &a_2;
47// }
48// }
49
test/cases/translate_c/simple_struct.c created+12
...@@ -0,0 +1,12 @@
1struct Foo {
2 int x;
3};
4
5// translate-c
6// c_frontend=aro,clang
7//
8// const struct_Foo = extern struct {
9// x: c_int = @import("std").mem.zeroes(c_int),
10// };
11//
12// pub const Foo = struct_Foo;
test/cases/translate_c/simple_union.c created+12
...@@ -0,0 +1,12 @@
1union Foo {
2 int x;
3};
4
5// translate-c
6// c_frontend=aro,clang
7//
8// pub const union_Foo = extern union {
9// x: c_int,
10// };
11//
12// pub const Foo = union_Foo;
test/cases/translate_c/struct_in_struct_init_to_zero.c created+24
...@@ -0,0 +1,24 @@
1struct Foo {
2 int a;
3 struct Bar {
4 int a;
5 } b;
6} a = {};
7#define PTR void *
8
9// translate-c
10// c_frontend=clang
11//
12// pub const struct_Bar_1 = extern struct {
13// a: c_int = @import("std").mem.zeroes(c_int),
14// };
15// pub const struct_Foo = extern struct {
16// a: c_int = @import("std").mem.zeroes(c_int),
17// b: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
18// };
19// pub export var a: struct_Foo = struct_Foo{
20// .a = 0,
21// .b = @import("std").mem.zeroes(struct_Bar_1),
22// };
23//
24// pub const PTR = ?*anyopaque;
test/cases/translate_c/struct_with_aligned_fields.c created+10
...@@ -0,0 +1,10 @@
1struct foo {
2 __attribute__((aligned(4))) short bar;
3};
4
5// translate-c
6// c_frontend=aro,clang
7//
8// pub const struct_foo = extern struct {
9// bar: c_short align(4) = @import("std").mem.zeroes(c_short),
10// };
test/cases/translate_c/struct_with_invalid_field_alignment.c created+35
...@@ -0,0 +1,35 @@
1// The aligned attribute cannot decrease the alignment of a field. The packed attribute is required
2// for decreasing the alignment. gcc and clang will compile these structs without error
3// (and possibly without warning), but checking the alignment will reveal a different value than
4// what was requested. This is consistent with the gcc documentation on type attributes.
5//
6// This test is currently broken for the clang frontend. See issue #19307.
7
8struct foo {
9 __attribute__((aligned(1)))int x;
10};
11
12struct bar {
13 __attribute__((aligned(2)))float y;
14};
15
16struct baz {
17 __attribute__((aligned(4)))double z;
18};
19
20// translate-c
21// c_frontend=aro
22// target=x86_64-linux
23//
24// pub const struct_foo = extern struct {
25// x: c_int = @import("std").mem.zeroes(c_int),
26// };
27//
28// pub const struct_bar = extern struct {
29// y: f32 = @import("std").mem.zeroes(f32),
30// };
31//
32// pub const struct_baz = extern struct {
33// z: f64 = @import("std").mem.zeroes(f64),
34// };
35//
test/cases/translate_c/type_referenced_struct.c created+19
...@@ -0,0 +1,19 @@
1// When clang uses the <arch>-windows-none, triple it behaves as MSVC and
2// interprets the inner `struct Bar` as an anonymous structure
3struct Foo {
4 struct Bar{
5 int b;
6 };
7 struct Bar c;
8};
9
10// translate-c
11// c_frontend=aro,clang
12// target=x86_64-linux-gnu
13//
14// pub const struct_Bar_1 = extern struct {
15// b: c_int = @import("std").mem.zeroes(c_int),
16// };
17// pub const struct_Foo = extern struct {
18// c: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
19// };
test/cases/translate_c/union_initializer.c created+22
...@@ -0,0 +1,22 @@
1union { int x; char c[4]; }
2 ua = {1},
3 ub = {.c={'a','b','b','a'}};
4
5// translate-c
6// c_frontend=clang
7//
8// const union_unnamed_1 = extern union {
9// x: c_int,
10// c: [4]u8,
11// };
12// pub export var ua: union_unnamed_1 = union_unnamed_1{
13// .x = @as(c_int, 1),
14// };
15// pub export var ub: union_unnamed_1 = union_unnamed_1{
16// .c = [4]u8{
17// 'a',
18// 'b',
19// 'b',
20// 'a',
21// },
22// };
test/cases/translate_c/union_struct_forward_decl.c created+37
...@@ -0,0 +1,37 @@
1struct A;
2union B;
3enum C;
4
5struct A {
6 short x;
7 double y;
8};
9
10union B {
11 short x;
12 double y;
13};
14
15struct Foo {
16 struct A a;
17 union B b;
18};
19
20
21// translate-c
22// c_frontend=aro,clang
23//
24// pub const struct_A = extern struct {
25// x: c_short = @import("std").mem.zeroes(c_short),
26// y: f64 = @import("std").mem.zeroes(f64),
27// };
28//
29// pub const union_B = extern union {
30// x: c_short,
31// y: f64,
32// };
33//
34// pub const struct_Foo = extern struct {
35// a: struct_A = @import("std").mem.zeroes(struct_A),
36// b: union_B = @import("std").mem.zeroes(union_B),
37// };
test/cases/translate_c/unnamed_fields_have_predictable_names.c created+22
...@@ -0,0 +1,22 @@
1struct a {
2 struct { int x; };
3};
4struct b {
5 struct { int y; };
6};
7
8// translate-c
9// c_frontend=aro,clang
10//
11// const struct_unnamed_1 = extern struct {
12// x: c_int = @import("std").mem.zeroes(c_int),
13// };
14// pub const struct_a = extern struct {
15// unnamed_0: struct_unnamed_1 = @import("std").mem.zeroes(struct_unnamed_1),
16// };
17// const struct_unnamed_2 = extern struct {
18// y: c_int = @import("std").mem.zeroes(c_int),
19// };
20// pub const struct_b = extern struct {
21// unnamed_0: struct_unnamed_2 = @import("std").mem.zeroes(struct_unnamed_2),
22// };
test/cases/translate_c/zero_width_field_alignment.c created+18
...@@ -0,0 +1,18 @@
1struct __attribute__((packed)) foo {
2 int x;
3 struct {};
4 float y;
5 union {};
6};
7
8// translate-c
9// c_frontend=aro
10//
11// const struct_unnamed_1 = extern struct {};
12// const union_unnamed_2 = extern union {};
13// pub const struct_foo = extern struct {
14// x: c_int align(1) = @import("std").mem.zeroes(c_int),
15// unnamed_0: struct_unnamed_1 align(1) = @import("std").mem.zeroes(struct_unnamed_1),
16// y: f32 align(1) = @import("std").mem.zeroes(f32),
17// unnamed_1: union_unnamed_2 align(1) = @import("std").mem.zeroes(union_unnamed_2),
18// };
test/cases/translate_c/zig_keywords_in_c_code.c created+12
...@@ -0,0 +1,12 @@
1struct comptime {
2 int defer;
3};
4
5// translate-c
6// c_frontend=aro,clang
7//
8// pub const struct_comptime = extern struct {
9// @"defer": c_int = @import("std").mem.zeroes(c_int),
10// };
11//
12// pub const @"comptime" = struct_comptime;
test/translate_c.zig-370
...@@ -74,24 +74,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -74,24 +74,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
74 \\pub extern fn main() c_int;74 \\pub extern fn main() c_int;
75 });75 });
7676
77 cases.add("field access is grouped if necessary",
78 \\unsigned long foo(unsigned long x) {
79 \\ return ((union{unsigned long _x}){x})._x;
80 \\}
81 , &[_][]const u8{
82 \\pub export fn foo(arg_x: c_ulong) c_ulong {
83 \\ var x = arg_x;
84 \\ _ = &x;
85 \\ const union_unnamed_1 = extern union {
86 \\ _x: c_ulong,
87 \\ };
88 \\ _ = &union_unnamed_1;
89 \\ return (union_unnamed_1{
90 \\ ._x = x,
91 \\ })._x;
92 \\}
93 });
94
95 cases.add("unnamed child types of typedef receive typedef's name",77 cases.add("unnamed child types of typedef receive typedef's name",
96 \\typedef enum {78 \\typedef enum {
97 \\ FooA,79 \\ FooA,
...@@ -149,78 +131,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -149,78 +131,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
149 \\}131 \\}
150 });132 });
151133
152 cases.add("struct in struct init to zero",
153 \\struct Foo {
154 \\ int a;
155 \\ struct Bar {
156 \\ int a;
157 \\ } b;
158 \\} a = {};
159 \\#define PTR void *
160 , &[_][]const u8{
161 \\pub const struct_Bar_1 = extern struct {
162 \\ a: c_int = @import("std").mem.zeroes(c_int),
163 \\};
164 \\pub const struct_Foo = extern struct {
165 \\ a: c_int = @import("std").mem.zeroes(c_int),
166 \\ b: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
167 \\};
168 \\pub export var a: struct_Foo = struct_Foo{
169 \\ .a = 0,
170 \\ .b = @import("std").mem.zeroes(struct_Bar_1),
171 \\};
172 ,
173 \\pub const PTR = ?*anyopaque;
174 });
175
176 cases.add("scoped record",
177 \\void foo() {
178 \\ struct Foo {
179 \\ int A;
180 \\ int B;
181 \\ int C;
182 \\ };
183 \\ struct Foo a = {0};
184 \\ {
185 \\ struct Foo {
186 \\ int A;
187 \\ int B;
188 \\ int C;
189 \\ };
190 \\ struct Foo a = {0};
191 \\ }
192 \\}
193 , &[_][]const u8{
194 \\pub export fn foo() void {
195 \\ const struct_Foo = extern struct {
196 \\ A: c_int = @import("std").mem.zeroes(c_int),
197 \\ B: c_int = @import("std").mem.zeroes(c_int),
198 \\ C: c_int = @import("std").mem.zeroes(c_int),
199 \\ };
200 \\ _ = &struct_Foo;
201 \\ var a: struct_Foo = struct_Foo{
202 \\ .A = @as(c_int, 0),
203 \\ .B = 0,
204 \\ .C = 0,
205 \\ };
206 \\ _ = &a;
207 \\ {
208 \\ const struct_Foo_1 = extern struct {
209 \\ A: c_int = @import("std").mem.zeroes(c_int),
210 \\ B: c_int = @import("std").mem.zeroes(c_int),
211 \\ C: c_int = @import("std").mem.zeroes(c_int),
212 \\ };
213 \\ _ = &struct_Foo_1;
214 \\ var a_2: struct_Foo_1 = struct_Foo_1{
215 \\ .A = @as(c_int, 0),
216 \\ .B = 0,
217 \\ .C = 0,
218 \\ };
219 \\ _ = &a_2;
220 \\ }
221 \\}
222 });
223
224 cases.add("scoped typedef",134 cases.add("scoped typedef",
225 \\void foo() {135 \\void foo() {
226 \\ typedef union {136 \\ typedef union {
...@@ -466,16 +376,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -466,16 +376,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
466 \\pub const BAR = (@as(c_int, 1) != 0) and (@as(c_int, 2) > @as(c_int, 4));376 \\pub const BAR = (@as(c_int, 1) != 0) and (@as(c_int, 2) > @as(c_int, 4));
467 });377 });
468378
469 cases.add("struct with aligned fields",
470 \\struct foo {
471 \\ __attribute__((aligned(1))) short bar;
472 \\};
473 , &[_][]const u8{
474 \\pub const struct_foo = extern struct {
475 \\ bar: c_short align(1) = @import("std").mem.zeroes(c_short),
476 \\};
477 });
478
479 cases.add("struct with flexible array",379 cases.add("struct with flexible array",
480 \\struct foo { int x; int y[]; };380 \\struct foo { int x; int y[]; };
481 \\struct bar { int x; int y[0]; };381 \\struct bar { int x; int y[0]; };
...@@ -661,28 +561,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -661,28 +561,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
661 \\}561 \\}
662 });562 });
663563
664 cases.add("union initializer",
665 \\union { int x; char c[4]; }
666 \\ ua = {1},
667 \\ ub = {.c={'a','b','b','a'}};
668 , &[_][]const u8{
669 \\const union_unnamed_1 = extern union {
670 \\ x: c_int,
671 \\ c: [4]u8,
672 \\};
673 \\pub export var ua: union_unnamed_1 = union_unnamed_1{
674 \\ .x = @as(c_int, 1),
675 \\};
676 \\pub export var ub: union_unnamed_1 = union_unnamed_1{
677 \\ .c = [4]u8{
678 \\ 'a',
679 \\ 'b',
680 \\ 'b',
681 \\ 'a',
682 \\ },
683 \\};
684 });
685
686 cases.add("struct initializer - simple",564 cases.add("struct initializer - simple",
687 \\typedef struct { int x; } foo;565 \\typedef struct { int x; } foo;
688 \\struct {double x,y,z;} s0 = {1.2, 1.3};566 \\struct {double x,y,z;} s0 = {1.2, 1.3};
...@@ -992,21 +870,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -992,21 +870,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
992 \\};870 \\};
993 });871 });
994872
995 cases.add("pointer to struct demoted to opaque due to bit fields",
996 \\struct Foo {
997 \\ unsigned int: 1;
998 \\};
999 \\struct Bar {
1000 \\ struct Foo *foo;
1001 \\};
1002 , &[_][]const u8{
1003 \\pub const struct_Foo = opaque {};
1004 ,
1005 \\pub const struct_Bar = extern struct {
1006 \\ foo: ?*struct_Foo = @import("std").mem.zeroes(?*struct_Foo),
1007 \\};
1008 });
1009
1010 cases.add("macro with left shift",873 cases.add("macro with left shift",
1011 \\#define REDISMODULE_READ (1<<0)874 \\#define REDISMODULE_READ (1<<0)
1012 , &[_][]const u8{875 , &[_][]const u8{
...@@ -1022,45 +885,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1022,45 +885,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1022 \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> @as(c_int, 1);885 \\pub const FLASH_BANK_SIZE = FLASH_SIZE >> @as(c_int, 1);
1023 });886 });
1024887
1025 cases.add("double define struct",
1026 \\typedef struct Bar Bar;
1027 \\typedef struct Foo Foo;
1028 \\
1029 \\struct Foo {
1030 \\ Foo *a;
1031 \\};
1032 \\
1033 \\struct Bar {
1034 \\ Foo *a;
1035 \\};
1036 , &[_][]const u8{
1037 \\pub const struct_Foo = extern struct {
1038 \\ a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
1039 \\};
1040 ,
1041 \\pub const Foo = struct_Foo;
1042 ,
1043 \\pub const struct_Bar = extern struct {
1044 \\ a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
1045 \\};
1046 ,
1047 \\pub const Bar = struct_Bar;
1048 });
1049
1050 cases.add("simple struct",
1051 \\struct Foo {
1052 \\ int x;
1053 \\ char *y;
1054 \\};
1055 , &[_][]const u8{
1056 \\const struct_Foo = extern struct {
1057 \\ x: c_int = @import("std").mem.zeroes(c_int),
1058 \\ y: [*c]u8 = @import("std").mem.zeroes([*c]u8),
1059 \\};
1060 ,
1061 \\pub const Foo = struct_Foo;
1062 });
1063
1064 cases.add("self referential struct with function pointer",888 cases.add("self referential struct with function pointer",
1065 \\struct Foo {889 \\struct Foo {
1066 \\ void (*derp)(struct Foo *foo);890 \\ void (*derp)(struct Foo *foo);
...@@ -1099,44 +923,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1099,44 +923,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1099 \\pub const THING2 = THING1;923 \\pub const THING2 = THING1;
1100 });924 });
1101925
1102 cases.add("circular struct definitions",
1103 \\struct Bar;
1104 \\
1105 \\struct Foo {
1106 \\ struct Bar *next;
1107 \\};
1108 \\
1109 \\struct Bar {
1110 \\ struct Foo *next;
1111 \\};
1112 , &[_][]const u8{
1113 \\pub const struct_Bar = extern struct {
1114 \\ next: [*c]struct_Foo = @import("std").mem.zeroes([*c]struct_Foo),
1115 \\};
1116 ,
1117 \\pub const struct_Foo = extern struct {
1118 \\ next: [*c]struct_Bar = @import("std").mem.zeroes([*c]struct_Bar),
1119 \\};
1120 });
1121
1122 cases.add("#define string",926 cases.add("#define string",
1123 \\#define foo "a string"927 \\#define foo "a string"
1124 , &[_][]const u8{928 , &[_][]const u8{
1125 \\pub const foo = "a string";929 \\pub const foo = "a string";
1126 });930 });
1127931
1128 cases.add("zig keywords in C code",
1129 \\struct comptime {
1130 \\ int defer;
1131 \\};
1132 , &[_][]const u8{
1133 \\pub const struct_comptime = extern struct {
1134 \\ @"defer": c_int = @import("std").mem.zeroes(c_int),
1135 \\};
1136 ,
1137 \\pub const @"comptime" = struct_comptime;
1138 });
1139
1140 cases.add("macro with parens around negative number",932 cases.add("macro with parens around negative number",
1141 \\#define LUA_GLOBALSINDEX (-10002)933 \\#define LUA_GLOBALSINDEX (-10002)
1142 , &[_][]const u8{934 , &[_][]const u8{
...@@ -1393,88 +1185,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1393,88 +1185,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1393 \\}1185 \\}
1394 });1186 });
13951187
1396 cases.add("simple union",
1397 \\union Foo {
1398 \\ int x;
1399 \\ double y;
1400 \\};
1401 , &[_][]const u8{
1402 \\pub const union_Foo = extern union {
1403 \\ x: c_int,
1404 \\ y: f64,
1405 \\};
1406 ,
1407 \\pub const Foo = union_Foo;
1408 });
1409
1410 cases.add("packed union - simple",
1411 \\union Foo {
1412 \\ char x;
1413 \\ double y;
1414 \\} __attribute__((packed));
1415 , &[_][]const u8{
1416 \\pub const union_Foo = extern union {
1417 \\ x: u8 align(1),
1418 \\ y: f64 align(1),
1419 \\};
1420 ,
1421 \\pub const Foo = union_Foo;
1422 });
1423
1424 cases.add("packed union - nested unpacked",
1425 \\union Foo{
1426 \\ char x;
1427 \\ double y;
1428 \\ struct {
1429 \\ char a;
1430 \\ int b;
1431 \\ } z;
1432 \\} __attribute__((packed));
1433 , &[_][]const u8{
1434 // NOTE: The nested struct is *not* packed/aligned,
1435 // even though the parent struct is
1436 // this is consistent with GCC docs
1437 \\const struct_unnamed_1 = extern struct {
1438 \\ a: u8 = @import("std").mem.zeroes(u8),
1439 \\ b: c_int = @import("std").mem.zeroes(c_int),
1440 \\};
1441 ,
1442 \\pub const union_Foo = extern union {
1443 \\ x: u8 align(1),
1444 \\ y: f64 align(1),
1445 \\ z: struct_unnamed_1 align(1),
1446 \\};
1447 ,
1448 \\pub const Foo = union_Foo;
1449 });
1450
1451 cases.add("packed union - nested packed",
1452 \\union Foo{
1453 \\ char x;
1454 \\ double y;
1455 \\ struct {
1456 \\ char a;
1457 \\ int b;
1458 \\ } __attribute__((packed)) z;
1459 \\} __attribute__((packed));
1460 , &[_][]const u8{
1461 // in order for the nested struct to be packed, it must
1462 // have an independent packed declaration on
1463 // the nested type (see GCC docs for details)
1464 \\const struct_unnamed_1 = extern struct {
1465 \\ a: u8 align(1) = @import("std").mem.zeroes(u8),
1466 \\ b: c_int align(1) = @import("std").mem.zeroes(c_int),
1467 \\};
1468 ,
1469 \\pub const union_Foo = extern union {
1470 \\ x: u8 align(1),
1471 \\ y: f64 align(1),
1472 \\ z: struct_unnamed_1 align(1),
1473 \\};
1474 ,
1475 \\pub const Foo = union_Foo;
1476 });
1477
1478 cases.add("string literal",1188 cases.add("string literal",
1479 \\const char *foo(void) {1189 \\const char *foo(void) {
1480 \\ return "bar";1190 \\ return "bar";
...@@ -2395,27 +2105,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2395,27 +2105,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2395 \\ }2105 \\ }
2396 \\}2106 \\}
2397 });2107 });
2398
2399 if (builtin.os.tag != .windows) {
2400 // When clang uses the <arch>-windows-none triple it behaves as MSVC and
2401 // interprets the inner `struct Bar` as an anonymous structure
2402 cases.add("type referenced struct",
2403 \\struct Foo {
2404 \\ struct Bar{
2405 \\ int b;
2406 \\ };
2407 \\ struct Bar c;
2408 \\};
2409 , &[_][]const u8{
2410 \\pub const struct_Bar_1 = extern struct {
2411 \\ b: c_int = @import("std").mem.zeroes(c_int),
2412 \\};
2413 \\pub const struct_Foo = extern struct {
2414 \\ c: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
2415 \\};
2416 });
2417 }
2418
2419 cases.add("undefined array global",2108 cases.add("undefined array global",
2420 \\int array[100] = {};2109 \\int array[100] = {};
2421 , &[_][]const u8{2110 , &[_][]const u8{
...@@ -2634,32 +2323,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2634,32 +2323,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2634 \\pub const Foo = enum_Foo;2323 \\pub const Foo = enum_Foo;
2635 });2324 });
26362325
2637 cases.add("qualified struct and enum",
2638 \\struct Foo {
2639 \\ int x;
2640 \\ int y;
2641 \\};
2642 \\enum Bar {
2643 \\ BarA,
2644 \\ BarB,
2645 \\};
2646 \\void func(struct Foo *a, enum Bar **b);
2647 , &[_][]const u8{
2648 \\pub const struct_Foo = extern struct {
2649 \\ x: c_int = @import("std").mem.zeroes(c_int),
2650 \\ y: c_int = @import("std").mem.zeroes(c_int),
2651 \\};
2652 \\pub const BarA: c_int = 0;
2653 \\pub const BarB: c_int = 1;
2654 \\pub const enum_Bar =
2655 ++ " " ++ default_enum_type ++
2656 \\;
2657 \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
2658 ,
2659 \\pub const Foo = struct_Foo;
2660 \\pub const Bar = enum_Bar;
2661 });
2662
2663 cases.add("bitwise binary operators, simpler parens",2326 cases.add("bitwise binary operators, simpler parens",
2664 \\int max(int a, int b) {2327 \\int max(int a, int b) {
2665 \\ return (a & b) ^ (a | b);2328 \\ return (a & b) ^ (a | b);
...@@ -3756,24 +3419,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3756,24 +3419,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3756 });3419 });
3757 }3420 }
37583421
3759 cases.add("unnamed fields have predictable names",
3760 \\struct a {
3761 \\ struct {};
3762 \\};
3763 \\struct b {
3764 \\ struct {};
3765 \\};
3766 , &[_][]const u8{
3767 \\const struct_unnamed_1 = extern struct {};
3768 \\pub const struct_a = extern struct {
3769 \\ unnamed_0: struct_unnamed_1 = @import("std").mem.zeroes(struct_unnamed_1),
3770 \\};
3771 \\const struct_unnamed_2 = extern struct {};
3772 \\pub const struct_b = extern struct {
3773 \\ unnamed_0: struct_unnamed_2 = @import("std").mem.zeroes(struct_unnamed_2),
3774 \\};
3775 });
3776
3777 cases.add("integer literal promotion",3422 cases.add("integer literal promotion",
3778 \\#define GUARANTEED_TO_FIT_1 10243423 \\#define GUARANTEED_TO_FIT_1 1024
3779 \\#define GUARANTEED_TO_FIT_2 10241024L3424 \\#define GUARANTEED_TO_FIT_2 10241024L
...@@ -4283,21 +3928,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -4283,21 +3928,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
4283 \\pub const FOO = @compileError("unable to translate macro: untranslatable usage of arg `x`");3928 \\pub const FOO = @compileError("unable to translate macro: untranslatable usage of arg `x`");
4284 });3929 });
42853930
4286 cases.add("global struct whose default name conflicts with global is mangled",
4287 \\struct foo {
4288 \\ int x;
4289 \\};
4290 \\const char *struct_foo = "hello world";
4291 , &[_][]const u8{
4292 \\pub const struct_foo_1 = extern struct {
4293 \\ x: c_int = @import("std").mem.zeroes(c_int),
4294 \\};
4295 ,
4296 \\pub const foo = struct_foo_1;
4297 ,
4298 \\pub export var struct_foo: [*c]const u8 = "hello world";
4299 });
4300
4301 cases.add("unsupport declare statement at the last of a compound statement which belongs to a statement expr",3931 cases.add("unsupport declare statement at the last of a compound statement which belongs to a statement expr",
4302 \\void somefunc(void) {3932 \\void somefunc(void) {
4303 \\ int y;3933 \\ int y;