[erlang-bugs] Double evaluation when using weird record syntax

Hans Bolinder <>
Thu Dec 27 15:17:46 CET 2007


[Dominic Williams:]
> The compiler seems to generate a double evaluation in the 
> following case:
> 
> -module(double_evaluation).
> -export([run/0]).
> -record(r, {f}).
> 
> maker(F) ->
>      io:format("maker(~p)~n",[F]),
>      #r{f=F}.
> 
> run()->
>      (maker(1))#r{f=0},
>      (maker(2))#r{}.      % maker/1 called twice

Thanks for the bug report.

The following patch should solve the problem: 

*** /usr/local/otp/releases/otp_beam_solaris8_r12b_patched/lib/stdlib-1.15/src/erl_expand_records.erl	Tue Dec  4 16:55:18 2007
--- erl_expand_records.erl	Thu Dec 27 14:59:36 2007
***************
*** 602,608 ****
      %% Try to be intelligent about which method of updating record to use.
      {Update,St} =
          if
!             Nu =:= 0 -> {R,St2};                 %No fields updated
              Nu =< Nc ->                         %Few fields updated
                  {record_setel(Var, Name, Fs, Us), St2};
              true ->                           %The wide area inbetween
--- 602,608 ----
      %% Try to be intelligent about which method of updating record to use.
      {Update,St} =
          if
!             Nu =:= 0 -> {Var,St2};              %No fields updated
              Nu =< Nc ->                         %Few fields updated
                  {record_setel(Var, Name, Fs, Us), St2};
              true ->                           %The wide area inbetween

There should be a test of the record type even if there are no updated
fields. The patch below, which solves that problem, might not be
included in any R12B release since it is not backwards compatible. For
instance, "(#r2{})#r{}" will generate a 'badrecord' exception.

Best regards,

Hans Bolinder, Erlang/OTP team

*** /usr/local/otp/releases/otp_beam_solaris8_r12b_patched/lib/stdlib-1.15/src/erl_expand_records.erl	Tue Dec  4 16:55:18 2007
--- erl_expand_records.erl	Thu Dec 27 14:31:46 2007
***************
*** 602,621 ****
      %% Try to be intelligent about which method of updating record to use.
      {Update,St} =
          if
!             Nu =:= 0 -> {R,St2};                 %No fields updated
              Nu =< Nc ->                         %Few fields updated
                  {record_setel(Var, Name, Fs, Us), St2};
!             true ->                           %The wide area inbetween
!                 record_match(Var, Name, Fs, Us, St2)
          end,
!     {{block,element(2, R),Pre ++ [{match,Line,Var,R},Update]},St}.
  
  %% record_match(Record, RecordName, [RecDefField], [Update], State)
  %%  Build a 'case' expression to modify record fields.
  
! record_match(R, Name, Fs, Us, St0) ->
      {Ps,News,St1} = record_upd_fs(Fs, Us, St0),
-     Lr = element(2, hd(Us)),
      {{'case',Lr,R,
        [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name} | Ps]}],[],
          [{tuple,Lr,[{atom,Lr,Name} | News]}]},
--- 602,621 ----
      %% Try to be intelligent about which method of updating record to use.
      {Update,St} =
          if
!             Nu =:= 0 -> 
!                 record_match(Var, Name, Line, Fs, Us, St2);
              Nu =< Nc ->                         %Few fields updated
                  {record_setel(Var, Name, Fs, Us), St2};
!             true ->                             %The wide area inbetween
!                 record_match(Var, Name, element(2, hd(Us)), Fs, Us, St2)
          end,
!     {{block,Line,Pre ++ [{match,Line,Var,R},Update]},St}.
  
  %% record_match(Record, RecordName, [RecDefField], [Update], State)
  %%  Build a 'case' expression to modify record fields.
  
! record_match(R, Name, Lr, Fs, Us, St0) ->
      {Ps,News,St1} = record_upd_fs(Fs, Us, St0),
      {{'case',Lr,R,
        [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name} | Ps]}],[],
          [{tuple,Lr,[{atom,Lr,Name} | News]}]},



More information about the erlang-bugs mailing list