Skip to content

semantic: a numeric literal outside its own type's range is not checked before codegen — 1e400, 1e39f are SPY0599 behind Roslyn, 1e29m is a SPY0909 ICE #2340

Closed

Description

@antonsynd

Class tracker. Found by the P68 verify round (sibling-cell prober, C1 × literal magnitude), measured @ 0d58024 with sharpyc run.

Contract

A numeric literal whose value its own type cannot hold gets a typed refusal in semantic analysis, or an explicit spec rule (CPython gives inf for a float). It must never reach code generation. Integers already work this way: an unsuffixed literal past int64 is SPY0210 "too large for 'long'". The float32 narrowing twin does too: float_literals.md:82 says x: float32 = 1e40 is SPY0220. The literal forms below are not checked. They reach Roslyn (SPY0599) or crash the compiler (SPY0909).

Cells

Literal type Program Observed @ 0d58024 CPython 3.12
float64 (unsuffixed) print(1e400) (also -1e400, 1e309, 1.8e308) SPY0599 "generated C# contains 1 syntax error(s): Floating-point constant is outside the range of type 'double'" inf / -inf
float64 (d suffix) print(1e400d) SPY0599, same message n/a (no suffix)
float64 in a typed slot x: float = 1e400; b: bool = 1.0 < 1e400 SPY0599 inf; True
float32 (f suffix) print(1e39f) (also 3.5e38f) SPY0599 "outside the range of type 'float'" (compare: x: float32 = 1e40 is SPY0220) n/a
decimal (m suffix) print(1e29m) (also 1e400m, 79228162514264337593543950336m) SPY0909 internal compiler error (InvalidOperationException): "The decimal literal '1e29' … could not be parsed as a decimal. Semantic analysis admits a literal into a decimal slot only after checking its range…" n/a

Minimal repro:

def main():
    print(1e400)

Notes

Activity

  1. added
    lane:iceInternal compiler error: SPY0908/SPY0909, a CSxxxx leak, a crash
    reach:programAn ordinary program a Python programmer would write hits it
    class-trackerThe one issue per class: carries the contract and the cell table
    on Oct 21, 2026
  2. antonsynd commented on Oct 22, 2026

    @antonsynd
    OwnerAuthor

    Batching 2026-10-21 @ e7e0a0e — charter .claude/plans/remediation-round-2026-09-20-batching.md §3.0, chores band (order 0, one commit, no plan; Semantic expression typing only; seam-free with P21e, rides its gate). Ruling R-HU (owner, 2026-10-21): (a) REFUSE every numeric literal outside the range of its own resolved type at ONE semantic seam — the SPY0210 family. Ruling R-HV (owner, 2026-10-21): (a) a chore in the current band with a closed-axis theory.

    Measured @ e7e0a0e (built CLI, lock-free): print(1e400) → SPY0599 "generated C# contains 1 syntax error(s): Floating-point constant is outside the range of type 'double'" (Roslyn CS0594 behind the net); print(1e29m) → SPY0909 internal compiler error InvalidOperationException whose own message (CodeGen/RoslynEmitter.Expressions.cs:796) says "Semantic analysis admits a literal into a decimal slot only after checking its range" — the emitter holds an invariant semantic does not keep; 1e39f → SPY0599 'float'. Controls: x: float32 = 1e40 → SPY0220 (float_literals.md:82, "not a silent infinity"); print(99999999999999999999) → SPY0210 "too large for any integer type". CPython 3.12.13: 1e400 → inf. Lane / reach: ice · program (rare shape). Class: the integer check and the float32 slot check exist; float64, f and m are unchecked — contract §1's position-missing-shared-check meta-class.

    Mechanism (R-HU a): one range check keyed on the literal's RESOLVED type; the SPY0210 family gains "too large for 'double' / 'float' / 'decimal'"; float_literals.md gains the sentence beside line 82 and decimal likewise; DiagnosticExplanations for any new code. Guard (R-HV a): theory type {int32→int64 promotion, int64, uint32, uint64, float64, float32, decimal} × spelling {unsuffixed, suffixed, slot-narrowed} × magnitude {max, max + one ulp, far, negative}; mutation (drop the float64 arm → its rows red) in the commit body; direction record: NO program that ran changes (every cell is SPY0599/SPY0909 today) — recorded in the commit body, no fixture moves. Blast radius: Semantic → the whole gate. Class tracker: this issue.

    Rejected — R-HU (b): float64 unsuffixed → inf (CPython's value, double.PositiveInfinity) with f/m refusing — two rules for one surface, and x: float = 1e400 then carries infinity into arithmetic silently; a runtime value is one-way, a refusal is relaxable to (b) later; Type Safety > Python syntax, and the spec already chose refusal for float32. R-HV (b): a P37 row — P37 is slot-DIRECTED operand totality (Some(v)/None() against a typed other operand); print(1e400) and 1e29m have no slot, so P37's generator would gain a foreign axis and an ICE would wait behind P70. Ordering two-way.

  3. antonsynd commented on Oct 22, 2026

    @antonsynd
    OwnerAuthor

    Landed (chores band 2026-10-22) on dev @ 4d22212, PR #2346 to mainline. Whole-solution gate 81669/0/7 @ 4d22212 (measured), against 81093/0/7 @ be4664e.

    Commit 64ff07e (R-HU a, R-HV a). FloatLiteralClassifier.Fits(text, double|float|decimal) is the one range question. The literal-typing arm refuses a miss with SPY0210 ("too large for 'double'/'float'/'decimal'"), and the float32 and decimal narrowing predicates ask the same Fits. The SPY0210 constant is renamed NumericLiteralOutOfRange. float_literals.md gains "A Literal Must Fit Its Own Type".

    Direction:

    • SPY0599 → SPY0210: 1e400, -1e400, 1e400d, 1e39f, the slot and pattern positions, and the parameter-default and const positions.
    • SPY0909 → SPY0210: 1e29m, 79228162514264337593543950336m.
    • Two SPY0908 sibling cells were cured along the way: -1UL → SPY0223, and x: uint32 = -1U → SPY0220.
    • No program that ran changes its output.

    Matrix: NumericLiteralRangeMatrixTests, 84 rows plus a 13-text Roslyn differential.

    Mutations:

    • The float64 arm returning true turned 14 rows red.
    • NegatesAsLiteral returning true turned 3 rows red.

    Sibling filed: #2344. A non-zero decimal literal below 1e-28 still gets two verdicts. 1e-30m is stored as zero, while d: decimal = 1e-30 is refused. The narrowing path keeps an underflow check the suffixed path never asks. That is a magnitude axis this issue's contract did not list, so it has its own tracker.

    Every cell in this issue's table is met. Closing.

  4. added a commit that references this issue on Oct 22, 2026
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

    class-trackerThe one issue per class: carries the contract and the cell tablelane:iceInternal compiler error: SPY0908/SPY0909, a CSxxxx leak, a crashreach:programAn ordinary program a Python programmer would write hits itsemantic

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions