Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dialyzer should not throw away spec information because of overspec #1722

Merged
merged 2 commits into from
Mar 13, 2018

Conversation

richcarl
Copy link
Contributor

This fixes a problem where Dialyzer did not warn about the following situation, because it discarded information unnecessarily:

-spec f(integer()) -> ook | error.
f(1) -> ok;
f(2) -> error.

t(X) when is_integer(X) ->
    ok = f(X).  % quietly accepted

If the spec was simply missing an entry, you got a warning even without this fix:

-spec f(integer()) -> error.  % forgot to mention that 'ok' is valid
f(1) -> ok;
f(2) -> error.

t(X) when is_integer(X) ->
    ok = f(X).  % warns about invalid match

The extraneous information in the first case simply made Dialyzer forget that there was any spec at all.

@CLAassistant
Copy link

CLAassistant commented Feb 20, 2018

CLA assistant check
All committers have signed the CLA.

@richcarl
Copy link
Contributor Author

richcarl commented Feb 20, 2018

Note that the changes to the test file chars.erl were necessary because with this patch, Dialyzer discovered that the example code in that module was totally bogus. :-)

(The chars.erl test file was introduced in https://bugs.erlang.org/browse/ERL-313)

@kostis
Copy link
Contributor

kostis commented Feb 20, 2018

This is clearly a step in the right direction, but the patch is not yet there, I think.

In dialyzer, there is a very fine line between something that is a clear improvement vs something that might be confusing (at least to some users) under some circumstances. Take a look at the warnings that dialyzer produces for beam_asm, for example. The current warnings are correct alright, but, if I am not mistaken --sorry, it has been a long day-- they fail to mention that it's actually the range of the beam_asm:encode/1 function that is not OK in the spec (as well as the type of the second argument), which is what triggered the two extra warnings with this patch. In such situations, I believe it's better for dialyzer to keep silent instead of presenting only half the truth, which may explain the rationale for the previous behavior. For example, the problems in the spec of this function are not fixed just by changing its second argument to integer() instead of pos_integer(). At least one more change/iteration is needed, right?

@richcarl
Copy link
Contributor Author

It looks to me that the beam_asm warnings worked exactly as intended. The code has a problem: encode/2 is specced as taking a strictly positive integer in the second argument, but is in fact called with constants 0 from two call sites (and encode/2 even has a clause for handling negative inputs) - but Dialyzer previously missed reporting this, because it had thrown away the spec for the unrelated reason that encode/2 can return a naked integer in the second clause, which is outside the declared iodata() return type (not in itself worth a warning by default). So the warnings are not "triggered" by the range discrepancy - they were just previously being weirdly suppressed by it, and that if anything is the surprise to the user.

I'll fix the spec in beam_asm. The patch to dialyzer_contracts is not optimal, but I don't have time to make a bigger refactoring. The real issue is that the function check_contract/3 only answers a yes/no question, but is called in two different situations: to tell whether or not to warn about some aspect of a spec (the result of which is then filtered depending on user options), and to tell whether or not a spec may be used at all or is considered broken. The answers do not overlap completely, and the function should preferably have a more expressive api, rather than making the caller filter out particular known not-really-broken cases.

@uabboli uabboli self-assigned this Feb 21, 2018
@uabboli
Copy link
Contributor

uabboli commented Feb 21, 2018

We'll need a day or two to understand and fix the warnings in SSL. There is also a warning in os_mon (easily fixed).

@kostis
Copy link
Contributor

kostis commented Feb 22, 2018

It looks to me that the beam_asm warnings worked exactly as intended. The code has a problem: encode/2 is specced as taking a strictly positive integer in the second argument, but is in fact called with constants 0 from two call sites (and encode/2 even has a clause for handling negative inputs) - but Dialyzer previously missed reporting this, because it had thrown away the spec for the unrelated reason that encode/2 can return a naked integer in the second clause, which is outside the declared iodata() return type (not in itself worth a warning by default). So the warnings are not "triggered" by the range discrepancy - they were just previously being weirdly suppressed by it, and that if anything is the surprise to the user.

I disagree. I am almost certain that the warnings are triggered by the range discrepancy, because dialyzer discovers (or at least should discover) that this function does not return iodata() :: iolist() | binary() but just iolist().

This is exactly what I meant that there is some warning which is missing here, i.e. one that informs the user about the real discrepancy that triggered the extra (correct in this case) warnings. Your fixing the spec of this function erroneously in some sense (i.e., suboptimally) confirms my position, I would argue.

Please do not misunderstand me. As I wrote, this is a change in the right direction, but I do not think it's there yet. Unfortunately, I do not have time right now to look into how it can be improved.

@uabboli uabboli merged commit 933e7fe into erlang:master Mar 13, 2018
@richcarl richcarl deleted the dialyzer-extra-range branch March 13, 2018 10:11
@kostis
Copy link
Contributor

kostis commented Mar 13, 2018

Why merge? This is clearly incomplete! Even the spec of beam_asm was not fixed properly...

martinsumner added a commit to martinsumner/leveled that referenced this pull request Jun 21, 2018
There were some bad specs in '|' OR'd specs.  These were being falsely ignored in dialyzer until erlang/otp#1722.

Running on OTP21 exposed these incomplete specs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants