Skip to content

Commit d6120cf

Browse files
committed
AstGen: better block_comptime elision
1 parent 71bcbd5 commit d6120cf

File tree

1 file changed

+74
-13
lines changed

1 file changed

+74
-13
lines changed

lib/std/zig/AstGen.zig

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
810810
.negation => return negation(gz, scope, ri, node),
811811
.negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .negate_wrap),
812812

813-
.identifier => return identifier(gz, scope, ri, node),
813+
.identifier => return identifier(gz, scope, ri, node, null),
814814

815815
.asm_simple,
816816
.@"asm",
@@ -2006,19 +2006,50 @@ fn comptimeExpr2(
20062006
const main_tokens = tree.nodes.items(.main_token);
20072007
const node_tags = tree.nodes.items(.tag);
20082008
switch (node_tags[node]) {
2009-
// Any identifier in `primitive_instrs` is trivially comptime. In particular, this includes
2010-
// some common types, so we can elide `block_comptime` for a few common type annotations.
20112009
.identifier => {
2012-
const ident_token = main_tokens[node];
2013-
const ident_name_raw = tree.tokenSlice(ident_token);
2014-
if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
2015-
// No need to worry about result location here, we're not creating a comptime block!
2016-
return rvalue(gz, ri, zir_const_ref, node);
2017-
}
2010+
// Many identifiers can be handled without a `block_comptime`, so `AstGen.identifier` has
2011+
// special handling for this case.
2012+
return identifier(gz, scope, ri, node, .{ .src_node = src_node, .reason = reason });
20182013
},
20192014

2020-
// We can also avoid the block for a few trivial AST tags which are always comptime-known.
2021-
.number_literal, .string_literal, .multiline_string_literal, .enum_literal, .error_value => {
2015+
// These are leaf nodes which are always comptime-known.
2016+
.number_literal,
2017+
.char_literal,
2018+
.string_literal,
2019+
.multiline_string_literal,
2020+
.enum_literal,
2021+
.error_value,
2022+
.anyframe_literal,
2023+
.error_set_decl,
2024+
// These nodes are not leaves, but will force comptime evaluation of all sub-expressions, and
2025+
// hence behave the same regardless of whether they're in a comptime scope.
2026+
.error_union,
2027+
.merge_error_sets,
2028+
.optional_type,
2029+
.anyframe_type,
2030+
.ptr_type_aligned,
2031+
.ptr_type_sentinel,
2032+
.ptr_type,
2033+
.ptr_type_bit_range,
2034+
.array_type,
2035+
.array_type_sentinel,
2036+
.fn_proto_simple,
2037+
.fn_proto_multi,
2038+
.fn_proto_one,
2039+
.fn_proto,
2040+
.container_decl,
2041+
.container_decl_trailing,
2042+
.container_decl_arg,
2043+
.container_decl_arg_trailing,
2044+
.container_decl_two,
2045+
.container_decl_two_trailing,
2046+
.tagged_union,
2047+
.tagged_union_trailing,
2048+
.tagged_union_enum_tag,
2049+
.tagged_union_enum_tag_trailing,
2050+
.tagged_union_two,
2051+
.tagged_union_two_trailing,
2052+
=> {
20222053
// No need to worry about result location here, we're not creating a comptime block!
20232054
return expr(gz, scope, ri, node);
20242055
},
@@ -8390,11 +8421,17 @@ fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
83908421
return x;
83918422
}
83928423

8424+
const ComptimeBlockInfo = struct {
8425+
src_node: Ast.Node.Index,
8426+
reason: std.zig.SimpleComptimeReason,
8427+
};
8428+
83938429
fn identifier(
83948430
gz: *GenZir,
83958431
scope: *Scope,
83968432
ri: ResultInfo,
83978433
ident: Ast.Node.Index,
8434+
force_comptime: ?ComptimeBlockInfo,
83988435
) InnerError!Zir.Inst.Ref {
83998436
const astgen = gz.astgen;
84008437
const tree = astgen.tree;
@@ -8413,6 +8450,7 @@ fn identifier(
84138450
}
84148451

84158452
if (ident_name_raw.len >= 2) integer: {
8453+
// Keep in sync with logic in `comptimeExpr2`.
84168454
const first_c = ident_name_raw[0];
84178455
if (first_c == 'i' or first_c == 'u') {
84188456
const signedness: std.builtin.Signedness = switch (first_c == 'i') {
@@ -8447,8 +8485,31 @@ fn identifier(
84478485
}
84488486
}
84498487

8450-
// Local variables, including function parameters.
8451-
return localVarRef(gz, scope, ri, ident, ident_token);
8488+
// Local variables, including function parameters, and container-level declarations.
8489+
8490+
if (force_comptime) |fc| {
8491+
// Mirrors the logic at the end of `comptimeExpr2`.
8492+
const block_inst = try gz.makeBlockInst(.block_comptime, fc.src_node);
8493+
8494+
var comptime_gz = gz.makeSubBlock(scope);
8495+
comptime_gz.is_comptime = true;
8496+
defer comptime_gz.unstack();
8497+
8498+
const sub_ri: ResultInfo = .{
8499+
.ctx = ri.ctx,
8500+
.rl = .none, // no point providing a result type, it won't change anything
8501+
};
8502+
const block_result = try localVarRef(&comptime_gz, scope, sub_ri, ident, ident_token);
8503+
assert(!comptime_gz.endsWithNoReturn());
8504+
_ = try comptime_gz.addBreak(.break_inline, block_inst, block_result);
8505+
8506+
try comptime_gz.setBlockComptimeBody(block_inst, fc.reason);
8507+
try gz.instructions.append(astgen.gpa, block_inst);
8508+
8509+
return rvalue(gz, ri, block_inst.toRef(), fc.src_node);
8510+
} else {
8511+
return localVarRef(gz, scope, ri, ident, ident_token);
8512+
}
84528513
}
84538514

84548515
fn localVarRef(

0 commit comments

Comments
 (0)