[gnu.g++.bug] G++ 1.34.1 named return values

rjc@maui.cs.ucla.edu (03/17/89)

G++ 1.34.1 with GCC 1.34
Vax 8350, Ultrix V2.2-1 System #4
config.g++ vax

I tried using named return values.  It didn't seem to work as advertised.
Is it broken, or was the advertisement wrong?

>From ucla-cs!oberon!bloom-beacon!tut.cis.ohio-state.edu!YAHI.STANFORD.EDU!mdt Thu Mar 16 09:53:32 PST 1989
>[...]
>
>GNU C++ 1.34.1 also has three new experimental features.
>
>1. Named return values.  Example:
>
>struct A { ... A (); A (A&); ... };
>
>// old way
>A foo ()
>{
>  A tmp;	// calls A() constructor
>
>  ... things which modify `tmp' ...
>  return tmp;	// calls A(A&) constructor
>}
>
>// new way
>A foo () return tmp;	// calls A () constructor on entry
>{
>  ... things which modify `tmp' ...
>  return tmp;	// call to A (A&) constructor not needed
>}
>[...]

Following this description, we are told that tmp is not declared:

bug.cc:
----------------------------------------------------------
/* test named return values
 * Rob Collins (rjc@cs.ucla.edu)
 */

struct A {
	int i;
	A(int x) { printf("A(%d)\n", x); i = x; };
	A(A& x) { printf("A(A& x), x.i == %d\n", x.i); i = x.i; };
	~A(void) { printf("~A(%d)\n", i); };
	void operator += (A& x) { printf("+=(%d, %d)\n", i, x.i); i += x.i; };
};

A foo (void) return tmp = 4;
{
	printf("foo(void), tmp.i == %d\n", tmp.i);
	return tmp;
}

main()
{
	A bar = 5;
	bar += foo();
	printf("main(), bar.i == %d\n", bar.i);
}
----------------------------------------------------------

Script started on Thu Mar 16 10:27:42 1989
% g++ -v bug.cc
g++ version 1.34.1
 /usr/local/lib/gcc-cpp -+ -v -undef -D__GNU__ -D__GNUG__ -Dvax -Dunix -D__vax__ -D__unix__ bug.cc /tmp/cc007922.cpp
GNU CPP version 1.34
 /usr/local/lib/gcc-c++ /tmp/cc007922.cpp -quiet -dumpbase bug.cc -noreg -version -o /tmp/cc007922.s
GNU C++ version 1.34.1 (vax) compiled by GNU C version 1.34.
In function foo ():
bug.cc:15: `tmp' was not declared (first use this function)
bug.cc:15: (Each undeclared identifier is reported only once
bug.cc:15: for each function it appears in.)
bug.cc:16: `tmp' was not declared (first use this function)
% ^D
script done on Thu Mar 16 10:27:51 1989

Adding a declaration of tmp inside the function foo results in successful
compilation:

bug.cc:
----------------------------------------------------------
/* test named return values
 * Rob Collins (rjc@cs.ucla.edu)
 */

struct A {
	int i;
	A(int x) { printf("A(%d)\n", x); i = x; };
	A(A& x) { printf("A(A& x), x.i == %d\n", x.i); i = x.i; };
	~A(void) { printf("~A(%d)\n", i); };
	void operator += (A& x) { printf("+=(%d, %d)\n", i, x.i); i += x.i; };
};

A foo (void) return tmp = 4;
{
	A tmp = 1;
	printf("foo(void), tmp.i == %d\n", tmp.i);
	return tmp;
}

main()
{
	A bar = 5;
	bar += foo();
	printf("main(), bar.i == %d\n", bar.i);
}
----------------------------------------------------------

Script started on Thu Mar 16 10:28:45 1989
% g++ -v bug.cc
g++ version 1.34.1
 /usr/local/lib/gcc-cpp -+ -v -undef -D__GNU__ -D__GNUG__ -Dvax -Dunix -D__vax__ -D__unix__ bug.cc /tmp/cc007930.cpp
GNU CPP version 1.34
 /usr/local/lib/gcc-c++ /tmp/cc007930.cpp -quiet -dumpbase bug.cc -noreg -version -o /tmp/cc007930.s
GNU C++ version 1.34.1 (vax) compiled by GNU C version 1.34.
 gas /tmp/cc007930.s -o bug.o
 /usr/local/lib/gcc-ld++ -C /usr/local/lib/crt0+.o bug.o -lg++ /usr/local/lib/gcc-gnulib -lc
% a.out
A(5)
A(1)
foo(void), tmp.i == 1
A(A& x), x.i == 1
~A(1)
+=(5, 1)
~A(1)
main(), bar.i == 6
~A(6)
% ^D
script done on Thu Mar 16 10:29:12 1989

In foo(), the return value constructor is not called.  In addition, tmp
is copied on return from foo().
-------------------------------------------------------------------------------
rjc@cs.ucla.edu
This is what happens when I sit on my keyboard: AISBDFAORI:KHGEDSYTFBFISKVDJ MN
Next week:  what happens when I throw my keyboard out the window!

rjc@maui.cs.ucla.edu (03/18/89)

rjc@cs.ucla.edu
Vax 8350, Ultrix 2.2-1 System #4
G++ 1.34.1 (plus patched cplus-decl.c) Gcc 1.34

>[ Patch from Michael for cplus-decl.c ... ]
>  
>yahi% 
>================================================================
>Here is the test program which now compiles:
>
>struct A {
>	int i;
>	A(int x) { printf("A(%d)\n", x); i = x; };
>	A(A& x) { printf("A(A& x), x.i == %d\n", x.i); i = x.i; };
>	~A(void) { printf("~A(%d)\n", i); };
>	void operator += (A& x) { printf("+=(%d, %d)\n", i, x.i); i += x.i; };
>};
>
>A foo (void) return tmp = 4;
>{
>	printf("foo(void), tmp.i == %d\n", tmp.i);
>	return tmp;
>}
>
>main()
>{
>	A bar = 5;
>	bar += foo();
>	printf("main(), bar.i == %d\n", bar.i);
>}
>================================================================
>And here are the results:
>
>yahi% a.out
>A(5)
>A(4)			<- ******** ~A() never called
>foo(void), tmp.i == 4
>A(A& x), x.i == 4	<- ******** Shouldn't be called
>+=(5, 4)
>~A(4)
>main(), bar.i == 9
>~A(9)
>yahi% 
>================================================================
>
>Michael

Michael,

Yes, the patch allows the test program to compile.  Unfortunately, the
code that is produced is badly broken.  You can see in the above output
that A(A&) is called on return from foo().  This is not supposed to
happen with a named return (as I understand it).  This is not too bad,
just the same old inefficiency that we have been suffering with up to
now.  The *big* problem is that ~A() is not called for the `tmp' in foo(),
but only for the copy produced by the call to A(A&) on return from foo().

I an not familiar with the interals of g++, so I cannot provide a fix.

Thanks,

rob collins
(rjc@cs.ucla.edu)




i
n
e
w
s

i
s

b
r
a
i
n

d
a
m
a
g
e
d

-------------------------------------------------------------------------------
rjc@cs.ucla.edu
This is what happens when I sit on my keyboard: AISBDFAORI:KHGEDSYTFBFISKVDJ MN
Next week:  what happens when I throw my keyboard out the window!

dl@ROCKY.OSWEGO.EDU (Doug Lea) (03/18/89)

I think Michael's documentation was wrong (or at least misleading). If
you have a named return value, you should *only* return via `return'
or by falling-off-the-edge. (Otherwise, in your example, it says to
make tmp out of itself using A(A&), which is not very sensible.)  See
below. [Anyone wanting further rationale for this construct should see
my recent comp.lang.c++ postings.]

A few other notes about my experience with 1.34.1+ (1.34.1 plus Michael's
2 posted patches) on a vax750, 4.3BSD:

The -felide-constructors switch seems buggy.  All libg++ problems I've
been able to diagnose when testing this switch appear due to the kind
of local-variable-trashing behavior shown below.  Note, however, that
constructs both of the form `var a = fun()' and `var a(fun())' now, in
1.34.1, produce the same results (no temps!) with or without this switch.

Using the -fsave-memoized switch also seems buggy. Several libg++
tests (test5, intList.cc) fail to compile with this switch.

By the way, in the example, if you #include <stream.h>, you ought to
use `cout.form(...)' (or `cout << form(...)') instead of `printf' to
ensure proper flushing of output on program termination (on most
machines, this apparently only matters if you are redirecting output).


-Doug

------ file A.cc (a revision of rjc's example)

#include <stream.h>

struct A {
	int i;
	A(int x) { cout.form("A(%d)\n", x); i = x; };
	A(A& x) { cout.form("A(A& x), x.i == %d\n", x.i); i = x.i; };
	~A(void) { cout.form("~A(%d)\n", i); };
	void operator += (A& x) { cout.form("+=(%d, %d)\n", i, x.i); i += x.i; };
};

A foo () return tmp(4); // essentially equivalent to `tmp=4', 
                        // but explicitly use a constr. for clarity!
{
	cout.form("foo(void), tmp.i == %d\n", tmp.i);
	return;             // *not* `return tmp'
}

main()
{
	A bar = 5;
	bar += foo();
	cout.form("main(), bar.i == %d\n", bar.i);
    A baz = foo();
	cout.form("main(), baz.i == %d\n", baz.i);
    A baz_con(foo());
	cout.form("main(), baz_con.i == %d\n", baz_con.i);
}

-------- A correct compile/run without -felide-constructors

R>g++ -g -O -finline-functions A.cc
R>a.out
A(5)
A(4)
foo(void), tmp.i == 4
+=(5, 4)
~A(4)
main(), bar.i == 9
A(4)
foo(void), tmp.i == 4
main(), baz.i == 4
A(4)
foo(void), tmp.i == 4
main(), baz_con.i == 4
~A(4)
~A(4)
~A(9)

----------- with -felide-constructors

R>g++ -g -O -finline-functions -felide-constructors A.cc
R>a.out
A(5)
A(4)
foo(void), tmp.i == 4
+=(4, 4)                  # `bar' is trashed!
main(), bar.i == 8
A(4)
foo(void), tmp.i == 4
main(), baz.i == 4
A(4)
foo(void), tmp.i == 4
main(), baz_con.i == 4
~A(4)
~A(4)
~A(8)