compiler: ensure local consts 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.
This commit is contained in:
mlugg 2024-12-29 21:48:09 +00:00
parent 6026a5f217
commit 9a70eeeac5
No known key found for this signature in database
GPG Key ID: 3F5B7DCCBF4AF02E
4 changed files with 29 additions and 0 deletions

View File

@ -2963,6 +2963,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.validate_array_init_result_ty,
.validate_ptr_array_init,
.validate_ref_ty,
.validate_const,
.try_operand_ty,
.try_ref_operand_ty,
=> break :b true,
@ -3280,6 +3281,7 @@ fn varDecl(
const init_inst = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, if (force_comptime) .comptime_keyword else null);
gz.anon_name_strategy = prev_anon_name_strategy;
_ = try gz.addUnNode(.validate_const, init_inst, var_decl.ast.init_node);
try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
// The const init expression may have modified the error return trace, so signal

View File

@ -711,6 +711,12 @@ pub const Inst = struct {
/// operator. Emit a compile error if not.
/// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type.
validate_ref_ty,
/// Given a value, check whether it is a valid local constant in this scope.
/// In a runtime scope, this is always a nop.
/// In a comptime scope, raises a compile error if the value is runtime-known.
/// Result is always void.
/// Uses the `un_node` union field. Node is the initializer. Operand is the initializer value.
validate_const,
/// Given a type `T`, construct the type `E!T`, where `E` is this function's error set, to be used
/// as the result type of a `try` operand. Generic poison is propagated.
/// Uses the `un_node` union field. Node is the `try` expression. Operand is the type `T`.
@ -1293,6 +1299,7 @@ pub const Inst = struct {
.array_init_elem_type,
.array_init_elem_ptr,
.validate_ref_ty,
.validate_const,
.try_operand_ty,
.try_ref_operand_ty,
.restore_err_ret_index_unconditional,
@ -1353,6 +1360,7 @@ pub const Inst = struct {
.validate_array_init_result_ty,
.validate_ptr_array_init,
.validate_ref_ty,
.validate_const,
.try_operand_ty,
.try_ref_operand_ty,
=> true,
@ -1736,6 +1744,7 @@ pub const Inst = struct {
.opt_eu_base_ptr_init = .un_node,
.coerce_ptr_elem_ty = .pl_node,
.validate_ref_ty = .un_tok,
.validate_const = .un_node,
.try_operand_ty = .un_node,
.try_ref_operand_ty = .un_node,
@ -4143,6 +4152,7 @@ fn findTrackableInner(
.opt_eu_base_ptr_init,
.coerce_ptr_elem_ty,
.validate_ref_ty,
.validate_const,
.try_operand_ty,
.try_ref_operand_ty,
.struct_init_empty,

View File

@ -1502,6 +1502,11 @@ fn analyzeBodyInner(
i += 1;
continue;
},
.validate_const => {
try sema.zirValidateConst(block, inst);
i += 1;
continue;
},
.@"export" => {
try sema.zirExport(block, inst);
i += 1;
@ -4614,6 +4619,17 @@ fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
}
}
fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
if (!block.isComptime()) return;
const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const src = block.nodeOffset(un_node.src_node);
const init_ref = try sema.resolveInst(un_node.operand);
if (!try sema.isComptimeKnown(init_ref)) {
return sema.failWithNeededComptime(block, src, null);
}
}
fn zirValidateArrayInitRefTy(
sema: *Sema,
block: *Block,

View File

@ -273,6 +273,7 @@ const Writer = struct {
.@"await",
.make_ptr_const,
.validate_deref,
.validate_const,
.check_comptime_control_flow,
.opt_eu_base_ptr_init,
.restore_err_ret_index_unconditional,