feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.varDecl
fn varDecl(
gz: *GenZir,
scope: *Scope,
node: Ast.Node.Index,
block_arena: Allocator,
var_decl: Ast.full.VarDecl,
) InnerError!*Scope
File
Code
fn varDecl(
gz: *GenZir,
scope: *Scope,
node: Ast.Node.Index,
block_arena: Allocator,
var_decl: Ast.full.VarDecl,
) InnerError!*Scope {
try emitDbgNode(gz, node);
const astgen = gz.astgen;
const tree = astgen.tree;
const name_token = var_decl.ast.mut_token + 1;
const ident_name_raw = tree.tokenSlice(name_token);
if (mem.eql(u8, ident_name_raw, "_")) {
return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
}
const ident_name = try astgen.identAsString(name_token);
try astgen.detectLocalShadowing(
scope,
ident_name,
name_token,
ident_name_raw,
if (tree.tokenTag(var_decl.ast.mut_token) == .keyword_const) .@"local constant" else .@"local variable",
);
const init_node = var_decl.ast.init_node.unwrap() orelse {
return astgen.failNode(node, "variables must be initialized", .{});
};
if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
return astgen.failTok(tree.nodeMainToken(addrspace_node), "cannot set address space of local variable '{s}'", .{ident_name_raw});
}
if (var_decl.ast.section_node.unwrap()) |section_node| {
return astgen.failTok(tree.nodeMainToken(section_node), "cannot set section of local variable '{s}'", .{ident_name_raw});
}
const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node.unwrap()) |align_node|
try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align")
else
.none;
switch (tree.tokenTag(var_decl.ast.mut_token)) {
.keyword_const => {
if (var_decl.comptime_token) |comptime_token| {
try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
}
const force_comptime = var_decl.comptime_token != null;
// or an rvalue as a result location. If it is an rvalue, we can use the instruction as
// the variable, no memory location needed.
if (align_inst == .none and
!astgen.nodes_need_rl.contains(node))
{
const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| .{
.rl = .{ .ty = try typeExpr(gz, scope, type_node) },
.ctx = .const_init,
} else .{ .rl = .none, .ctx = .const_init };
const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, result_info, init_node, .dbg_var) orelse
try reachableExprComptime(gz, scope, result_info, init_node, node, if (force_comptime) .comptime_keyword else null);
_ = try gz.addUnNode(.validate_const, init_inst, init_node);
try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
// to Sema that it should save the new index for restoring later.
if (nodeMayAppendToErrorTrace(tree, init_node))
_ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
const sub_scope = try block_arena.create(Scope.LocalVal);
sub_scope.* = .{
.parent = scope,
.gen_zir = gz,
.name = ident_name,
.inst = init_inst,
.token_src = name_token,
.id_cat = .@"local constant",
};
return &sub_scope.base;
}
const is_comptime = gz.is_comptime or
tree.nodeTag(init_node) == .@"comptime";
const init_rl: ResultInfo.Loc = if (var_decl.ast.type_node.unwrap()) |type_node| init_rl: {
const type_inst = try typeExpr(gz, scope, type_node);
if (align_inst == .none) {
break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } };
} else {
break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{
.node = node,
.type_inst = type_inst,
.align_inst = align_inst,
.is_const = true,
.is_comptime = is_comptime,
}) } };
}
} else init_rl: {
const alloc_inst = if (align_inst == .none) ptr: {
const tag: Zir.Inst.Tag = if (is_comptime)
.alloc_inferred_comptime
else
.alloc_inferred;
break :ptr try gz.addNode(tag, node);
} else ptr: {
break :ptr try gz.addAllocExtended(.{
.node = node,
.type_inst = .none,
.align_inst = align_inst,
.is_const = true,
.is_comptime = is_comptime,
});
};
break :init_rl .{ .inferred_ptr = alloc_inst };
};
const var_ptr: Zir.Inst.Ref, const resolve_inferred: bool = switch (init_rl) {
.ptr => |ptr| .{ ptr.inst, false },
.inferred_ptr => |inst| .{ inst, true },
else => unreachable,
};
const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init };
const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, init_result_info, init_node, .dbg_var) orelse
try reachableExprComptime(gz, scope, init_result_info, init_node, node, if (force_comptime) .comptime_keyword else null);
// to Sema that it should save the new index for restoring later.
if (nodeMayAppendToErrorTrace(tree, init_node))
_ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
const const_ptr = if (resolve_inferred)
try gz.addUnNode(.resolve_inferred_alloc, var_ptr, node)
else
try gz.addUnNode(.make_ptr_const, var_ptr, node);
try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
const sub_scope = try block_arena.create(Scope.LocalPtr);
sub_scope.* = .{
.parent = scope,
.gen_zir = gz,
.name = ident_name,
.ptr = const_ptr,
.token_src = name_token,
.maybe_comptime = true,
.id_cat = .@"local constant",
};
return &sub_scope.base;
},
.keyword_var => {
if (var_decl.comptime_token != null and gz.is_comptime)
return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{});
const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| a: {
const type_inst = try typeExpr(gz, scope, type_node);
const alloc = alloc: {
if (align_inst == .none) {
const tag: Zir.Inst.Tag = if (is_comptime)
.alloc_comptime_mut
else
.alloc_mut;
break :alloc try gz.addUnNode(tag, type_inst, node);
} else {
break :alloc try gz.addAllocExtended(.{
.node = node,
.type_inst = type_inst,
.align_inst = align_inst,
.is_const = false,
.is_comptime = is_comptime,
});
}
};
break :a .{ alloc, false, .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
} else a: {
const alloc = alloc: {
if (align_inst == .none) {
const tag: Zir.Inst.Tag = if (is_comptime)
.alloc_inferred_comptime_mut
else
.alloc_inferred_mut;
break :alloc try gz.addNode(tag, node);
} else {
break :alloc try gz.addAllocExtended(.{
.node = node,
.type_inst = .none,
.align_inst = align_inst,
.is_const = false,
.is_comptime = is_comptime,
});
}
};
break :a .{ alloc, true, .{ .rl = .{ .inferred_ptr = alloc } } };
};
_ = try nameStratExpr(
gz,
scope,
result_info,
init_node,
.dbg_var,
) orelse try reachableExprComptime(
gz,
scope,
result_info,
init_node,
node,
if (var_decl.comptime_token != null) .comptime_keyword else null,
);
const final_ptr: Zir.Inst.Ref = if (resolve_inferred) ptr: {
break :ptr try gz.addUnNode(.resolve_inferred_alloc, alloc, node);
} else alloc;
try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr);
const sub_scope = try block_arena.create(Scope.LocalPtr);
sub_scope.* = .{
.parent = scope,
.gen_zir = gz,
.name = ident_name,
.ptr = final_ptr,
.token_src = name_token,
.maybe_comptime = is_comptime,
.id_cat = .@"local variable",
};
return &sub_scope.base;
},
else => unreachable,
}
}