E05240: type is not permitted to extend/implement this sealed type

Category: E05xxx Explicit type symbol definition · Reported in phase: PRE_IR_CHECKS · Classification: SEALED_TYPE_NOT_PERMITTED · All error codes

What went wrong

A class extends or implements a sealed type (trait or class) that uses 'allow only', but the class is not in the permitted list. The 'allow only' clause creates a sealed type -- only the explicitly named classes may extend or implement it. This restriction is enforced after the full type hierarchy has been established (PRE_IR_CHECKS phase).

This works identically for both sealed traits and sealed classes:

  • Sealed trait: 'Shape allow only Circle, Square' on a trait
  • Sealed class: 'Shape allow only Circle, Square as open' on a class

Enforcement is TRANSITIVE. Every concrete descendant must appear in the 'allow only' list of every sealed type above it -- at any depth, not just the direct subclasses. A grandchild that extends a permitted subclass (rather than the sealed type itself) must STILL be named in the sealed type's list. For example, given 'Vehicle allow only Car, Sedan as open' where 'Sedan extends Car' (not Vehicle directly), Sedan must still appear in Vehicle's list because Vehicle is sealed. See 'ek9 -q 303'.

Abstract classes are exempt from this check because they cannot be instantiated. The sealed set controls which concrete runtime types exist. If an abstract helper class implements a sealed type, only its concrete subclasses are checked -- and each of those must still appear in the sealed type's 'allow only' list even though it inherits the type transitively rather than declaring it directly.

Dynamic classes (anonymous classes created inline) are always rejected because their generated names cannot appear in an 'allow only' list.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 101 (Why are types closed by default in EK9?)
  • ek9 -q 303 (Can a permitted subclass also be sealed in EK9?)

How to fix it

Either add the class to the sealed type's 'allow only' list, or remove the extension/implementation from the class. If the class is an intermediate abstract helper, mark it 'as abstract' to exempt it from the check.

Common causes

  • Refactoring: extracting a new class that extends a sealed type without updating the 'allow only' list
  • Adding a new implementation of a sealed type without checking constraints
  • Dynamic classes implementing sealed types (always rejected)
  • Forgetting to update 'allow only' after splitting a class hierarchy

See also

  • E05100: incompatible genus (allow only requires classes)
  • E05110: incompatible category (allow only rejects templates)
  • E05270: sealed class must be declared 'as open'
  • E05030: not open to be extended

Examples

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

Fails with E05240: type is not permitted to extend/implement this sealed type

#!ek9
defines module error.e05240.bad

  defines trait

    SealedTrait allow only AllowedClass1, AllowedClass2
      process() as abstract

  defines class

    AllowedClass1 with trait of SealedTrait
      override process()
        content <- "Allowed 1"
        require content?

    AllowedClass2 with trait of SealedTrait
      override process()
        content <- "Allowed 2"
        require content?

    AddedLater with trait of SealedTrait
      override process()
        content <- "Added later"
        require content?

    AlsoAddedLater with trait of SealedTrait
      override process()
        content <- "Also added later"
        require content?

  defines function

    testDynamic()
      processor <- () trait of SealedTrait as class
        override process()
          content <- "Dynamic"
          require content?
      require processor?

Fixed: Add each class to the allow only list - a dynamic class can never be on it, so use one that is

#!ek9
defines module error.e05240.good.fix

  defines trait

    SealedTrait allow only AllowedClass1, AllowedClass2, AddedLater, AlsoAddedLater
      process() as abstract

  defines class

    AllowedClass1 with trait of SealedTrait
      override process()
        content <- "Allowed 1"
        require content?

    AllowedClass2 with trait of SealedTrait
      override process()
        content <- "Allowed 2"
        require content?

    AddedLater with trait of SealedTrait
      override process()
        content <- "Added later"
        require content?

    AlsoAddedLater with trait of SealedTrait
      override process()
        content <- "Also added later"
        require content?

  defines function

    testDynamic()
      processor <- AllowedClass1()
      require processor?

Fixed: Only the types the sealed trait names take it on; any other type uses composition

#!ek9
defines module error.e05240.good

  defines trait

    SealedTrait allow only AllowedClass1, AllowedClass2
      process() as abstract

  defines class

    AllowedClass1 with trait of SealedTrait
      override process()
        content <- "Allowed 1"
        require content?

    AllowedClass2 with trait of SealedTrait
      override process()
        content <- "Allowed 2"
        require content?

    //Not in the list: it holds an allowed type rather than taking on the trait
    Composed
      delegate as AllowedClass1: AllowedClass1()

      process()
        delegate.process()

      default operator ?

Questions and answers

These Q&A entries show E05240 and its fix: