translate-c: support scoped typedef, enum and record decls

Closes #5256
This commit is contained in:
Veikka Tuominen 2021-02-17 16:26:11 +02:00
parent e2974759dd
commit d5fecbd0ba
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
3 changed files with 318 additions and 169 deletions

View File

@ -433,13 +433,13 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {
return visitFnDecl(c, @ptrCast(*const clang.FunctionDecl, decl));
},
.Typedef => {
_ = try transTypeDef(c, @ptrCast(*const clang.TypedefNameDecl, decl), true);
try transTypeDef(c, &c.global_scope.base, @ptrCast(*const clang.TypedefNameDecl, decl));
},
.Enum => {
_ = try transEnumDecl(c, @ptrCast(*const clang.EnumDecl, decl));
try transEnumDecl(c, &c.global_scope.base, @ptrCast(*const clang.EnumDecl, decl));
},
.Record => {
_ = try transRecordDecl(c, @ptrCast(*const clang.RecordDecl, decl));
try transRecordDecl(c, &c.global_scope.base, @ptrCast(*const clang.RecordDecl, decl));
},
.Var => {
return visitVarDecl(c, @ptrCast(*const clang.VarDecl, decl), null);
@ -622,11 +622,11 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
}
fn transQualTypeMaybeInitialized(c: *Context, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node {
fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node {
return if (decl_init) |init_expr|
transQualTypeInitialized(c, qt, init_expr, loc)
transQualTypeInitialized(c, scope, qt, init_expr, loc)
else
transQualType(c, qt, loc);
transQualType(c, scope, qt, loc);
}
/// if mangled_name is not null, this var decl was declared in a block scope.
@ -658,7 +658,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
var is_extern = storage_class == .Extern and !has_init;
var is_export = !is_extern and storage_class != .Static;
const type_node = transQualTypeMaybeInitialized(c, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
const type_node = transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
error.UnsupportedTranslation, error.UnsupportedType => {
return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});
},
@ -733,11 +733,6 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
return addTopLevelDecl(c, checked_name, node);
}
fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !Node {
_ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name);
return Tag.identifier.create(c.arena, builtin_name);
}
const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{
.{ "uint8_t", "u8" },
.{ "int8_t", "i8" },
@ -753,42 +748,28 @@ const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{
.{ "size_t", "usize" },
});
fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?Node {
fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNameDecl) Error!void {
if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name|
return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice
return; // Avoid processing this decl twice
const toplevel = scope.id == .root;
const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
const typedef_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
const bare_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
// TODO https://github.com/ziglang/zig/issues/3756
// TODO https://github.com/ziglang/zig/issues/1802
const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name;
if (builtin_typedef_map.get(checked_name)) |builtin| {
_ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin);
return try Tag.identifier.create(c.arena, builtin);
var name: []const u8 = if (isZigPrimitiveType(bare_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ bare_name, c.getMangle() }) else bare_name;
if (builtin_typedef_map.get(name)) |builtin| {
return c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin);
}
if (!toplevel) name = try bs.makeMangledName(c, name);
try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name);
if (!top_level_visit) {
return try Tag.identifier.create(c.arena, checked_name);
}
_ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), checked_name);
const node = (try transCreateNodeTypedef(c, typedef_decl, true, checked_name)) orelse return null;
try addTopLevelDecl(c, checked_name, node);
return try Tag.identifier.create(c.arena, checked_name);
}
fn transCreateNodeTypedef(
c: *Context,
typedef_decl: *const clang.TypedefNameDecl,
toplevel: bool,
checked_name: []const u8,
) Error!?Node {
const child_qt = typedef_decl.getUnderlyingType();
const typedef_loc = typedef_decl.getLocation();
const init_node = transQualType(c, child_qt, typedef_loc) catch |err| switch (err) {
const init_node = transQualType(c, scope, child_qt, typedef_loc) catch |err| switch (err) {
error.UnsupportedType => {
try failDecl(c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});
return null;
return failDecl(c, typedef_loc, name, "unable to resolve typedef child type", .{});
},
error.OutOfMemory => |e| return e,
};
@ -797,17 +778,25 @@ fn transCreateNodeTypedef(
payload.* = .{
.base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(toplevel)] },
.data = .{
.name = checked_name,
.name = name,
.init = init_node,
},
};
return Node.initPayload(&payload.base);
const node = Node.initPayload(&payload.base);
if (toplevel) {
try addTopLevelDecl(c, name, node);
} else {
try scope.appendNode(node);
}
}
fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Node {
fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void {
if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name|
return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice
return; // Avoid processing this decl twice
const record_loc = record_decl.getLocation();
const toplevel = scope.id == .root;
const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin());
var is_unnamed = false;
@ -826,14 +815,15 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod
} else if (record_decl.isStruct()) {
container_kind_name = "struct";
} else {
try warn(c, &c.global_scope.base, record_loc, "record {s} is not a struct or union", .{bare_name});
return null;
try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), bare_name);
return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});
}
const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
_ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
if (!toplevel) name = try bs.makeMangledName(c, name);
try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
const is_pub = !is_unnamed;
const is_pub = toplevel and !is_unnamed;
const init_node = blk: {
const record_def = record_decl.getDefinition() orelse {
_ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
@ -854,13 +844,13 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod
if (field_decl.isBitField()) {
_ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
try warn(c, &c.global_scope.base, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
break :blk Tag.opaque_literal.init();
}
if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) {
_ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
try warn(c, &c.global_scope.base, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name});
try warn(c, scope, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name});
break :blk Tag.opaque_literal.init();
}
@ -872,10 +862,10 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod
unnamed_field_count += 1;
is_anon = true;
}
const field_type = transQualType(c, field_qt, field_loc) catch |err| switch (err) {
const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) {
error.UnsupportedType => {
_ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
try warn(c, &c.global_scope.base, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name });
try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name });
break :blk Tag.opaque_literal.init();
},
else => |e| return e,
@ -891,7 +881,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod
};
if (is_anon) {
_ = try c.decl_table.put(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name);
try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name);
}
try fields.append(.{
@ -921,16 +911,21 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod
},
};
try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
if (!is_unnamed)
try c.alias_list.append(.{ .alias = bare_name, .name = name });
return try Tag.identifier.create(c.arena, name);
if (toplevel) {
try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
if (!is_unnamed)
try c.alias_list.append(.{ .alias = bare_name, .name = name });
} else {
try scope.appendNode(Node.initPayload(&payload.base));
}
}
fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node {
fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) Error!void {
if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name|
return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice
return; // Avoid processing this decl twice
const enum_loc = enum_decl.getLocation();
const toplevel = scope.id == .root;
const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
var is_unnamed = false;
@ -939,10 +934,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node {
is_unnamed = true;
}
const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
_ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
if (!toplevel) _ = try bs.makeMangledName(c, name);
try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
const is_pub = !is_unnamed;
const is_pub = toplevel and !is_unnamed;
var redecls = std.ArrayList(Tag.enum_redecl.Data()).init(c.gpa);
defer redecls.deinit();
const init_node = if (enum_decl.getDefinition()) |enum_def| blk: {
var pure_enum = true;
@ -968,10 +966,9 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node {
const init_arg_expr = if (int_type.ptr != null and
!isCBuiltinType(int_type, .UInt) and
!isCBuiltinType(int_type, .Int))
transQualType(c, int_type, enum_loc) catch |err| switch (err) {
transQualType(c, scope, int_type, enum_loc) catch |err| switch (err) {
error.UnsupportedType => {
try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
return null;
return failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
},
else => |e| return e,
}
@ -1001,11 +998,11 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node {
// In C each enum value is in the global namespace. So we put them there too.
// At this point we can rely on the enum emitting successfully.
try addTopLevelDecl(c, field_name, try Tag.enum_redecl.create(c.arena, .{
try redecls.append(.{
.enum_val_name = enum_val_name,
.field_name = field_name,
.enum_name = name,
}));
});
}
break :blk try Tag.@"enum".create(c.arena, .{
@ -1026,10 +1023,25 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node {
},
};
try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
if (!is_unnamed)
try c.alias_list.append(.{ .alias = bare_name, .name = name });
return try Tag.identifier.create(c.arena, name);
if (toplevel) {
try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
if (!is_unnamed)
try c.alias_list.append(.{ .alias = bare_name, .name = name });
} else {
try scope.appendNode(Node.initPayload(&payload.base));
}
for (redecls.items) |redecl| {
if (toplevel) {
try addTopLevelDecl(c, redecl.field_name, try Tag.pub_enum_redecl.create(c.arena, redecl));
} else {
try scope.appendNode(try Tag.enum_redecl.create(c.arena, .{
.enum_val_name = try bs.makeMangledName(c, redecl.enum_val_name),
.field_name = redecl.field_name,
.enum_name = redecl.enum_name,
}));
}
}
}
const ResultUsed = enum {
@ -1251,6 +1263,7 @@ fn transCompoundStmtInline(
const end_it = stmt.body_end();
while (it != end_it) : (it += 1) {
const result = try transStmt(c, parent_scope, it[0], .unused);
if (result.tag() == .declaration) continue;
try block.statements.append(result);
}
}
@ -1285,7 +1298,7 @@ fn transDeclStmtOne(
scope: *Scope,
decl: *const clang.Decl,
block_scope: *Scope.Block,
) TransError!Node {
) TransError!void {
switch (decl.getKind()) {
.Var => {
const var_decl = @ptrCast(*const clang.VarDecl, decl);
@ -1299,8 +1312,7 @@ fn transDeclStmtOne(
.Extern, .Static => {
// This is actually a global variable, put it in the global scope and reference it.
// `_ = mangled_name;`
try visitVarDecl(c, var_decl, mangled_name);
return try maybeSuppressResult(c, scope, .unused, try Tag.identifier.create(c.arena, mangled_name));
return visitVarDecl(c, var_decl, mangled_name);
},
else => {},
}
@ -1308,7 +1320,7 @@ fn transDeclStmtOne(
const is_const = qual_type.isConstQualified();
const loc = decl.getLocation();
const type_node = try transQualTypeMaybeInitialized(c, qual_type, decl_init, loc);
const type_node = try transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, loc);
var init_node = if (decl_init) |expr|
if (expr.getStmtClass() == .StringLiteralClass)
@ -1320,7 +1332,7 @@ fn transDeclStmtOne(
if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
init_node = try Tag.bool_to_int.create(c.arena, init_node);
}
return Tag.var_decl.create(c.arena, .{
const node = try Tag.var_decl.create(c.arena, .{
.is_pub = false,
.is_const = is_const,
.is_extern = false,
@ -1332,18 +1344,16 @@ fn transDeclStmtOne(
.type = type_node,
.init = init_node,
});
try block_scope.statements.append(node);
},
.Typedef => {
const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl);
const name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
const underlying_qual = typedef_decl.getUnderlyingType();
const underlying_type = underlying_qual.getTypePtr();
const mangled_name = try block_scope.makeMangledName(c, name);
const node = (try transCreateNodeTypedef(c, typedef_decl, false, mangled_name)) orelse
return error.UnsupportedTranslation;
return node;
try transTypeDef(c, scope, @ptrCast(*const clang.TypedefNameDecl, decl));
},
.Record => {
try transRecordDecl(c, scope, @ptrCast(*const clang.RecordDecl, decl));
},
.Enum => {
try transEnumDecl(c, scope, @ptrCast(*const clang.EnumDecl, decl));
},
else => |kind| return fail(
c,
@ -1356,21 +1366,14 @@ fn transDeclStmtOne(
}
fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node {
const block_scope = scope.findBlockScope(c) catch unreachable;
const block_scope = try scope.findBlockScope(c);
var it = stmt.decl_begin();
const end_it = stmt.decl_end();
assert(it != end_it);
while (true) : (it += 1) {
const node = try transDeclStmtOne(c, scope, it[0], block_scope);
if (it + 1 == end_it) {
return node;
} else {
try block_scope.statements.append(node);
}
while (it != end_it) : (it += 1) {
try transDeclStmtOne(c, scope, it[0], block_scope);
}
unreachable;
return Tag.declaration.init();
}
fn transDeclRefExpr(
@ -1619,7 +1622,7 @@ fn transIntegerLiteral(
// @as(T, x)
const expr_base = @ptrCast(*const clang.Expr, expr);
const ty_node = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc());
const ty_node = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc());
const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt());
const as = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs });
return maybeSuppressResult(c, scope, result_used, as);
@ -1697,7 +1700,7 @@ fn transStringLiteralAsArray(
const ty = expr_base.getType().getTypePtr();
const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
const elem_type = try transQualType(c, const_arr_ty.getElementType(), expr_base.getBeginLoc());
const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc());
const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type });
const init_list = try c.arena.alloc(Node, array_size);
@ -1744,9 +1747,9 @@ fn transCCast(
if (qualTypeCanon(dst_type).isVoidType()) return expr;
if (dst_type.eq(src_type)) return expr;
if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type))
return transCPtrCast(c, loc, dst_type, src_type, expr);
return transCPtrCast(c, scope, loc, dst_type, src_type, expr);
const dst_node = try transQualType(c, dst_type, loc);
const dst_node = try transQualType(c, scope, dst_type, loc);
if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) {
// 1. If src_type is an enum, determine the underlying signed int type
// 2. Extend or truncate without changing signed-ness.
@ -1903,7 +1906,7 @@ fn transInitListExprRecord(
const record_def = record_decl.getDefinition() orelse
unreachable;
const ty_node = try transType(c, ty, loc);
const ty_node = try transType(c, scope, ty, loc);
const init_count = expr.getNumInits();
var field_inits = std.ArrayList(ast.Payload.ContainerInit.Initializer).init(c.gpa);
defer field_inits.deinit();
@ -1952,7 +1955,7 @@ fn transInitListExprArray(
) TransError!Node {
const arr_type = ty.getAsArrayTypeUnsafe();
const child_qt = arr_type.getElementType();
const child_type = try transQualType(c, child_qt, loc);
const child_type = try transQualType(c, scope, child_qt, loc);
const init_count = expr.getNumInits();
assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType());
const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type);
@ -2217,7 +2220,7 @@ fn transForLoop(
block_scope = try Scope.Block.init(c, scope, false);
loop_scope.parent = &block_scope.?.base;
const init_node = try transStmt(c, &block_scope.?.base, init, .unused);
try block_scope.?.statements.append(init_node);
if (init_node.tag() != .declaration) try block_scope.?.statements.append(init_node);
}
var cond_scope = Scope.Condition{
.base = .{
@ -2328,7 +2331,7 @@ fn transCase(
scope: *Scope,
stmt: *const clang.CaseStmt,
) TransError!Node {
const block_scope = scope.findBlockScope(c) catch unreachable;
const block_scope = try scope.findBlockScope(c);
const switch_scope = scope.getSwitch();
const label = try block_scope.makeMangledName(c, "case");
@ -2366,7 +2369,7 @@ fn transDefault(
scope: *Scope,
stmt: *const clang.DefaultStmt,
) TransError!Node {
const block_scope = scope.findBlockScope(c) catch unreachable;
const block_scope = try scope.findBlockScope(c);
const switch_scope = scope.getSwitch();
switch_scope.default_label = try block_scope.makeMangledName(c, "default");
@ -2400,7 +2403,7 @@ fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used:
// @as(T, x)
const expr_base = @ptrCast(*const clang.Expr, expr);
const as_node = try Tag.as.create(c.arena, .{
.lhs = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()),
.lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()),
.rhs = try transCreateNodeAPInt(c, result.Val.getInt()),
});
return maybeSuppressResult(c, scope, used, as_node);
@ -2446,7 +2449,7 @@ fn transCharLiteral(
// @as(T, x)
const expr_base = @ptrCast(*const clang.Expr, stmt);
const as_node = try Tag.as.create(c.arena, .{
.lhs = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()),
.lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()),
.rhs = int_lit_node,
});
return maybeSuppressResult(c, scope, result_used, as_node);
@ -2464,6 +2467,7 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used:
const end_it = comp.body_end();
while (it != end_it - 1) : (it += 1) {
const result = try transStmt(c, &block_scope.base, it[0], .unused);
if (result.tag() == .declaration) continue;
try block_scope.statements.append(result);
}
const break_node = try Tag.break_val.create(c.arena, .{
@ -2657,7 +2661,7 @@ fn transUnaryExprOrTypeTraitExpr(
result_used: ResultUsed,
) TransError!Node {
const loc = stmt.getBeginLoc();
const type_node = try transQualType(c, stmt.getTypeOfArgument(), loc);
const type_node = try transQualType(c, scope, stmt.getTypeOfArgument(), loc);
const kind = stmt.getKind();
switch (kind) {
@ -2917,9 +2921,9 @@ fn transCreateCompoundAssign(
if (is_shift or requires_int_cast) {
// @intCast(rhs)
const cast_to_type = if (is_shift)
try qualTypeToLog2IntRef(c, getExprQualType(c, rhs), loc)
try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
else
try transQualType(c, getExprQualType(c, lhs), loc);
try transQualType(c, scope, getExprQualType(c, lhs), loc);
rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
}
@ -2960,9 +2964,9 @@ fn transCreateCompoundAssign(
if (is_shift or requires_int_cast) {
// @intCast(rhs)
const cast_to_type = if (is_shift)
try qualTypeToLog2IntRef(c, getExprQualType(c, rhs), loc)
try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
else
try transQualType(c, getExprQualType(c, lhs), loc);
try transQualType(c, scope, getExprQualType(c, lhs), loc);
rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
}
@ -2981,6 +2985,7 @@ fn transCreateCompoundAssign(
fn transCPtrCast(
c: *Context,
scope: *Scope,
loc: clang.SourceLocation,
dst_type: clang.QualType,
src_type: clang.QualType,
@ -2990,7 +2995,7 @@ fn transCPtrCast(
const child_type = ty.getPointeeType();
const src_ty = src_type.getTypePtr();
const src_child_type = src_ty.getPointeeType();
const dst_type_node = try transType(c, ty, loc);
const dst_type_node = try transType(c, scope, ty, loc);
if ((src_child_type.isConstQualified() and
!child_type.isConstQualified()) or
@ -3011,7 +3016,7 @@ fn transCPtrCast(
// For opaque types a ptrCast is enough
expr
else blk: {
const child_type_node = try transQualType(c, child_type, loc);
const child_type_node = try transQualType(c, scope, child_type, loc);
const alignof = try Tag.alignof.create(c.arena, child_type_node);
const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });
break :blk align_cast;
@ -3160,6 +3165,7 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
/// by the size of the initializer
fn transQualTypeInitialized(
c: *Context,
scope: *Scope,
qt: clang.QualType,
decl_init: *const clang.Expr,
source_loc: clang.SourceLocation,
@ -3167,7 +3173,7 @@ fn transQualTypeInitialized(
const ty = qt.getTypePtr();
if (ty.getTypeClass() == .IncompleteArray) {
const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty);
const elem_ty = try transType(c, incomplete_array_ty.getElementType().getTypePtr(), source_loc);
const elem_ty = try transType(c, scope, incomplete_array_ty.getElementType().getTypePtr(), source_loc);
switch (decl_init.getStmtClass()) {
.StringLiteralClass => {
@ -3184,11 +3190,11 @@ fn transQualTypeInitialized(
else => {},
}
}
return transQualType(c, qt, source_loc);
return transQualType(c, scope, qt, source_loc);
}
fn transQualType(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node {
return transType(c, qt.getTypePtr(), source_loc);
fn transQualType(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node {
return transType(c, scope, qt.getTypePtr(), source_loc);
}
/// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness.
@ -3273,7 +3279,7 @@ fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType) !u32 {
}
}
fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) !Node {
fn qualTypeToLog2IntRef(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) !Node {
const int_bit_width = try qualTypeIntBitWidth(c, qt);
if (int_bit_width != 0) {
@ -3282,7 +3288,7 @@ fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.Sourc
return Tag.log2_int_type.create(c.arena, cast_bit_width);
}
const zig_type = try transQualType(c, qt, source_loc);
const zig_type = try transQualType(c, scope, qt, source_loc);
return Tag.std_math_Log2Int.create(c.arena, zig_type);
}
@ -3641,14 +3647,14 @@ fn transCreateNodeShiftOp(
const lhs = try transExpr(c, scope, lhs_expr, .used);
const rhs_type = try qualTypeToLog2IntRef(c, stmt.getType(), rhs_location);
const rhs_type = try qualTypeToLog2IntRef(c, scope, stmt.getType(), rhs_location);
const rhs = try transExprCoercing(c, scope, rhs_expr, .used);
const rhs_casted = try Tag.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs });
return transCreateNodeInfixOp(c, scope, op, lhs, rhs_casted, used);
}
fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node {
fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node {
switch (ty.getTypeClass()) {
.Builtin => {
const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
@ -3687,16 +3693,16 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio
},
.Paren => {
const paren_ty = @ptrCast(*const clang.ParenType, ty);
return transQualType(c, paren_ty.getInnerType(), source_loc);
return transQualType(c, scope, paren_ty.getInnerType(), source_loc);
},
.Pointer => {
const child_qt = ty.getPointeeType();
if (qualTypeChildIsFnProto(child_qt)) {
return Tag.optional_type.create(c.arena, try transQualType(c, child_qt, source_loc));
return Tag.optional_type.create(c.arena, try transQualType(c, scope, child_qt, source_loc));
}
const is_const = child_qt.isConstQualified();
const is_volatile = child_qt.isVolatileQualified();
const elem_type = try transQualType(c, child_qt, source_loc);
const elem_type = try transQualType(c, scope, child_qt, source_loc);
if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) {
const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
return Tag.optional_type.create(c.arena, ptr);
@ -3709,7 +3715,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio
const size_ap_int = const_arr_ty.getSize();
const size = size_ap_int.getLimitedValue(math.maxInt(usize));
const elem_type = try transType(c, const_arr_ty.getElementType().getTypePtr(), source_loc);
const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc);
return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type });
},
@ -3719,7 +3725,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio
const child_qt = incomplete_array_ty.getElementType();
const is_const = child_qt.isConstQualified();
const is_volatile = child_qt.isVolatileQualified();
const elem_type = try transQualType(c, child_qt, source_loc);
const elem_type = try transQualType(c, scope, child_qt, source_loc);
return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
},
@ -3727,38 +3733,41 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio
const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
const typedef_decl = typedef_ty.getDecl();
return (try transTypeDef(c, typedef_decl, false)) orelse
fail(c, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{});
try transTypeDef(c, scope, typedef_decl);
const name = c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl())).?;
return Tag.identifier.create(c.arena, name);
},
.Record => {
const record_ty = @ptrCast(*const clang.RecordType, ty);
const record_decl = record_ty.getDecl();
return (try transRecordDecl(c, record_decl)) orelse
fail(c, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{});
try transRecordDecl(c, scope, record_decl);
const name = c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl())).?;
return Tag.identifier.create(c.arena, name);
},
.Enum => {
const enum_ty = @ptrCast(*const clang.EnumType, ty);
const enum_decl = enum_ty.getDecl();
return (try transEnumDecl(c, enum_decl)) orelse
fail(c, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{});
try transEnumDecl(c, scope, enum_decl);
const name = c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl())).?;
return Tag.identifier.create(c.arena, name);
},
.Elaborated => {
const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);
return transQualType(c, elaborated_ty.getNamedType(), source_loc);
return transQualType(c, scope, elaborated_ty.getNamedType(), source_loc);
},
.Decayed => {
const decayed_ty = @ptrCast(*const clang.DecayedType, ty);
return transQualType(c, decayed_ty.getDecayedType(), source_loc);
return transQualType(c, scope, decayed_ty.getDecayedType(), source_loc);
},
.Attributed => {
const attributed_ty = @ptrCast(*const clang.AttributedType, ty);
return transQualType(c, attributed_ty.getEquivalentType(), source_loc);
return transQualType(c, scope, attributed_ty.getEquivalentType(), source_loc);
},
.MacroQualified => {
const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty);
return transQualType(c, macroqualified_ty.getModifiedType(), source_loc);
return transQualType(c, scope, macroqualified_ty.getModifiedType(), source_loc);
},
else => {
const type_name = c.str(ty.getTypeClassName());
@ -3890,6 +3899,7 @@ fn finishTransFnProto(
) !*ast.Payload.Func {
const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
const scope = &c.global_scope.base;
// TODO check for always_inline attribute
// TODO check for align attribute
@ -3914,7 +3924,7 @@ fn finishTransFnProto(
break :blk param_name;
} else null;
const type_node = try transQualType(c, param_qt, source_loc);
const type_node = try transQualType(c, scope, param_qt, source_loc);
fn_params.addOneAssumeCapacity().* = .{
.is_noalias = is_noalias,
@ -3955,9 +3965,9 @@ fn finishTransFnProto(
// convert primitive c_void to actual void (only for return type)
break :blk Tag.void_type.init();
} else {
break :blk transQualType(c, return_qt, source_loc) catch |err| switch (err) {
break :blk transQualType(c, scope, return_qt, source_loc) catch |err| switch (err) {
error.UnsupportedType => {
try warn(c, &c.global_scope.base, source_loc, "unsupported function proto return type", .{});
try warn(c, scope, source_loc, "unsupported function proto return type", .{});
return err;
},
error.OutOfMemory => |e| return e,

View File

@ -14,6 +14,8 @@ pub const Node = extern union {
ptr_otherwise: *Payload,
pub const Tag = enum {
/// Declarations add themselves to the correct scopes and should not be emitted as this tag.
declaration,
null_literal,
undefined_literal,
/// opaque {}
@ -186,6 +188,7 @@ pub const Node = extern union {
/// pub const name = init;
pub_var_simple,
/// pub const enum_field_name = @enumToInt(enum_name.field_name);
pub_enum_redecl,
enum_redecl,
/// pub inline fn name(params) return_type body
@ -201,6 +204,7 @@ pub const Node = extern union {
pub fn Type(comptime t: Tag) type {
return switch (t) {
.declaration,
.null_literal,
.undefined_literal,
.opaque_literal,
@ -325,7 +329,7 @@ pub const Node = extern union {
.arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
.log2_int_type => Payload.Log2IntType,
.var_simple, .pub_var_simple => Payload.SimpleVarDecl,
.enum_redecl => Payload.EnumRedecl,
.pub_enum_redecl, .enum_redecl => Payload.EnumRedecl,
.array_filler => Payload.ArrayFiller,
.pub_inline_fn => Payload.PubInlineFn,
.field_access => Payload.FieldAccess,
@ -742,6 +746,7 @@ fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange {
fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
switch (node.tag()) {
.declaration => unreachable,
.warning => {
const payload = node.castTag(.warning).?.data;
try c.buf.appendSlice(payload);
@ -1585,9 +1590,9 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
},
});
},
.enum_redecl => {
const payload = node.castTag(.enum_redecl).?.data;
_ = try c.addToken(.keyword_pub, "pub");
.pub_enum_redecl, .enum_redecl => {
const payload = @fieldParentPtr(Payload.EnumRedecl, "base", node.ptr_otherwise).data;
if (node.tag() == .pub_enum_redecl) _ = try c.addToken(.keyword_pub, "pub");
const const_tok = try c.addToken(.keyword_const, "const");
_ = try c.addIdentifier(payload.enum_val_name);
_ = try c.addToken(.equal, "=");
@ -1878,6 +1883,7 @@ fn addSemicolonIfNotBlock(c: *Context, node: Node) !void {
fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
switch (node.tag()) {
.declaration => unreachable,
.null_literal,
.undefined_literal,
.true_literal,
@ -1991,6 +1997,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.alias,
.var_simple,
.pub_var_simple,
.pub_enum_redecl,
.enum_redecl,
.@"while",
.@"switch",

View File

@ -3,6 +3,136 @@ const std = @import("std");
const CrossTarget = std.zig.CrossTarget;
pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("scoped enum",
\\void foo() {
\\ enum Foo {
\\ A,
\\ B,
\\ C,
\\ };
\\ enum Foo a = B;
\\ {
\\ enum Foo {
\\ A,
\\ B,
\\ C,
\\ };
\\ enum Foo a = B;
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ const enum_Foo = extern enum(c_int) {
\\ A,
\\ B,
\\ C,
\\ _,
\\ };
\\ const A = @enumToInt(enum_Foo.A);
\\ const B = @enumToInt(enum_Foo.B);
\\ const C = @enumToInt(enum_Foo.C);
\\ var a: enum_Foo = @intToEnum(enum_Foo, B);
\\ {
\\ const enum_Foo = extern enum(c_int) {
\\ A,
\\ B,
\\ C,
\\ _,
\\ };
\\ const A_2 = @enumToInt(enum_Foo.A);
\\ const B_3 = @enumToInt(enum_Foo.B);
\\ const C_4 = @enumToInt(enum_Foo.C);
\\ var a_5: enum_Foo = @intToEnum(enum_Foo, B_3);
\\ }
\\}
});
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,
\\ B: c_int,
\\ C: c_int,
\\ };
\\ var a: struct_Foo = struct_Foo{
\\ .A = @as(c_int, 0),
\\ .B = 0,
\\ .C = 0,
\\ };
\\ {
\\ const struct_Foo_1 = extern struct {
\\ A: c_int,
\\ B: c_int,
\\ C: c_int,
\\ };
\\ var a_2: struct_Foo_1 = struct_Foo_1{
\\ .A = @as(c_int, 0),
\\ .B = 0,
\\ .C = 0,
\\ };
\\ }
\\}
});
cases.add("scoped typedef",
\\void foo() {
\\ typedef union {
\\ int A;
\\ int B;
\\ int C;
\\ } Foo;
\\ Foo a = {0};
\\ {
\\ typedef union {
\\ int A;
\\ int B;
\\ int C;
\\ } Foo;
\\ Foo a = {0};
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ const union_unnamed_1 = extern union {
\\ A: c_int,
\\ B: c_int,
\\ C: c_int,
\\ };
\\ const Foo = union_unnamed_1;
\\ var a: Foo = Foo{
\\ .A = @as(c_int, 0),
\\ };
\\ {
\\ const union_unnamed_2 = extern union {
\\ A: c_int,
\\ B: c_int,
\\ C: c_int,
\\ };
\\ const Foo_1 = union_unnamed_2;
\\ var a_2: Foo_1 = Foo_1{
\\ .A = @as(c_int, 0),
\\ };
\\ }
\\}
});
cases.add("use cast param as macro fn return type",
\\#define MEM_PHYSICAL_TO_K0(x) (void*)((u32)(x) + SYS_BASE_CACHED)
, &[_][]const u8{
@ -62,7 +192,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export var bar: f32 = @import("std").mem.zeroes(f32);
\\threadlocal var bar_1: c_int = 2;
\\pub export fn foo() c_int {
\\ _ = bar_1;
\\ return 0;
\\}
});
@ -579,9 +708,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ '2',
\\ 0,
\\};
\\pub export fn foo() void {
\\ _ = v2;
\\}
\\pub export fn foo() void {}
});
cases.add("simple function definition",
@ -1355,11 +1482,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\extern enum enum_ty my_enum;
\\enum enum_ty { FOO };
, &[_][]const u8{
\\pub const FOO = @enumToInt(enum_enum_ty.FOO);
\\pub const enum_enum_ty = extern enum(c_int) {
\\ FOO,
\\ _,
\\};
\\pub const FOO = @enumToInt(enum_enum_ty.FOO);
\\pub extern var my_enum: enum_enum_ty;
});
@ -1501,48 +1628,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ p,
\\};
, &[_][]const u8{
\\pub const a = @enumToInt(enum_unnamed_1.a);
\\pub const b = @enumToInt(enum_unnamed_1.b);
\\pub const c = @enumToInt(enum_unnamed_1.c);
\\const enum_unnamed_1 = extern enum(c_int) {
\\ a,
\\ b,
\\ c,
\\ _,
\\};
\\pub const a = @enumToInt(enum_unnamed_1.a);
\\pub const b = @enumToInt(enum_unnamed_1.b);
\\pub const c = @enumToInt(enum_unnamed_1.c);
\\pub const d = enum_unnamed_1;
\\pub const e = @enumToInt(enum_unnamed_2.e);
\\pub const f = @enumToInt(enum_unnamed_2.f);
\\pub const g = @enumToInt(enum_unnamed_2.g);
\\const enum_unnamed_2 = extern enum(c_int) {
\\ e = 0,
\\ f = 4,
\\ g = 5,
\\ _,
\\};
\\pub const e = @enumToInt(enum_unnamed_2.e);
\\pub const f = @enumToInt(enum_unnamed_2.f);
\\pub const g = @enumToInt(enum_unnamed_2.g);
\\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);
\\pub const i = @enumToInt(enum_unnamed_3.i);
\\pub const j = @enumToInt(enum_unnamed_3.j);
\\pub const k = @enumToInt(enum_unnamed_3.k);
\\const enum_unnamed_3 = extern enum(c_int) {
\\ i,
\\ j,
\\ k,
\\ _,
\\};
\\pub const i = @enumToInt(enum_unnamed_3.i);
\\pub const j = @enumToInt(enum_unnamed_3.j);
\\pub const k = @enumToInt(enum_unnamed_3.k);
\\pub const struct_Baz = extern struct {
\\ l: enum_unnamed_3,
\\ m: d,
\\};
\\pub const n = @enumToInt(enum_i.n);
\\pub const o = @enumToInt(enum_i.o);
\\pub const p = @enumToInt(enum_i.p);
\\pub const enum_i = extern enum(c_int) {
\\ n,
\\ o,
\\ p,
\\ _,
\\};
\\pub const n = @enumToInt(enum_i.n);
\\pub const o = @enumToInt(enum_i.o);
\\pub const p = @enumToInt(enum_i.p);
,
\\pub const Baz = struct_Baz;
});
@ -1989,13 +2116,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ Two,
\\};
, &[_][]const u8{
\\pub const One = @enumToInt(enum_unnamed_1.One);
\\pub const Two = @enumToInt(enum_unnamed_1.Two);
\\const enum_unnamed_1 = extern enum(c_int) {
\\ One,
\\ Two,
\\ _,
\\};
\\pub const One = @enumToInt(enum_unnamed_1.One);
\\pub const Two = @enumToInt(enum_unnamed_1.Two);
});
cases.add("c style cast",
@ -2093,15 +2220,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
\\}
, &[_][]const u8{
\\pub const FooA = @enumToInt(enum_Foo.A);
\\pub const FooB = @enumToInt(enum_Foo.B);
\\pub const FooC = @enumToInt(enum_Foo.C);
\\pub const enum_Foo = extern enum(c_int) {
\\ A,
\\ B,
\\ C,
\\ _,
\\};
\\pub const FooA = @enumToInt(enum_Foo.A);
\\pub const FooB = @enumToInt(enum_Foo.B);
\\pub const FooC = @enumToInt(enum_Foo.C);
\\pub const SomeTypedef = c_int;
\\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int {
\\ var a = arg_a;
@ -2147,6 +2274,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ B,
\\ _,
\\};
\\pub const BarA = @enumToInt(enum_Bar.A);
\\pub const BarB = @enumToInt(enum_Bar.B);
\\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
,
\\pub const Foo = struct_Foo;
@ -2413,6 +2542,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ C,
\\ _,
\\};
\\pub const A = @enumToInt(enum_SomeEnum.A);
\\pub const B = @enumToInt(enum_SomeEnum.B);
\\pub const C = @enumToInt(enum_SomeEnum.C);
\\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int {
\\ var a = arg_a;
\\ var b = arg_b;
@ -2872,15 +3004,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ Foo1,
\\};
, &[_][]const u8{
\\pub const FooA = @enumToInt(enum_Foo.A);
\\pub const FooB = @enumToInt(enum_Foo.B);
\\pub const Foo1 = @enumToInt(enum_Foo.@"1");
\\pub const enum_Foo = extern enum(c_int) {
\\ A = 2,
\\ B = 5,
\\ @"1" = 6,
\\ _,
\\};
\\pub const FooA = @enumToInt(enum_Foo.A);
\\pub const FooB = @enumToInt(enum_Foo.B);
\\pub const Foo1 = @enumToInt(enum_Foo.@"1");
,
\\pub const Foo = enum_Foo;
});