LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File 1017-dialyzer-Improve-handling-of-complex-guards.patch of Package erlang (Project home:Ledest:erlang:20)

From fd9b5ae4193fbab66093c88728c0dbeaa3b57312 Mon Sep 17 00:00:00 2001
From: Hans Bolinder <hasse@erlang.org>
Date: Mon, 27 Aug 2018 13:14:29 +0200
Subject: [PATCH] dialyzer: Improve handling of complex guards

See also https://bugs.erlang.org/browse/ERL-680.

The right associative short circuit expressions 'andalso' and 'orelse'
are expanded by the Compiler (see v3_core) into 'case' expressions. If
parentheses are used to enforce left associativeness, variables are
introduced, and the time needed by Dialyzer increases exponentially.

Rather than trying to fix Dialyzer itself, v3_core now rewrites
repeated use of 'andalso' ('orelse') into right associative
expressions before creating the 'case' expressions.
---
 lib/compiler/src/v3_core.erl                       | 19 ++++-
 .../test/small_SUITE_data/results/left_assoc       |  2 +
 .../test/small_SUITE_data/src/left_assoc.erl       | 96 ++++++++++++++++++++++
 3 files changed, 115 insertions(+), 2 deletions(-)
 create mode 100644 lib/dialyzer/test/small_SUITE_data/results/left_assoc
 create mode 100644 lib/dialyzer/test/small_SUITE_data/src/left_assoc.erl

diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 3b746ab5bf..c9517c3e51 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -328,14 +328,16 @@ gexpr({protect,Line,Arg}, Bools0, St0) ->
             Anno = lineno_anno(Line, St),
 	    {#iprotect{anno=#a{anno=Anno},body=Eps++[E]},[],Bools0,St}
     end;
-gexpr({op,L,'andalso',E1,E2}, Bools, St0) ->
+gexpr({op,_,'andalso',_,_}=E0, Bools, St0) ->
+    {op,L,'andalso',E1,E2} = right_assoc(E0, 'andalso', St0),
     Anno = lineno_anno(L, St0),
     {#c_var{name=V0},St} = new_var(Anno, St0),
     V = {var,L,V0},
     False = {atom,L,false},
     E = make_bool_switch_guard(L, E1, V, E2, False),
     gexpr(E, Bools, St);
-gexpr({op,L,'orelse',E1,E2}, Bools, St0) ->
+gexpr({op,_,'orelse',_,_}=E0, Bools, St0) ->
+    {op,L,'orelse',E1,E2} = right_assoc(E0, 'orelse', St0),
     Anno = lineno_anno(L, St0),
     {#c_var{name=V0},St} = new_var(Anno, St0),
     V = {var,L,V0},
@@ -2054,6 +2056,19 @@ fail_clause(Pats, Anno, Arg) ->
 	     body=[#iprimop{anno=#a{anno=Anno},name=#c_literal{val=match_fail},
 			    args=[Arg]}]}.
 
+%% Optimization for Dialyzer.
+right_assoc(E, Op, St) ->
+    case member(dialyzer, St#core.opts) of
+        true ->
+            right_assoc2(E, Op);
+        false ->
+            E
+    end.
+
+right_assoc2({op,L1,Op,{op,L2,Op,E1,E2},E3}, Op) ->
+    right_assoc2({op,L2,Op,E1,{op,L1,Op,E2,E3}}, Op);
+right_assoc2(E, _Op) -> E.
+
 annotate_tuple(A, Es, St) ->
     case member(dialyzer, St#core.opts) of
         true ->
diff --git a/lib/dialyzer/test/small_SUITE_data/results/left_assoc b/lib/dialyzer/test/small_SUITE_data/results/left_assoc
new file mode 100644
index 0000000000..58cdad29de
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/left_assoc
@@ -0,0 +1,2 @@
+
+left_assoc.erl:93: The variable __@2 can never match since previous clauses completely covered the type binary()
diff --git a/lib/dialyzer/test/small_SUITE_data/src/left_assoc.erl b/lib/dialyzer/test/small_SUITE_data/src/left_assoc.erl
new file mode 100644
index 0000000000..0250e4ab49
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/left_assoc.erl
@@ -0,0 +1,96 @@
+-module(left_assoc).
+
+%% As pointed out in ERL-680, analyzing guards with short circuit
+%% operators becomes very slow as the number of left associations
+%% grows.
+
+-spec from_iso8601('Elixir.String':t(), 'Elixir.Calendar':calendar()) ->
+                          {ok, t()} | {error, atom()}.
+
+-export_type([t/0]).
+
+-type t() ::
+        #{'__struct__' := 'Elixir.Date',
+          calendar := 'Elixir.Calendar':calendar(),
+          day := 'Elixir.Calendar':day(),
+          month := 'Elixir.Calendar':month(),
+          year := 'Elixir.Calendar':year()}.
+
+-export([from_iso8601/1,
+         from_iso8601/2]).
+
+from_iso8601(__@1) ->
+    from_iso8601(__@1, 'Elixir.Calendar.ISO').
+
+from_iso8601(<<45/integer,_rest@1/binary>>, _calendar@1) ->
+    case raw_from_iso8601(_rest@1, _calendar@1) of
+        {ok,#{year := _year@1} = _date@1} ->
+            {ok,_date@1#{year := - _year@1}};
+        __@1 ->
+            __@1
+    end;
+from_iso8601(<<_rest@1/binary>>, _calendar@1) ->
+    raw_from_iso8601(_rest@1, _calendar@1).
+
+raw_from_iso8601(_string@1, _calendar@1) ->
+    case _string@1 of
+        <<_y1@1/integer,
+          _y2@1/integer,
+          _y3@1/integer,
+          _y4@1/integer,
+          45/integer,
+          _m1@1/integer,
+          _m2@1/integer,
+          45/integer,
+          _d1@1/integer,
+          _d2@1/integer>>
+          when
+              ((((((((((((((_y1@1 >= 48
+                            andalso
+                            _y1@1 =< 57)
+                           andalso
+                           _y2@1 >= 48)
+                          andalso
+                          _y2@1 =< 57)
+                         andalso
+                         _y3@1 >= 48)
+                        andalso
+                        _y3@1 =< 57)
+                       andalso
+                       _y4@1 >= 48)
+                      andalso
+                      _y4@1 =< 57)
+                     andalso
+                     _m1@1 >= 48)
+                    andalso
+                    _m1@1 =< 57)
+                   andalso
+                   _m2@1 >= 48)
+                  andalso
+                  _m2@1 =< 57)
+                 andalso
+                 _d1@1 >= 48)
+                andalso
+                _d1@1 =< 57)
+               andalso
+               _d2@1 >= 48)
+              andalso
+              _d2@1 =< 57 ->
+            {ok,
+             #{year => (_y1@1 - 48) * 1000 + (_y2@1 - 48) * 100
+               +
+                   (_y3@1 - 48) * 10
+               +
+                   (_y4@1 - 48),
+               month => (_m1@1 - 48) * 10 + (_m2@1 - 48),
+               day => (_d1@1 - 48) * 10 + (_d2@1 - 48),
+               calendar => _calendar@1,
+               '__struct__' => 'Elixir.Date'}};
+        __@1 ->
+            case __@1 of
+                _ ->
+                    {error,invalid_format};
+                __@2 ->
+                    error({with_clause,__@2})
+            end
+    end.
-- 
2.16.4