E05210: dispatcher handler type is unreachable from base dispatch type's hierarchy
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
- Ensure all handler parameter types are in the base type's hierarchy
- If dispatching across unrelated types (e.g. String, Integer, Float), introduce a common abstract supertype or use 'Any' as the base
- 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:
- Q105: How does method dispatch work in EK9?
- Q612: What happens when a dispatcher entry and handler have different purity?
- Q613: Can a derived class dispatcher target a private handler in the parent?
- Q614: What happens if a dispatcher handler's parameter type is outside the hierarchy?
- Q615: How does EK9 check dispatcher return type compatibility?
- Q616: What is dispatcher ambiguity and how is it detected?
- Q618: How do sealed dispatchers enforce exhaustive handler coverage?
- Q619: What happens with two-parameter dispatchers and type validation?
- Q620: How does dispatcher resolution work across a class hierarchy?
- Q621: What is the complete set of dispatcher compile-time checks?
- Q1377: Can a dispatcher method dispatch on function types?