localVarRef
Function parameters
Parameters
- gz:*GenZir
- scope:*Scope
- ident:Ast.Node.Index
- ident_token:Ast.TokenIndex
Functions in this namespace
Functions
Source
Implementation
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 s = scope;
var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
var found_needs_tunnel: bool = undefined; // defined when `found_already != null`
var found_namespaces_out: u32 = undefined; // defined when `found_already != null`
// The number of namespaces above `gz` we currently are
var num_namespaces_out: u32 = 0;
// defined by `num_namespaces_out != 0`
var capturing_namespace: *Scope.Namespace = undefined;
while (true) switch (s.tag) {
.local_val => {
const local_val = s.cast(Scope.LocalVal).?;
if (local_val.name == name_str_index) {
// Locals cannot shadow anything, so we do not need to look for ambiguous
// 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);
}
s = local_val.parent;
},
.local_ptr => {
const local_ptr = s.cast(Scope.LocalPtr).?;
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);
}
// Can't close over a runtime variable
if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
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", .{}),
});
}
switch (ri.rl) {
.ref, .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;
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);
},
}
}
s = local_ptr.parent;
},
.gen_zir => s = s.cast(GenZir).?.parent,
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
.namespace => {
const ns = s.cast(Scope.Namespace).?;
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", .{}),
});
}
// We found a match but must continue looking for ambiguous references to decls.
found_already = i;
found_needs_tunnel = ns.maybe_generic;
found_namespaces_out = num_namespaces_out;
}
num_namespaces_out += 1;
capturing_namespace = ns;
s = ns.parent;
},
.top => break,
};
if (found_already == null) {
const ident_name = try astgen.identifierTokenString(ident_token);
return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
}
// Decl references happen by name rather than ZIR index so that when unrelated
// 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_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_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);
},
}
}