Dialyzer considering return type none() if type definition is violated with OTP-28
Describe the bug I'm not sure this is a bug, but I am observing that in some circumstances calling a function with the wrong type makes dialyzer consider the return type is none(), so jumps to the conclusion that using the wrong type will lead to an exception.
To Reproduce To reproduce you can use this example:
-type option() ::
{'tracefun', term()} |
{'context', list()}.
-spec start(term(), term()) -> {ok, term()} | error.
start(_StartType, _StartArgs) ->
x_eval([{context, []},
{tracefun, fun() -> ok end},
{not_in_the_type_example, anything} %% Not in the type declaration
]).
-spec x_eval(Options) ->
Result when
Options :: [option()],
State :: term(),
Result :: {'ok', State} | error.
x_eval(_Options) ->
{ok,ok}.
Which will give the following warning:
src/exercises_app.erl
Line 25 Column 2: Invalid type specification for function exercises_app:start/2. The success typing is (_,_) -> none() but the spec is (term(),term()) -> {'ok',term()} | 'error'
Line 26 Column 1: Function start/2 has no local return
Other situation where I saw this was when calling the function erl_tar:extract/2 passing cwd being a binary instead of a string.
For instance:
ok = erl_tar:extract({binary, Filename}, [compressed, {cwd, <<"">>}]).
Expected behavior I would expect that either no warning is shown or that the message points to the type that is being violated instead of considering that the return type will be none() which is incorrect in the cases I have analysed.
Affected versions OTP-28.0 and all the release candidates.
Additional context Even if this is not a bug I would argue that this kind of error is not helpful. It was quite hard to figure out what was causing the warning.
I'm not sure this is a bug, but I am observing that in some circumstances calling a function with the wrong type makes dialyzer consider the return type is none(), so jumps to the conclusion that using the wrong type will lead to an exception.
Your spec promises that it will raise an exception ("not return") if x_eval/1 is passed anything other than [option(), ...] | [], so it draws the conclusion that it will. The new warning in 28 is because we've made it raise spec warnings more often. Previous versions are content with raising the second warning, Function start/2 has no local return.
Hence it's not a bug per se, but I agree that it's confusing since I'd also expect a warning on the call.
Fixing it is going to take a while however. The pass that determines the upper bound of function signatures (dialyzer_typesig) draws stricter conclusions than the pass that determines a refined (i.e. "lower") bound of function signatures (dialyzer_dataflow). The conclusions aren't incorrect as a result of this since it uses the lowest of the bounds anyway, but certain warnings fail to appear because of it.
I'll see what I can do about this over the summer, but it's not going to be very high priority however.
Thanks for the explanation.
I see now that we are actually passing -Wno_return on our dialyzer analysis, that's why this appeared to be "new".
Given this, I see this more like a feature request for dialyzer to give a warning on the call. These "no return" warnings alone are quite tricky to "debug".