E07185: an abstract '$', '$$' or '#?' is only for a type with no fields (counting inherited ones): a sub-type's generated operator covers only its own fields, so these fields would be left out - give the operator a body, or use 'default operator' for it

Category: E07xxx Full resolution · Reported in phase: FULL_RESOLUTION · Classification: ABSTRACT_OPERATOR_OVER_FIELDS · All error codes

What went wrong

An abstract '$', '$$' or '#?' is declared on a type that has fields - its own, or ones it inherits. These three operators are the ones a sub-type's 'default operator' builds from fields. Below a super whose operator has a body, the generated one calls it and adds the sub-type's own fields: 'Circle(Shape(name=circle), radius=5.0)'. Below an ABSTRACT one there is nothing to call, so the generated operator covers only the sub-type's own fields - and the super's fields would silently be left out: 'Circle(radius=5.0)', with Shape's 'name' gone.

So an abstract '$', '$$' or '#?' is only for a type with nothing to lose: a field-less root of a hierarchy, or a trait. Where the data is, the operator has a body. The error is reported at the abstract operator, on the type that has (or inherits) the fields.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 1180 (Define an abstract Shape class with an area method, and a concrete Circle subclass.)

The [EXAMPLE] section below shows an abstract Shape with a 'name' field and an abstract '$', then the fix - the '$' generated on the type that has the field, so a Circle's generated '$' folds 'name' in - and a field-less root that may keep its '$' abstract (good_root.ek9).

How to fix it

  1. Replace the abstract operator with 'default operator $' (or '$$', '#?').
  2. Or give it a hand-written body - for example when the fields are not to be exposed in '$' or '$$'.
  3. Or, if the type is meant to be a pure polymorphic root, move the fields down to the sub-types.

Common causes

  • Marking '$' abstract on an abstract base class "so every sub-type must say how it prints", while the base itself holds data
  • A field added later to a root that was field-less when '$' was made abstract
  • An abstract middle type, with no fields of its own, that inherits fields from its super

See also

  • E07185: an abstract '$', '$$' or '#?' on a type with fields (this error)
  • E07190: 'default operator' on a type whose super does not declare the operator at all
  • E07183: '==' and '<=>' (sameness) cannot be abstract at all
  • E07184: '#?' goes with the type's sameness
  • E07237: a concrete record needs a field

Examples

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

Fails with E07185: an abstract '$', '$$' or '#?' is only for a type with no fields (counting inherited ones): a sub-type's generated operator covers only its own fields, so these fields would be left out - give the operator a body, or use 'default operator' for it

#!ek9
defines module error.e07185.bad

  defines class

    Shape as abstract
      name as String: String()

      //A sub-type's generated '$' would cover only its own fields, never name
      default operator ?

      operator $ as pure abstract
        <- rtn as String?

Fixed: Use 'default operator $' (or give it a body)

#!ek9
defines module error.e07185.good

  defines class

    Shape as abstract
      name as String: String()

      //Generated over name; a sub-type that adds fields declares its own
      default operator ?

      default operator $

Fixed: A field-less root may keep its '$' abstract: it has nothing to lose

#!ek9
defines module error.e07185.good.root

  defines class

    Measurement as abstract
      operator $ as pure abstract
        <- rtn as String?
      default operator ?

    Length extends Measurement
      metres as Float: 3.0
      override operator $ as pure
        <- rtn as String: `${metres}m`
      default operator ?

Questions and answers

These Q&A entries show E07185 and its fix: