Skip to content

Use int-cast for type-safe integer conversions instead of fromIntegral #68

@Unisay

Description

@Unisay

fromIntegral is unchecked: it silently truncates or wraps when the target type is narrower than the source, and the type checker never complains. The codebase converts between Int, Integer, Natural, Word/Word8, and Char code points in several places, and a wrong conversion here is exactly the class of bug behind the array-index off-by-one (#49). Right now nothing distinguishes a provably-safe widening (Int -> Integer) from a lossy narrowing (Integer -> Int); both read as fromIntegral.

Proposal

Adopt the int-cast library:

  • intCast only type-checks when the target provably contains every value of the source (e.g. Word8 -> Int, Int -> Integer, Natural -> Integer). A lossy conversion becomes a compile error instead of a silent runtime corruption.
  • For genuine narrowings (Integer -> Int, Natural -> Int), use intCastMaybe and handle the overflow case, or keep an explicit, commented conversion where the bound is known.

This turns "did I pick a wide enough target?" from a runtime accident into a compile-time check.

Sites to migrate

Audit every integer coercion in lib/ and exe/; the current ones (12 fromIntegral, plus toEnum/fromEnum) cluster as:

  • lib/Language/PureScript/PSString.hs: code point / UTF-16 / byte encoding (Char <-> Int <-> Word), the densest cluster.
  • lib/Language/PureScript/Backend/IR/Types.hs: De Bruijn Index (a Natural) mixed with Int arithmetic (around lines 702, 756, 789).
  • lib/Language/PureScript/Backend/IR.hs: PatArrayLength length conversion.
  • lib/Language/PureScript/Backend/Lua.hs: ArrayIndex (Natural -> Lua Integer).
  • lib/Language/PureScript/Backend/IR/Optimizer.hs: unIndex index used to index a list.

Scope and verification

Pure refactor, no behavior change intended. Each site keeps its current semantics; the win is that the safe ones become statically guaranteed and the lossy ones become explicit. The existing golden and unit suites guard against regressions; narrowings that gain a Maybe need their overflow branch covered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: irIR / optimizer / DCE / inlinerenhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions