<div dir="ltr">Hi,<div><br></div><div>I think it's not you doing something wrong, but these examples are probably stretching the boundaries of what errors success typing can detect.</div><div>I made a slight modification to your example to better illustrate my point:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><font face="monospace">-module(mylib).</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">-export([foo/1, t/0]).</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">-record(bar, {one, two}).</font></div><div><font face="monospace">-type bar() :: #bar{one :: integer(), two :: list()}.</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">foo(A) when is_atom(A) -></font></div><div><font face="monospace"> #bar{two = []};</font></div><div><font face="monospace">foo(X) when is_integer(X) -></font></div><div><font face="monospace"> #bar{one = X, two = []}.</font></div><div><font face="monospace"><br></font></div><div><font face="monospace">-spec t() -> bar().</font></div><div><font face="monospace">t() -></font></div><div><font face="monospace"> foo(atom).</font></div></blockquote><div><br></div><div>Then you can check the success types with <font face="monospace">typer</font>:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><font face="monospace">%% File: "mylib.erl"</font></div><div><font face="monospace">%% -----------------</font></div><div><font face="monospace">-spec foo(atom() | integer()) -> #bar{one::'undefined' | integer(),two::[]}.</font></div><div><font face="monospace">-spec t() -> bar().</font></div></blockquote><div><br></div><div>The "problem" is that in your human mind foo's type spec is <font face="monospace">foo(atom()) -> #bar{one::undefined,two::[]}); (integer()) -> bar()</font>, but Dialyzer "collapses" this overloaded type specification. And since <font face="monospace">bar()</font> is a subtype of <font face="monospace">#bar{one::'undefined' | integer(),two::[]}</font>, it won't complain about the type spec of <font face="monospace">t/0</font>.</div><div><br></div><div>By the way, if <font face="monospace">foo/1</font> is not an exported function, you will get an error (even with your original example). I guess this is because Dialyzer does some dead code elimination before calculating the success type of <font face="monospace">foo/1</font> and realises the second clause will never apply, so there are no overloaded types left to collapse.</div><div><br></div><div>I also <a href="http://erlang.org/pipermail/erlang-questions/2020-February/099128.html">found a similar</a>, very worrying example where Dialyzer cannot detect obvious violations of type specs:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><font face="monospace">-spec bars() -> [bar()].</font></div><div><font face="monospace">bars() -></font></div><div><font face="monospace"> [#bar{two = []}, #bar{one = 1, two = []}].</font></div></blockquote><div><br></div><div>This is the same problem, the success type of the function (when ignoring type specs) is <font face="monospace">bars() -> [#bar{one::'undefined' | 1,two::[]},...]</font>, and <font face="monospace">[bar()]</font> is a subtype of this return type, so no error.</div><div><br></div><div>I honestly don't know any more how to write code that is Dialyzer-friendly. Looks like putting type specs within record definitions may help, because it forces type checking to happen right at the expression level, instead of using union types assembled from different bits of the code. But that technique has some serious drawbacks, and it isn't even applicable to non-record types. The same errors could go undetected when using a map instead of a record, for example.</div><div><br></div><div>/Dániel</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, 13 Mar 2020 at 18:03, Attila Rajmund Nohl <<a href="mailto:attila.r.nohl@gmail.com">attila.r.nohl@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hello!<br>
<br>
I looked at a largish codebase with full of dialyzer warnings due to<br>
record values created via multiple functions and the temporary values<br>
does not satisfy the type spec. It's essentially the same problem as<br>
mentioned in this thread:<br>
<a href="https://erlang-questions.erlang.narkive.com/i74Hlqbm/temporarily-violating-record-type-constraints-annoys-dialyzer" rel="noreferrer" target="_blank">https://erlang-questions.erlang.narkive.com/i74Hlqbm/temporarily-violating-record-type-constraints-annoys-dialyzer</a><br>
<br>
I applied the solution recommended by Dániel and the warnings went<br>
away - however, I'm a little afraid that if I end up creating a<br>
"wrong" record, dialyzer still won't warn me. So I created a minimal<br>
example:<br>
<br>
-module(mylib).<br>
<br>
-export([foo/1, t/0]).<br>
<br>
-record(bar, {one, two}).<br>
-type bar() :: #bar{one :: integer(), two :: list()}.<br>
<br>
-spec foo(integer()) -> bar().<br>
foo(12) -><br>
#bar{two = []};<br>
foo(X) when is_integer(X) -><br>
#bar{one = X, two = []}.<br>
<br>
-spec t() -> bar().<br>
t() -><br>
foo(12).<br>
<br>
Dialyzer does not warn me that the t/0 function returns a value that<br>
does not satisfy the typespec (the field `one` will have `undefined`<br>
value, not an integer). What am I doing wrong?<br>
</blockquote></div>