So, does anyone have a fix for this bug?
I'll trade for a fix for a problem with void functions and the ?: operator.
void f3(which)
{
extern void f1(),f2();
which?f1():f2();
}
cc(1) gives an "incompatible types" error.
In /usr/src/lib/mip/trees.c add the line indicated by "->>".
This lets the above code lint and compile ok (and I don't think it causes
any new problems).
...
opact( p ) NODE *p; {
...
switch( optype(o=p->in.op) ){
...
case COLON:
if( mt12 & MENU ) return( NCVT+PUN+PTMATCH );
else if( mt12 & MDBI ) return( TYMATCH );
...
else if( mt12 & MSTR ) return( NCVT+TYPL+OTHER );
->> else if ( (!mt1) && (!mt2) ) return TYPL;
break;
case ASSIGN:
As it should. The only thing you're allowed to do with void values
is throw them away.
But it is being thrown away. In e1?e2:e3, the contexts of e2 and e3 are
inherited from the context of the ?: operator itself. In this case,
that is in a 'for effect' or void context, so f1() and f2() should be
treated as 'thrown away' too.
--
"You'll need more than a Tylenol if you don't tell me where my father is!"
- The Ice Pirates
----------------------------------------------------------------------
Greg Smith University of Toronto UUCP: ..utzoo!utcsri!greg
The 4.3 compiler no longer generates an 'incompatible types' message
for void expressions in the ':' part of a '?' expression -- it prints
'value of void expression used'!
Will wonders never Cse,
Donn Seeley University of Utah CS Dept do...@utah-cs.arpa
40 46' 6"N 111 50' 34"W (801) 581-5668 decvax!utah-cs!donn
Quoted from <2...@watmath.UUCP> ["Re: C Compiler bug (and fix for a different one)"], by rbutte...@watmath.UUCP (Ray Butterworth)...
+---------------
| I'll trade for a fix for a problem with void functions and the ?: operator.
|
| void f3(which)
| {
| extern void f1(),f2();
| which?f1():f2();
| }
| cc(1) gives an "incompatible types" error.
+---------------
That's not a bug, it's a feature. Literally.
Before you start complaining, consider that the intent of functions returning
(void) is that of:
#define procedure void
procedure f1(x, y) {
...
}
++Brandon
--
---------------- /--/ Brandon S. Allbery UUCP: decvax!cwruecmp!
/ / /|\/ Tridelta Industries, Inc. ncoast!tdi2!brandon
---- -------- /-++ 7350 Corporate Blvd. PHONE: +1 216 974 9210
/ / /---, ---- Mentor, Ohio 44060 SYSOP: UNaXcess/ncoast
/ / / / / / -- HOME -- (216) 781-6201 24 hrs.
/ / / / / / 6615 Center St. Apt. A1-105 ARPA: ncoast!allbery%
---- -----~ ---- Mentor, Ohio 44060-4101 case.CSNET@csnet-relay
Scan again, Andrew, the (void) values are being thrown away.
--
John Carl Zeigler "Just once I'd like to meet an alien menace
SAS Institute Inc. that wasn't impervious to bullets !"
Cary, NC 27511
(919) 467-8000 ...!mcnc!rti-sel!sas!jcz
> Also, the proposed ANSI draft explicitly states that the second and third
> operands of ?: may each have (void) type, so I'm not trying anything
> unusual here.
I think you're using them to form another value. It is irrelevant
that you're then throwing that value away. Suppose f() is void
and you say:
f()+1;
Now you're adding 1 to a void and throwing the result away. Should that
be permitted?
More seriously, I can actually see both sides of the argument. But the
construction ...?f():g() where f() and g() are void is close enough
to the edge that I wouldn't want to use it in any context where I might
ultimately want to run it on a lot of compilers. Why not just say
if (...) f(); else g();
??
Quoted from <31...@utcsri.UUCP> ["Re: C Compiler bug (and fix for a different one)"], by gr...@utcsri.UUCP (Gregory Smith)...
+---------------
| >> void f3(which)
| >> {
| >> extern void f1(),f2();
| >> which?f1():f2();
| >> }
| >> cc(1) gives an "incompatible types" error.
| >
| >As it should. The only thing you're allowed to do with void values
| >is throw them away.
|
| But it is being thrown away. In e1?e2:e3, the contexts of e2 and e3 are
| inherited from the context of the ?: operator itself. In this case,
| that is in a 'for effect' or void context, so f1() and f2() should be
| treated as 'thrown away' too.
+---------------
If the result of ?: were an lvalue, this might be true; but it's not. In
effect ?: evaluates its arguments and therefore teh arguments must have
values.
My 1984 version of X3J11 (has it been that long?) agrees with K&R
that each of the operands must have a value. This makes intuitive
sense. The meaning of X ? Y : Z is: an expression whose value is
the value of Y, if the value of X is non-zero; otherwise, the value
of Z. By this, all three must evaluate to some value.
Do you have a more recent edition of X3J11 that r e a l l y lets
voids in there? Ugh! (What date?) Besides which, all C compilers
until just recently (Lattice, Microsoft) had been written to K&R
and v7-s3-s5, not to the ANSI standard.
--
Joe Yao hadron!jsdy@seismo.{CSS.GOV,ARPA,UUCP}
js...@hadron.COM (not yet domainised)
The void values are not thrown away! Remember that (A?B:C) is an
expression *returning a value*. C is giving the error because it can't
determine the type of the return value. This is clearer in the following:
void f3(which)
{
int f1();
void f2();
int foo;
...
foo = which?f1():f2();
...
}
which results in the same error message, for the same reason. Another
example:
f4()
{
...
return ((void) expression);
}
Note it doesn't mater what use is made of the return value (in the original
example it is thrown on the floor, which is what probably confused some
people).
No they are NOT; the value from the '?:' operator is being thrown away.
The '?:' operator expects a non-void value so it can decide 'which' and
pass the result to a higher level of expression (in this case there happens
to be none). The complaint of "incompatible types" is the result of the
'?:' operator expecting a non-void type as arguments and the arguments
providing a void type.
Wasn't that obvious from the diagnostics??? ;-)
To re-phrase, the '?:' doesn't look at its operands (parameters or arguments)
as function calls -- they might as well be 'f1()+5' or so -- but as
expressions. Therefore it '*RETURNS THE VALUE* of one of its operands
depending on the third'.
A semantic point of view:
A function call is an expression and can, therefore, be used anywhere an
expression can be used. When you declare a function (void), you state that
you intend to use that function as a statement instead, that you do not
intend to use it in any operations. It can now only be used where a statement
can. Keep in mind that an expression is a statement, but NOT vice-versa.
If you look up the reference section of K&R, somewhere it says something
like this (I don't have my K&R with me):
...
expression -> expression ? expression : expression
...
Thus you can not use a statement (ie (void) f1()) where an expression is
expected.
The BUG: there ain't none.
The FIX: use 'if' instead.
- andy kashyap
--
+---------------------------------------------------------------------------+
: Jim, what's the value of Pi? : Andy Kashyap :
: About 3.14159. Why do you ask, Doctor? : AT&T Bell Labs :
: Actually, Captain, the exact value is 3.1415926535...: Columbus OH :
: Kirk & McCoy: Shut Up, Spock!!! : ..!cbosgd!cbrma!aka:
+---------------------------------------------------------------------------+
Don't forget side effects.
>>Scan again, Andrew, the (void) values are being thrown away.
>
> The void values are not thrown away! Remember that (A?B:C) is an
> expression *returning a value*.
Uh, well, no actually. I'll join in this tis-so/tis-not debate using
the radical approach of seeing what fairly-well-respected references
have to say on the subject.
First, note that K&R don't say anything about it, since they didn't have
(void) back then.
Second, Harbison and Steele say that ?: expressions come in four
flavors. One flavor is interesting here (from page 183):
3. They [the second and third subexpressions] may have identical
types (structure, union, enumeration, or void). The result is of
this same type.
Third, the ANSI C draft standard, C.3.15.
The first operand shall have scalar type. Both the second and third
operands shall have arithmetic type, or shall have the same
structure, union, or pointer type, or shall be void expressions. In
addition, one may be an object pointer and the other a pointer to
void, or one may be a pointer and the other a null pointer constant.
...
If both the operands are void expressions, the result is a void
expression.
All fairly clear and straightforward. It is legal. So, the objection:
> C is giving the error because it can't
> determine the type of the return value.
is incorrect. The "type" returned from a ?: expression with void
trailing operands is void.
--
Any clod can have facts, but having opinions is an art.
--- Charles McCabe, San Francisco Chronicle
--
Wayne Throop <the-known-world>!mcnc!rti-sel!dg_rtp!throopw
No problem using a void as the LHS of a comma operator, just as
there's no problem using a void before a semicolon.
Of course, you'd better parenthesize:
#define foo(c) (empty?(fill(),first):first)
Moreover, fill() probably returns a value you don't want to ignore,
so maybe you should write it to return either the value of first or
an error code. You can then write
(empty?fill():first)
which avoids the void issue altogether.
It seems clear that according to the ANSI draft, e1?e2:e3 is legal when e2
and e3 are both void. The next question is, should it be?
It seems to me that there are two different paradigms for how to interpret
the ?: operator here. One is as an ordinary operator, which returns one of
the values of e2 or e3, depending on the value of e1. Under this
interpretation, it does not seem to me appropriate to permit e2 and e3 void,
since in that case they have no values.
The other paradigm is that e1?e2:e3 is precisely one of e2 or e3, depending
on the value of e1. This is a very reasonable interpretation; but if it
were correct, there would be one other important consequence which is not in
fact legal. This is that when e2 and e3 are lvalues, the compound
expression should also be an lvalue. In particular, one could write
e1?e2:e3 = e4;
which would mean the same thing as
if (e1) then e2 = e4; else e3 = e4;
(This would be permitted only if e2 and e3 had exactly the same type, of
course; one could not do this if e2 was a short and e3 a long.)
This seems to me like a reasonable extension to the language. But I believe
it should be one or the other -- the draft falls uncomfortably in the middle.
Frank Adams ihnp4!philabs!pwa-b!mmintl!franka
Multimate International 52 Oakland Ave North E. Hartford, CT 06108
*sigh* OK, let's get this straight. PROPOSED ANSI standard
X3J11 describes a language (set of languages, over the past
few years) that has (and have) not yet been implemented any-
where by anyone (or if they have, word hasn't gotten to this
corner yet). H&S, while a very good book, does take some
liberties at interpretation. They are on the ANSI X3J11
committee, and could have been influenced in their interpre-
tations by the committees deliberations. Most compilers
today follow K&R, which by declaring that the conditional
expression has (1) a result (2) with a data type predicated
on the types of the operands, seems to be prohibiting use
of void-type objects. Until recently, Proposed X3J11
concurred with this, specifically allowing only arith types,
structures, unions, and pointers of the same type (remembering
that "0" can be construed as a pointer of any type).
Bottom line: it's a mite early to be pointing to Proposed
X3J11 and saying something is "right" or "not right" based
on that. Agreed, we should all follow and be aware (as I
was not, of this) of things affecting our good programming
practice. As of now, the standards are K&R with s3 and s5
enhancements. Anything that is not explicitly defined in
one of these is subject to interpretation. As H&S points
out, the C language is what the C compiler accepts.
> It seems clear that according to the ANSI draft, e1?e2:e3 is legal when e2
> and e3 are both void. The next question is, should it be?
> It seems to me that there are two different paradigms for how to interpret
> the ?: operator here. One is as an ordinary operator, which returns one of
> the values of e2 or e3, depending on the value of e1. Under this
> interpretation, it does not seem to me appropriate to permit e2 and e3 void,
> since in that case they have no values.
Well, not quite. The standard takes the position that, while a void
expression has no value, it has something that is close enough for
government work. A way to think of is that a void expression indicates
a "value" which requires no storage and has no legal operations (other
than selection operations). Then specifically, the ?: operation chooses
which of these odd non-value values to "evaluate".
> The other paradigm is that e1?e2:e3 is precisely one of e2 or e3, depending
> on the value of e1.
Yes. The standard doesn't go this way. But I think it is reasonable
for the standard to go the way it does, making void expressions legal
wherever any other expressions are, except that their result is illegal
as input for any but selection operations, such as (?:) and (,). Any
other operation implies an interpretation and transformation of a value,
and (void) cannot be interpreted or transformed, but these operations
only imply evaluation and selection.
So I agree that there are two reasonable interpretations of (?:), and
the standard has chosen the first. But it has also chosen an
interpretation of "what it means to be (void)" which makes void
expressions to the right of a "?" reasonable.
--
If a listener nods his head when you're explaining your program,
wake him up.
--- Alan J. Perlis
Not quite. This type of expression is very useful for macros which must
in themselves be expressions (to avoid peculiar {} and ; problems). Such
as:
#define biff(pow) ((pow) ? kayo() : oof())
Defining the macro as:
#define biff(pow) if (pow) kayo(); else oof();
causes difficulties with constructs like:
if (socko)
biff(flez);
else
bang(glurp);
Similar problems exist for the other permutations of defining biff().
Now for the RIGHT (!) definition of when a void expressions is valid:
A void expression is valid only under circumstances where the
value of an expression is not used.
This means that:
(a ? voidexp : voidexp),(a=b)
is valid. But a=a?voidexp:voidexp isn't valid, as the value is used. Apply
the rule above to all the cases and you have the RIGHT (!) answer. Note that
by implication, if the value of the ?: expression is not used, the operands
of the : need not be of compatible types, and need not be brought to a
common type.
I would like to see the ANSI C spec clarified on this point.
You are correct, but this is true of ALL uses of '?:`. In fact, '?:` is
VERY useful, especially when you want to embed conditionals in a macro.
Using the if() {} else {} form restricts the contexts in which the macro
may appear.
--
-------------
Brett Galloway
{pesnta,twg,ios,qubix,turtlevax,tymix,vecpyr,certes,isi}!wjvax!brett
This kicked off an interesting thought:
*(e1 ? <ptr expr> : <ptr expr>) = expr;
Lo and behold it does what the quoted expression indicates. In an
actual example:
*(a < b : &b : &a) = 70;
I'm not sure it *good* for anything, but if I do it with macros using
cute names, I can enter it in the obfuscated C contest...
--
-bill davidsen
ihnp4!seismo!rochester!steinmetz!--\
\
unirot ------------->---> crdos1!davidsen
chinet ------/
sixhub ---------------------/ (davi...@ge-crd.ARPA)
"Stupidity, like virtue, is its own reward"
Presuming that
void kayo(), oof();
is intended, whatsa matter with
#define biff(pow) { if (pow) kayo(); else oof(); }
This combines the if-else into one single statement.
There is a slight handicap that this doesn't work too well as the first
operand of the comma operator ("syntax error") but at least some common
modern C compilers (as on the SV 3B20) don't like Bright's macro either in
this context or for that matter in any other context
("operands of : have incompatible types"). O.K., Bright (and others) have
been saying "change the definition of C to allow this." Well I say the same
thing about my suggestion, so THERE! :-)
--
------------------------------- Disclaimer: The views contained herein are
| dan levy | yvel nad | my own and are not at all those of my em-
| an engihacker @ | ployer or the administrator of any computer
| at&t computer systems division | upon which I may hack.
| skokie, illinois |
-------------------------------- Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa,
go for it! allegra,ulysses,vax135}!ttrdc!levy
I thought I said the same thing -- '?:` is useful in some cases where
if/then/else isn't.
>without duplicating code. On the other hand, anywhere you want do
>if/then/else (or any other complex statement, such as loops and switches)
>inside a macro without restricting where the code can be used, you can
>just write:
>
> #define abc(x) do { \
> /* anything you want goes here, even declarations */ \
> } while (0)
I don't think this method is general. For example, your macro abc() cannot be
used inside a for() construct.
Would you let me say:
.
.
.
f1() + f2();
.
.
Since I am throwing both away?
Alan Curtis
True, but one can imagine the ":" half-operator selecting between one of the
two "void" values. One then throws away the value returned by the "?"/":"
operator.
>
> Would you let me say:
> .
> .
> .
> f1() + f2();
> .
> .
>
> Since I am throwing both away?
No, you're not. You're adding them and then throwing the *sum* away.
"void" "value"s can't be added. If you consider "void" to be a sort-of
type, the set of whose values is a singleton set, then you can consider
boolean_expression ? void_value_1 : void_value_2
to select one of the two "void" values and yield it as a result, so the ":"
half-operator, unlike the "+" operator, can act on two "void" values.
(Regardless of the value of the <boolean_expression>, the value yielded by
the expression will be the same, since <void_value_1> == <void_value_2> ==
any other void value you can think of.)
Think of it this way: "void" values require 0 bits to represent them, since
the set of all such values has only one element. As such, "sizeof(void)"
should be 0. As such, if you say
void x;
"x" takes up no memory. Given that, declaring objects of type "void" isn't
very useful. An attempt to do so is probably an error, so it is rejected.
Also, if objects of type "void" were implemented, most implementations would
run the risk of giving it the same address as the next datum declared. So
taking the address of a "void" is kind of useless, and so "void *" isn't
useful as a true "pointer to void", so it can be overloaded.
Also, since all "void" values are the same, an attempt to compare them is
probably an error, so "==", etc. aren't defined over the set of "void"
values either.
--
Guy Harris
{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
g...@sun.com (or g...@sun.arpa)
What about:
if (up)
{
i = 0;
step = 1;
}
else
{
i = 99;
step = -1;
}
for (; i < 100 && i >= 0; i += step)
{
/* complex loop body */
}
????
-- Cary Timar /* NOT REACHABLE */
< Generic Disclaimer >