E05280: dispatcher handler can never be reached - no concrete type in the program routes to it
What went wrong
A dispatcher handler can never be reached. No concrete type anywhere in the program routes to it, so the method is dead code: it compiles, it looks like it runs, and it never does.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 1378 (How do I dispatch over a mix of classes and functions?)
- ek9 -q 1377 (Can a dispatcher method dispatch on function types?)
Why this is not a local mistake
Whether a handler is reachable is NOT a property of the dispatcher on its own. It depends on which types elsewhere in the program extend or implement the handler's parameter type.
That means this error can appear in a file you did not edit. Deleting the last class that implements a trait, removing an 'extends' during a refactor, or narrowing a hierarchy can all leave a handler in a different file with nothing that can reach it. Without this check the handler would simply stop being called, with no diagnostic anywhere.
How reachability is decided
A handler is reachable when it WINS for at least one concrete type that can arrive at the dispatcher.
- Only CONCRETE types count. A value never has an abstract type at runtime, so a handler for an abstract class, a trait or an abstract function is reachable only if something concrete extends or implements it.
- WINNING matters, not merely matching. A handler that always loses to a more specific one is just as dead.
This is the same decision the compiler makes when it builds the dispatch table, so if this error fires, no dispatch entry would have been emitted for that handler.
How to fix it
- If the handler is still wanted, give it something to dispatch on - declare a concrete type that extends or implements its parameter type
- If a refactor removed the last implementor, restore it, or point the handler at a type that still has one
- If the handler is genuinely obsolete, delete it
- If the handler is shadowed by a more specific one, widen or remove the competing handler
Common causes
- A hierarchy refactor removed the last concrete implementor of the handler's parameter type
- A handler for an abstract function type where nothing declares 'extends <that function>' - EK9 function conformance is nominal, so a matching signature alone is NOT conformance
- A handler for a trait that no concrete type implements
- A handler for a parameterised generic function (for example 'Consumer of Integer') with no conforming dynamic function anywhere
- A handler shadowed by a more specific handler that always wins
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E05280: dispatcher handler can never be reached - no concrete type in the program routes to it
#!ek9 defines module error.e05280.bad defines trait <?- Nothing implements this trait, so no value can ever have it. -?> Orphaned describe() as pure <- rtn as String: "orphaned" <?- Control: Adopted IS implemented by a concrete class below. -?> Adopted describe() as pure <- rtn as String: "adopted" defines class <?- Nothing extends this abstract class. -?> OrphanedBase as abstract default operator ? ConcreteAdopter with trait of Adopted default operator ? <?- Every handler here is declared against a type with no concrete form, so none can ever be selected. The 'Any' base means these are NOT hierarchy errors - every type is under Any - which is exactly why this needed its own check. -?> UnreachableHandlers handle() as dispatcher -> item as Any <- rtn as String: "unreachable-set fallback" handle() -> item as OrphanedBase <- rtn as String: "nothing extends OrphanedBase" handle() -> item as OrphanedFunction <- rtn as String: "nothing extends OrphanedFunction" handle() -> item as Orphaned <- rtn as String: "no concrete type implements Orphaned" handle() -> item as Supplier of String <- rtn as String: "nothing in this program conforms to Supplier of String" <?- Control class: every handler below IS reachable, so none of them may report. If this class ever starts reporting, the check has become over-eager. -?> ReachableHandlers handle() -> item as AdoptedFunction <- rtn as String: "ExtendedFunction extends AdoptedFunction" handle() -> item as Adopted <- rtn as String: "ConcreteAdopter implements Adopted" handle() as dispatcher -> item as Any <- rtn as String: "reachable-set fallback" handle() -> item as ConcreteAdopter <- rtn as String: "a concrete type is its own instantiation" handle() -> item as Integer <- rtn as String: "built-in concrete type" <?- The same rule under a NAMED base. MidAbstract is under Shape, so this is not an E05210 hierarchy error - but nothing extends MidAbstract, so the handler is still dead. -?> Shape as open default operator ? ConcreteShape extends Shape default operator ? MidAbstract extends Shape as abstract default operator ? NamedBaseHandlers render() -> s as ConcreteShape <- rtn as String: "reachable" render() -> s as MidAbstract <- rtn as String: "nothing extends MidAbstract" render() as dispatcher -> s as Shape <- rtn as String: "named-base fallback" defines function <?- Nothing extends this abstract function. Function conformance in EK9 is nominal, so a matching signature elsewhere would NOT make this reachable. -?> OrphanedFunction() as pure abstract -> inputText as String <- outputText as String? <?- Control: ExtendedFunction is concrete and extends AdoptedFunction. -?> AdoptedFunction() as pure abstract -> inputText as String <- outputText as String? ExtendedFunction() extends AdoptedFunction as pure -> inputText as String <- outputText as String: inputText
Fixed: Keep only handlers some concrete type in the program can reach; delete the dead ones
#!ek9 defines module error.e05280.good defines trait Adopted describe() as pure <- rtn as String: "adopted" defines class ConcreteAdopter with trait of Adopted default operator ? Shape as open default operator ? ConcreteShape extends Shape default operator ? ReachableHandlers handle() -> item as Adopted <- rtn as String: "ConcreteAdopter implements Adopted" handle() as dispatcher -> item as Any <- rtn as String: "fallback" handle() -> item as Integer <- rtn as String: "built-in concrete type" NamedBaseHandlers render() -> shape as ConcreteShape <- rtn as String: "reachable" render() as dispatcher -> shape as Shape <- rtn as String: "named-base fallback"
Questions and answers
These Q&A entries show E05280 and its fix: