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...@@ -2963,6 +2963,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2963 .validate_array_init_result_ty,2963 .validate_array_init_result_ty,
2964 .validate_ptr_array_init,2964 .validate_ptr_array_init,
2965 .validate_ref_ty,2965 .validate_ref_ty,
2966 .validate_const,
2966 .try_operand_ty,2967 .try_operand_ty,
2967 .try_ref_operand_ty,2968 .try_ref_operand_ty,
2968 => break :b true,2969 => break :b true,
...@@ -3280,6 +3281,7 @@ fn varDecl(...@@ -3280,6 +3281,7 @@ fn varDecl(
3280 const init_inst = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, if (force_comptime) .comptime_keyword else null);3281 const init_inst = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, if (force_comptime) .comptime_keyword else null);
3281 gz.anon_name_strategy = prev_anon_name_strategy;3282 gz.anon_name_strategy = prev_anon_name_strategy;
32823283
3284 _ = try gz.addUnNode(.validate_const, init_inst, var_decl.ast.init_node);
3283 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);3285 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
32843286
3285 // The const init expression may have modified the error return trace, so signal3287 // 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 {...@@ -711,6 +711,12 @@ pub const Inst = struct {
711 /// operator. Emit a compile error if not.711 /// operator. Emit a compile error if not.
712 /// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type.712 /// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type.
713 validate_ref_ty,713 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,
714 /// Given a type `T`, construct the type `E!T`, where `E` is this function's error set, to be used720 /// Given a type `T`, construct the type `E!T`, where `E` is this function's error set, to be used
715 /// as the result type of a `try` operand. Generic poison is propagated.721 /// as the result type of a `try` operand. Generic poison is propagated.
716 /// Uses the `un_node` union field. Node is the `try` expression. Operand is the type `T`.722 /// Uses the `un_node` union field. Node is the `try` expression. Operand is the type `T`.
...@@ -1293,6 +1299,7 @@ pub const Inst = struct {...@@ -1293,6 +1299,7 @@ pub const Inst = struct {
1293 .array_init_elem_type,1299 .array_init_elem_type,
1294 .array_init_elem_ptr,1300 .array_init_elem_ptr,
1295 .validate_ref_ty,1301 .validate_ref_ty,
1302 .validate_const,
1296 .try_operand_ty,1303 .try_operand_ty,
1297 .try_ref_operand_ty,1304 .try_ref_operand_ty,
1298 .restore_err_ret_index_unconditional,1305 .restore_err_ret_index_unconditional,
...@@ -1353,6 +1360,7 @@ pub const Inst = struct {...@@ -1353,6 +1360,7 @@ pub const Inst = struct {
1353 .validate_array_init_result_ty,1360 .validate_array_init_result_ty,
1354 .validate_ptr_array_init,1361 .validate_ptr_array_init,
1355 .validate_ref_ty,1362 .validate_ref_ty,
1363 .validate_const,
1356 .try_operand_ty,1364 .try_operand_ty,
1357 .try_ref_operand_ty,1365 .try_ref_operand_ty,
1358 => true,1366 => true,
...@@ -1736,6 +1744,7 @@ pub const Inst = struct {...@@ -1736,6 +1744,7 @@ pub const Inst = struct {
1736 .opt_eu_base_ptr_init = .un_node,1744 .opt_eu_base_ptr_init = .un_node,
1737 .coerce_ptr_elem_ty = .pl_node,1745 .coerce_ptr_elem_ty = .pl_node,
1738 .validate_ref_ty = .un_tok,1746 .validate_ref_ty = .un_tok,
1747 .validate_const = .un_node,
1739 .try_operand_ty = .un_node,1748 .try_operand_ty = .un_node,
1740 .try_ref_operand_ty = .un_node,1749 .try_ref_operand_ty = .un_node,
17411750
...@@ -4143,6 +4152,7 @@ fn findTrackableInner(...@@ -4143,6 +4152,7 @@ fn findTrackableInner(
4143 .opt_eu_base_ptr_init,4152 .opt_eu_base_ptr_init,
4144 .coerce_ptr_elem_ty,4153 .coerce_ptr_elem_ty,
4145 .validate_ref_ty,4154 .validate_ref_ty,
4155 .validate_const,
4146 .try_operand_ty,4156 .try_operand_ty,
4147 .try_ref_operand_ty,4157 .try_ref_operand_ty,
4148 .struct_init_empty,4158 .struct_init_empty,
src/Sema.zig+16
...@@ -1502,6 +1502,11 @@ fn analyzeBodyInner(...@@ -1502,6 +1502,11 @@ fn analyzeBodyInner(
1502 i += 1;1502 i += 1;
1503 continue;1503 continue;
1504 },1504 },
1505 .validate_const => {
1506 try sema.zirValidateConst(block, inst);
1507 i += 1;
1508 continue;
1509 },
1505 .@"export" => {1510 .@"export" => {
1506 try sema.zirExport(block, inst);1511 try sema.zirExport(block, inst);
1507 i += 1;1512 i += 1;
...@@ -4614,6 +4619,17 @@ fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -4614,6 +4619,17 @@ fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
4614 }4619 }
4615}4620}
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
4617fn zirValidateArrayInitRefTy(4633fn zirValidateArrayInitRefTy(
4618 sema: *Sema,4634 sema: *Sema,
4619 block: *Block,4635 block: *Block,
src/print_zir.zig+1
...@@ -273,6 +273,7 @@ const Writer = struct {...@@ -273,6 +273,7 @@ const Writer = struct {
273 .@"await",273 .@"await",
274 .make_ptr_const,274 .make_ptr_const,
275 .validate_deref,275 .validate_deref,
276 .validate_const,
276 .check_comptime_control_flow,277 .check_comptime_control_flow,
277 .opt_eu_base_ptr_init,278 .opt_eu_base_ptr_init,
278 .restore_err_ret_index_unconditional,279 .restore_err_ret_index_unconditional,