File 3651-Teach-the-debugger-to-handle-the-maybe-expression.patch of Package erlang

From 39f160fc55b70c1ea5a755aee698791838cb28b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Tue, 29 Aug 2023 09:06:35 +0200
Subject: [PATCH] Teach the debugger to handle the maybe expression

Fixes #7410
---
 lib/debugger/src/dbg_ieval.erl    |  37 +++-
 lib/debugger/src/dbg_iload.erl    |  11 ++
 lib/debugger/test/Makefile        |   1 +
 lib/debugger/test/maybe_SUITE.erl | 305 ++++++++++++++++++++++++++++++
 4 files changed, 352 insertions(+), 2 deletions(-)
 create mode 100644 lib/debugger/test/maybe_SUITE.erl

diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 2d736898d8..978a605dae 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -620,8 +620,12 @@ seq([E|Es], Bs0, Ieval) ->
 	{skip,Bs} ->
 	    seq(Es, Bs, Ieval);
 	Bs1 ->
-	    {value,_,Bs} = expr(E, Bs1, Ieval#ieval{top=false}),
-	    seq(Es, Bs, Ieval)
+	    case expr(E, Bs1, Ieval#ieval{top=false}) of
+                {value,_,Bs} ->
+                    seq(Es, Bs, Ieval);
+                {bad_maybe_match,_}=Bad ->
+                    Bad
+            end
     end;
 seq([], Bs, _) ->
     {value,true,Bs}.
@@ -755,6 +759,24 @@ expr({'orelse',Line,E1,E2}, Bs0, Ieval) ->
             exception(error, {badarg,Val}, Bs, Ieval)
     end;
 
+%% Maybe statement without else
+expr({'maybe',Line,Es}, Bs, Ieval) ->
+    case seq(Es, Bs, Ieval#ieval{line=Line}) of
+        {bad_maybe_match,Val} ->
+            {value,Val,Bs};
+        {value,_,_}=Other ->
+            Other
+    end;
+
+%% Maybe statement with else
+expr({'maybe',Line,Es,Cs}, Bs, Ieval) ->
+    case seq(Es, Bs, Ieval#ieval{line=Line}) of
+        {bad_maybe_match,Val} ->
+            case_clauses(Val, Cs, Bs, else_clause, Ieval#ieval{line=Line});
+        {value,_,_}=Other ->
+            Other
+    end;
+
 %% Matching expression
 expr({match,Line,Lhs,Rhs0}, Bs0, Ieval0) ->
     Ieval = Ieval0#ieval{line=Line},
@@ -766,6 +788,17 @@ expr({match,Line,Lhs,Rhs0}, Bs0, Ieval0) ->
 	    exception(error, {badmatch,Rhs}, Bs1, Ieval)
     end;
 
+%% Conditional match expression (?=)
+expr({maybe_match,Line,Lhs,Rhs0}, Bs0, Ieval0) ->
+    Ieval = Ieval0#ieval{line=Line},
+    {value,Rhs,Bs1} = expr(Rhs0, Bs0, Ieval#ieval{top=false}),
+    case match(Lhs, Rhs, Bs1) of
+	{match,Bs} ->
+	    {value,Rhs,Bs};
+	nomatch ->
+            {bad_maybe_match,Rhs}
+    end;
+
 %% Construct a fun
 expr({make_fun,Line,Name,Cs}, Bs, #ieval{module=Module}=Ieval) ->
     Arity = length(element(3,hd(Cs))),
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index 4649f7cef4..7499a0d94b 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -512,6 +512,13 @@ expr({'receive',Anno,Cs0,To0,ToEs0}, Lc, St) ->
     ToEs1 = exprs(ToEs0, Lc, St),
     Cs1 = icr_clauses(Cs0, Lc, St),
     {'receive',ln(Anno),Cs1,To1,ToEs1};
+expr({'maybe',Anno,Es0}, Lc, St) ->
+    Es1 = exprs(Es0, Lc, St),
+    {'maybe',ln(Anno),Es1};
+expr({'maybe',Anno,Es0,{'else',_ElseAnno,Cs0}}, Lc, St) ->
+    Es1 = exprs(Es0, Lc, St),
+    Cs1 = icr_clauses(Cs0, Lc, St),
+    {'maybe',ln(Anno),Es1,Cs1};
 expr({'fun',Anno,{clauses,Cs0}}, _Lc, St) ->
     %% New R10B-2 format (abstract_v2).
     Cs = fun_clauses(Cs0, St),
@@ -619,6 +626,10 @@ expr({match,Anno,P0,E0}, _Lc, St) ->
     E1 = expr(E0, false, St),
     P1 = pattern(P0, St),
     {match,ln(Anno),P1,E1};
+expr({maybe_match,Anno,P0,E0}, _Lc, St) ->
+    E1 = expr(E0, false, St),
+    P1 = pattern(P0, St),
+    {maybe_match,ln(Anno),P1,E1};
 expr({op,Anno,Op,A0}, _Lc, St) ->
     A1 = expr(A0, false, St),
     {op,ln(Anno),Op,[A1]};
diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile
index 015b5f9c29..45a2cdc74f 100644
--- a/lib/debugger/test/Makefile
+++ b/lib/debugger/test/Makefile
@@ -47,6 +47,7 @@ MODULES= \
 	lc_SUITE \
 	line_number_SUITE \
 	map_SUITE \
+	maybe_SUITE \
 	overridden_bif_SUITE \
 	record_SUITE \
 	trycatch_SUITE \
diff --git a/lib/debugger/test/maybe_SUITE.erl b/lib/debugger/test/maybe_SUITE.erl
new file mode 100644
index 0000000000..564c99a719
--- /dev/null
+++ b/lib/debugger/test/maybe_SUITE.erl
@@ -0,0 +1,305 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2023. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(maybe_SUITE).
+-feature(maybe_expr, enable).
+-include_lib("common_test/include/ct.hrl").
+
+-export([all/0, groups/0, init_per_suite/1, end_per_suite/1,
+         init_per_testcase/2, end_per_testcase/2]).
+-export([basic/1, nested/1, in_try_catch/1]).
+
+all() ->
+    [{group,p}].
+
+groups() ->
+    [{p,[parallel],
+      [basic,nested,in_try_catch]}].
+
+init_per_testcase(_Case, Config) ->
+    test_lib:interpret(?MODULE),
+    Config.
+
+end_per_testcase(_Case, _Config) ->
+    ok.
+
+init_per_suite(Config) ->
+    test_lib:interpret(?MODULE),
+    true = lists:member(?MODULE, int:interpreted()),
+    Config.
+
+end_per_suite(_Config) ->
+    ok.
+
+-record(value, {v}).
+
+basic(_Config) ->
+    {ok,42,fish} = basic_1(0, #{0 => {ok,42}, 42 => {ok,fish}}),
+    error = basic_1(0, #{0 => {ok,42}, 42 => {error,whatever}}),
+    error = basic_1(0, #{0 => {ok,42}, 42 => error}),
+    error = basic_1(0, #{0 => error}),
+    error = basic_1(0, #{0 => {error,whatever}}),
+    some_value = basic_1(0, #{0 => #value{v=some_value}}),
+    {'EXIT',{{else_clause,something_wrong},[_|_]}} = catch basic_1(0, #{0 => something_wrong}),
+
+    {ok,life,"universe",everything} = basic_2(0, #{0 => {ok,life},
+                                                   life => "universe",
+                                                   "universe" => {ok,everything}}),
+    error = basic_2(0, #{0 => {ok,life},
+                         life => "universe",
+                         "universe" => error}),
+    {'EXIT',{{badmatch,not_a_list},[_|_]}} = catch basic_2(0, #{0 => {ok,life},
+                                                                life => not_a_list}),
+    {'EXIT',{{else_clause,not_ok},[_|_]}} = catch basic_2(0, #{0 => {ok,life},
+                                                               life => "universe",
+                                                               "universe" => not_ok}),
+    {'EXIT',{{else_clause,not_ok},[_|_]}} = catch basic_2(0, #{0 => not_ok}),
+
+    {ok,42,fish,dolphins} = basic_3(0, #{0 => {ok,42}, 42 => {ok,fish},
+                                         fish => {ok,#value{v=dolphins}}}),
+    {error,whatever} = basic_3(0, #{0 => {ok,42}, 42 => {error,whatever}}),
+    failed = basic_3(0, #{0 => {ok,42}, 42 => failed}),
+    failed_early = basic_3(0, #{0 => failed_early}),
+
+    y = maybe nomatch ?= id(x) else _ -> y end,
+    y = maybe nomatch ?= id(x) else _ -> x, y end,
+
+    x = maybe nomatch ?= id(x) else E1 -> E1 end,
+
+    6 = maybe X1 = 2+2, X1+2 end,
+    6 = maybe X2 = 2+2, X2+2 else {error, T} -> T end,
+    {"llo", "hello", "hello"} = maybe Y1 = "he"++X3=Z1 ?= "hello", {X3,Y1,Z1} end,
+    {"llo", "hello", "llo"} = maybe Y2 = "he"++(X4=Z2) ?= "hello", {X4,Y2,Z2} end,
+
+    whatever = maybe
+                   AlwaysMatching ?= id(whatever),
+                   AlwaysMatching
+               else
+                   E2 -> E2
+               end,
+
+    <<0>> = basic_4(id({<<0>>})),
+
+    ok.
+
+basic_1(V0, M) ->
+    Res = basic_1a(V0, M),
+    {wrapped,Res} = basic_1b(V0, M),
+    {wrapped,Res} = basic_1c(V0, M),
+    Res.
+
+basic_1a(V0, M) ->
+    maybe
+        {ok,V1} ?= do_something(V0, M),
+        {ok,V2} ?= do_something(V1, M),
+        {ok,V1,V2}
+    else
+        {error,_} ->
+            error;
+        error ->
+            error;
+        #value{v=V} ->
+            V
+    end.
+
+basic_1b(V0, M) ->
+    Result =
+        maybe
+            {ok,V1} ?= do_something(V0, M),
+            {ok,V2} ?= do_something(V1, M),
+            {ok,V1,V2}
+        else
+            {error,_} ->
+                error;
+            error ->
+                error;
+            #value{v=V} ->
+                V
+        end,
+    {wrapped,Result}.
+
+basic_1c(V0, M) ->
+    OK = id(ok),
+    Error = id(error),
+    Result =
+        maybe
+            {OK,V1} ?= do_something(V0, M),
+            {OK,V2} ?= do_something(V1, M),
+            {OK,V1,V2}
+        else
+            {Error,_} ->
+                Error;
+            Error ->
+                Error;
+            #value{v=V} ->
+                V
+        end,
+    {wrapped,Result}.
+
+basic_2(V0, M) ->
+    Res = basic_2a(V0, M),
+    {wrapped,Res} = basic_2b(V0, M),
+    Res.
+
+basic_2a(V0, M) ->
+    maybe
+        {ok,V1} ?= do_something(V0, M),
+        V2 = [_|_] = do_something(V1, M),
+        {ok,V3} ?= do_something(V2, M),
+        {ok,V1,V2,V3}
+    else
+        {error,_} ->
+            error;
+        error ->
+            error;
+        #value{v=V} ->
+            V
+    end.
+
+basic_2b(V0, M) ->
+    Result =
+        maybe
+            {ok,V1} ?= do_something(V0, M),
+            V2 = [_|_] = do_something(V1, M),
+            {ok,V3} ?= do_something(V2, M),
+            {ok,V1,V2,V3}
+        else
+            {error,_} ->
+                error;
+            error ->
+                error;
+            #value{v=V} ->
+                V
+        end,
+    _ = id(0),
+    {wrapped,Result}.
+
+basic_3(V0, M) ->
+    Res = basic_3a(V0, M),
+    {wrapped,Res} = basic_3b(V0, M),
+    Res.
+
+basic_3a(V0, M) ->
+    maybe
+        {ok,V1} ?= do_something(V0, M),
+        {ok,V2} ?= do_something(V1, M),
+        {ok,#value{v=V3}} ?= do_something(V2, M),
+        {ok,V1,V2,V3}
+    end.
+
+basic_3b(V0, M) ->
+    Result =
+        maybe
+            {ok,V1} ?= do_something(V0, M),
+            {ok,V2} ?= do_something(V1, M),
+            {ok,#value{v=V3}} ?= do_something(V2, M),
+            {ok,V1,V2,V3}
+        end,
+    {wrapped,Result}.
+
+basic_4({X}) ->
+    maybe
+        <<_:(ok)>> ?= X
+    end.
+
+nested(_Config) ->
+    {outer_fail,not_ok} = nested_1(0, #{0 => not_ok}),
+    {x,{error,inner}} = nested_1(0, #{0 => {ok,x}, x => {error,inner}}),
+    {outer_fail,{unexpected,not_error}} = nested_1(0, #{0 => {ok,x}, x => not_error}),
+    ok.
+
+nested_1(V0, M) ->
+    Res = nested_1a(V0, M),
+    {wrapped,Res} = nested_1b(V0, M),
+    {wrapped,Res} = nested_1c(V0, M),
+    Res.
+
+nested_1a(V0, M) ->
+    maybe
+        {ok,V1} ?= do_something(V0, M),
+        V2 = {error,_} ?=
+            maybe
+                {error, _} ?= id(do_something(V1, M))
+            else
+                Unexpected -> {unexpected, Unexpected}
+            end,
+        {V1,V2}
+    else
+        Res -> {outer_fail,Res}
+    end.
+
+nested_1b(V0, M) ->
+    Result =
+        maybe
+            {ok,V1} ?= do_something(V0, M),
+            V2 = {error,_} ?=
+                maybe
+                    {error, _} ?= id(do_something(V1, M))
+                else
+                    Unexpected -> {unexpected, Unexpected}
+                end,
+            {V1,V2}
+        else
+            Res -> {outer_fail,Res}
+        end,
+    {wrapped,Result}.
+
+nested_1c(V0, M) ->
+    Result =
+        maybe
+            R ?= maybe
+                     {ok,V1} ?= do_something(V0, M),
+                     {error,_} = V2 ?=
+                         maybe
+                             {error, _} ?= id(do_something(V1, M))
+                         else
+                             Unexpected -> {unexpected, Unexpected}
+                         end,
+                     {V1,V2}
+                 else
+                     Res -> {outer_fail,Res}
+                 end,
+            R
+        else
+            Var -> Var
+        end,
+    {wrapped,Result}.
+
+in_try_catch(_Config) ->
+    Ref = make_ref(),
+    try
+        in_try_catch_1(Ref),
+        ct:failed(should_not_succeed)
+    catch
+        {bad_maybe_match,Ref} ->
+            ok
+    end.
+
+in_try_catch_1(Ref) ->
+    maybe
+        throw({bad_maybe_match,Ref})
+    end.
+
+%% Utility functions.
+
+do_something(V, M) ->
+    map_get(id(V), M).
+
+id(X) -> X.
-- 
2.35.3

openSUSE Build Service is sponsored by