Search code examples
elixirbitstringdialyzer

Elixir typespec for fixed byte length bitstring - Dialyzer not happy


I'm trying to create a typespec that represents a fixed length framed binary packet. Therefore using a bitstring of fixed N bytes (say 25 for example) seemed like the right idea.

The Elixir typespec docs stated the following:

                                ## Bitstrings
| <<>>                          # empty bitstring
| <<_::size>>                   # size is 0 or a positive integer
| <<_::_*unit>>                 # unit is an integer from 1 to 256
| <<_::size, _::_*unit>>

From this I would assume you could use @spec my_type :: <_::25, _::_*8>>

@type my_type :: <<_::25, _::_*8>>

@spec my_type_test() :: my_type
def my_type_test() do
    # 25 byte bitstring with start-of-frame byte
    << 0xA5::size(8) , 0::size(24)-unit(8) >>
end

But Dialyzer comes back with the following:

[ElixirLS Dialyzer] Invalid type specification for function 
'TestModule':my_type_test/0. The success typing 
is () -> <<_:200>>

Huh? But they're both bitstrings and the bit length is the same!

Anybody know why Dialyzer doesn't like this?


Solution

  • The number just after :: specifies the number of bits, not bytes. If you want the type to match 25 bytes plus N * 8 bytes, the type needs to be:

    @type my_type :: <<_::200, _::_*64>>
    

    After this change, your original expression passes Dialyzer's checks and incrementing the size by 1 bit or 1 byte fails, as expected.