E06131: an alias or constrained type is made from a copy of its base value, so the base needs a public copy constructor - one parameter of exactly its own type

Category: E06xxx Type hierarchy checks · Reported in phase: TYPE_HIERARCHY_CHECKS · Classification: CONSTRAINED_TYPE_BASE_NOT_COPYABLE · All error codes

What went wrong

An alias or constrained type ('X as Base', with or without 'constrain as') is always made from a copy of a Base value, and that copy is made by the base's public copy constructor - a constructor with one parameter of exactly the base type. The base you named has none, or it is not public, or it is only inherited from a super type (which cannot copy the base's own fields).

How to fix it

  1. Give the base type a public copy constructor - one parameter of exactly the base type. Decide in it how deep the copy goes: copy each field you do not want shared (a List field copied with its own constructor, for example). There is no generated form; the copy is yours to write. The [EXAMPLE] section below shows the wrong bases and a base with its copy constructor.
  2. Or alias or constrain a different base. Only 3 of the 18 genera may be constrained, and the base must have a public copy constructor.

Rationale

'Squad(original)' reads as "a Squad made from this value". If the copy were made behind the scenes ('Base()' then ':=:'), it would be shallow and nobody would have written it: a List inside 'original' would still be shared, and a change to 'original' would show through the Squad. Requiring the copy constructor makes the copy visible and puts its depth in the author's hands - the same rule a range type follows (E08272). A constraint is also re-checked on the value copied in, so the copy is what the constraint guards.

Common causes

  • Base type declares constructors but none of them is a copy constructor
  • Base has a copy constructor that is not public
  • Base inherits a copy constructor from its super type but declares none of its own
  • Relying on 'default operator' (its ':=:') to make the copy - it no longer does

See also

  • E06132: constrained base cannot be unset (a collection base)
  • E06130: constrained type missing constructors
  • E06120: type cannot be constrained (genus not permitted)
  • E08272: a range type with no copy constructor (the same rule)

Examples

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

Fails with E06131: an alias or constrained type is made from a copy of its base value, so the base needs a public copy constructor - one parameter of exactly its own type

#!ek9
defines module error.e06131.bad

  defines type

    Squad as Team

    WrappedGuarded as Guarded

    WrappedDerived as Derived

  defines record

    //A no-argument constructor and 'default operator' (so ':=:'), but no copy constructor.
    Team
      name as String: String()
      members as List of String: List()
      default operator

  defines class

    Base as open
      a as String: String()

      Base() as pure
        a :=? "x"

      Base() as pure
        -> other as Base
        a :=? String(other.a)

      default operator ?

    //Its only copy constructor is private.
    Guarded
      text as String: String()

      Guarded() as pure
        text :=? "x"

      private Guarded() as pure
        -> other as Guarded
        text :=? String(other.text)

      default operator ?

    //Inherits Base's copy constructor, which copies a Base - not a Derived.
    Derived is Base
      extra as String: String()

      Derived() as pure
        super()
        extra :=? "y"

      default operator ?

Fixed: Give the base a public copy constructor, copying as deeply as it needs

#!ek9
defines module error.e06131.good

  defines type

    Squad as Team

  defines record

    Team
      name as String: String()
      members as List of String: List()

      Team()
        name: String()
        members: List()

      //The copy constructor: a new list, so a Squad made from a Team does not share its members.
      Team()
        -> other as Team
        name: String(other.name)
        members: List()
        members += other.members

      default operator