E05210: dispatcher handler type is unreachable from base dispatch type's hierarchy

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

What went wrong

A dispatcher method has a handler whose parameter type is not reachable from the base dispatch type's hierarchy. The dispatcher can never route to this handler because no dispatched value will match the handler type.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 619 (What happens with two-parameter dispatchers and type vali...)

How dispatching works

A dispatcher method declares a base parameter type. At runtime, callers pass a value of that base type (or a subtype). The dispatcher inspects the RUNTIME type of the value and routes to the most specific handler.

This means the only types the dispatcher will ever see are the base type itself and its subtypes: classes that extend it, types that implement it if it is a trait, and — because EK9 functions are polymorphic too — functions that extend it if the base is a function. A handler for a type OUTSIDE this hierarchy can never be reached: no caller can pass a value that would match it, because the compiler enforces that arguments conform to the base type.

For example, if the base is 'Shape', callers can only pass Shape or its subtypes (Circle, Rectangle, etc.). An Integer can never arrive at this dispatcher — it is not a Shape and cannot be passed where Shape is expected. The handler for Integer is dead code.

How to fix it

  1. Ensure all handler parameter types are in the base type's hierarchy
  2. If dispatching across unrelated types (e.g. String, Integer, Float), introduce a common abstract supertype or use 'Any' as the base
  3. Or remove the unreachable handler method

Trait and function handlers

When a handler uses a trait type, the compiler checks that at least one subtype of the base type implements that trait. If no subtype implements the trait, the handler is unreachable.

The same applies to functions. A dispatcher may take an abstract base function and declare handlers for the functions that extend it, exactly as it would for an abstract class or a trait. Function conformance in EK9 is NOMINAL: a function only conforms to another function type if it declares 'extends'. A function whose signature merely happens to match is an unrelated type, and a handler for it is unreachable.

Common causes

  • Using a concrete type (e.g. String) as the base when handlers need unrelated types (e.g. Integer, Float) — use a common supertype instead
  • Adding a handler for a type from a different hierarchy
  • Adding a trait handler when no subtype of the base implements it
  • Adding a handler for a function that does not 'extends' the base function - a matching signature alone is not conformance
  • Copy-paste from a different dispatcher with a different base type
  • A refactor removed the 'extends' or trait that put the handler's type under the base

See also

  • E05280: dispatcher handler can never be reached - the companion check. This error says the handler's type is not under the base. E05280 says the type IS reachable in principle but has no concrete form anywhere in the program, so no value can ever have it. That one is the only of the two that applies under an 'Any' base, where nothing is ever outside the hierarchy.
  • E05230: dispatcher has ambiguous handler resolution
  • E05260: dispatcher on sealed type must handle all permitted types
  • E05170: dispatcher/target purity mismatch
  • E05180: dispatcher private in super

Examples

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

Fails with E05210: dispatcher handler type is unreachable from base dispatch type's hierarchy

#!ek9
defines module error.e05210.bad

  defines class

    Shape as abstract
      draw() as abstract

    Circle is Shape
      override draw()
        Stdout().println("Circle")

    Renderer

      render()
        -> s as Circle
        Stdout().println("Circle render")

      render()
        -> s as Integer
        Stdout().println($s)

      render() as dispatcher
        -> s as Shape
        s.draw()

Fixed: Give handlers only types the dispatcher's parameter can hold: sub-types of Shape

#!ek9
defines module error.e05210.good

  defines class

    Shape as abstract
      draw() as abstract

    Circle is Shape
      override draw()
        Stdout().println("Circle")

    Triangle is Shape
      override draw()
        Stdout().println("Triangle")

    Renderer

      render()
        -> s as Circle
        Stdout().println("Circle render")

      render() as dispatcher
        -> s as Shape
        s.draw()

      render()
        -> s as Triangle
        Stdout().println("Triangle render")

Questions and answers

These Q&A entries show E05210 and its fix: