Problem using setelement tail recursive function

  • Erlang R23 + version has problems
  • Fixed on 2022-03-28 fixed

Test

-module(setelement_test).
-export([
    test_a/2,
    test_b/2,
    test_c/2,
    test_d/2,
    test/0
]).
-record(r_element, {key1, key2, key3}).

test_a([], Element) -> Element;
test_a([{Index, AddVal} | T], Element) ->
    #r_element{} = Element,
    OldVal = element(Index, Element),
    Element2 = setelement(Index, Element, OldVal + AddVal),
    test_a(T, Element2).

test_b([], Element) -> Element;
test_b([{Index, AddVal} | T], Element) ->
    OldVal = element(Index, Element),
    Element2 = setelement(Index, Element, OldVal + AddVal),
    test_b(T, Element2).

test_c([], Element) -> Element;
test_c([{IndexName, AddVal} | T], Element) ->
    #r_element{} = Element,                                   %% diff and test_d
    Element2 =
        case to_index(IndexName) of
            Index when Index > 0 ->
                OldVal = element(Index, Element),
                setelement(Index, Element, OldVal + AddVal);  %% {call_ext_last,3,{extfunc,erlang,setelement,3},3},
            0 ->
                Element
        end,
    test_c(T, Element2).

test_d([], Element) -> Element;
test_d([{IndexName, AddVal} | T], Element) ->
    Element2 =
        case to_index(IndexName) of
            Index when Index > 0 ->
                OldVal = element(Index, Element),
                setelement(Index, Element, OldVal + AddVal);  %% {call_ext,3,{extfunc,erlang,setelement,3}},
            0 ->
                Element
        end,
    test_d(T, Element2).

to_index(a) -> #r_element.key1;
to_index(b) -> #r_element.key2;
to_index(c) -> #r_element.key3;
to_index(_) -> 0.

test() ->
    Element = #r_element{key1 = 10, key2 = 20, key3 = 30},
    ListA = [{2, 1}, {3, 2}, {4, 3}, {2, 11}],
    A = test_a(ListA, Element),
    io:format("A=~w~n",[A]),                      %% A={r_element,22,22,33}
    B = test_b(ListA, Element),
    io:format("B=~w~n",[B]),                      %% B={r_element,22,22,33}
    ListB = [{a, 1}, {b, 2}, {d, 3}, {c, 11}],
    C = test_c(ListB, Element),
    io:format("C=~w~n",[C]),                      %% C={r_element,11,20,30} ??
    D = test_d(ListB, Element),
    io:format("D=~w~n",[D]),                      %% D={r_element,11,22,41}
    ok.

Run

c(setelement_test, [debug_info]).
setelement_test:test().
File = "./setelement_test.beam".
rp(beam_lib:chunks(File,[abstract_code])).
rp(beam_disasm:file(File)).

Note: Erlang Runtime System You can query the instructions such as call_ext_last

Fix

test_c([], Element) -> Element;
test_c([{IndexName, AddVal} | T], Element) ->
    #r_element{} = Element,
    Element2 =
        case to_index(IndexName) of
            none ->
                Element;
            Index ->
                OldVal = element(Index, Element),
                setelement(Index, Element, OldVal + AddVal)
        end,
    test_c(T, Element2).

to_index(a) -> #r_element.key1;
to_index(b) -> #r_element.key2;
to_index(c) -> #r_element.key3;
to_index(_) -> none.

Others

  • To query otp issues, you can directly check the latest fixes in Issues