Skip to content

Possible problem with safe destructive update of tuples #8875

Closed
@badlop

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.

Metadata

Assignees

Labels

bugIssue is reported as a bugteam:VMAssigned to OTP team VM

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions