[comp.lang.c] Function-returned structures

KLH@sri-nic.arpa (02/08/87)

OK, I have a question that I haven't been able to satisfactorily answer
after poring through both H&S and the 6/87 ANSI draft, so I'll try this
group.

The problem has to do with selecting components from structures
returned by functions, and the question is "can you select an array?".
Many C operators have a constraint that their operand(s) be an lvalue,
and a simple check for this helps the compiler catch many of the
meaningless constructs that a user might attempt.  However, a
structure returned by a function is NOT an lvalue, yet component
selection is explicitly permitted for this case.  At first glance this
might seem straightforward, but it turns out that if you permit
array-type members to be selected, it is more like opening a trapdoor.

Here's an illustration.  Given:
	struct s { int x; int a[10]; };	/* The structure */
	struct s f();			/* The function returning it */
	int i, *ip;			/* Bit players */

Then a "typical" example of
	i = f().x;		/* Legal and makes sense. */
is explicitly permitted.  What I haven't been able to figure out is whether
something like
	i = f().a[i];		/* Looks plausible, whether legal or not. */
should also be permitted.  The wording of both ANSI and H&S implies that it
is, and I can't find anything to do with lvalue-ness or array-to-pointer
conversion that would prohibit it.  But if you accept this case, then you
are allowed to create pointers to structures (or structure components)
returned by such functions.  After all,
	i = *(f().a + i);	/* is "precisely equivalent" to f().a[i] */
	ip = f().a;		/* therefore f().a is a valid expression */
				/* although it is pretty nonsensical */

But the whole point of making the function result a non-lvalue is so
you cannot refer to it in this way!  &(f().x) for example is illegal.
There seems to be some inconsistency here, and I'm not sure how to
resolve it.  I'm not so worried about the user screwing up (there are
plenty of other ways to accomplish this) as I am about making sure my
compiler has both a definite way of knowing whether a construct is
legal, and a way of implementing it if it is.  (Incidentally, 4.3BSD C
won't even accept f().x)

Comments?
-------

tps@sdchem.UUCP (02/09/87)

In article <4319@brl-adm.ARPA> KLH@sri-nic.arpa (Ken Harrenstien) writes:
>Here's an illustration.  Given:
>	struct s { int x; int a[10]; };	/* The structure */
>	struct s f();			/* The function returning it */
>	int i, *ip;			/* Bit players */
>
>Then a "typical" example of
>	i = f().x;		/* Legal and makes sense. */
>is explicitly permitted.  What I haven't been able to figure out is whether
>something like
>	i = f().a[i];		/* Looks plausible, whether legal or not. */
>should also be permitted.  The wording of both ANSI and H&S implies that it
>is, and I can't find anything to do with lvalue-ness or array-to-pointer
>conversion that would prohibit it.  But if you accept this case, then you
>are allowed to create pointers to structures (or structure components)
>returned by such functions.  After all,
>	i = *(f().a + i);	/* is "precisely equivalent" to f().a[i] */
>	ip = f().a;		/* therefore f().a is a valid expression */
>				/* although it is pretty nonsensical */
>
>But the whole point of making the function result a non-lvalue is so
>you cannot refer to it in this way!  &(f().x) for example is illegal.
>There seems to be some inconsistency here, and I'm not sure how to
>resolve it.  I'm not so worried about the user screwing up (there are
>plenty of other ways to accomplish this) as I am about making sure my
>compiler has both a definite way of knowing whether a construct is
>legal, and a way of implementing it if it is.  (Incidentally, 4.3BSD C
>won't even accept f().x)
>Comments?

4.3BSD won't take

	f().x,
	
but amazingly it will take
	
	( &f() ) -> x

And if you try

	p =	&f();
	printf( "%d\n", *p );

it even more amazingly prints the "right" answer.
Perhaps if this is really too hard to check in a compiler
(is this what you are
saying?), ANSI should make the result of

	f().a

undefined and then leave checking of this to lint.








|| Tom Stockfisch, UCSD Chemistry	tps%chem@sdcsvax.UCSD

guy@gorodish.UUCP (02/10/87)

>4.3BSD won't take
>
>	f().x,

Because there's a bug in the compiler.  The System V PCC has changes
that fix this, but break other things.

The trouble here is that there is a routine "notlval" that returns
TRUE for structure assignment and structure call nodes and for nodes
of type "array" or "function", FALSE for other NAME, OREG, and REG
nodes, and TRUE for everything else.  However, there are some nodes
that are not lvalues but *are* legitimate structure references,
namely the aforementioned structure assignment and structure call
nodes.

This routine is used by the code in "cgram.y" to test whether
the LHS of the "." operator is a legitimate structure reference.
This means that "f()" will not be considered a legitimate structure
reference.

The "fix" in the System V PCC was to change "notlval" to consider
structure assignments and structure calls to be lvalues.  This fixes
this problem, but allows you to write things like

	f() = x;

which is not too cool.

The correct fix is to add a new routine "notstref" that returns FALSE
for the structure assignment and structure call nodes, and have
"notlval" return TRUE for those nodes (both routines return the same
values for other types of nodes), and use that routine instead of
"notlval" in the test in "cgram.y".

>but amazingly it will take
>	
>	( &f() ) -> x

This one is harder to fix; I haven't looked at it in painful detail,
but I think the problem is that by the time it gets around to
checking these things it's already decided that "f()" is really a
"dereference" operator on top of a "pointer to structure" node, and
of *course* you can take the address of *that*.

>Perhaps if this is really too hard to check in a compiler
>(is this what you are saying?),

No, I think he's saying it's too messy to *define* what is valid and
what isn't.  If you allow

	f().a

when "a" is *not* an array, what happens if it is?  Sticking in an
"except if it's an array" clause is a bit messy.  This isn't an
implementation issue (although forbidding this would be hard in PCC -
as would forbidding

	&(f().a)

if "a" is not an array), it's a language definition issue.