Skip to content

Preserve checked numeric conversions during constant publication #155

Description

@itsfuad

Problem

Compiler audit reproduced accepted module constants that lose checked numeric conversion/type evidence before MIR static publication. This is separate from resolved binding identity fixed in commit 80b3fd3.

Audited baseline: cfcae13, Linux/amd64, Go 1.26.8, Clang 22.1.8.

Reproductions

const Total: i64 = 1i32 + 2i64;

fn main() {
    println(Total);
}

Observed: check succeeds; native build fails with an undefined reference to the constant's mangled symbol. Equivalent local arithmetic builds and prints 3.

const Ratio: f32 = 1.25f64 as f32;

fn main() {
    println(Ratio);
}

Observed: check succeeds; native build fails with a missing constant symbol. Equivalent local cast builds and prints 1.25.

The audit also reproduced const Wide: i64 = 2i8 passed to an i64 parameter producing either a missing static symbol or an LLVM argument-type invariant failure depending on unrelated narrow-type interning.

Existing owners and cause

  • internal/semantics/typechecker/constant_eval.go: explicitly typed literals, casts, and binary folding do not consistently consume the recorded numeric conversions or publish the declared constant type.
  • internal/ir/mir/module_lower.go: staticEntryForConst selects physical static type from value.TypeText(); failed materialization is skipped during module lowering.
  • Existing checked conversion evidence, SymbolID caches, authoritative symbol-index values, and static lowering should remain the canonical implementation paths.

Acceptance criteria

  • Apply already-checked numeric operand/initializer conversions instead of independently re-deriving compatibility.
  • Publish constant values consistently with the declared semantic type and MIR static type.
  • Preserve finite-width intermediate overflow before final initializer conversion.
  • Missing required materialization produces a compiler diagnostic rather than a silent missing static or backend panic.
  • Preserve imported-constant ownership, private evaluator cache/cycles, and authoritative generation publication.
  • Add focused evidence/static-type regressions and positive/applicable negative x_test/ fixtures; run with rebuilt bundled compiler and affected target widths/backends.

Implement as a focused follow-up after the binding-identity PR. No generic constant store or parallel conversion system is needed.

Local checked-index follow-up

Final review finding 4 is fixed locally on fix/constant-numeric-publication: typeIndexExpr evaluates constant bounds using the already-checked indexType, retaining default i32 only for literal inference. Wide float casts no longer become zero indexes; exact source-located T0009 diagnostics occur before MIR. This same query correction resolves the binding-truncation repro in #154 without a second conversion implementation.

Coverage: 14 exact-value/type/diagnostic unit cases (including u64 max and u128 beyond host integer size); 8 rejection and 6 typed-projection/LLVM/Clang cases across Linux/386 and amd64; new runtime_checked_constant_indexes and negative_wide_constant_indexes Peeper fixtures. Both fixtures proved red with previous bundle, then passed with rebuilt compiler.

Validation passed after final production edit: changed/affected packages, bundle rebuild, full go test -count=1 ./... with rebuilt PEEPER_BIN, go vet ./..., typechecker/pipeline race tests, formatting and diff checks. All four confirmed review findings are fixed locally. Implementation remains uncommitted; issue stays open pending review/landing.

Local exact-bounds follow-up

Approved latest review items 5–8 are fixed locally on fix/constant-numeric-publication: host-independent source bounds and unsigned backend lengths/located diagnostics; one test-local Clang compilation assertion replacing six repeated blocks; existing evaluatedConst now owns setup/name lookup/guard/publication read for six callers while retaining nil unpublished values; explicit casts reuse their already-read checkedType with source lookup/resolver fallback preserved. No production helper, API or state added. All eight review items are now resolved locally.

The matching #154 backend repair retains valid symbolic constant/runtime accesses for 386 length 2^31 and amd64 lengths 2^63 and 2^63+1. Invariant review additionally proved negative i8/i64 runtime indexes escaped an unsigned-only guard at uint64-max length. Canonical shared guard now combines signed negativity and unsigned upper bound; all eight native guard controls pass, including high valid unsigned values and wide rejected indexes. Numeric source-width overflow/IEEE casts, cache/cycles/import publication and source diagnostic identity remain validated.

Final validation passed with CCACHE_DISABLE=1: focused/affected packages; bundle rebuild; seven affected Peeper fixture contracts with rebuilt compiler; full PEEPER_BIN=/home/itsfuad/Dev/Peeper/compiler/build/bin/peeper go test -count=1 ./... (executable fixtures 21.190s, LSP 38.910s); go vet ./...; typechecker/pipeline/LLVM race tests; formatting and diff checks. Reports and local plans record red/green evidence and rule audit. Large-array coverage is symbolic, not huge-object allocation. Implementation remains uncommitted; issue stays open pending review/landing.

Local narrow-unsigned GEP follow-up

Completed locally on fix/constant-numeric-publication (STEP 1E / review finding 11). Newly folded 1f64 as u1 selected a negative LLVM offset because GEP sign-extends narrow operands. After exact constant bounds validation, emitIndexPtr now reuses existing emitCast to produce an i64 physical offset. Source/MIR checked type and value, unsigned lengths, located malformed/OOB rejection and runtime guards remain intact. No new production helper, API or state.

Permanent red proof: 12 u1/u8/u24 read/store failures across 386/amd64, with 4 u64 controls passing; 18 source-to-MIR conversion failures; previous bundled fixture builds then exits 8 at wrong-element read. Green coverage: 36 backend read/store cases, 24 source/MIR/LLVM/Clang cases across both widths (including top-bit u8/u24 borrowed fields), and native fixture reads, stores, borrows and raw addresses selecting the correct slot while preserving guard/neighbor values.

Validation passed with CCACHE_DISABLE=1: changed/affected packages; bundle rebuild; 18 affected positive/negative/trap/place/slice fixture contracts; full PEEPER_BIN=/home/itsfuad/Dev/Peeper/compiler/build/bin/peeper go test -count=1 ./... (executable fixtures 19.622s, LSP 38.816s); go vet ./...; LLVM/pipeline/IR race checks; formatting, diff and rule audit. 386 is IR/object validation, not native execution; large-array controls are symbolic.

Latest disposition: earlier findings 1-8 plus 11 fixed locally; 9 (single-use ABI helper), optional 10 (duplicate cast adaptation), and 12 (qualified imported constant bounds) remain Proposed with full repair plan. Qualified known-OOB gap remains tracked by #154; no completeness claim for that path. Implementation uncommitted and awaiting step review/landing; issues remain open.

Local qualified-constant follow-up

STEP 1F / review finding 12 is fixed locally on fix/constant-numeric-publication, following user approval of STEP 1E. Existing evaluator symbol-read case now accepts Ident and ScopeResolution and queries original occurrence binding. SymbolConst gate, evalConstSymbol, defining-module PublishedConstant and numeric adaptation remain canonical. Variant dispatch stays first; no new production helper/API/state/store, foreign consumer cache, AST re-evaluation or lexical/import lookup fallback.

Permanent red/green proof: 30 real-import index cases on Linux/386 and amd64 (16 exact source-located bounds errors before MIR/LLVM, 14 typed THIR/MIR positive cases including aliases, transitive publication, u1, source-width negation and narrowing), 8 shifts, 8 range width/entry/missing-return cases and 3 conditions whose warning appears exactly once at CFG, not Typechecked. Foreign-owner controls preserve published value after owner AST changes, reject missing publication despite poisoned cache, and keep unbound/nonconstant paths unsuccessful. Previous bundle missed imported errors and rejected valid consumer statics; rebuilt bundle passes new negative imported-query fixture and native imported u1 borrow/store, integer/float consumer-static and qualified-default/caller-shadow controls. Existing cache/cycle/default/fingerprint/enum/place behavior passes.

Final validation passed with CCACHE_DISABLE=1: changed/affected packages; 386/amd64 LLVM/Clang objects; bundle rebuild; 27 affected fixture contracts; full suite with rebuilt PEEPER_BIN (all executable fixtures 46.738s, LSP 40.247s); go vet ./...; typechecker/project/pipeline/IR/LLVM race checks; formatting/diff/rule audit. Two test-local helpers consolidate real-file/import setup and shared THIR/MIR evidence assertions, removing copied traversal. Native execution is amd64; 386 is object validation and large-array controls are symbolic.

Disposition: findings 1-8 and 11/12 Fixed locally; 9 (ABI helper) and optional 10 (duplicate cast adaptation) remain Proposed. STEP 1F awaits review; implementation uncommitted and issues remain open pending landing. Separate pre-existing parser ambiguity for bare qualified range endpoints before loop braces is tracked in #157; casted endpoints isolate evaluator tests. No parser repair or completeness claim for that syntax in this checkpoint.

Local ABI/cast cleanup and combined review

User-approved combined STEP 1G/1H is complete locally on fix/constant-numeric-publication. Finding 9: ABIKey now owns nominal-key/text fallback directly; single-use abiKeyLocked deleted. Existing nil/invalid-ID guards, one read lock and raw reserved-shell access remain intact. Finding 10: explicit numeric cast returns its successfully validated value when canonical target/context types match; differing/nil queries and captured deferred destination conversion remain. Source-width intermediates, narrowing before widening, aliases/byte distinction, IEEE results and invalid-value rejection are preserved. No new production helper/API/state/instrumentation or performance claim.

Meaningful invariant coverage passed both before and after cleanup: reserved-shell ABI identity before completion (public Type remains incomplete), inferred alias/byte casts without typing evidence, differing u16 context, aliased f32-to-f64 rounding and negative-zero widening. Existing imported constants, cache/cycles/finalization, exact bounds, source/MIR index types, guarded places, nominal/static-address and default/fingerprint contracts remain tested.

Final validation passed with CCACHE_DISABLE=1: focused and affected packages; Linux/386 and amd64 LLVM/Clang checks; bundle rebuild; 28 affected executable fixture contracts; full PEEPER_BIN=/home/itsfuad/Dev/Peeper/compiler/build/bin/peeper go test -count=1 ./... (executable fixtures 21.924s, LSP 38.820s); go vet ./...; IR/typechecker/project/pipeline/LLVM race checks; formatting/diff/deleted-helper/rule audits. Independent Standards review found zero hard violations and zero optional smells; independent Spec review found zero actionable defects and independently reran selected IR/typechecker/pipeline tests including numeric statics, recursive enums and interface consumers.

Disposition: all 12 constant-evaluation review findings Fixed locally; supplemental target-reuse cleanup also Fixed. Implementation remains uncommitted and awaits human review/landing; this issue remains OPEN in 0.2 Language Foundations. PR #156 merge remains deferred. Native execution is amd64; 386 is object validation and large-array controls are symbolic. Parser issue #157 and other original audit work remain separate. Roadmap linkage retains existing token-scope blocker; no credential changes.

PR publication

Reviewed implementation is now published in PR #158: #158. Earlier local-progress sections preserve checkpoint history; the implementation is now committed and pushed.

  • Compiler fixes: 28a3ea0; repo review skill/integration: b065375.
  • Stacked base: fix/constant-binding-identity at 80b3fd3; head: fix/constant-numeric-publication at b065375. Depends on Preserve resolved bindings during constant evaluation #156; retarget to main after that PR lands.
  • Latest local validation passed after final cleanup: focused IR/typechecker, affected IR/pipeline/LLVM on Linux/386 and amd64, bundle rebuild, full rebuilt-compiler executable-fixture suite (fixtures 13.354s, LSP 38.414s), formatting and diff checks. Earlier vet/race results remain separately recorded. No source changed after validation.
  • PR title/body, foundation milestone, both commits and exact thirty-six-file scope verified. Hosted main CI and self-review workflows target main, so their checks await retargeting; no non-author human approval recorded.
  • Issue remains OPEN pending landing. Parser Disambiguate qualified range endpoints from loop bodies #157 and other original audit work remain separate. Roadmap association retains the known read:project permission blocker; credentials/config unchanged.

Activity

  1. itsfuad commented on Oct 5, 2026

    @itsfuad
    MemberAuthor

    Implemented locally on fix/constant-numeric-publication, based on reviewed PR #156 head 80b3fd3; pending user review and commit.

    • Constant evaluation consumes checked numeric conversion evidence after folding at source width. Mixed-width operands, initializer widening, float casts, and comparison operands now publish correct values/types.
    • MIR static storage uses declared semantic type and verifies published ABI identity. Missing values report a compile-time diagnostic; mismatched values report invalid evidence. Removed single-use static helper and silent skipping.
    • Added publication and diagnostic/location regressions, Linux/386 and Linux/amd64 MIR/LLVM/Clang checks, imported runtime fixture, and negative materialization fixture. Source-width integer overflow and float rounding controls pass.

    Validation passed: focused/affected packages; rebuilt bundled compiler and executable fixtures; PEEPER_BIN=build/bin/peeper go test -count=1 ./...; go vet ./...; race tests for typechecker, pipeline, and MIR; formatting and git diff --check. Original failures were reproduced before the repair.

    Issue stays open until reviewed work lands. Roadmap association remains blocked by existing missing project-token scope.

  2. itsfuad commented on Oct 5, 2026

    @itsfuad
    MemberAuthor

    All three local review findings are now fixed on fix/constant-numeric-publication (uncommitted, pending review):

    1. Valid module discard constants are source-checked but omitted from static materialization; repeated _ fixture and 386/amd64 pipeline tests pass.
    2. Integer-to-float conversion rounds exact integers directly to destination Float32/Float64, preserving midpoint behavior and cast-produced IEEE infinity. Positive/negative midpoint and overflow unit cases, both-target LLVM/Clang checks, and imported native constant/runtime comparisons pass.
    3. Removed test-only reverse-ABI lookup/cache, initialization and writes. Forward ABI identity, canonical interning, completion validation and concurrency checks remain.

    New regressions reproduced failures before repair. Latest validation passed after final production edit: changed/affected package tests, bundle and executable fixtures, full Go suite with rebuilt PEEPER_BIN, vet, race tests for IR/MIR/typechecker/pipeline, formatting, deleted-symbol search and diff checks.

    Annotated local nightmare report: 3 Fixed. Existing named-constant diagnostics, cache/cycle/import ownership and source-width folding are preserved. Issue remains open until reviewed work lands; no new commit, push or merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions