E08265: a constraint's literal must match the operator's parameter type exactly — a constraint is never converted, so write the literal in that type or add the operator taking this one

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

What went wrong

The literal in a constraint does not match the parameter type of the operator it is compared with. A constraint is never converted: its literal is compared with the base type's operator exactly as it is written, so there is no promotion inside a 'constrain as'. A 'Ratio as Float' constrained '>= 1 and <= 100' compares Integers with Float's '>=', which takes a Float.

In an ordinary expression 'ratio >= 1' promotes the Integer to a Float and works. A constraint is a DECLARATION of the values a type admits, and the check generated from it uses the literal as written. Before this error existed such a constraint compiled and then failed at the FIRST construction of the type - the generated check called a Float comparison taking an Integer, which does not exist. The declaration must say exactly what it means instead.

The rule holds for every comparison operator ('==', '<>', '<', '<=', '>', '>=') and for every promotable pair - an Integer does not become a Float, and a Character does not become a String.

Worked examples

Compilable examples demonstrating this error and the correct fix:

The [EXAMPLE] section below shows a Float base given Integer literals, a String base given a Character, and a user-defined Price whose '>=' takes only a Float; then the literals respelt ('1.0', '"x"'), and Price given a '>=' that takes an Integer.

How to fix it

  1. For a built-in base (Float, String, ...), respell the literal in the operator's parameter type - the error message gives it: '1' becomes '1.0', 'x' becomes "x". Built-in types cannot gain operators.
  2. For a user-defined base you can choose: respell the literal to match the operator that already exists, or ADD a comparison taking the literal's type (good_operator.ek9 below).
  3. Do not look for a way to make the constraint convert its literal. The exact match is deliberate: what the declaration states and what is checked at construction are then the same thing.

Common causes

  • Writing whole-number bounds on a Float constraint ('>= 1' instead of '>= 1.0')
  • Using a Character literal ('x') where a String constraint needs a String ("x")
  • A user-defined base whose comparison operators take a different type from the literals used

See also

  • E08265: a constraint's literal does not match the operator's type (this error)
  • E08260: a compile-time constant value violates the type's constraint
  • E08261: no value of the base type can satisfy this constraint
  • E08262: a constraint mixes suffixes
  • E50060: the operator does not exist for the literal's type at all, even with promotion

Examples

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

Fails with E08265: a constraint's literal must match the operator's parameter type exactly — a constraint is never converted, so write the literal in that type or add the operator taking this one

#!ek9
defines module error.e08265.bad

  defines type

    //Built-in base: Float's '>=' takes a Float, so the Integer literal must be written 1.0.
    Ratio as Float constrain as
      >= 1 and <= 100

    //The same constraint written in the operator's own type is fine - the false-positive guard.
    GoodRatio as Float constrain as
      >= 1.0 and <= 100.0

    //User-defined base: either add 'operator >= as pure -> limit as Integer' to Price, or write 1.0.
    Bounded as Price constrain as
      >= 1 and <= 100.0

    //Not just Float: a Character does not silently become a String either - write "x".
    NotInitial as String constrain as
      <> 'x'

  defines record

    //A user-defined base whose comparison operators take a Float, not an Integer.
    Price
      amount as Float?

      Price()
        amount: Float()

      Price()
        -> initial as Float
        amount: initial

      Price()
        -> other as Price
        amount: other.amount

      operator <= as pure
        -> limit as Float
        <- rtn as Boolean: amount <= limit

      operator >= as pure
        -> limit as Float
        <- rtn as Boolean: amount >= limit

      override operator ? as pure
        <- rtn as Boolean: amount?

      operator :=:
        -> other as Price
        amount :=: other.amount

Fixed: Write each literal in the type the operator takes

#!ek9
defines module error.e08265.good

  defines type

    Ratio as Float constrain as
      >= 1.0 and <= 100.0

    NotInitial as String constrain as
      <> "x"

Fixed: A user-defined base can instead ADD the operator that takes the literal's type

#!ek9
defines module error.e08265.good.operator

  defines type
    Bounded as Price constrain as
      >= 1

  defines record
    Price
      amount as Float?
      Price()
        amount: Float()
      Price()
        -> initial as Float
        amount: initial
      Price()
        -> other as Price
        amount: other.amount
      operator >= as pure
        -> limit as Float
        <- rtn as Boolean: amount >= limit
      //Takes an Integer, so the constraint below may compare with '1'
      operator >= as pure
        -> threshold as Integer
        <- rtn as Boolean: amount >= #^threshold
      override operator ? as pure
        <- rtn as Boolean: amount?
      operator :=:
        -> other as Price
        amount :=: other.amount

Questions and answers

These Q&A entries show E08265 and its fix: