E07621: the type argument's operator does not return the type the generic uses it as

Category: E07xxx Full resolution · Reported in phase: POST_RESOLUTION_CHECKS · Classification: GENERIC_OPERATOR_RETURN_TYPE_MISMATCH · All error codes

What went wrong

A generic's body is checked once, against the operators every 'T' is assumed to have, and each of those has a fixed return type: '+', '-', '*', '/', 'abs', '~', '#<', '#>', '>>', '<<', 'and', 'or' and 'xor' return 'T' itself; 'mod', 'rem', 'length', '#?', '<=>' and '<~>' return an Integer; 'empty', 'contains', 'matches', '?' and the comparisons return a Boolean.

When the generic is given a type argument, the operator that serves each use must not only exist (E07620) but also return a type the body can hold where it used the result - that return type with 'T' replaced by the argument. The type argument named in the message has the operator, but it returns something else: a '+' returning Integer on 'Total', so 'sumOf() of Total' would hand an Integer back as a 'Total'. An INHERITED operator counts the same: a 'Leaf' that has its super's '+', returning 'Base', does not return a 'Leaf'.

No conversion is made here: the body was written for the operator's assumed return type, so there is nowhere to insert a promotion ('#^').

A constrained 'T' ('of type T constrain by Base') is not checked this way: its body already uses Base's own operators, with their real return types.

Worked examples

Compilable source showing this error, and the fix - see the [EXAMPLE] section below (bad.ek9 and good.ek9).

How to fix it

  1. Make the operator return what the generic uses it as: Total's '+' returns a Total (as good.ek9 in the [EXAMPLE] section does)
  2. For an inherited operator, override it in the sub-type with a covariant return (Leaf's own '+' returning a Leaf), or parameterise the generic with the super type itself ('sumOf() of Base' accepts Leaf values)
  3. If the operator really means something else (a '+' that yields a count, say), give it a method name instead and do not use the generic for it
  4. Or constrain the generic's 'T' to the type ('constrain by Total') when the body is written for that type's own operators

Common causes

  • An operator written to return a plain value (Integer, Float) rather than the type itself
  • A sub-type used as the type argument, where the operator is inherited from a super type and returns that super type
  • A built-in type argument whose operator returns a different type: '-' on Date, DateTime or Time returns a Duration; '/' on Money, Dimension or Millisecond returns a Float; '#<' and '#>' on String return a Character, and on Float, Money, Date, DateTime, Time, Bits, Dimension and Millisecond return another type

See also

  • E07621: operator return type does not fit the generic (this error)
  • E07620: operator not defined - the operator is missing altogether
  • E07440: covariance mismatch - the rule an overriding operator's return type follows
  • E50030: types not compatible - a type argument that is not the constraining type or a sub-type of it

Examples

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

Fails with E07621: the type argument's operator does not return the type the generic uses it as

#!ek9
defines module error.e07621.bad

  defines class

    Total
      amount as Integer?

      Total() as pure
        amount :=? Integer()

      Total() as pure
        -> initial as Integer
        amount :=? Integer(initial)

      amountOf() as pure
        <- rtn as Integer: Integer(amount)

      //Returns an Integer, but 'sumOf' uses '+' on T as returning T - here a Total
      default operator ?

      default operator $

      operator + as pure
        -> other as Total
        <- rtn as Integer: amount + other.amountOf()

  defines function

    sumOf() of type T
      ->
        first as T
        second as T
      <- rtn as T: first + second

  defines program

    Totals()
      stdout <- Stdout()

      totals <- sumOf() of Total
      stdout.println($totals(Total(1), Total(2)))

Fixed: Make the operator return the type itself, as the generic uses it

#!ek9
defines module error.e07621.good

  defines class

    Total
      amount as Integer?

      Total() as pure
        amount :=? Integer()

      Total() as pure
        -> initial as Integer
        amount :=? Integer(initial)

      amountOf() as pure
        <- rtn as Integer: Integer(amount)

      //A Total plus a Total is a Total
      default operator ?

      default operator $

      operator + as pure
        -> other as Total
        <- rtn as Total: Total(amount + other.amountOf())

  defines function

    sumOf() of type T
      ->
        first as T
        second as T
      <- rtn as T: first + second

  defines program

    Totals()
      stdout <- Stdout()

      totals <- sumOf() of Total
      stdout.println($totals(Total(1), Total(2)))

Questions and answers

These Q&A entries show E07621 and its fix: