The pattern ... can never match since previous clauses completely covered the type ...

Hello fellow Erlangers!

I’m writing quite a beefy Erlang system (I’m not quite a beginner - using Erlang one way or another for over 10 years). Last week dialyzer hit me with an unexpected error:

The pattern <Packet, OldState> can never match since previous clauses completely covered the type …

The code it refers to is like that (it is a module-private function):

processor(Packet = #{seq := 0}, OldState) ->
    % Body 1
;
processor(Packet = #{seq := Seq}, OldState) ->
    % Body 2
;
processor(Packet, OldState) ->
    % Catch all - report unprocessed
.

My question is: in my system as of now, processor/2 is indeed never called without the seq field. Is dialyzer sophisticated enough to notice there’s no path to supply Packet without a seq field?

OTOH, I wouldn’t be much surprised after my hunt for all “… has no local return” which all turned either bugs in my code (boo) or error/1 in NIF stubs instead of the proper erlang:nif_error/1 (yay - Erlang manual pages should be updated to reflect this change).

3 Likes

I am not an expert on dialyzer, but yes, dialyzer performs very sophisticated analysis of the control flow of the program. I assume that processor function is not exported. Then from all invocations of this function within the module it can derive that it’s always called with Packet argument having a certain type.

3 Likes

Replying to self:

  • Yes, dialyzer is that sophisticated. After making processor/2 public (so guarantees are off the table) the warning disappears. Wow, just wow.
  • I just reported error/1 in NIF documentation problem.
6 Likes