E08260: compile-time constant value violates the type's constraint — this would Panic at runtime

Category: E08xxx Pre-IR checks (flow analysis) · Reported in phase: PRE_IR_CHECKS · Classification: CONSTRAINED_CONSTRUCTOR_VIOLATION · All error codes

What went wrong

A constrained type was constructed with a value that is KNOWN at compile time and provably violates the type's constraint. At runtime this construction would raise an uncatchable Panic; because the value is known now, the compiler rejects it now instead. This applies to EVERY constrainable base type — Integer, Float, String (equality, ordering, 'matches' regex, 'contains'), Date, Time, DateTime, Duration, Money, Dimension, Colour and Character — the value is evaluated against the real constraint, so a regex or value comparison is checked statically, not deferred to runtime.

The value does NOT have to be written as a literal at the construction. Flow analysis follows the value that ARRIVES there, so all of these are rejected alike: a literal; a named constant; a local assigned a literal, however many times it is reassigned; a value bound by a guard on if/while/switch/try/do; a parameter that a path condition has pinned to an exact value; and an arm of a ternary. An Integer literal reaching a Float-based constraint is promoted first, so Ratio(2) is rejected exactly as Ratio(2.0) is.

If the value cannot be determined — it comes from a call, from arithmetic, or from an un-narrowed parameter or field — nothing is reported and the constraint is checked at runtime as before. The compiler only rejects what it can prove.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1185 (Define a Percentage type constrained to 0..100 — carries the worked
  • incorrect/correct pair for THIS code, including the laundered form)
  • ek9 -q 1241 (Create me a constrained EmailAddress type as String valid...)
  • ek9 -q 257 (How do constrained types work in EK9?)

The [EXAMPLE] section below has an Age constrained to '>= 16 and <= 100': Age(15) is provably outside it, Age(16) is in range (boundaries are inclusive). The same holds for every base: an Email constrained to 'matches /^[a-z]+@[a-z]+$/' rejects Email("not an email"), and a Budget of '>= 0#GBP and <= 1000#GBP' rejects Budget(2000#GBP).

How to fix it

  1. If you have a known-good value, use one that satisfies the constraint (boundaries are inclusive).
  2. If the value comes from untrusted/external input that MIGHT be invalid, do NOT use a bare constructor (a bare constructor ASSERTS validity and Panics at runtime on a set out-of-range value). Use the fallible factory 'of' instead, which returns an UNSET value on violation and never Panics, and guard on its result (good_checked.ek9 in the [EXAMPLE] section below). The of(baseValue) factory takes the base-type value (Integer here, String for a String-constrained type, Money for Money, the base enum for a constrained enumeration, and so on).

Common causes

  • A literal argument outside the declared range (e.g. Age(15) for '>= 16 and <= 100')
  • The SAME value moved into a variable or constant first. This does not defer the check: a local 'limit' given 15 then 'Age(limit)', or a constant 'UNDER_AGE' of 15 then 'Age(UNDER_AGE)', are rejected exactly as Age(15) is. Hoisting a value into a constant also disables nothing for later edits — if the constant is changed to a violating value, this error appears at every construction that uses it
  • A string literal that fails a 'matches' regex or a 'contains' check (e.g. Email("not an email"))
  • A Date/Time/Money/Dimension/Colour literal outside the declared bounds (e.g. Budget(2000#GBP))
  • An off-by-one at an inclusive boundary
  • A value that falls in a forbidden gap of an 'or' constraint (e.g. Edge(50) for '< 10 or > 90')
  • Treating a bare constructor as a validator: it ASSERTS validity (Panics on bad set input) — use of() with untrusted input

See also

  • E08260: compile-time constant violates the type's constraint (this error)
  • E07900: invalid value (a literal that is not even valid for its base type)

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E08260: compile-time constant value violates the type's constraint — this would Panic at runtime

#!ek9
defines module error.e08260.bad

  defines type

    Age as Integer constrain as
      >= 16 and <= 100

  defines program

    AgeCheck()
      stdout <- Stdout()
      tooYoung <- Age(15)
      stdout.println($tooYoung)

Fixed: Construct a value the constraint admits

#!ek9
defines module error.e08260.good

  defines type

    Age as Integer constrain as
      >= 16 and <= 100

  defines program

    AgeCheck()
      stdout <- Stdout()
      youngest <- Age(16)
      stdout.println($youngest)

Fixed: Or check a value only known at run time with of()

#!ek9
defines module error.e08260.good.checked

  defines type

    Age as Integer constrain as
      >= 16 and <= 100

  defines program

    AgeCheck()
      -> rawInput as Integer
      stdout <- Stdout()
      if validAge <- Age().of(rawInput)
        stdout.println($validAge)
      else
        stdout.println("rejected: out of range")

Questions and answers

These Q&A entries show E08260 and its fix: