feature. See also
. The project being documented here (as the example) is the Zig library itself.
Translator.transRecordDecl
fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!void
File
Code
fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!void {
const base = record_qt.base(t.comp);
const record_ty = switch (base.type) {
.@"struct", .@"union" => |record_ty| record_ty,
else => unreachable,
};
if (t.type_decls.get(record_ty.decl_node)) |_|
return;
const toplevel = scope.id == .root;
const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
const container_kind: ZigTag = if (base.type == .@"union") .@"union" else .@"struct";
const container_kind_name = @tagName(container_kind);
var bare_name = record_ty.name.lookup(t.comp);
var is_unnamed = false;
var name = bare_name;
if (t.unnamed_typedefs.get(base.qt)) |typedef_name| {
bare_name = typedef_name;
name = typedef_name;
} else {
if (record_ty.isAnonymous(t.comp)) {
bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
is_unnamed = true;
}
name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ container_kind_name, bare_name });
if (toplevel and !is_unnamed) {
name = try t.mangleWeakGlobalName(name);
}
}
if (!toplevel) name = try bs.makeMangledName(name);
try t.type_decls.putNoClobber(t.gpa, record_ty.decl_node, name);
const is_pub = toplevel and !is_unnamed;
const init_node = init: {
if (record_ty.layout == null) {
try t.opaque_demotes.put(t.gpa, base.qt, {});
break :init ZigTag.opaque_literal.init();
}
var fields: std.ArrayList(ast.Payload.Container.Field) = .empty;
defer fields.deinit(t.gpa);
try fields.ensureUnusedCapacity(t.gpa, record_ty.fields.len);
var functions: std.ArrayList(ZigNode) = .empty;
defer functions.deinit(t.gpa);
var unnamed_field_count: u32 = 0;
// layout, then we can just use a simple `extern` type. If it does have attributes,
// then we need to inspect the layout and assign an `align` value for each field.
const has_alignment_attributes = aligned: {
if (record_qt.hasAttribute(t.comp, .@"packed")) break :aligned true;
if (record_qt.hasAttribute(t.comp, .aligned)) break :aligned true;
for (record_ty.fields) |field| {
const field_attrs = field.attributes(t.comp);
for (field_attrs) |field_attr| {
switch (field_attr.tag) {
.@"packed", .aligned => break :aligned true,
else => {},
}
}
}
break :aligned false;
};
const head_field_alignment: ?c_uint = if (has_alignment_attributes) t.headFieldAlignment(record_ty) else null;
for (record_ty.fields, 0..) |field, field_index| {
const field_loc = field.name_tok;
if (field.bit_width != .null) {
try t.opaque_demotes.put(t.gpa, base.qt, {});
try t.warn(scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
break :init ZigTag.opaque_literal.init();
}
var field_name = field.name.lookup(t.comp);
if (field.name_tok == 0) {
field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});
unnamed_field_count += 1;
try t.anonymous_record_field_names.put(t.gpa, .{
.parent = base.qt,
.field = field.qt,
}, field_name);
}
const field_type = field_type: {
flexible: {
if (field_index != record_ty.fields.len - 1 and container_kind != .@"union") break :flexible;
const array_ty = field.qt.get(t.comp, .array) orelse break :flexible;
if (!t.isFlexibleArrayLen(array_ty.len)) break :flexible;
const elem_type = t.transType(scope, array_ty.elem, field_loc) catch |err| switch (err) {
error.UnsupportedType => break :flexible,
else => |e| return e,
};
const backing_array_len: usize = switch (array_ty.len) {
.fixed => |n| @intCast(n),
else => 0,
};
const backing_array = try ZigTag.array_type.create(t.arena, .{ .len = backing_array_len, .elem_type = elem_type });
const member_name = field_name;
field_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});
const member = try t.createFlexibleMemberFn(member_name, field_name);
try functions.append(t.gpa, member);
break :field_type backing_array;
}
break :field_type t.transType(scope, field.qt, field_loc) catch |err| switch (err) {
error.UnsupportedType => {
try t.opaque_demotes.put(t.gpa, base.qt, {});
try t.warn(scope, field.name_tok, "{s} demoted to opaque type - unable to translate type of field {s}", .{
container_kind_name,
field_name,
});
break :init ZigTag.opaque_literal.init();
},
else => |e| return e,
};
};
if (t.typeWasDemotedToOpaque(field.qt)) {
try t.opaque_demotes.put(t.gpa, base.qt, {});
try t.warn(scope, field_loc, "{s} demoted to opaque type - has opaque field", .{container_kind_name});
break :init ZigTag.opaque_literal.init();
}
const field_alignment = if (has_alignment_attributes)
t.alignmentForField(record_ty, head_field_alignment, field_index)
else
null;
// initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
// values for translated struct fields permits Zig code to comfortably use such an API.
const default_value = if (t.default_init and container_kind == .@"struct")
try t.createZeroValueNode(field.qt, field_type, .no_as)
else
null;
fields.appendAssumeCapacity(.{
.name = field_name,
.type = field_type,
.alignment = field_alignment,
.default_value = default_value,
});
}
if (record_ty.fields.len == functions.items.len and
t.comp.target.os.tag == .windows and t.comp.target.abi == .msvc)
{
const padding_bits = record_ty.layout.?.size_bits;
const alignment_bits = record_ty.layout.?.field_alignment_bits;
try fields.append(t.gpa, .{
.name = "_padding",
.type = try ZigTag.type.create(t.arena, try std.fmt.allocPrint(t.arena, "u{d}", .{padding_bits})),
.alignment = @divExact(alignment_bits, 8),
.default_value = if (t.default_init and container_kind == .@"struct")
ZigTag.zero_literal.init()
else
null,
});
}
const container_payload = try t.arena.create(ast.Payload.Container);
container_payload.* = .{
.base = .{ .tag = container_kind },
.data = .{
.layout = .@"extern",
.fields = try t.arena.dupe(ast.Payload.Container.Field, fields.items),
.decls = try t.arena.dupe(ZigNode, functions.items),
},
};
break :init ZigNode.initPayload(&container_payload.base);
};
const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
payload.* = .{
.base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple },
.data = .{
.name = name,
.init = init_node,
},
};
const node = ZigNode.initPayload(&payload.base);
if (toplevel) {
try t.addTopLevelDecl(name, node);
// name detection. Don't bother performing a weak mangle, since a
// mangled name is of no real use here.
if (!is_unnamed and !t.global_names.contains(bare_name) and t.weak_global_names.contains(bare_name))
try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name });
try t.global_scope.container_member_fns_map.put(t.gpa, record_qt, .{
.container_decl_ptr = &payload.data.init,
});
} else {
try scope.appendNode(node);
try bs.discardVariable(name);
}
}