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.