%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%% Purpose : Test records.

-module(record_SUITE).

-include_lib("common_test/include/ct.hrl").

-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
	 init_per_group/2,end_per_group/2,
	 init_per_testcase/2,end_per_testcase/2,
	 errors/1,record_test_2/1,record_test_3/1,record_access_in_guards/1,
	 guard_opt/1,eval_once/1,foobar/1,missing_test_heap/1,
	 nested_access/1,coverage/1,grab_bag/1]).

init_per_testcase(_Case, Config) ->
    Config.

end_per_testcase(_Case, _Config) ->
    ok.

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap,{minutes,2}}].

all() -> 
    [{group,p}].

groups() -> 
    [{p,test_lib:parallel(),
      [errors,record_test_2,record_test_3,
       record_access_in_guards,guard_opt,eval_once,foobar,
       missing_test_heap,nested_access,coverage,grab_bag]}].


init_per_suite(Config) ->
    test_lib:recompile(?MODULE),
    Config.

end_per_suite(_Config) ->
    ok.

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


-record(foo, {a,b,c,d}).
-record(bar, {a,b,c,d}).
-record(barf, {a,b,c,d,e}).

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)),

    ok.

update_foo(#foo{}=R, A, B) ->
    R#foo{a=A,b=B}.

update_foo_bar(#foo{}=R, A) ->
    R#bar{a=A}.

update_foo_bar(#foo{}=R, A, _B) ->
    R#bar{a=A,b=A}.

update_foo_bar(#foo{}=R, A, _B, C) ->
    R#bar{a=A,b=A,c=C}.

update_foo_bar(#foo{}=R, A, _B, C, D) ->
    R#bar{a=A,b=A,c=C,d=D}.

update_foo_barf(#foo{}=R, A) ->
    R#barf{a=A}.

update_foo_barf(#foo{}=R, A, _B) ->
    R#barf{a=A,b=A}.

update_foo_barf(#foo{}=R, A, _B, C) ->
    R#barf{a=A,b=A,c=C}.

update_foo_barf(#foo{}=R, A, _B, C, D) ->
    R#barf{a=A,b=A,c=C,d=D}.

update_foo_barf(#foo{}=R, A, _B, C, D, E) ->
    R#barf{a=A,b=A,c=C,d=D,e=E}.


-define(TrueGuard(Expr), if Expr -> ok; true -> ct:fail(failed) end).
-define(FalseGuard(Expr), if Expr -> ct:fail(failed); true -> ok end).
				
record_test_2(Config) when is_list(Config) ->
    true = is_record(#foo{}, foo),
    false = is_record(#foo{}, barf),
    false = is_record({foo}, foo),

    true = erlang:is_record(#foo{}, foo),
    false = erlang:is_record(#foo{}, barf),
    false = erlang:is_record({foo}, foo),

    false = is_record([], foo),
    false = is_record(Config, foo),

    ?TrueGuard(is_record(#foo{}, foo)),
    ?FalseGuard(is_record(#foo{}, barf)),
    ?FalseGuard(is_record({foo}, foo)),

    ?TrueGuard(erlang:is_record(#foo{}, foo)),
    ?FalseGuard(erlang:is_record(#foo{}, barf)),
    ?FalseGuard(erlang:is_record({foo}, foo)),

    ?FalseGuard(is_record([], foo)),
    ?FalseGuard(is_record(Config, foo)),

    %% 'not is_record/2' to test guard optimization.

    ?FalseGuard(not is_record(#foo{}, foo)),
    ?TrueGuard(not is_record(#foo{}, barf)),
    ?TrueGuard(not is_record({foo}, foo)),

    ?FalseGuard(not erlang:is_record(#foo{}, foo)),
    ?TrueGuard(not erlang:is_record(#foo{}, barf)),
    ?TrueGuard(not erlang:is_record({foo}, foo)),

    Foo = id(#foo{}),
    ?FalseGuard(not erlang:is_record(Foo, foo)),
    ?TrueGuard(not erlang:is_record(Foo, barf)),

    ?TrueGuard(not is_record(Config, foo)),

    ?TrueGuard(not is_record(a, foo)),
    ?TrueGuard(not is_record([], foo)),

    %% Pass non-literal first argument.

    true = is_record(id(#foo{}), foo),
    false = is_record(id(#foo{}), barf),
    false = is_record(id({foo}), foo),

    true = erlang:is_record(id(#foo{}), foo),
    false = erlang:is_record(id(#foo{}), barf),
    false = erlang:is_record(id({foo}), foo),

    NoRec1 = id(blurf),
    NoRec2 = id([]),

    ?TrueGuard(not is_record(NoRec1, foo)),
    ?TrueGuard(not is_record(NoRec2, foo)),

    %% The optimizer attempts to move expressions to guards,
    %% but it must not move an is_record/2 call that is not
    %% allowed in a guard in the first place.

    ok = case is_record(id({a}), id(a)) of
		   true -> ok;
		   false -> error
	       end,

    %% Force the use of guard bifs by using the 'xor' operation.

    False = id(false),
    ?TrueGuard(is_record(#foo{}, foo) xor False),
    ?FalseGuard(is_record(#foo{}, barf) xor False),
    ?FalseGuard(is_record({foo}, foo) xor False ),

    ?TrueGuard(is_record(Foo, foo) xor False),
    ?FalseGuard(is_record(Foo, barf) xor False),


    %% Implicit guards by using a list comprehension.

    List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]),

    [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo)],
    [#bar{d=4}] = [X || X <- List, is_record(X, bar)],
    [1,#foo{a=2},3,5,#foo{a=6},7] =
	[X || X <- List, not is_record(X, bar)],
    [1,3,5,7] =
	[X || X <- List, ((not is_record(X, bar)) and (not is_record(X, foo)))],
    [#foo{a=2},#bar{d=4},#foo{a=6}] =
	[X || X <- List, ((is_record(X, bar)) or (is_record(X, foo)))],
    [1,3,#bar{d=4}] =
	[X || X <- List, ((is_record(X, bar)) or (X < 5))],

    MyList = [#foo{a=3},x,[],{a,b}],
    [#foo{a=3}] = [X || X <- MyList, is_record(X, foo)],
    [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo)],
    [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo) end],
    [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo) end],
    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo) or
				       not is_binary(X)],
    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
				       not is_binary(X)],
    [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)],
    [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
			     is_reference(X)],
    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
				   begin is_record(X, foo) or
					     not is_binary(X) end],
    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
				   begin not is_record(X, foo) or
					     not is_binary(X) end],
    [#foo{a=3}] = [X || X <- MyList,
			begin is_record(X, foo) or is_reference(X) end],
    [x,[],{a,b}] = [X || X <- MyList,
			 begin not is_record(X, foo) or
				   is_reference(X) end],

    %% Call is_record/2 with illegal arguments.
    [] = [X || X <- [], is_record(t, id(X))],
    {'EXIT',{badarg,_}} = (catch [X || X <- [1], is_record(t, id(X))]),

    %% Update several fields with a string literal.
    #barf{} = Barf0 = id(#barf{}),
    Barf = update_barf(Barf0),
    #barf{a="abc",b=1} = id(Barf),

    %% Test optimization of is_record/3.
    false = case id({a,b}) of
		{_,_}=Tuple -> is_record(Tuple, foo)
	    end,
    false = case id(true) of
		true=Bool -> is_record(Bool, foo)
	    end,

    ok.

record_test_3(Config) when is_list(Config) ->
    true = is_record(#foo{}, foo, 5),
    false = is_record(#foo{}, barf, 5),
    false = is_record(#foo{}, barf, 6),
    false = is_record({foo}, foo, 5),

    true = erlang:is_record(#foo{}, foo, 5),
    false = erlang:is_record(#foo{}, barf, 5),
    false = erlang:is_record({foo}, foo, 5),

    false = is_record([], foo),
    false = is_record(Config, foo),

    ?TrueGuard(is_record(#foo{}, foo, 5)),
    ?FalseGuard(is_record(#foo{}, barf, 5)),
    ?FalseGuard(is_record(#foo{}, barf, 6)),
    ?FalseGuard(is_record({foo}, foo, 5)),

    ?TrueGuard(erlang:is_record(#foo{}, foo, 5)),
    ?FalseGuard(erlang:is_record(#foo{}, barf, 5)),
    ?FalseGuard(erlang:is_record(#foo{}, barf, 6)),
    ?FalseGuard(erlang:is_record({foo}, foo, 5)),

    ?FalseGuard(is_record([], foo, 5)),
    ?FalseGuard(is_record(Config, foo, 5)),

    %% 'not is_record/2' to test guard optimization.

    ?FalseGuard(not is_record(#foo{}, foo, 5)),
    ?TrueGuard(not is_record(#foo{}, barf, 6)),
    ?TrueGuard(not is_record({foo}, foo, 5)),

    ?FalseGuard(not erlang:is_record(#foo{}, foo, 5)),
    ?TrueGuard(not erlang:is_record(#foo{}, barf, 5)),
    ?TrueGuard(not erlang:is_record({foo}, foo, 5)),

    Foo = id(#foo{}),
    ?FalseGuard(not erlang:is_record(Foo, foo, 5)),
    ?TrueGuard(not erlang:is_record(Foo, barf, 6)),

    ?TrueGuard(not is_record(Config, foo, 5)),

    ?TrueGuard(not is_record(a, foo, 5)),
    ?TrueGuard(not is_record([], foo, 5)),

    %% Pass non-literal first argument.

    true = is_record(id(#foo{}), foo, 5),
    false = is_record(id(#foo{}), barf, 6),
    false = is_record(id({foo}), foo, 5),

    true = erlang:is_record(id(#foo{}), foo, 5),
    false = erlang:is_record(id(#foo{}), barf, 6),
    false = erlang:is_record(id({foo}), foo, 5),

    NoRec1 = id(blurf),
    NoRec2 = id([]),

    ?TrueGuard(not is_record(NoRec1, foo, 5)),
    ?TrueGuard(not is_record(NoRec2, foo, 5)),

    %% Force the use of guard bifs by using the 'xor' operation.

    False = id(false),
    ?TrueGuard(is_record(#foo{}, foo, 5) xor False),
    ?FalseGuard(is_record(#foo{}, barf, 6) xor False),
    ?FalseGuard(is_record({foo}, foo, 5) xor False ),

    ?TrueGuard(is_record(Foo, foo, 5) xor False),
    ?FalseGuard(is_record(Foo, barf, 6) xor False),


    %% Implicit guards by using a list comprehension.

    List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]),

    [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo, 5)],
    [#bar{d=4}] = [X || X <- List, is_record(X, bar, 5)],
    [1,#foo{a=2},3,5,#foo{a=6},7] =
	[X || X <- List, not is_record(X, bar, 5)],
    [1,3,5,7] =
	[X || X <- List, ((not is_record(X, bar, 5)) and (not is_record(X, foo, 5)))],
    [#foo{a=2},#bar{d=4},#foo{a=6}] =
	[X || X <- List, ((is_record(X, bar, 5)) or (is_record(X, foo, 5)))],
    [1,3,#bar{d=4}] =
	[X || X <- List, ((is_record(X, bar, 5)) or (X < 5))],

    MyList = [#foo{a=3},x,[],{a,b}],
    [#foo{a=3}] = [X || X <- MyList, is_record(X, foo, 5)],
    [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo, 5)],
    [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo, 5) end],
    [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo, 5) end],
    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo, 5) or
				       not is_binary(X)],
    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo, 5) or
				       not is_binary(X)],
    [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)],
    [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
			     is_reference(X)],
    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
				   begin is_record(X, foo, 5) or
					     not is_binary(X) end],
    [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
				   begin not is_record(X, foo, 5) or
					     not is_binary(X) end],
    [#foo{a=3}] = [X || X <- MyList,
			begin is_record(X, foo, 5) or is_reference(X) end],
    [x,[],{a,b}] = [X || X <- MyList,
			 begin not is_record(X, foo, 5) or
				   is_reference(X) end],

    %% Update several fields with a string literal.
    #barf{} = Barf0 = id(#barf{}),
    Barf = update_barf(Barf0),
    #barf{a="abc",b=1} = id(Barf),

    %% Non-literal arguments.
    true = is_record(id(#barf{}), id(barf), id(6)),
    false = is_record(id(#barf{}), id(barf), id(42)),
    false = is_record(id(#barf{}), id(foo), id(6)),

    Rec = id(#barf{}),
    Good = id(barf),
    Bad = id(foo),
    Size = id(6),

    true = is_record(Rec, Good, Size) orelse error,
    error = is_record(Rec, Bad, Size) orelse error,

    ok.

record_access_in_guards(Config) when is_list(Config) ->
    Priv = proplists:get_value(priv_dir, Config),
    file:set_cwd(test_lib:get_data_dir(Config)),
    Opts0 = [{outdir,Priv},report_errors|test_lib:opt_opts(?MODULE)],
    M = record_access_in_guards,
 
    Opts = [strict_record_tests|Opts0],
    io:format("Options: ~p\n", [Opts]),
    {ok,M} = c:c(M, Opts),
    ok = M:t(),
    ok.


%% Test optimization of record access and is_record/2 in guards.

-record(r, {a = 4,b}).
-record(r1, {a,b}).
-record(r2, {a = #r1{},b,c=length([1,2,3])}).
-record(r3, {a = fun(_) -> #r1{} end(1), b}).

guard_opt(Config) when is_list(Config) ->
    ok = fun() ->
		 F = fun(F, [H,H|T]) when is_record(H, r) ->
			     [H|F(F, T)];
			(F, [H|T]) when is_record(H, r) ->
			     [H|F(F, T)];
			(_, []) -> []
		     end,
		 [#r{a=4,b=7},#r{a=1,b=42}] =
		     F(F, [#r{a=4,b=7},#r{a=4,b=7},#r{a=1,b=42}]),
		 {'EXIT',_} = (catch F(F, [#r1{}])),
		 ok
	 end(),

    true = fun() ->
		   R = #r{},
		   if is_record(R, r) -> true; true -> false end
	   end(),

    ok = fun() ->
		 F = fun(true, B) when B#r1.a -> ok;
			(false, _) -> error
		     end,
		 ok = F(true, #r1{a=true}),
		 error = F(false, anything_goes),
		 {'EXIT',_} = (catch F(true, #r1{})),
		 {'EXIT',_} = (catch F(true, #r{})),
		 ok
	 end(),

    ok = fun() ->
		 F = fun([{a,R}=T]) when R#r.a =:= 42 ->
			     {ok,tuple_size(T)};
			([{a,R}=T]) when R#r1.a =:= 7 ->
			     {ok,tuple_size(T)};
			(_) -> error
		     end,
		 {ok,2} = F([{a,#r{a=42}}]),
		 {ok,2} = F([{a,#r1{a=7}}]),
		 error = F([{a,#r1{}}]),
		 error = F({a,b,c}),
		 error = F([]),
		 ok
	 end(),

    ok = fun() ->
		 F = fun(X, Y, Z) when is_record(X, r1) andalso
				       (is_record(Y, r2) orelse
					is_record(Z, r3)) -> true;
			(_, _, _) -> false
		     end,
		 true = F(#r1{}, #r2{}, #r3{}),
		 true = F(#r1{}, #r2{}, blurf),
		 true = F(#r1{}, blurf, #r3{}),
		 false = F(#r1{}, blurf, blurf),
		 false = F(blurf, #r2{}, #r3{}),
		 false = F(blurf, #r2{}, blurf),
		 false = F(blurf, blurf, #r3{}),
		 false = F(blurf, blurf, blurf),
		 ok
	 end(),

    ok = fun() ->
		 F = fun(R=#r{a=42}) when R#r.b =:= 7 ->
			     {ok,R};
			(_) -> error
		     end,
		 {ok,#r{a=42,b=7}} = F(#r{a=42,b=7}),
		 error = F(#r{}),
		 error = F([a,b,c]),
		 ok
	 end(),

    ok.

update_barf(R) ->
    R#barf{a="abc",b=1}.

eval_once(Config) when is_list(Config) ->
    once(fun(GetRec) ->
		       true = erlang:is_record(GetRec(), foo)
	       end, #foo{}),
    once(fun(GetRec) ->
		       (GetRec())#foo{a=1}
	       end, #foo{}),
    once(fun(GetRec) ->
		       (GetRec())#foo{a=1,b=2}
	       end, #foo{}),
    once(fun(GetRec) ->
		       (GetRec())#foo{a=1,b=2,c=3}
	       end, #foo{}),
    once(fun(GetRec) ->
		       (GetRec())#foo{a=1,b=2,c=3,d=4}
	       end, #foo{}),
    ok.

once(Test, Record) ->
    put(?MODULE, 0),
    GetRec = fun() ->
		     put(?MODULE, 1+get(?MODULE)),
		     Record
	     end,
    Result = Test(GetRec),
    case get(?MODULE) of
	1 -> ok;
	N ->
	    io:format("Evaluated ~w times\n", [N]),
	    ct:fail(more_than_once)
    end,
    Result.

%% Thanks to Martin Bjorklund.

-record(foobar, {status}).

foobar(Config) when is_list(Config) ->
    {ok,_,_} = x({foo, 1}),
    ok.
				       
get_bar() ->
    #foobar{status = 1}.

x(Trans) ->
    {foo, Barno} = Trans,
    case get_bar() of
	Bar when Bar#foobar.status == 1 ->
	    noop(Bar),
	    Bar33 = Bar#foobar{status = 1},
	    {ok, Bar33, Barno};
	_ ->
	    Trans
    end.

noop(_) ->
    ok.

-record(foo_rec,
	{foo_1,
	 foo_2 = 0,
	 foo_3 = 0}).

missing_test_heap(Config) when is_list(Config) ->
    #foo_rec{foo_2=2,foo_3=5} = missing_test_heap_1(#foo_rec{foo_2=1,foo_3=4}),
    ok.


%% Two test_heap instructions would be incorrectly merged (not allowed
%% because of gc_bif instructions for addition).
missing_test_heap_1(A = #foo_rec {foo_1 = _B,
				  foo_3 = C,
				  foo_2 = D}) ->
    A#foo_rec {foo_1 = {C, D},
  	       foo_3 = C + 1,
  	       foo_2 = D + 1}.

-record(nrec0, {name = <<"nested0">>}).
-record(nrec1, {name = <<"nested1">>, nrec0=#nrec0{}}).
-record(nrec2, {name = <<"nested2">>, nrec1=#nrec1{}}).

nested_access(Config) when is_list(Config) ->
    N0 = #nrec0{},
    N1 = #nrec1{},
    N2 = #nrec2{},
    <<"nested0">> = N0#nrec0.name,
    <<"nested1">> = N1#nrec1.name,
    <<"nested2">> = N2#nrec2.name,
    <<"nested0">> = N1#nrec1.nrec0#nrec0.name,
    <<"nested0">> = N2#nrec2.nrec1#nrec1.nrec0#nrec0.name,
    <<"nested1">> = N2#nrec2.nrec1#nrec1.name,
    <<"nested0">> = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0.name,

    N1a = N2#nrec2.nrec1#nrec1{name = <<"nested1a">>},
    <<"nested1a">> = N1a#nrec1.name,

    N2a = N2#nrec2.nrec1#nrec1.nrec0#nrec0{name = <<"nested0a">>},
    N2b = ((N2#nrec2.nrec1)#nrec1.nrec0)#nrec0{name = <<"nested0a">>},
    <<"nested0a">> = N2a#nrec0.name,
    N2a = N2b,
    ok.

-record(rr, {a,b,c}).

coverage(Config) when is_list(Config) ->
    %% There should only remain one record test in the code below.
    R0 = id(#rr{a=1,b=2,c=3}),
    B = R0#rr.b,				%Test the record here.
    R = R0#rr{c=42},				%No need to test here.
    if 
	B > R#rr.a ->				%No need to test here.
	    ok
    end,
    #rr{a=1,b=2,c=42} = id(R),			%Test for correctness.
    ok.


-record(default_fun, {a = fun(X) -> X*X end}).

%% compiler treats records with 1 and 2 fields differently...
-record(gb_nil, {}).
-record(gb_foo, {hello=1}).
-record(gb_bar, {hello=2,there=3}).

%% Taken from compilation_SUITE.
grab_bag(_Config) ->
    T1 = fun() ->
		 X = #foo{},
		 Y = #foo{},
		 {X#foo.a == Y#foo.a,X#foo.b}
	 end,
    {true,undefined} = T1(),

    T2 = fun(X, Y) ->
		 first_arg(X#foo.a =/= Y#foo.a, X#foo.b =/= X#foo.b)
	 end,
    true = T2(#foo{a=x,b=z}, #foo{a=y,b=z}),

    T3 = fun() ->
		 #default_fun{a=Fun} = id(#default_fun{}),
		 9 = Fun(3)
	 end,
    T3(),

    %% Stupid code, but the compiler used to crash.
    T4 = fun() ->
		 F0 = fun() ->
			      R1 = #gb_nil{},
			      R2 = R1#gb_nil{},
			      R1 = R2
		      end,
		 F1 = fun() ->
			      R1 = #gb_foo{},
			      R2 = R1#gb_foo{},
			      R1 = R2
		      end,

		 F2 = fun() ->
			      R1 = #gb_bar{},
			      R2 = R1#gb_bar{},
			      R1 = R2
		      end,
		 F0(),
		 F1(),
		 F2()
	 end,
    T4(),

    ok.

first_arg(First, _) -> First.

id(I) -> I.
