containerDecl
Function parameters
Parameters
- gz:*GenZir
- scope:*Scope
- node:Ast.Node.Index
- container_decl:Ast.full.ContainerDecl
- name_strat:Zir.Inst.NameStrategy
Functions in this namespace
Functions
Source
Implementation
fn containerDecl(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
container_decl: Ast.full.ContainerDecl,
name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const gpa = astgen.gpa;
const tree = astgen.tree;
const prev_fn_block = astgen.fn_block;
astgen.fn_block = null;
defer astgen.fn_block = prev_fn_block;
// We must not create any types until Sema. Here the goal is only to generate
// ZIR for all the field types, alignments, and default value expressions.
switch (tree.tokenTag(container_decl.ast.main_token)) {
.keyword_struct => {
const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) {
.keyword_packed => .@"packed",
.keyword_extern => .@"extern",
else => unreachable,
} else .auto;
const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg, name_strat);
return rvalue(gz, ri, result, node);
},
.keyword_union => {
const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) {
.keyword_packed => .@"packed",
.keyword_extern => .@"extern",
else => unreachable,
} else .auto;
const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token, name_strat);
return rvalue(gz, ri, result, node);
},
.keyword_enum => {
if (container_decl.layout_token) |t| {
return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
}
// Count total fields as well as how many have explicitly provided tag values.
const counts = blk: {
var values: usize = 0;
var total_fields: usize = 0;
var decls: usize = 0;
var opt_nonexhaustive_node: Ast.Node.OptionalIndex = .none;
var nonfinal_nonexhaustive = false;
for (container_decl.ast.members) |member_node| {
var member = tree.fullContainerField(member_node) orelse {
decls += 1;
continue;
};
member.convertToNonTupleLike(astgen.tree);
if (member.ast.tuple_like) {
return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
}
if (member.comptime_token) |comptime_token| {
return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
}
if (member.ast.type_expr.unwrap()) |type_expr| {
return astgen.failNodeNotes(
type_expr,
"enum fields do not have types",
.{},
&[_]u32{
try astgen.errNoteNode(
node,
"consider 'union(enum)' here to make it a tagged union",
.{},
),
},
);
}
if (member.ast.align_expr.unwrap()) |align_expr| {
return astgen.failNode(align_expr, "enum fields cannot be aligned", .{});
}
const name_token = member.ast.main_token;
if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
if (opt_nonexhaustive_node.unwrap()) |nonexhaustive_node| {
return astgen.failNodeNotes(
member_node,
"redundant non-exhaustive enum mark",
.{},
&[_]u32{
try astgen.errNoteNode(
nonexhaustive_node,
"other mark here",
.{},
),
},
);
}
opt_nonexhaustive_node = member_node.toOptional();
if (member.ast.value_expr.unwrap()) |value_expr| {
return astgen.failNode(value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
}
continue;
} else if (opt_nonexhaustive_node != .none) {
nonfinal_nonexhaustive = true;
}
total_fields += 1;
if (member.ast.value_expr.unwrap()) |value_expr| {
if (container_decl.ast.arg == .none) {
return astgen.failNode(value_expr, "value assigned to enum tag with inferred tag type", .{});
}
values += 1;
}
}
if (nonfinal_nonexhaustive) {
return astgen.failNode(opt_nonexhaustive_node.unwrap().?, "'_' field of non-exhaustive enum must be last", .{});
}
break :blk .{
.total_fields = total_fields,
.values = values,
.decls = decls,
.nonexhaustive_node = opt_nonexhaustive_node,
};
};
if (counts.nonexhaustive_node != .none and container_decl.ast.arg == .none) {
const nonexhaustive_node = counts.nonexhaustive_node.unwrap().?;
return astgen.failNodeNotes(
node,
"non-exhaustive enum missing integer tag type",
.{},
&[_]u32{
try astgen.errNoteNode(
nonexhaustive_node,
"marked non-exhaustive here",
.{},
),
},
);
}
// In this case we must generate ZIR code for the tag values, similar to
// how structs are handled above.
const nonexhaustive = counts.nonexhaustive_node != .none;
const decl_inst = try gz.reserveInstructionIndex();
var namespace: Scope.Namespace = .{
.parent = scope,
.node = node,
.inst = decl_inst,
.declaring_gz = gz,
.maybe_generic = astgen.within_fn,
};
defer namespace.deinit(gpa);
// The enum_decl instruction introduces a scope in which the decls of the enum
// are in scope, so that tag values can refer to decls within the enum itself.
astgen.advanceSourceCursorToNode(node);
var block_scope: GenZir = .{
.parent = &namespace.base,
.decl_node_index = node,
.decl_line = gz.decl_line,
.astgen = astgen,
.is_comptime = true,
.instructions = gz.instructions,
.instructions_top = gz.instructions.items.len,
};
defer block_scope.unstack();
_ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum");
namespace.base.tag = .namespace;
const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg.unwrap()) |arg|
try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, arg, .type)
else
.none;
const bits_per_field = 1;
const max_field_size = 2;
var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size);
defer wip_members.deinit();
const old_hasher = astgen.src_hasher;
defer astgen.src_hasher = old_hasher;
astgen.src_hasher = std.zig.SrcHasher.init(.{});
if (container_decl.ast.arg.unwrap()) |arg| {
astgen.src_hasher.update(tree.getNodeSource(arg));
}
astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)});
for (container_decl.ast.members) |member_node| {
if (member_node.toOptional() == counts.nonexhaustive_node)
continue;
astgen.src_hasher.update(tree.getNodeSource(member_node));
var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
.decl => continue,
.field => |field| field,
};
member.convertToNonTupleLike(astgen.tree);
assert(member.comptime_token == null);
assert(member.ast.type_expr == .none);
assert(member.ast.align_expr == .none);
const field_name = try astgen.identAsString(member.ast.main_token);
wip_members.appendToField(@intFromEnum(field_name));
const have_value = member.ast.value_expr != .none;
wip_members.nextField(bits_per_field, .{have_value});
if (member.ast.value_expr.unwrap()) |value_expr| {
if (arg_inst == .none) {
return astgen.failNodeNotes(
node,
"explicitly valued enum missing integer tag type",
.{},
&[_]u32{
try astgen.errNoteNode(
value_expr,
"tag value specified here",
.{},
),
},
);
}
const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, value_expr);
wip_members.appendToField(@intFromEnum(tag_value_inst));
}
}
if (!block_scope.isEmpty()) {
_ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
}
var fields_hash: std.zig.SrcHash = undefined;
astgen.src_hasher.final(&fields_hash);
const body = block_scope.instructionsSlice();
const body_len = astgen.countBodyLenAfterFixups(body);
try gz.setEnum(decl_inst, .{
.src_node = node,
.nonexhaustive = nonexhaustive,
.tag_type = arg_inst,
.captures_len = @intCast(namespace.captures.count()),
.body_len = body_len,
.fields_len = @intCast(counts.total_fields),
.decls_len = @intCast(counts.decls),
.fields_hash = fields_hash,
.name_strat = name_strat,
});
wip_members.finishBits(bits_per_field);
const decls_slice = wip_members.declsSlice();
const fields_slice = wip_members.fieldsSlice();
try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
astgen.extra.appendSliceAssumeCapacity(decls_slice);
astgen.appendBodyWithFixups(body);
astgen.extra.appendSliceAssumeCapacity(fields_slice);
block_scope.unstack();
return rvalue(gz, ri, decl_inst.toRef(), node);
},
.keyword_opaque => {
assert(container_decl.ast.arg == .none);
const decl_inst = try gz.reserveInstructionIndex();
var namespace: Scope.Namespace = .{
.parent = scope,
.node = node,
.inst = decl_inst,
.declaring_gz = gz,
.maybe_generic = astgen.within_fn,
};
defer namespace.deinit(gpa);
astgen.advanceSourceCursorToNode(node);
var block_scope: GenZir = .{
.parent = &namespace.base,
.decl_node_index = node,
.decl_line = gz.decl_line,
.astgen = astgen,
.is_comptime = true,
.instructions = gz.instructions,
.instructions_top = gz.instructions.items.len,
};
defer block_scope.unstack();
const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque");
var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
defer wip_members.deinit();
if (container_decl.layout_token) |layout_token| {
return astgen.failTok(layout_token, "opaque types do not support 'packed' or 'extern'", .{});
}
for (container_decl.ast.members) |member_node| {
const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node);
if (res == .field) {
return astgen.failNode(member_node, "opaque types cannot have fields", .{});
}
}
try gz.setOpaque(decl_inst, .{
.src_node = node,
.captures_len = @intCast(namespace.captures.count()),
.decls_len = decl_count,
.name_strat = name_strat,
});
wip_members.finishBits(0);
const decls_slice = wip_members.declsSlice();
try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
astgen.extra.appendSliceAssumeCapacity(decls_slice);
block_scope.unstack();
return rvalue(gz, ri, decl_inst.toRef(), node);
},
else => unreachable,
}
}