feature. See also
. The project being documented here (as the example) is the Zig library itself.
Translator.transVarDecl
fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable, decl_node: Node.Index) Error!void
File
Code
fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable, decl_node: Node.Index) Error!void {
const base_name = t.tree.tokSlice(variable.name_tok);
const toplevel = scope.id == .root;
const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
const name, const use_base_name = blk: {
if (toplevel) break :blk .{ base_name, false };
const prefix: ?[]const u8 = switch (variable.storage_class) {
.@"extern" => Scope.Block.extern_local_prefix,
.static => Scope.Block.static_local_prefix,
else => null,
};
break :blk .{ try bs.createMangledName(base_name, false, prefix), prefix != null };
};
if (t.typeWasDemotedToOpaque(variable.qt)) {
if (variable.storage_class != .@"extern" and scope.id == .root) {
return t.failDecl(scope, variable.name_tok, name, "non-extern variable has opaque type", .{});
} else {
return t.failDecl(scope, variable.name_tok, name, "local variable has opaque type", .{});
}
}
const type_node = (if (variable.initializer) |init|
t.transTypeInit(scope, variable.qt, init, variable.name_tok)
else
t.transType(scope, variable.qt, variable.name_tok)) catch |err| switch (err) {
error.UnsupportedType => {
return t.failDecl(scope, variable.name_tok, name, "unable to translate variable declaration type", .{});
},
else => |e| return e,
};
const array_ty = variable.qt.get(t.comp, .array);
var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const");
var is_extern = variable.storage_class == .@"extern";
var self_referential = false;
const init_node = init: {
if (variable.initializer) |init| {
const maybe_literal = init.get(t.tree);
if (!toplevel) try t.wip_var_inits.putNoClobber(t.gpa, decl_node, {});
defer _ = t.wip_var_inits.remove(decl_node);
const init_node = (if (maybe_literal == .string_literal_expr)
t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node)
else
t.transExprCoercing(scope, init, .used)) catch |err| switch (err) {
error.SelfReferential => {
self_referential = true;
break :init ZigTag.undefined_literal.init();
},
error.UnsupportedTranslation, error.UnsupportedType => {
return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
},
else => |e| return e,
};
break :init try t.toNonBool(init_node, variable.qt);
}
if (variable.storage_class == .@"extern") {
if (array_ty != null and array_ty.?.len == .incomplete) {
// direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer
// to the data initialized via @extern.
// Since this is really a pointer to the underlying data, we tweak a few properties.
is_extern = false;
is_const = true;
const name_str = try std.fmt.allocPrint(t.arena, "\"{s}\"", .{base_name});
break :init try ZigTag.builtin_extern.create(t.arena, .{
.type = type_node,
.name = try ZigTag.string_literal.create(t.arena, name_str),
});
}
break :init null;
}
if (toplevel or variable.storage_class == .static or variable.thread_local) {
// storage without an initializer are initialized to a zero value.
break :init try t.createZeroValueNode(variable.qt, type_node, .no_as);
}
break :init ZigTag.undefined_literal.init();
};
const linksection_string = blk: {
if (variable.qt.getAttribute(t.comp, .section)) |section| {
break :blk t.comp.interner.get(section.name.ref()).bytes;
}
break :blk null;
};
const linkage = variable.qt.linkage(t.comp);
if (linkage != .strong) {
try t.warn(scope, variable.name_tok, "TODO {s} linkage ignored", .{@tagName(linkage)});
}
const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;
var node = try ZigTag.var_decl.create(t.arena, .{
.is_pub = toplevel,
.is_const = is_const and !self_referential,
.is_extern = is_extern,
.is_export = toplevel and variable.storage_class == .auto and linkage == .strong,
.is_threadlocal = variable.thread_local,
.linksection_string = linksection_string,
.alignment = alignment,
.name = if (use_base_name) base_name else name,
.type = type_node,
.init = init_node,
});
if (toplevel) {
try t.addTopLevelDecl(name, node);
} else {
if (use_base_name) {
node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node });
}
try scope.appendNode(node);
if (self_referential) {
const deferred_init = t.transExprCoercing(scope, variable.initializer.?, .used) catch |err| switch (err) {
error.SelfReferential => unreachable,
error.UnsupportedTranslation, error.UnsupportedType => {
return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
},
else => |e| return e,
};
const assign = try ZigTag.assign.create(t.arena, .{
.lhs = try ZigTag.identifier.create(t.arena, name),
.rhs = try t.toNonBool(deferred_init, variable.qt),
});
try scope.appendNode(assign);
}
try bs.discardVariable(name);
if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| {
const cleanup_fn_name = t.tree.tokSlice(cleanup_attr.function.tok);
const mangled_fn_name = scope.getAlias(cleanup_fn_name) orelse cleanup_fn_name;
const fn_id = try ZigTag.identifier.create(t.arena, mangled_fn_name);
const varname = try ZigTag.identifier.create(t.arena, name);
const args = try t.arena.alloc(ZigNode, 1);
args[0] = try ZigTag.address_of.create(t.arena, varname);
const cleanup_call = try ZigTag.call.create(t.arena, .{ .lhs = fn_id, .args = args });
const discard = try ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = cleanup_call });
const deferred_cleanup = try ZigTag.@"defer".create(t.arena, discard);
try bs.statements.append(t.gpa, deferred_cleanup);
}
}
}