Commit 20563d8457
Changed files (4)
lib
compiler
aro_translate_c
test
cases
run_translated_c
lib/compiler/aro_translate_c/ast.zig
@@ -55,6 +55,8 @@ pub const Node = extern union {
var_decl,
/// const name = struct { init }
static_local_var,
+ /// const ExternLocal_name = struct { init }
+ extern_local_var,
/// var name = init.*
mut_str,
func,
@@ -365,7 +367,7 @@ pub const Node = extern union {
.c_pointer, .single_pointer => Payload.Pointer,
.array_type, .null_sentinel_array_type => Payload.Array,
.arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
- .var_simple, .pub_var_simple, .static_local_var, .mut_str => Payload.SimpleVarDecl,
+ .var_simple, .pub_var_simple, .static_local_var, .extern_local_var, .mut_str => Payload.SimpleVarDecl,
.enum_constant => Payload.EnumConstant,
.array_filler => Payload.ArrayFiller,
.pub_inline_fn => Payload.PubInlineFn,
@@ -1269,6 +1271,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
},
});
},
+ .extern_local_var => {
+ const payload = node.castTag(.extern_local_var).?.data;
+
+ const const_tok = try c.addToken(.keyword_const, "const");
+ _ = try c.addIdentifier(payload.name);
+ _ = try c.addToken(.equal, "=");
+
+ const kind_tok = try c.addToken(.keyword_struct, "struct");
+ _ = try c.addToken(.l_brace, "{");
+
+ const container_def = try c.addNode(.{
+ .tag = .container_decl_two_trailing,
+ .main_token = kind_tok,
+ .data = .{
+ .lhs = try renderNode(c, payload.init),
+ .rhs = 0,
+ },
+ });
+ _ = try c.addToken(.r_brace, "}");
+ _ = try c.addToken(.semicolon, ";");
+
+ return c.addNode(.{
+ .tag = .simple_var_decl,
+ .main_token = const_tok,
+ .data = .{
+ .lhs = 0,
+ .rhs = container_def,
+ },
+ });
+ },
.mut_str => {
const payload = node.castTag(.mut_str).?.data;
@@ -2292,7 +2324,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn
fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
switch (node.tag()) {
.warning => unreachable,
- .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .mut_str => {},
+ .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .extern_local_var, .mut_str => {},
.while_true => {
const payload = node.castTag(.while_true).?.data;
return addSemicolonIfNotBlock(c, payload);
@@ -2388,6 +2420,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.shuffle,
.builtin_extern,
.static_local_var,
+ .extern_local_var,
.mut_str,
.macro_arithmetic,
=> {
lib/compiler/aro_translate_c.zig
@@ -1277,6 +1277,12 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
/// struct itself is given this name.
pub const static_inner_name = "static";
+ /// C extern variables declared within a block are wrapped in a block-local
+ /// struct. The struct is named ExternLocal_[variable_name], the Zig variable
+ /// within the struct itself is [variable_name] by neccessity since it's an
+ /// extern reference to an existing symbol.
+ pub const extern_inner_prepend = "ExternLocal";
+
pub fn init(c: *ScopeExtraContext, parent: *ScopeExtraScope, labeled: bool) !Block {
var blk = Block{
.base = .{
@@ -1356,6 +1362,24 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
return scope.base.parent.?.getAlias(name);
}
+ /// Finds the (potentially) mangled struct name for a locally scoped extern variable given the original declaration name.
+ ///
+ /// Block scoped extern declarations translate to:
+ /// const MangledStructName = struct {extern [qualifiers] original_extern_variable_name: [type]};
+ /// This finds MangledStructName given original_extern_variable_name for referencing correctly in transDeclRefExpr()
+ pub fn getLocalExternAlias(scope: *Block, name: []const u8) ?[]const u8 {
+ for (scope.statements.items) |node| {
+ if (node.tag() == .extern_local_var) {
+ const parent_node = node.castTag(.extern_local_var).?;
+ const init_node = parent_node.data.init.castTag(.var_decl).?;
+ if (std.mem.eql(u8, init_node.data.name, name)) {
+ return parent_node.data.name;
+ }
+ }
+ }
+ return null;
+ }
+
pub fn localContains(scope: *Block, name: []const u8) bool {
for (scope.variables.items) |p| {
if (std.mem.eql(u8, p.alias, name))
@@ -1451,6 +1475,16 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
};
}
+ pub fn getLocalExternAlias(scope: *ScopeExtraScope, name: []const u8) ?[]const u8 {
+ return switch (scope.id) {
+ .block => ret: {
+ const block = @as(*Block, @fieldParentPtr("base", scope));
+ break :ret block.getLocalExternAlias(name);
+ },
+ .root, .loop, .do_loop, .condition => null,
+ };
+ }
+
pub fn contains(scope: *ScopeExtraScope, name: []const u8) bool {
return switch (scope.id) {
.root => @as(*Root, @fieldParentPtr("base", scope)).contains(name),
src/translate_c.zig
@@ -1734,6 +1734,48 @@ const ClangAlignment = struct {
}
};
+/// Translate an "extern" variable that's been declared within a scoped block.
+/// Similar to static local variables, this will be wrapped in a struct to work with Zig's syntax requirements.
+///
+/// Assumptions made:
+/// - No need to mangle the actual NamedDecl, as by definition this MUST be the same name as the external symbol it's referencing
+/// - It's not valid C to have an initializer with this type of declaration, so we can safely operate assuming no initializer
+/// - No need to look for any cleanup attributes with getCleanupAttribute(), not relevant for this type of decl
+fn transLocalExternStmt(c: *Context, scope: *Scope, var_decl: *const clang.VarDecl, block_scope: *Scope.Block) TransError!void {
+ const extern_var_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin());
+
+ // Special naming convention for local extern variable wrapper struct
+ const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ Scope.Block.extern_inner_prepend, extern_var_name });
+
+ // On the off chance there's already a variable in scope named "ExternLocal_[extern_var_name]"
+ const mangled_name = try block_scope.makeMangledName(c, name);
+
+ const qual_type = var_decl.getTypeSourceInfo_getType();
+ const is_const = qual_type.isConstQualified();
+ const loc = var_decl.getLocation();
+ const type_node = try transQualType(c, scope, qual_type, loc);
+
+ // Inner Node for the extern variable declaration
+ var node = try Tag.var_decl.create(c.arena, .{
+ .is_pub = false,
+ .is_const = is_const,
+ .is_extern = true,
+ .is_export = false,
+ .is_threadlocal = var_decl.getTLSKind() != .None, // TODO: Neccessary?
+ .linksection_string = null, // TODO: Neccessary?
+ .alignment = ClangAlignment.forVar(c, var_decl).zigAlignment(),
+ .name = extern_var_name,
+ .type = type_node,
+ .init = null,
+ });
+
+ // Outer Node for the wrapper struct
+ node = try Tag.extern_local_var.create(c.arena, .{ .name = mangled_name, .init = node });
+
+ try block_scope.statements.append(node);
+ try block_scope.discardVariable(c, mangled_name);
+}
+
fn transDeclStmtOne(
c: *Context,
scope: *Scope,
@@ -1743,6 +1785,13 @@ fn transDeclStmtOne(
switch (decl.getKind()) {
.Var => {
const var_decl = @as(*const clang.VarDecl, @ptrCast(decl));
+
+ // Translation behavior for a block scope declared "extern" variable
+ // is enough of an outlier that it needs it's own function
+ if (var_decl.getStorageClass() == .Extern) {
+ return transLocalExternStmt(c, scope, var_decl, block_scope);
+ }
+
const decl_init = var_decl.getInit();
const loc = decl.getLocation();
@@ -1750,11 +1799,7 @@ fn transDeclStmtOne(
const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin());
const mangled_name = try block_scope.makeMangledName(c, name);
- if (var_decl.getStorageClass() == .Extern) {
- // This is actually a global variable, put it in the global scope and reference it.
- // `_ = mangled_name;`
- return visitVarDecl(c, var_decl, mangled_name);
- } else if (qualTypeWasDemotedToOpaque(c, qual_type)) {
+ if (qualTypeWasDemotedToOpaque(c, qual_type)) {
return fail(c, error.UnsupportedTranslation, loc, "local variable has opaque type", .{});
}
@@ -1851,18 +1896,37 @@ fn transDeclRefExpr(
const value_decl = expr.getDecl();
const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(value_decl)).getName_bytes_begin());
const mangled_name = scope.getAlias(name);
- var ref_expr = if (cIsFunctionDeclRef(@as(*const clang.Expr, @ptrCast(expr))))
- try Tag.fn_identifier.create(c.arena, mangled_name)
- else
- try Tag.identifier.create(c.arena, mangled_name);
+ const decl_is_var = @as(*const clang.Decl, @ptrCast(value_decl)).getKind() == .Var;
+ const potential_local_extern = if (decl_is_var) ((@as(*const clang.VarDecl, @ptrCast(value_decl)).getStorageClass() == .Extern) and (scope.id == .block)) else false;
+
+ var confirmed_local_extern = false;
+ var ref_expr = val: {
+ if (cIsFunctionDeclRef(@as(*const clang.Expr, @ptrCast(expr)))) {
+ break :val try Tag.fn_identifier.create(c.arena, mangled_name);
+ } else if (potential_local_extern) {
+ if (scope.getLocalExternAlias(name)) |v| {
+ confirmed_local_extern = true;
+ break :val try Tag.identifier.create(c.arena, v);
+ } else {
+ break :val try Tag.identifier.create(c.arena, mangled_name);
+ }
+ } else {
+ break :val try Tag.identifier.create(c.arena, mangled_name);
+ }
+ };
- if (@as(*const clang.Decl, @ptrCast(value_decl)).getKind() == .Var) {
+ if (decl_is_var) {
const var_decl = @as(*const clang.VarDecl, @ptrCast(value_decl));
if (var_decl.isStaticLocal()) {
ref_expr = try Tag.field_access.create(c.arena, .{
.lhs = ref_expr,
.field_name = Scope.Block.static_inner_name,
});
+ } else if (confirmed_local_extern) {
+ ref_expr = try Tag.field_access.create(c.arena, .{
+ .lhs = ref_expr,
+ .field_name = name, // by necessity, name will always == mangled_name
+ });
}
}
scope.skipVariableDiscard(mangled_name);
test/cases/run_translated_c/extern_typedef_variables_in_functions.c
@@ -0,0 +1,18 @@
+const int ev = 40;
+
+static int func(void)
+{
+ typedef int test_type_t;
+ extern const test_type_t ev;
+ return ev + 2;
+}
+
+int main()
+{
+ if (func() != 42)
+ return 1;
+ return 0;
+}
+
+// run-translated-c
+// c_frontend=clang