feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.localVarRef
fn localVarRef(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
ident: Ast.Node.Index,
ident_token: Ast.TokenIndex,
) InnerError!Zir.Inst.Ref
File
Code
fn localVarRef(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
ident: Ast.Node.Index,
ident_token: Ast.TokenIndex,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const name_str_index = try astgen.identAsString(ident_token);
var found_already: ?Ast.Node.Index = null;
var found_needs_tunnel: bool = undefined;
var found_namespaces_out: u32 = undefined;
// The number of namespaces above `gz` we currently are
var num_namespaces_out: u32 = 0;
var capturing_namespace: *Scope.Namespace = undefined;
find_scope: switch (scope.unwrap()) {
.local_val => |local_val| {
if (local_val.name == name_str_index) {
// references in this case.
if (ri.rl == .discard and ri.ctx == .assignment) {
local_val.discarded = .fromToken(ident_token);
} else {
local_val.used = .fromToken(ident_token);
}
if (local_val.is_used_or_discarded) |ptr| ptr.* = true;
const value_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
gz,
ident,
num_namespaces_out,
.{ .ref = local_val.inst },
.{ .token = local_val.token_src },
name_str_index,
) else local_val.inst;
return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
}
continue :find_scope local_val.parent.unwrap();
},
.local_ptr => |local_ptr| {
if (local_ptr.name == name_str_index) {
if (ri.rl == .discard and ri.ctx == .assignment) {
local_ptr.discarded = .fromToken(ident_token);
} else {
local_ptr.used = .fromToken(ident_token);
}
if (!local_ptr.maybe_comptime and !gz.is_typeof) {
if (num_namespaces_out != 0) {
const ident_name = try astgen.identifierTokenString(ident_token);
return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),
});
} else if (ri.ctx == .return_addrof) {
const ident_name = try astgen.identifierTokenString(ident_token);
return astgen.failNodeNotes(ident, "returning address of expired local variable '{s}'", .{ident_name}, &.{
try astgen.errNoteTok(local_ptr.token_src, "declared runtime-known here", .{}),
});
}
}
switch (ri.rl) {
.ref, .ref_const, .ref_coerced_ty => {
const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
gz,
ident,
num_namespaces_out,
.{ .ref = local_ptr.ptr },
.{ .token = local_ptr.token_src },
name_str_index,
) else local_ptr.ptr;
if (ri.rl != .ref_const) local_ptr.used_as_lvalue = true;
return ptr_inst;
},
else => {
const val_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
gz,
ident,
num_namespaces_out,
.{ .ref_load = local_ptr.ptr },
.{ .token = local_ptr.token_src },
name_str_index,
) else try gz.addUnNode(.load, local_ptr.ptr, ident);
return rvalueNoCoercePreRef(gz, ri, val_inst, ident);
},
}
}
continue :find_scope local_ptr.parent.unwrap();
},
.gen_zir => |gen_zir| continue :find_scope gen_zir.parent.unwrap(),
.defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
.namespace => |ns| {
if (ns.decls.get(name_str_index)) |i| {
if (found_already) |f| {
return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
try astgen.errNoteNode(f, "declared here", .{}),
try astgen.errNoteNode(i, "also declared here", .{}),
});
}
found_already = i;
found_needs_tunnel = ns.maybe_generic;
found_namespaces_out = num_namespaces_out;
}
num_namespaces_out += 1;
capturing_namespace = ns;
continue :find_scope ns.parent.unwrap();
},
.top => break :find_scope,
}
if (found_already == null) {
const ident_name = try astgen.identifierTokenString(ident_token);
return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
}
// decls are modified, ZIR code containing references to them can be unmodified.
if (found_namespaces_out > 0 and found_needs_tunnel) {
switch (ri.rl) {
.ref, .ref_const, .ref_coerced_ty => return tunnelThroughClosure(
gz,
ident,
found_namespaces_out,
.{ .decl_ref = name_str_index },
.{ .node = found_already.? },
name_str_index,
),
else => {
const result = try tunnelThroughClosure(
gz,
ident,
found_namespaces_out,
.{ .decl_val = name_str_index },
.{ .node = found_already.? },
name_str_index,
);
return rvalueNoCoercePreRef(gz, ri, result, ident);
},
}
}
switch (ri.rl) {
.ref, .ref_const, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
else => {
const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
return rvalueNoCoercePreRef(gz, ri, result, ident);
},
}
}