sema: skip lazy const declarations in zirStructDecl

The upstream Zig sema is lazy — it only evaluates const declarations
when first accessed.  The C sema was eagerly evaluating ALL non-function
declarations, including ones never accessed during analysis (e.g.
`pub const panic = common.panic` in neghf2.zig).

Only evaluate comptime declarations (id == 3) and function declarations
(detected by ZIR_INST_FUNC / FUNC_FANCY).  Skip all other const/var
declarations, matching upstream behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Motiejus Jakštys
2026-02-25 13:47:36 +00:00
parent 7ac674878b
commit 83996c0649

View File

@@ -5608,23 +5608,13 @@ static void zirStructDecl(Sema* sema, SemaBlock* block, uint32_t inst) {
}
// Analyze value body if present.
// The upstream Zig sema is lazy: it only evaluates const
// declarations when first accessed. We must only eagerly
// evaluate comptime declarations (id == 3) and function
// declarations (detected by ZIR_INST_FUNC / FUNC_FANCY).
// All other const/var declarations are skipped here.
bool is_comptime_decl = (id == 3);
if (value_body_len > 0) {
// Set declaration context so zirFunc can read name/linkage.
uint32_t old_decl_name = sema->cur_decl_name;
bool old_decl_is_export = sema->cur_decl_is_export;
sema->cur_decl_name = decl_name;
sema->cur_decl_is_export = declIdIsExport(id);
// Reset compile errors before each declaration so a
// failure in one comptime decl doesn't cascade.
sema->has_compile_errors = false;
// Evaluate non-function declarations in a comptime block.
// In upstream, struct declarations are lazily evaluated at
// comptime. Using a comptime block here prevents runtime
// inline expansions (which would produce extra
// DBG_INLINE_BLOCKs). Function declarations (func/
// func_fancy) still use the parent block.
const uint32_t* value_body = &sema->code.extra[di];
bool is_func = false;
for (uint32_t vi = 0; vi < value_body_len; vi++) {
@@ -5634,20 +5624,34 @@ static void zirStructDecl(Sema* sema, SemaBlock* block, uint32_t inst) {
break;
}
}
if (is_func) {
(void)analyzeBodyInner(
sema, block, value_body, value_body_len);
} else {
SemaBlock decl_block;
semaBlockInit(&decl_block, sema, block);
decl_block.is_comptime = true;
(void)analyzeBodyInner(
sema, &decl_block, value_body, value_body_len);
semaBlockDeinit(&decl_block);
}
sema->cur_decl_name = old_decl_name;
sema->cur_decl_is_export = old_decl_is_export;
if (is_func || is_comptime_decl) {
// Set declaration context so zirFunc can read
// name/linkage.
uint32_t old_decl_name = sema->cur_decl_name;
bool old_decl_is_export = sema->cur_decl_is_export;
sema->cur_decl_name = decl_name;
sema->cur_decl_is_export = declIdIsExport(id);
// Reset compile errors before each declaration so a
// failure in one comptime decl doesn't cascade.
sema->has_compile_errors = false;
if (is_func) {
(void)analyzeBodyInner(
sema, block, value_body, value_body_len);
} else {
SemaBlock decl_block;
semaBlockInit(&decl_block, sema, block);
decl_block.is_comptime = true;
(void)analyzeBodyInner(
sema, &decl_block, value_body, value_body_len);
semaBlockDeinit(&decl_block);
}
sema->cur_decl_name = old_decl_name;
sema->cur_decl_is_export = old_decl_is_export;
}
}
}
}