authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-29 21:48:09+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-31 09:55:03+00:00
log9a70eeeac52f1526b9df45614b86912565e53e54
tree9509465bcd10becdf6dff9b49a3682235df246f5
parent6026a5f217398b202b92a4ecc2e129691bbb3a69
signaturelock-open Commit is signed but in an unrecognized format.

compiler: ensure local `const`s in comptime scope are comptime-known

This fixes a bug which exposed a compiler implementation detail (ZIR alloc elision). Previously, `const` declarations with a runtime-known value in a comptime scope were permitted only if AstGen was able to elide the alloc in ZIR, since the error was reported by storing to the comptime alloc. This just adds a new instruction to also emit this error when the alloc is elided.

4 files changed, 29 insertions(+), 0 deletions(-)

lib/std/zig/AstGen.zig+2
......@@ -2963,6 +2963,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
29632963 .validate_array_init_result_ty,
29642964 .validate_ptr_array_init,
29652965 .validate_ref_ty,
2966 .validate_const,
29662967 .try_operand_ty,
29672968 .try_ref_operand_ty,
29682969 => break :b true,
......@@ -3280,6 +3281,7 @@ fn varDecl(
32803281 const init_inst = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, if (force_comptime) .comptime_keyword else null);
32813282 gz.anon_name_strategy = prev_anon_name_strategy;
32823283
3284 _ = try gz.addUnNode(.validate_const, init_inst, var_decl.ast.init_node);
32833285 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
32843286
32853287 // The const init expression may have modified the error return trace, so signal
lib/std/zig/Zir.zig+10
......@@ -711,6 +711,12 @@ pub const Inst = struct {
711711 /// operator. Emit a compile error if not.
712712 /// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type.
713713 validate_ref_ty,
714 /// Given a value, check whether it is a valid local constant in this scope.
715 /// In a runtime scope, this is always a nop.
716 /// In a comptime scope, raises a compile error if the value is runtime-known.
717 /// Result is always void.
718 /// Uses the `un_node` union field. Node is the initializer. Operand is the initializer value.
719 validate_const,
714720 /// Given a type `T`, construct the type `E!T`, where `E` is this function's error set, to be used
715721 /// as the result type of a `try` operand. Generic poison is propagated.
716722 /// Uses the `un_node` union field. Node is the `try` expression. Operand is the type `T`.
......@@ -1293,6 +1299,7 @@ pub const Inst = struct {
12931299 .array_init_elem_type,
12941300 .array_init_elem_ptr,
12951301 .validate_ref_ty,
1302 .validate_const,
12961303 .try_operand_ty,
12971304 .try_ref_operand_ty,
12981305 .restore_err_ret_index_unconditional,
......@@ -1353,6 +1360,7 @@ pub const Inst = struct {
13531360 .validate_array_init_result_ty,
13541361 .validate_ptr_array_init,
13551362 .validate_ref_ty,
1363 .validate_const,
13561364 .try_operand_ty,
13571365 .try_ref_operand_ty,
13581366 => true,
......@@ -1736,6 +1744,7 @@ pub const Inst = struct {
17361744 .opt_eu_base_ptr_init = .un_node,
17371745 .coerce_ptr_elem_ty = .pl_node,
17381746 .validate_ref_ty = .un_tok,
1747 .validate_const = .un_node,
17391748 .try_operand_ty = .un_node,
17401749 .try_ref_operand_ty = .un_node,
17411750
......@@ -4143,6 +4152,7 @@ fn findTrackableInner(
41434152 .opt_eu_base_ptr_init,
41444153 .coerce_ptr_elem_ty,
41454154 .validate_ref_ty,
4155 .validate_const,
41464156 .try_operand_ty,
41474157 .try_ref_operand_ty,
41484158 .struct_init_empty,
src/Sema.zig+16
......@@ -1502,6 +1502,11 @@ fn analyzeBodyInner(
15021502 i += 1;
15031503 continue;
15041504 },
1505 .validate_const => {
1506 try sema.zirValidateConst(block, inst);
1507 i += 1;
1508 continue;
1509 },
15051510 .@"export" => {
15061511 try sema.zirExport(block, inst);
15071512 i += 1;
......@@ -4614,6 +4619,17 @@ fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
46144619 }
46154620}
46164621
4622fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
4623 if (!block.isComptime()) return;
4624
4625 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
4626 const src = block.nodeOffset(un_node.src_node);
4627 const init_ref = try sema.resolveInst(un_node.operand);
4628 if (!try sema.isComptimeKnown(init_ref)) {
4629 return sema.failWithNeededComptime(block, src, null);
4630 }
4631}
4632
46174633fn zirValidateArrayInitRefTy(
46184634 sema: *Sema,
46194635 block: *Block,
src/print_zir.zig+1
......@@ -273,6 +273,7 @@ const Writer = struct {
273273 .@"await",
274274 .make_ptr_const,
275275 .validate_deref,
276 .validate_const,
276277 .check_comptime_control_flow,
277278 .opt_eu_base_ptr_init,
278279 .restore_err_ret_index_unconditional,