[erlang-questions] Strange Dialyzer behavior when matching record fields with opaque types

Kostis Sagonas kostis@REDACTED
Thu Jan 19 01:07:44 CET 2017

On 01/18/2017 05:24 PM, Nick Marino wrote:
> Hi Kostis,
> Thanks for the quick and helpful response! Glad I'm not crazy after all
> :). That all makes sense, and in my original use case that uncovered
> this, I'm fine with just commenting out the unused code for now.
> I also just uncovered a related(?) issue, in case anyone is interested:
> in my example code, I found that changing #b{} to #a{} in the call to
> add_element actually causes Dialyzer to crash. Here's the error message:
> Proceeding with analysis...{"init terminating in
> do_boot",{function_clause,[{dialyzer,message_to_string,[{opaque_match,["pattern
> <{'b', Queue}, Key,
> Value>","<#a{d::dict:dict(_,_)},'my_key','my_value'>"]}],[{file,"dialyzer.erl"},{line,310}]},{dialyzer,format_warning,2,[{file,"dialyzer.erl"},{line,300}]},{dialyzer_cl,'-print_warnings/1-lc$^0/1-0-',2,[{file,"dialyzer_cl.erl"},{line,818}]},{dialyzer_cl,print_warnings,1,[{file,"dialyzer_cl.erl"},{line,818}]},{dialyzer_cl,return_value,2,[{file,"dialyzer_cl.erl"},{line,715}]},{dialyzer_cl,do_analysis,4,[{file,"dialyzer_cl.erl"},{line,405}]},{dialyzer,'-cl/1-fun-0-',1,[{file,"dialyzer.erl"},{line,153}]},{dialyzer,doit,1,[{file,"dialyzer.erl"},{line,243}]}]}}
> I'm tempted to try and debug this myself, but I'm not sure I have the
> necessary skills and background to safely make changes to a tool like
> Dialyzer. Figured I should leave this one to the experts ;)

Thanks again for reporting this issue, which I can reproduce.  One can 
easily avoid the crash by the following patch:

diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index d25ffd0..d5352fd 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -438,6 +438,8 @@ message_to_string({opaque_guard, [Arg1, Infix, Arg2, 
ArgNs]}) ->
  message_to_string({opaque_guard, [Guard, Args]}) ->
    io_lib:format("Guard test ~w~s breaks the opacity of its argument\n",
                 [Guard, Args]);
+message_to_string({opaque_match, [Pat, Term]}) ->
+  io_lib:format("The ~s can never match the term ~s\n", [Pat, Term]);
  message_to_string({opaque_match, [Pat, OpaqueType, OpaqueTerm]}) ->
    Term = if OpaqueType =:= OpaqueTerm -> "the term";
             true -> OpaqueTerm

which produces the following warning in your example program:

opaque_weirdness.erl:13: The pattern <{'b', Queue}, Key, Value> can 
never match the term <#a{d::dict:dict(_,_)},'my_key','my_value'>

However, this patch is sub-optimal.  Similarly to the previous issue you 
reported the core of the problem is that dialyzer erroneously classifies 
the problem as opaque-related, which is not.  It's a simple case of dead 
code (a clause that will never match) and the warning should be tagged 
with 'pattern_match' instead of 'opaque_match' or, better yet, simply 
say that the 2nd clause is unused (cannot match).

I will leave this issue to the responsible developer at Ericsson to 
handle properly.


PS. The test case is:


-record(a, {d = dict:new() :: dict:dict()}).

-record(b, {q = queue:new() :: queue:queue()}).

public_func() ->
     add_element(#a{}, my_key, my_value).

add_element(#a{d = Dict}, Key, Value) ->
     dict:store(Key, Value, Dict);
add_element(#b{q = Queue}, Key, Value) ->
     queue:in({Key, Value}, Queue).

More information about the erlang-questions mailing list