feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.structInitExpr
fn structInitExpr(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
struct_init: Ast.full.StructInit,
) InnerError!Zir.Inst.Ref
File
Code
fn structInitExpr(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
struct_init: Ast.full.StructInit,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const tree = astgen.tree;
if (struct_init.ast.type_expr == .none) {
if (struct_init.ast.fields.len == 0) {
switch (ri.rl) {
.discard => return .void_value,
.ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node),
.ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node),
.ptr => {
const ty_inst = (try ri.rl.resultType(gz, node)).?;
const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node);
return rvalue(gz, ri, val, node);
},
.none, .ref, .ref_const, .inferred_ptr => {
return rvalue(gz, ri, .empty_tuple, node);
},
.destructure => |destructure| {
return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{
try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
});
},
}
}
} else array: {
const type_expr = struct_init.ast.type_expr.unwrap().?;
const array_type: Ast.full.ArrayType = tree.fullArrayType(type_expr) orelse {
if (struct_init.ast.fields.len == 0) {
const ty_inst = try typeExpr(gz, scope, type_expr);
const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
return rvalue(gz, ri, result, node);
}
break :array;
};
const is_inferred_array_len = tree.nodeTag(array_type.ast.elem_count) == .identifier and
mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_");
if (struct_init.ast.fields.len == 0) {
if (is_inferred_array_len) {
const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
const array_type_inst = if (array_type.ast.sentinel == .none) blk: {
break :blk try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{
.lhs = .zero_usize,
.rhs = elem_type,
});
} else blk: {
const sentinel_node = array_type.ast.sentinel.unwrap().?;
const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel);
break :blk try gz.addPlNode(
.array_type_sentinel,
type_expr,
Zir.Inst.ArrayTypeSentinel{
.len = .zero_usize,
.elem_type = elem_type,
.sentinel = sentinel,
},
);
};
const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
return rvalue(gz, ri, result, node);
}
const ty_inst = try typeExpr(gz, scope, type_expr);
const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
return rvalue(gz, ri, result, node);
} else {
return astgen.failNode(
type_expr,
"initializing array with struct syntax",
.{},
);
}
}
{
var bfa_buf: [256]u8 = undefined;
var bfa_state: std.heap.BufferFirstAllocator = .init(&bfa_buf, astgen.arena);
const bfa = bfa_state.allocator();
var duplicate_names: std.array_hash_map.Auto(Zir.NullTerminatedString, ArrayList(Ast.TokenIndex)) = .empty;
try duplicate_names.ensureTotalCapacity(bfa, @intCast(struct_init.ast.fields.len));
var any_duplicate = false;
for (struct_init.ast.fields) |field| {
const name_token = tree.firstToken(field) - 2;
const name_index = try astgen.identAsString(name_token);
const gop = try duplicate_names.getOrPut(bfa, name_index);
if (gop.found_existing) {
try gop.value_ptr.append(bfa, name_token);
any_duplicate = true;
} else {
gop.value_ptr.* = .empty;
try gop.value_ptr.append(bfa, name_token);
}
}
if (any_duplicate) {
var it = duplicate_names.iterator();
while (it.next()) |entry| {
const record = entry.value_ptr.*;
if (record.items.len > 1) {
var error_notes = std.array_list.Managed(u32).init(astgen.arena);
for (record.items[1..]) |duplicate| {
try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{}));
}
try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
try astgen.appendErrorTokNotes(
record.items[0],
"duplicate struct field name",
.{},
error_notes.items,
);
}
}
return error.AnalysisFail;
}
}
if (struct_init.ast.type_expr.unwrap()) |type_expr| {
const ty_inst = try typeExpr(gz, scope, type_expr);
_ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
switch (ri.rl) {
.ref, .ref_const => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true),
else => {
const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false);
return rvalue(gz, ri, struct_inst, node);
},
}
}
switch (ri.rl) {
.none => return structInitExprAnon(gz, scope, node, struct_init),
.discard => {
for (struct_init.ast.fields) |field_init| {
_ = try expr(gz, scope, .{ .rl = .discard }, field_init);
}
return .void_value;
},
.ref, .ref_const => {
const result = try structInitExprAnon(gz, scope, node, struct_init);
return gz.addUnTok(.ref, result, tree.firstToken(node));
},
.ref_coerced_ty => |ptr_ty_inst| {
const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node);
_ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true);
},
.ty, .coerced_ty => |result_ty_inst| {
_ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false);
},
.ptr => |ptr| {
try structInitExprPtr(gz, scope, node, struct_init, ptr.inst);
return .void_value;
},
.inferred_ptr => {
// standard anonymous initialization followed by an rvalue store.
// See corresponding logic in arrayInitExpr.
const struct_inst = try structInitExprAnon(gz, scope, node, struct_init);
return rvalue(gz, ri, struct_inst, node);
},
.destructure => |destructure| {
// not support destructuring.
return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{
try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
});
},
}
}