feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.ret
fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
File
Code
fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const tree = astgen.tree;
if (astgen.fn_block == null) {
return astgen.failNode(node, "'return' outside function scope", .{});
}
if (gz.any_defer_node.unwrap()) |any_defer_node| {
return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
try astgen.errNoteNode(
any_defer_node,
"defer expression here",
.{},
),
});
}
// Then we will save the line/column so that we can emit another one that goes
// "backwards" because we want to evaluate the operand, but then put the debug
// info back at the return keyword for error return tracing.
if (!gz.is_comptime) {
try emitDbgNode(gz, node);
}
const ret_lc: LineColumn = .{ astgen.source_line - gz.decl_line, astgen.source_column };
const defer_outer = &astgen.fn_block.?.base;
const operand_node = tree.nodeData(node).opt_node.unwrap() orelse {
try genDefers(gz, defer_outer, scope, .normal_only);
_ = try gz.addRestoreErrRetIndex(.ret, .always, node);
_ = try gz.addUnNode(.ret_node, .void_value, node);
return Zir.Inst.Ref.unreachable_value;
};
if (tree.nodeTag(operand_node) == .error_value) {
// for detecting whether to add something to the function's inferred error set.
const ident_token = tree.nodeMainToken(operand_node) + 2;
const err_name_str_index = try astgen.identAsString(ident_token);
try genDefers(gz, defer_outer, scope, .normal_and_error);
try emitDbgStmt(gz, ret_lc);
_ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
return .unreachable_value;
}
const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{
.rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } },
.ctx = .@"return",
} else .{
.rl = .{ .coerced_ty = astgen.fn_ret_ty },
.ctx = .@"return",
};
const operand: Zir.Inst.Ref = try nameStratExpr(gz, scope, ri, operand_node, .func) orelse
try reachableExpr(gz, scope, ri, operand_node, node);
switch (nodeMayEvalToError(tree, operand_node)) {
.never => {
try genDefers(gz, defer_outer, scope, .normal_only);
_ = try gz.addRestoreErrRetIndex(.ret, .always, node);
try emitDbgStmt(gz, ret_lc);
try gz.addRet(ri, operand, node);
return Zir.Inst.Ref.unreachable_value;
},
.always => {
try genDefers(gz, defer_outer, scope, .normal_and_error);
try emitDbgStmt(gz, ret_lc);
try gz.addRet(ri, operand, node);
return Zir.Inst.Ref.unreachable_value;
},
.maybe => {
if (!anyErrdefers(defer_outer, scope)) {
try genDefers(gz, defer_outer, scope, .normal_only);
try emitDbgStmt(gz, ret_lc);
const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
_ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node);
try gz.addRet(ri, operand, node);
return Zir.Inst.Ref.unreachable_value;
}
const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node);
const condbr = try gz.addCondBr(.condbr, node);
var then_scope = gz.makeSubBlock(scope);
defer then_scope.unstack();
try genDefers(&then_scope, defer_outer, scope, .normal_only);
_ = try then_scope.addRestoreErrRetIndex(.ret, .always, node);
try emitDbgStmt(&then_scope, ret_lc);
try then_scope.addRet(ri, operand, node);
var else_scope = gz.makeSubBlock(scope);
defer else_scope.unstack();
try genDefers(&else_scope, defer_outer, scope, .normal_and_error);
try emitDbgStmt(&else_scope, ret_lc);
try else_scope.addRet(ri, operand, node);
try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
return Zir.Inst.Ref.unreachable_value;
},
}
}