josefs / Gradualizer

A Gradual type system for Erlang
MIT License
612 stars 35 forks source link

Handle simple guard chains #525

Closed bartekgorny closed 1 year ago

bartekgorny commented 1 year ago

...so that a second guard uses type inferred by the first one

bartekgorny commented 1 year ago

I found out that it still doesn't work as expected, will push an update soon.

bartekgorny commented 1 year ago

done

erszcz commented 1 year ago

This look good ✅

It doesn't make the mechanism complete, as there are more cases of how guards can be combined, but it's a step in the right direction, so I think it's good to merge as is.

For the context, we can have the following combinations of guards:

-module(guards).

single(A) when is_integer(A) ->
    ok.

comma(A) when is_integer(A), A > 5 ->
    ok.

semicolon(A) when is_integer(A); is_float(A) ->
    ok.

comma_semicolon(A) when is_integer(A), A > 5; is_float(A) ->
    ok.

andalso_(A) when is_integer(A) andalso A > 5 ->
    ok.

orelse_(A) when is_integer(A) orelse is_float(A) ->
    ok.

Which result in the following AST forms:

20> rp(beam_lib:chunks(guards, [debug_info])).
{ok,{guards,
        [{debug_info,
             {debug_info_v1,erl_abstract_code,
                 {[{attribute,{1,1},file,{"guards.erl",1}},
                   {attribute,{1,2},module,guards},
                   {function,
                       {3,1},
                       single,1,
                       [{clause,
                            {3,1},
                            [{var,{3,8},'A'}],
                            [[{call,
                                  {3,16},
                                  {atom,{3,16},is_integer},
                                  [{var,{3,27},'A'}]}]],
                            [{atom,{4,5},ok}]}]},
                   {function,
                       {6,1},
                       comma,1,
                       [{clause,
                            {6,1},
                            [{var,{6,7},'A'}],
                            [[{call,{6,15},{atom,{6,15},is_integer},[{var,{6,26},'A'}]},
                              {op,{6,32},'>',{var,{6,30},'A'},{integer,{6,34},5}}]],
                            [{atom,{7,5},ok}]}]},
                   {function,
                       {9,1},
                       semicolon,1,
                       [{clause,
                            {9,1},
                            [{var,{9,11},'A'}],
                            [[{call,{9,19},{atom,{9,19},is_integer},[{var,{9,30},'A'}]}],
                             [{call,{9,34},{atom,{9,34},is_float},[{var,{9,43},'A'}]}]],
                            [{atom,{10,5},ok}]}]},
                   {function,
                       {12,1},
                       comma_semicolon,1,
                       [{clause,
                            {12,1},
                            [{var,{12,17},'A'}],
                            [[{call,
                                  {12,25},
                                  {atom,{12,25},is_integer},
                                  [{var,{12,36},'A'}]},
                              {op,{12,42},'>',{var,{12,40},'A'},{integer,{12,44},5}}],
                             [{call,
                                  {12,47},
                                  {atom,{12,47},is_float},
                                  [{var,{12,56},'A'}]}]],
                            [{atom,{13,5},ok}]}]},
                   {function,
                       {15,1},
                       andalso_,1,
                       [{clause,
                            {15,1},
                            [{var,{15,10},'A'}],
                            [[{op,{15,32},
                                  'andalso',
                                  {call,{15,18},{atom,{15,18},is_integer},[{var,{15,29},'A'}]},
                                  {op,{15,42},'>',{var,{15,40},'A'},{integer,{15,44},5}}}]],
                            [{atom,{16,5},ok}]}]},
                   {function,
                       {18,1},
                       orelse_,1,
                       [{clause,
                            {18,1},
                            [{var,{18,9},'A'}],
                            [[{op,{18,31},
                                  'orelse',
                                  {call,{18,17},{atom,{18,17},is_integer},[{var,{18,28},'A'}]},
                                  {call,
                                      {18,38},
                                      {atom,{18,38},is_float},
                                      [{var,{18,47},'A'}]}}]],
                            [{atom,{19,5},ok}]}]},
                   {eof,{20,1}}],
                  [debug_info]}}}]}}
ok

With this PR we'll be handling not only single but also comma style guards. We also match the structure of non-nested andalso or orelse guards as well as comparison operators, but they're later filtered out by the check_guard_call check.

erszcz commented 1 year ago

@zuiderkwast I've merged it already, but if you have any notes please let us know