Possible problem with safe destructive update of tuples #8875
Description
Describe the bug
I found unexpected behavior when testing some old source code with Erlang 27.
The problem may be related to this improvement introduced in OTP 27.0 (I'm just guessing):
Safe destructive update of tuples has been implemented in the compiler and runtime system. This allows the VM to update tuples in-place when it is safe to do so, thus improving performance by doing less copying but also by producing less garbage.
To Reproduce
The problem is 100% reproducible with the example code. I tried to reduce the code as much as possible, but it still includes calls to ets, maps... that probably can be simplified once the problem is better delimited.
Expected behavior
test1/0 finishes correctly, test2 and test3 fail, even if they are almost identical to test1.
Affected versions
Tested and affected: Erlang/OTP 27.0.1 and 27.1.1
Not tested with older 27 versions or git master branch.
Tested and not affected: Erlang 26.2.5.
Additional context
Example module to reproduce the problem:
-module(test).
-export([test1/0, test2/0, test3/0]).
%%%
%%% test1 succeeds
%%%
test1() ->
{Find, Details} = setup(),
{ok1, _} = Find,
{ok2, _, _} = Details,
ok.
setup() ->
TableName = table_name,
catch ets:new(TableName, [named_table, public]),
ElementName = <<"element-name">>,
ContentName = <<"content-name">>,
Element = {ElementName, maps:put(ContentName, [], maps:new())},
ets:insert(TableName, Element),
Content =
try
ets:lookup_element(TableName, ElementName, 2)
catch
_:badarg ->
#{}
end,
Find =
case maps:find(ContentName, Content) of
{ok, _} ->
{ok1, aa};
error ->
ok
end,
Details =
try function1() of
good ->
{ok2, bb, cc}
catch
_:_ ->
io:format("~nsome error was catched!~n", []),
{error, some_explanation}
end,
{Find, Details}.
function1() ->
good.
%%%
%%% test2 fails with Erlang/OTP 27
%%%
%%% ** exception error: no match of right hand side value {error,some_explanation}
%%% in function test:test2/0 (test.erl, line 99)
%%%
test2() ->
TableName = table_name,
catch ets:new(TableName, [named_table, public]),
ElementName = <<"element-name">>,
ContentName = <<"content-name">>,
Element = {ElementName, maps:put(ContentName, [], maps:new())},
ets:insert(TableName, Element),
Content =
try
ets:lookup_element(TableName, ElementName, 2)
catch
_:badarg ->
#{}
end,
Find =
case maps:find(ContentName, Content) of
{ok, _} ->
{ok1, aa};
error ->
ok
end,
Details =
try function2() of
good ->
{ok2, bb, cc}
catch
_:_ ->
io:format("~nsome error was catched!~n", []),
{error, some_explanation}
end,
{ok1, _} = Find,
{ok2, _, _} = Details,
ok.
function2() ->
good.
%%%
%%% test3 fails with Erlang/OTP 27
%%%
%%% ** exception error: no match of right hand side value {{ok1,aa},{ok2,bb,cc}}
%%% in function test:test3/0 (test.erl, line 147)
%%%
test3() ->
TableName = table_name,
catch ets:new(TableName, [named_table, public]),
ElementName = <<"element-name">>,
ContentName = <<"content-name">>,
Element = {ElementName, maps:put(ContentName, [], maps:new())},
ets:insert(TableName, Element),
Content =
try
ets:lookup_element(TableName, ElementName, 2)
catch
_:badarg ->
#{}
end,
Find =
case maps:find(ContentName, Content) of
{ok, _} ->
{ok1, aa};
error ->
ok
end,
Details =
try function3() of
good ->
{ok2, bb, cc}
catch
_:_ ->
io:format("~nsome error was catched!~n", []),
{error, some_explanation}
end,
{{ok1, _}, {ok2, _, _}} = {Find, Details},
ok.
function3() ->
good.