Like comptimeExpr, but draws a distinction between node, the expression to evaluate at comptime,
and src_node, the node to attach to the block_comptime.
fn comptimeExpr2(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
src_node: Ast.Node.Index,
reason: std.zig.SimpleComptimeReason,
) InnerError!Zir.Inst.Ref
fn comptimeExpr2(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
src_node: Ast.Node.Index,
reason: std.zig.SimpleComptimeReason,
) InnerError!Zir.Inst.Ref {
if (gz.is_comptime) {
// No need to change anything!
return expr(gz, scope, ri, node);
}
// There's an optimization here: if the body will be evaluated at comptime regardless, there's
// no need to wrap it in a block. This is hard to determine in general, but we can identify a
// common subset of trivially comptime expressions to take down the size of the ZIR a bit.
const tree = gz.astgen.tree;
switch (tree.nodeTag(node)) {
.identifier => {
// Many identifiers can be handled without a `block_comptime`, so `AstGen.identifier` has
// special handling for this case.
return identifier(gz, scope, ri, node, .{ .src_node = src_node, .reason = reason });
},
// These are leaf nodes which are always comptime-known.
.number_literal,
.char_literal,
.string_literal,
.multiline_string_literal,
.enum_literal,
.error_value,
.anyframe_literal,
.error_set_decl,
// These nodes are not leaves, but will force comptime evaluation of all sub-expressions, and
// hence behave the same regardless of whether they're in a comptime scope.
.error_union,
.merge_error_sets,
.optional_type,
.anyframe_type,
.ptr_type_aligned,
.ptr_type_sentinel,
.ptr_type,
.ptr_type_bit_range,
.array_type,
.array_type_sentinel,
.fn_proto_simple,
.fn_proto_multi,
.fn_proto_one,
.fn_proto,
.container_decl,
.container_decl_trailing,
.container_decl_arg,
.container_decl_arg_trailing,
.container_decl_two,
.container_decl_two_trailing,
.tagged_union,
.tagged_union_trailing,
.tagged_union_enum_tag,
.tagged_union_enum_tag_trailing,
.tagged_union_two,
.tagged_union_two_trailing,
=> {
// No need to worry about result location here, we're not creating a comptime block!
return expr(gz, scope, ri, node);
},
// Lastly, for labelled blocks, avoid emitting a labelled block directly inside this
// comptime block, because that would be silly! Note that we don't bother doing this for
// unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`).
.block_two, .block_two_semicolon, .block, .block_semicolon => {
const lbrace = tree.nodeMainToken(node);
// Careful! We can't pass in the real result location here, since it may
// refer to runtime memory. A runtime-to-comptime boundary has to remove
// result location information, compute the result, and copy it to the true
// result location at runtime. We do this below as well.
const ty_only_ri: ResultInfo = .{
.ctx = ri.ctx,
.rl = if (try ri.rl.resultType(gz, node)) |res_ty|
.{ .coerced_ty = res_ty }
else
.none,
};
if (tree.isTokenPrecededByTags(lbrace, &.{ .identifier, .colon })) {
var buf: [2]Ast.Node.Index = undefined;
const stmts = tree.blockStatements(&buf, node).?;
// Replace result location and copy back later - see above.
const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal);
return rvalue(gz, ri, block_ref, node);
}
},
// In other cases, we don't optimize anything - we need a wrapper comptime block.
else => {},
}
var block_scope = gz.makeSubBlock(scope);
block_scope.is_comptime = true;
defer block_scope.unstack();
const block_inst = try gz.makeBlockInst(.block_comptime, src_node);
// Replace result location and copy back later - see above.
const ty_only_ri: ResultInfo = .{
.ctx = ri.ctx,
.rl = if (try ri.rl.resultType(gz, src_node)) |res_ty|
.{ .coerced_ty = res_ty }
else
.none,
};
const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node, .normal);
if (!gz.refIsNoReturn(block_result)) {
_ = try block_scope.addBreak(.break_inline, block_inst, block_result);
}
try block_scope.setBlockComptimeBody(block_inst, reason);
try gz.instructions.append(gz.astgen.gpa, block_inst);
return rvalue(gz, ri, block_inst.toRef(), src_node);
}