Skip to content

stdlib: Add MAX, MIN, EPSILON, BITS, PI, and INFINITY constants #38

Description

@julia-script

Context

No module in the standard library names a numeric limit. None of the 10 integer modules and
none of the 2 float modules declares MAX, MIN, EPSILON, BITS, PI, or INFINITY.
The compiler knows every one of these bounds, because the checked intrinsics test against
them. Silk source cannot name them.

Current behavior

A user who wants the largest i32 value must write the literal 2147483647 in the source.
A user who wants the smallest value must write -2147483648. Each literal is a place where
the user can make a mistake, and no reader can confirm the value without a calculation.

The float case is worse. A user cannot write an infinity literal at all, so a user must
reach for f64.fromBits and a hexadecimal bit pattern (f64.silk:102). A user cannot write
an epsilon value without the same trick.

Typed const already ships, so these declarations are expressible today. This ticket adds
declarations only.

Requirements

  1. Each integer module must declare MAX and MIN with the type of that module.
  2. Each integer module must declare BITS as a u32 value.
  3. Each float module must declare MAX, MIN, EPSILON, INFINITY, and NAN.
  4. Each float module must declare PI and E.
  5. Each constant must have an explicit type, so no constant may depend on literal defaults.
  6. The value of MAX must equal the largest value that the checked intrinsics accept.
  7. The usize and isize constants must match the pointer width of the target.
  8. The change must add no intrinsic and no compiler phase.

Example

// In i32.silk
pub const MAX: i32 = 2147483647
pub const MIN: i32 = -2147483648
pub const BITS: u32 = 32

// In f64.silk
pub const EPSILON: f64 = 0.0000000000000002220446049250313
pub const PI: f64 = 3.141592653589793

pub fn wouldOverflow(value: i32) -> bool {
  return value > i32.MAX - 1
}

Out of scope

  • A computed constant expression. The compiler rejects const computed: i32 = 40 + 2
    with SEM0086, and a test pins that behavior
    (packages/compiler/test/TypedConstants.test.ts:108). Each constant must be one literal.
  • A generic bound that gives MAX for any integer type. That needs the bound form change
    from ticket 30.
  • A change to the checked arithmetic intrinsics.
  • A conversion between a number and text. See ticket 36.
  • The float math functions. See ticket 37.

Dependencies

  • A decision on usize and isize. These two types depend on the target pointer width, so
    their constants cannot be one fixed literal for every target.

Acceptance criteria

  • A test shows that i32.MAX + 1 traps in the checked arithmetic path.
  • A test compares each integer MAX against the bound in the checked intrinsic.
  • A test shows f64.INFINITY has the same bits as the infinity bit pattern.
  • A test shows f64.NAN is not equal to itself.
  • A test shows the same constant values in the evaluator, the Wasm backend, and the
    native LLVM backend.
  • A test shows the usize constants match the target pointer width.
  • A test shows that no new constant declaration reports SEM0086.

Implementation note

A constant initializer must be one literal. Therefore each float constant must be written as
a decimal literal, and no constant may be written as an arithmetic expression over another
constant. An expression over a constant is legal inside a function body, so
i32.MAX - 1 compiles there. The restriction applies to the declaration only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Clear valueenhancementNew feature or requestgood first issueGood for newcomersstdlibSilk standard library

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions