summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--erts/emulator/beam/atom.names3
-rw-r--r--erts/emulator/beam/beam_common.c2
-rw-r--r--erts/emulator/beam/emu/instrs.tab7
-rw-r--r--erts/emulator/beam/emu/ops.tab2
-rw-r--r--erts/emulator/beam/error.h4
-rw-r--r--erts/emulator/beam/jit/arm/instr_common.cpp5
-rw-r--r--erts/emulator/beam/jit/arm/ops.tab2
-rw-r--r--erts/emulator/beam/jit/x86/instr_common.cpp5
-rw-r--r--erts/emulator/beam/jit/x86/ops.tab2
-rw-r--r--erts/emulator/test/exception_SUITE.erl9
-rw-r--r--lib/compiler/src/beam_disasm.erl2
-rw-r--r--lib/compiler/src/beam_jump.erl1
-rw-r--r--lib/compiler/src/beam_ssa_pre_codegen.erl3
-rw-r--r--lib/compiler/src/beam_trim.erl2
-rw-r--r--lib/compiler/src/beam_validator.erl3
-rwxr-xr-xlib/compiler/src/genop.tab4
-rw-r--r--lib/compiler/src/v3_core.erl16
-rw-r--r--lib/compiler/test/beam_jump_SUITE.erl5
-rw-r--r--lib/compiler/test/compile_SUITE.erl22
-rw-r--r--lib/compiler/test/record_SUITE.erl22
-rw-r--r--lib/compiler/test/record_SUITE_data/record_access_in_guards.erl2
-rw-r--r--lib/stdlib/src/erl_expand_records.erl8
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl32
23 files changed, 126 insertions, 37 deletions
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 313c60d342..206bb5e278 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -118,7 +118,8 @@ atom await_sched_wall_time_modifications
atom awaiting_load
atom awaiting_unload
atom backtrace backtrace_depth
-atom badarg badarith badarity badfile badfun badkey badmap badmatch badsig
+atom badarg badarith badarity badfile badfun badkey badmap badmatch
+atom badrecord badsig
atom badopt badtype
atom bad_map_iterator
atom bag
diff --git a/erts/emulator/beam/beam_common.c b/erts/emulator/beam/beam_common.c
index 75a74e6fcf..304b4968a1 100644
--- a/erts/emulator/beam/beam_common.c
+++ b/erts/emulator/beam/beam_common.c
@@ -395,6 +395,7 @@ Eterm error_atom[NUMBER_EXIT_CODES] = {
am_notsup, /* 17 */
am_badmap, /* 18 */
am_badkey, /* 19 */
+ am_badrecord, /* 20 */
};
/* Returns the return address at E[0] in printable form, skipping tracing in
@@ -753,6 +754,7 @@ expand_error_value(Process* c_p, Uint freason, Eterm Value) {
case (GET_EXC_INDEX(EXC_BADARITY)):
case (GET_EXC_INDEX(EXC_BADMAP)):
case (GET_EXC_INDEX(EXC_BADKEY)):
+ case (GET_EXC_INDEX(EXC_BADRECORD)):
/* Some common exceptions: value -> {atom, value} */
ASSERT(is_value(Value));
hp = HAlloc(c_p, 3);
diff --git a/erts/emulator/beam/emu/instrs.tab b/erts/emulator/beam/emu/instrs.tab
index 79dd6d566e..34b6c1be32 100644
--- a/erts/emulator/beam/emu/instrs.tab
+++ b/erts/emulator/beam/emu/instrs.tab
@@ -1074,6 +1074,13 @@ if_end() {
//| -no_next;
}
+badrecord(Src) {
+ c_p->fvalue = $Src;
+ c_p->freason = EXC_BADRECORD;
+ goto find_func_info;
+ //| -no_next;
+}
+
system_limit_body() {
c_p->freason = SYSTEM_LIMIT;
$FAIL_BODY();
diff --git a/erts/emulator/beam/emu/ops.tab b/erts/emulator/beam/emu/ops.tab
index a11c0de7bc..b827052239 100644
--- a/erts/emulator/beam/emu/ops.tab
+++ b/erts/emulator/beam/emu/ops.tab
@@ -287,6 +287,8 @@ badmatch x
if_end
+badrecord s
+
# Operands for raise/2 are almost always in x(2) and x(1).
# Optimize for that case.
raise x==2 x==1 => i_raise
diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h
index 2067505eda..e4abe85761 100644
--- a/erts/emulator/beam/error.h
+++ b/erts/emulator/beam/error.h
@@ -165,8 +165,10 @@
/* Bad map */
#define EXC_BADKEY ((19 << EXC_OFFSET) | EXC_ERROR)
/* Bad key in map */
+#define EXC_BADRECORD ((20 << EXC_OFFSET) | EXC_ERROR)
+ /* Bad key in map */
-#define NUMBER_EXIT_CODES 20 /* The number of exit code indices */
+#define NUMBER_EXIT_CODES 21 /* The number of exit code indices */
/*
* Internal pseudo-error codes.
diff --git a/erts/emulator/beam/jit/arm/instr_common.cpp b/erts/emulator/beam/jit/arm/instr_common.cpp
index a3be03a34e..bdfbca4afa 100644
--- a/erts/emulator/beam/jit/arm/instr_common.cpp
+++ b/erts/emulator/beam/jit/arm/instr_common.cpp
@@ -1473,6 +1473,11 @@ void BeamModuleAssembler::emit_if_end() {
emit_error(EXC_IF_CLAUSE);
}
+void BeamModuleAssembler::emit_badrecord(const ArgVal &Src) {
+ mov_arg(arm::Mem(c_p, offsetof(Process, fvalue)), Src);
+ emit_error(EXC_BADRECORD);
+}
+
void BeamModuleAssembler::emit_catch(const ArgVal &Y, const ArgVal &Handler) {
a.ldr(TMP1, arm::Mem(c_p, offsetof(Process, catches)));
a.add(TMP1, TMP1, imm(1));
diff --git a/erts/emulator/beam/jit/arm/ops.tab b/erts/emulator/beam/jit/arm/ops.tab
index 02a997e27b..ad79a9b2b2 100644
--- a/erts/emulator/beam/jit/arm/ops.tab
+++ b/erts/emulator/beam/jit/arm/ops.tab
@@ -298,6 +298,8 @@ badmatch s
if_end
+badrecord s
+
raise s s
# Workaround the limitation that generators must always return at least one instruction.
diff --git a/erts/emulator/beam/jit/x86/instr_common.cpp b/erts/emulator/beam/jit/x86/instr_common.cpp
index 2758b1a3b6..514aabb6f4 100644
--- a/erts/emulator/beam/jit/x86/instr_common.cpp
+++ b/erts/emulator/beam/jit/x86/instr_common.cpp
@@ -1736,6 +1736,11 @@ void BeamModuleAssembler::emit_if_end() {
emit_error(EXC_IF_CLAUSE);
}
+void BeamModuleAssembler::emit_badrecord(const ArgVal &Src) {
+ mov_arg(x86::qword_ptr(c_p, offsetof(Process, fvalue)), Src);
+ emit_error(EXC_BADRECORD);
+}
+
void BeamModuleAssembler::emit_catch(const ArgVal &Y, const ArgVal &Fail) {
a.inc(x86::qword_ptr(c_p, offsetof(Process, catches)));
diff --git a/erts/emulator/beam/jit/x86/ops.tab b/erts/emulator/beam/jit/x86/ops.tab
index 2880244778..b5c6da35cd 100644
--- a/erts/emulator/beam/jit/x86/ops.tab
+++ b/erts/emulator/beam/jit/x86/ops.tab
@@ -321,6 +321,8 @@ badmatch s
if_end
+badrecord s
+
raise s s
# Workaround the limitation that generators must always return at least one instruction.
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index eb115b0fb8..30f0c4e575 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -1589,6 +1589,10 @@ line_numbers(Config) when is_list(Config) ->
{?MODULE,line_numbers,1,_}|_]}} =
(catch crash_huge_line(gurka)),
+ {'EXIT',{{badrecord,[1,2,3]},
+ [{?MODULE,bad_record,1,[{file,"bad_records.erl"},{line,4}]}|_]}} =
+ catch bad_record([1,2,3]),
+
ok.
id(I) -> I.
@@ -1716,3 +1720,8 @@ foo() -> id(100).
crash_huge_line(_) -> %Line 100000002
erlang:error(crash). %Line 100000003
+
+-file("bad_records.erl", 1).
+-record(foobar, {a,b,c,d}). %Line 2.
+bad_record(R) -> %Line 3.
+ R#foobar.a. %Line 4.
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index aed0f5bb78..e63755d22c 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1235,6 +1235,8 @@ resolve_inst({bs_create_bin,Args},_,_,_) ->
{bs_create_bin,Args};
resolve_inst({call_fun2,[Tag,{u,Arity},Func]},_,_,_) ->
{call_fun2,Tag,Arity,Func};
+resolve_inst({badrecord,[Arg]},_,_,_) ->
+ {badrecord,resolve_arg(Arg)};
%%
%% Catches instructions that are not yet handled.
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index f4ca52ebe7..fffc940d9f 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -777,6 +777,7 @@ is_exit_instruction(if_end) -> true;
is_exit_instruction({case_end,_}) -> true;
is_exit_instruction({try_case_end,_}) -> true;
is_exit_instruction({badmatch,_}) -> true;
+is_exit_instruction({badrecord,_}) -> true;
is_exit_instruction(_) -> false.
%% remove_unused_labels(Instructions0) -> Instructions
diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl
index b39292e15d..33dacad296 100644
--- a/lib/compiler/src/beam_ssa_pre_codegen.erl
+++ b/lib/compiler/src/beam_ssa_pre_codegen.erl
@@ -916,6 +916,9 @@ expand_mf_instr(#b_set{args=[#b_literal{val=try_clause} | Args]}=I0,
expand_mf_instr(#b_set{args=[#b_literal{val=badmatch} | _Args]}=I,
Is, Count, Acc) ->
{reverse(Acc, [I | Is]), Count};
+expand_mf_instr(#b_set{args=[#b_literal{val=badrecord} | _Args]}=I,
+ Is, Count, Acc) ->
+ {reverse(Acc, [I | Is]), Count};
expand_mf_instr(#b_set{args=[#b_literal{val=function_clause} | Args]}=I0,
Is, Count0, Acc0) ->
%% We can't make a direct jump to `func_info` or an inlined stub: simulate
diff --git a/lib/compiler/src/beam_trim.erl b/lib/compiler/src/beam_trim.erl
index a332bd522d..c3b1677bb7 100644
--- a/lib/compiler/src/beam_trim.erl
+++ b/lib/compiler/src/beam_trim.erl
@@ -362,6 +362,8 @@ is_safe_label([{try_case_end,{Tag,_}}|_]) ->
Tag =/= y;
is_safe_label([if_end|_]) ->
true;
+is_safe_label([{badrecord,{Tag,_}}|_]) ->
+ Tag =/= y;
is_safe_label([{block,Bl}|Is]) ->
is_safe_label_block(Bl) andalso is_safe_label(Is);
is_safe_label([{call_ext,_,{extfunc,M,F,A}}|_]) ->
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 7a6ca09d5c..11f328b470 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1058,6 +1058,9 @@ vi(if_end, Vst) ->
vi({try_case_end,Src}, Vst) ->
assert_durable_term(Src, Vst),
branch(?EXCEPTION_LABEL, Vst, fun kill_state/1);
+vi({badrecord,Src}, Vst) ->
+ assert_durable_term(Src, Vst),
+ branch(?EXCEPTION_LABEL, Vst, fun kill_state/1);
vi(raw_raise=I, Vst0) ->
validate_body_call(I, 3, Vst0);
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index f5930f7b01..bcddaa9481 100755
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -666,3 +666,7 @@ BEAM_FORMAT_NUMBER=0
## @spec nif_start
## @doc No-op at start of each function declared in -nifs().
179: nif_start/0
+
+## @spec badrecord Value
+## @doc Raises a {badrecord,Value} error exception.
+180: badrecord/1
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index f82bf5aa04..139828084d 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -742,10 +742,20 @@ expr({named_fun,L,'_',Cs}, St) ->
fun_tq(Cs, L, St, unnamed);
expr({named_fun,L,Name,Cs}, St) ->
fun_tq(Cs, L, St, {named,Name});
-expr({call,L,{remote,_,M,F},As0}, St0) ->
- {[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0),
+expr({call,L,{remote,_,M0,F0},As0}, St0) ->
+ {[M1,F1|As1],Aps,St1} = safe_list([M0,F0|As0], St0),
Anno = full_anno(L, St1),
- {#icall{anno=#a{anno=Anno},module=M1,name=F1,args=As1},Aps,St1};
+ case {M1,F1,As1} of
+ {#c_literal{val=erlang},
+ #c_literal{val=error},
+ [#c_tuple{es=[#c_literal{val=badrecord},_]}=Tuple]} ->
+ Fail = #iprimop{anno=#a{anno=Anno},
+ name=#c_literal{val=match_fail},
+ args=[Tuple]},
+ {Fail,Aps,St1};
+ {_,_,_} ->
+ {#icall{anno=#a{anno=Anno},module=M1,name=F1,args=As1},Aps,St1}
+ end;
expr({call,Lc,{atom,Lf,F},As0}, St0) ->
{As1,Aps,St1} = safe_list(As0, St0),
Op = #c_var{anno=lineno_anno(Lf, St1),name={F,length(As1)}},
diff --git a/lib/compiler/test/beam_jump_SUITE.erl b/lib/compiler/test/beam_jump_SUITE.erl
index a97d863160..22b7e4f679 100644
--- a/lib/compiler/test/beam_jump_SUITE.erl
+++ b/lib/compiler/test/beam_jump_SUITE.erl
@@ -232,7 +232,8 @@ ambiguous_catch_try_state_3() ->
-record(message3, {id, p1, p2}).
build_tuple(_Config) ->
- {'EXIT',{{badrecord,message3},_}} = (catch do_build_tuple(#message2{})),
+ Message2 = #message2{},
+ {'EXIT',{{badrecord,Message2},_}} = (catch do_build_tuple(#message2{})),
ok.
do_build_tuple(Message) ->
@@ -319,7 +320,7 @@ cs_2(I) -> I.
undecided_allocation(_Config) ->
ok = catch undecided_allocation_1(<<10:(3*7)>>),
- {'EXIT',{{badrecord,rec},_}} = catch undecided_allocation_1(8),
+ {'EXIT',{{badrecord,<<0>>},_}} = catch undecided_allocation_1(8),
ok.
-record(rec, {}).
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index 5469379792..9bb1a57c6f 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -931,21 +931,21 @@ strict_record(Config) when is_list(Config) ->
%% Default (possibly influenced by ERL_COMPILER_OPTIONS).
{ok,M} = c:c(M, [{outdir,Priv},report_errors]),
try
- {1,2} = record_access:test(Turtle),
- {comment,"Default: no_strict_record_tests"}
- catch
- error:{badrecord,tortoise} ->
- {comment,"Default: strict_record_tests"}
- end.
+ {1,2} = record_access:test(Turtle),
+ {comment,"Default: no_strict_record_tests"}
+ catch
+ error:{badrecord,Turtle} ->
+ {comment,"Default: strict_record_tests"}
+ end.
test_strict() ->
Turtle = record_access:turtle(),
try
- record_access:test(Turtle)
- catch
- error:{badrecord,tortoise} ->
- ok
- end,
+ record_access:test(Turtle)
+ catch
+ error:{badrecord,Turtle} ->
+ ok
+ end,
Turtle.
test_sloppy() ->
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index 2dd40e6a77..ae959d0743 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -73,17 +73,17 @@ errors(Config) when is_list(Config) ->
Foo = #foo{a=1,b=2,c=3,d=4},
#foo{a=19,b=42,c=3,d=4} = update_foo(Foo, 19, 42),
- {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19)),
- {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35)),
- {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17)),
- {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17, 42)),
-
- {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19)),
- {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35)),
- {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17)),
- {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42)),
- {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19,
- 35, 17, 42, -2)),
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_bar(Foo, 19)),
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_bar(Foo, 19, 35)),
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_bar(Foo, 19, 35, 17)),
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_bar(Foo, 19, 35, 17, 42)),
+
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_barf(Foo, 19)),
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_barf(Foo, 19, 35)),
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_barf(Foo, 19, 35, 17)),
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42)),
+ {'EXIT',{{badrecord,Foo},_}} = (catch update_foo_barf(Foo, 19,
+ 35, 17, 42, -2)),
ok.
diff --git a/lib/compiler/test/record_SUITE_data/record_access_in_guards.erl b/lib/compiler/test/record_SUITE_data/record_access_in_guards.erl
index dbd2419ad2..60a98cfe80 100644
--- a/lib/compiler/test/record_SUITE_data/record_access_in_guards.erl
+++ b/lib/compiler/test/record_SUITE_data/record_access_in_guards.erl
@@ -172,7 +172,7 @@ t() ->
ok = F(R, 42, tab),
error = F(R, 42, a),
error = F(R, 0, tab),
- {'EXIT',{{badrecord,r},_}} = (catch F({x,y,z}, 4, 5)),
+ {'EXIT',{{badrecord,{x,y,z}},_}} = (catch F({x,y,z}, 4, 5)),
ok
end(#r{a=42,b=tab}),
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 4c8ba0578b..47da4bda9d 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -595,11 +595,11 @@ strict_get_record_field(Anno, R, {atom,_,F}=Index, Name, St0) ->
RAnno = mark_record(NAnno, St),
E = {'case',Anno,R,
[{clause,NAnno,[{tuple,RAnno,P}],[],[Var]},
- {clause,NAnno,[{var,NAnno,'_'}],[],
+ {clause,NAnno,[Var],[],
[{call,NAnno,{remote,NAnno,
{atom,NAnno,erlang},
{atom,NAnno,error}},
- [{tuple,NAnno,[{atom,NAnno,badrecord},{atom,NAnno,Name}]}]}]}]},
+ [{tuple,NAnno,[{atom,NAnno,badrecord},Var]}]}]}]},
expr(E, St);
true -> %In a guard.
Fs = record_fields(Name, Anno, St0),
@@ -714,7 +714,7 @@ record_match(R, Name, AnnoR, Fs, Us, St0) ->
[{clause,AnnoR,[{tuple,RAnno,[{atom,AnnoR,Name} | Ps]}],[],
[{tuple,RAnno,[{atom,AnnoR,Name} | News]}]},
{clause,NAnnoR,[{var,NAnnoR,'_'}],[],
- [call_error(NAnnoR, {tuple,NAnnoR,[{atom,NAnnoR,badrecord},{atom,NAnnoR,Name}]})]}
+ [call_error(NAnnoR, {tuple,NAnnoR,[{atom,NAnnoR,badrecord},R]})]}
]},
St1}.
@@ -752,7 +752,7 @@ record_setel(R, Name, Fs, Us0) ->
{atom,Anno,setelement}},[I,Acc,Val]} end,
R, Us)]},
{clause,NAnnoR,[{var,NAnnoR,'_'}],[],
- [call_error(NAnnoR, {tuple,NAnnoR,[{atom,NAnnoR,badrecord},{atom,NAnnoR,Name}]})]}]}.
+ [call_error(NAnnoR, {tuple,NAnnoR,[{atom,NAnnoR,badrecord},R]})]}]}.
%% Expand a call to record_info/2. We have checked that it is not
%% shadowed by an import.
diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index ed5b6325fc..4151f211e6 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -39,7 +39,8 @@
-export([attributes/1, expr/1, guard/1,
init/1, pattern/1, strict/1, update/1,
otp_5915/1, otp_7931/1, otp_5990/1,
- otp_7078/1, otp_7101/1, maps/1]).
+ otp_7078/1, otp_7101/1, maps/1,
+ side_effects/1]).
init_per_testcase(_Case, Config) ->
Config.
@@ -53,7 +54,8 @@ suite() ->
all() ->
[attributes, expr, guard, init,
- pattern, strict, update, maps, {group, tickets}].
+ pattern, strict, update, maps,
+ side_effects, {group, tickets}].
groups() ->
[{tickets, [],
@@ -317,7 +319,7 @@ strict(Config) when is_list(Config) ->
ok = try
{1, 2} = {A#r2.a, A#r2.b},
not_ok
- catch error:{badrecord,r2} -> ok
+ catch error:{badrecord,{r1,1,2}} -> ok
end,
try
case foo of
@@ -761,6 +763,30 @@ otp_7101_update3(R) ->
otp_7101_update4(R) ->
R#otp_7101{a=1,b=2}.
+
+-record(side_effects, {a,b,c}).
+
+%% Make sure that the record expression is only evaluated once.
+side_effects(_Config) ->
+ init_counter(),
+
+ {'EXIT',{{badrecord,0},_}} = catch (id(bump_counter()))#side_effects{a=1},
+ 1 = read_counter(),
+
+ {'EXIT',{{badrecord,1},_}} = catch (id(bump_counter()))#side_effects.b,
+ 2 = read_counter(),
+
+ ok.
+
+init_counter() ->
+ put(counter, 0).
+
+bump_counter() ->
+ put(counter, get(counter) + 1).
+
+read_counter() ->
+ get(counter).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
run(Config, Tests) ->