> It is not clear (to me, at least), whether the standard makes a
> distinction between translation-time undefined behaviour and execution-time
> undefined behaviour.
It is clear (to everyone else) that the standard only cares
about execution-time undefined behaviour. Here is a strictly
conforming program:
int main(void) { return 0 && (1 / 0); }
[Crossposted to comp.std.c. Corrections are welcomed.]
Tak-Shing
Actually, it was NOT clear to me, which is why I asked in the first
place.
-----= Posted via Newsfeed.Com, Uncensored Usenet News =-----
http://www.newsfeed.com - The #1 Newsgroup Service in the World!
-----== 100,000 Groups! - 19 Servers! - Unlimited Download! =-----
Dan is right. There is no distinction between translation-time UB and
execution-time UB.
> Here is a strictly
> conforming program:
>
> int main(void) { return 0 && (1 / 0); }
>
Yes, this is a s. c. program. IIRC, the Standard requires an
implementation not to fail to translate and to execute a program
unless every possible execution of the program results in undefined
behavior; if every possible execution of a program results in
undefined behavior, then an implementation is allowed to fail to
translate it.
Thanks.
--
Jun, Woong (myco...@hanmail.net)
Dept. of Physics, Univ. of Seoul
It is not clear to me. Section 3.4.3 defines undefined behavior in the
following way:
| 1 undefined behavior
| behavior, upon use of a nonportable or erroneous program construct
| or of erroneous data, for which this International Standard
| imposes no requirements
|
| 2 NOTE Possible undefined behavior ranges from ignoring the
| situation completely with unpredictable results, to behaving
| during translation or program execution in a documented manner
| characteristic of the environment (with or without the issuance of
| a diagnostic message), to terminating a translation or execution
| (with the issuance of a diagnostic message).
I understand the phrase "imposes no requirements" to mean that no
requirements are imposed on either the translation or the execution-
time behavior. The note, although not normative, seems to indicate
that this is in fact the intended meaning.
Martin
Now I'm completely confused. If you agree that there is no distinction
between translation-time and execution-time undefined behavior, how
can this program be strictly conforming?
> IIRC, the Standard requires an implementation not to fail to
> translate and to execute a program unless every possible execution
> of the program results in undefined behavior; if every possible
> execution of a program results in undefined behavior, then an
> implementation is allowed to fail to translate it.
Which section of the standard says so?
Martin
> In comp.lang.c Tak-Shing Chan <es...@city.ac.uk> wrote:
>> It is clear (to everyone else) that the standard only cares about
>> execution-time undefined behaviour.
>
> It is not clear to me. Section 3.4.3 defines undefined behavior in the
> following way:
>
> | 1 undefined behavior
> | behavior, upon use of a nonportable or erroneous program construct
> | or of erroneous data, for which this International Standard
> | imposes no requirements
If the program construct or data is not in the execution
path, how can it ever be used?
Tak-Shing
It is used by the programmer. S/he uses a nonportable or erroneous
construct and feeds it to the compiler.
This section defines the term "undefined behavior"; it does not define
under what circumstances some behavior is undefined. The latter is
defined in 4 (2).
Martin
> In comp.lang.c Tak-Shing Chan <es...@city.ac.uk> wrote:
>> If the program construct or data is not in the execution path,
>> how can it ever be used?
>
> It is used by the programmer. S/he uses a nonportable or erroneous
> construct and feeds it to the compiler.
I believe that ``used by the programmer'' is outside the
scope of the standard.
Tak-Shing
This, taken in isolation, sounds to me like it refers to "use" in the
sense of using a construct when *writing* a program. I'd expect some
other word or phrasing to be used, like "upon execution", if that's
what was meant.
After all, programs can "use" macros, #define statements, symbolic
constants, FILE pointers, etc., right? But arguably, these things
no longer exist as such, by the time of *execution*.
#include "somefile.h"
void f(int n)
{
if(n)
do_something();
else
do_something_else();
}
int main(void)
{
f(1);
return 0;
return 1; /*NOTREACHED*/
}
The function f() above, as called from main(), clearly seems to "use" the
if statement. But does it "use" the else? It would certainly have a
different meaning if the else were not there! I would say this program
"uses" if and else. Does it "use" an #include directive? Does it "use"
somefile.h? It "uses" a "/*NOTREACHED*/" comment, does it not? But
surely you'll agree that *that* isn't *executed*!
--Ben
--
According to the as-if rule, your program is equivalent to:
#include "somefile.h"
int main(void) { do_something(); return 0; }
So no, I don't think the ``else'' part is used.
Tak-Shing
Perhaps you're right; I went through all that with defining an
additional function, specifically to guard against the notion that
parts of the program might be optimized out of existence (since I
wanted to use a constant in the test). Perhaps a judiciously-placed
"extern" could help? Or how about this:
#include <stdio.h>
int main(void)
{
f(getchar()=='y');
return 0;
return 1; /*NOTREACHED*/
}
(And the rest same as above.)
All my points above still hold for this modified program, and I hope this
lays to rest your "as-if" diversion. Of course, in *my* interpretation,
"as-if" doesn't matter because "use" refers to constructs that are "used"
in the *source code* of the program, prior to compilation, execution, or
any applicability of "as-if". Anyway, please address my points in light
of this new program.
--Ben
--
> In article <Pine.GSO.4.21.0211052241270.14654-100000@swindon>,
> Tak-Shing Chan <es...@city.ac.uk> wrote:
>> So no, I don't think the ``else'' part is used.
>
> Perhaps you're right; I went through all that with defining an
> additional function, specifically to guard against the notion that
> parts of the program might be optimized out of existence (since I
> wanted to use a constant in the test). Perhaps a judiciously-placed
> "extern" could help? Or how about this:
>
> #include <stdio.h>
>
> int main(void)
> {
> f(getchar()=='y');
> return 0;
> return 1; /*NOTREACHED*/
> }
>
> (And the rest same as above.)
You now have two distinct execution paths for this program:
one ``uses'' the if part while the other ``uses'' the else part.
> All my points above still hold for this modified program, and I hope this
> lays to rest your "as-if" diversion. Of course, in *my* interpretation,
> "as-if" doesn't matter because "use" refers to constructs that are "used"
> in the *source code* of the program, prior to compilation, execution, or
> any applicability of "as-if". Anyway, please address my points in light
> of this new program.
If this were true, then you should be able to state whether
the following program invokes undefined behavior or not (prior to
compilation or execution):
#include <stdio.h>
int main() { char s[9]; gets(s); puts(s); }
Can you do this?
Tak-Shing
There seems to be misunderstanding. I said there is no distinction
in the sense of that the criterion is when the visible effect of the
undefined behavior can occur.
int main(void)
{
return 1/0;
}
The every possible execution of this program results in undefined
behavior, so the implementation can do anything in anytime including
translation-time and execution-time.
>
> > IIRC, the Standard requires an implementation not to fail to
> > translate and to execute a program unless every possible execution
> > of the program results in undefined behavior; if every possible
> > execution of a program results in undefined behavior, then an
> > implementation is allowed to fail to translate it.
>
> Which section of the standard says so?
>
A DR that's normative. I think we don't need to discuss on this issue
unless the Standard is not defective in this regard.
IIRC,
- If every possible execution of a program results in undefined
behavior, then the program is not s. c.
- The implementation is not allowed to fail translating or to
executing a program just because some of its possible executions
results in undefined behavior.
- If an expression appears in which a constant expression is required
and the evaluation of the expression results in UB, then the program
is not s. c.
Of course, the provisions of the conformance clause and the
requirements of the entire Standard still apply effectively.
#include <stdio.h>
int foo(int a)
{
if (a == 'x') return 1 / 0;
else return 0;
}
int main(void)
{
return foo(getchar());
}
The expression 1 / 0 *can* cause undefined behavior, but the context
in which it appears doesn't require a constant expression. The program
results in undefined behavior when the return value of getchar()
compare equal to 'x'. But it's just some possible execution of the
program.
int func(void);
double func(int a)
{
return 3.14159;
}
int main(void)
{
return 0;
}
Now, this program is not s. c. since it violates the requirements of
the Standard. An implementation can fail to translate this program.
Correction:
"unless the Standard is not defective ..." should read as "unless the
Standard is defective"
Sorry.
Try this one:
int main (void)
{
#pragma undefined behaviour
return 0;
}
But the path that "uses" the if part and doesn't "use" the else part,
by your definition of "use", is still affected if the else is removed.
>> All my points above still hold for this modified program, and I hope this
>> lays to rest your "as-if" diversion. Of course, in *my* interpretation,
>> "as-if" doesn't matter because "use" refers to constructs that are "used"
>> in the *source code* of the program, prior to compilation, execution, or
>> any applicability of "as-if". Anyway, please address my points in light
>> of this new program.
>
> If this were true, then you should be able to state whether
>the following program invokes undefined behavior or not (prior to
>compilation or execution):
That doesn't follow at all.
> #include <stdio.h>
> int main() { char s[9]; gets(s); puts(s); }
>
> Can you do this?
No, of course not. That proves nothing.
But I *can* tell you whether it "uses" gets() or not: it does. It's using
it already, as it sits there on the page. I don't have to execute
anything for this *source code program* to "use" the construct known as
gets().
Please address my points.
--Ben
--
I believe that this will invoke implementation-defined
behaviour, not undefined behaviour. I'm not sure what you
wanted to say with this.
> return 0;
> }
--
Andreas Kähäri --==::{ Have a Unix: netbsd.org
--==::{ This post ends with :wq
How could it not be? The division is not evaluated.
>In article <slrnasgo...@otaku.freeshell.org>,
> Andreas Kähäri <a...@freeshell.org.REMOVE> wrote:
>
>> Submitted by "Christian Bau" to comp.lang.c:
>> > In article <Pine.GSO.4.21.0211052332020.16848-100000@swindon>,
>> [cut]
>> >
>> > Try this one:
>> >
>> > int main (void)
>> > {
>> > #pragma undefined behaviour
>>
>> I believe that this will invoke implementation-defined
>> behaviour, not undefined behaviour. I'm not sure what you
>> wanted to say with this.
>
>Your believe is wrong. Of course this does _NOT_ invoke implementation
>defined behaviour. Nowhere in the C standard will you find that an
>implementation has to define what it will do if the source code contains
>a line
>
> #pragma undefined behaviour
>
>All pragmas apart from the three or four defined in the C99 Standard
>will invoke undefined behaviour. Check your compiler manual. Does it
>define the behaviour for this pragma or not? Didn't think so. What you
>have got here is an example of _undefined behaviour at compile time_.
>
restoring comp.std.c to the list:
According to the standard, it is implementation defined.
6.10.6 Pragma directive
Semantics
1 A preprocessing directive of the form
# pragma pp-tokens opt new-line
where the preprocessing token STDC does not immediately follow pragma in
the directive (prior to any macro replacement)146) causes the
implementation to behave in an implementation-defined manner. The
behavior might cause translation to fail or cause the translator or the
resulting program to behave in a non-conforming manner. Any such
pragma that is not recognized by the implementation is ignored.
Regards,
Bruce Wheeler
As I understand, yes; the implementation is not free to fail
translating the construct. The expression following the "return"
statement need not to be a constant expression. See my previous
posting in this thread.
My MSVC++5.0 compiler won't compile my_example.c
Is my compiler broken ?
/* BEGIN my_example.c */
int main(void) {int zero = 0; return zero && (1 / 0); }
/* END my_example.c */
my_example.c(3) : error C2124: divide or mod by zero
--
pete
Yes. My answer is not from my own interpretation; it's from the
Committee. I don't recall the exact document number but you can find
the identical asnwer from a DR.
Why does MSVC have so many non-conforming features? :(
[skip the example and error message]
If the compiler pre-evaluates the expression, it must pre-evalute it
correctly, respecting the short-circuiting properties of &&.
In a case where the branch depends upon data processed at run-time, the
compiler must defer the undefined consequences of pre-computing 1/0,
until after it has verified that the branch was taken which actually
results in that computation; if it can't defer those consequences, its
not allowed to pre-compute it.
Sorry, but I still don't see how this assumption can be derived from
the words of the standard.
Martin
The Committee's answer from the DR system has the equivalent force
to the wording of the Standard itself.
Reporting the error is fine. Refusal to translate the program is not.
Jeremy.
> Yes. My answer is not from my own interpretation; it's from the
> Committee. I don't recall the exact document number but you can find
> the identical asnwer from a DR.
If reporting the snipped example as an error is broken, then I'll take
broken over "fixed", thank you very much.
Bill, IMHO.
> In article <Pine.GSO.4.21.0211052332020.16848-100000@swindon>,
> Tak-Shing Chan <es...@city.ac.uk> wrote:
>>On 5 Nov 2002, E. Gibbons wrote:
>> You now have two distinct execution paths for this program:
>>one ``uses'' the if part while the other ``uses'' the else part.
>
> But the path that "uses" the if part and doesn't "use" the else part,
> by your definition of "use", is still affected if the else is removed.
You are saying that ``A doesn't use B but is still affected
if B is removed,'' which contradicts the as-if rule.
>> If this were true, then you should be able to state whether
>>the following program invokes undefined behavior or not (prior to
>>compilation or execution):
>
> That doesn't follow at all.
It does follow, because given your definition of ``use'',
you should be able to tell it from source code alone.
>> #include <stdio.h>
>> int main() { char s[9]; gets(s); puts(s); }
>>
>> Can you do this?
>
> No, of course not. That proves nothing.
If you cannot do this, then it is a proof that your
definition of ``use'' is flawed.
> But I *can* tell you whether it "uses" gets() or not: it does. It's using
> it already, as it sits there on the page. I don't have to execute
> anything for this *source code program* to "use" the construct known as
> gets().
Irrelevant. Without knowing the user input in advance, you
cannot even try to tell me whether it ``uses'' s[5] or not.
> Please address my points.
I have already done so.
Tak-Shing
I don't know why.
Thank you.
--
pete
Are you refering to DR#247?
Martin
No, DR#247 deals with the connection between use of values and
behaviors, which is irrelevant to this problem.
See DR#109 and DR#132.
http://std.dkuug.dk/JTC1/SC22/WG14/www/docs/dr_109.html
http://std.dkuug.dk/JTC1/SC22/WG14/www/docs/dr_132.html
Thanks!
I have not found these because I misunderstood how the standardization
process works. Since I didn't know that DRs from 1993 are a normative
part of the current (1999) C standard, I didn't even look at these old
DRs. The fact that the WG14 www site has a page entitled "WG14 Defect
Report Summary for ISO/IEC 9899:1999", which does not include these old
reports, made me believe that older DRs are not relevant.
Martin
No, *you* are saying A doesn't use B, and *I* am making the point that it
*must* use B, because if B is removed, the behaviour changes. It's all
plainly quoted above. Try to keep the arguments straight, here!
>>> If this were true, then you should be able to state whether
>>>the following program invokes undefined behavior or not (prior to
>>>compilation or execution):
>>
>> That doesn't follow at all.
>
> It does follow, because given your definition of ``use'',
>you should be able to tell it from source code alone.
But you weren't asking about "use". As I said, quoted below, I *can*
tell whether it "uses" a given construct, by my interpretation of "use".
>>> #include <stdio.h>
>>> int main() { char s[9]; gets(s); puts(s); }
>>>
>>> Can you do this?
>>
>> No, of course not. That proves nothing.
>
> If you cannot do this, then it is a proof that your
>definition of ``use'' is flawed.
No, it is a proof that a program can "use" a construct, according to
my interpretation of "use", without *executing* that construct, which
seems to be what your interpretation of "use" amounts to.
>> But I *can* tell you whether it "uses" gets() or not: it does. It's using
>> it already, as it sits there on the page. I don't have to execute
>> anything for this *source code program* to "use" the construct known as
>> gets().
>
> Irrelevant. Without knowing the user input in advance, you
>cannot even try to tell me whether it ``uses'' s[5] or not.
Yes, I can, and in fact I did tell you so. Please read my quoted text
above carefully.
>> Please address my points.
>
> I have already done so.
No, you haven't. You have demonstrated rather clearly that you don't
understand my point.
Here, let's try some fresh code, with even less ambiguity.
#include <stdio.h>
int main(void)
{
char * p;
if(0) {
gets(p);
}
return 0;
}
By my definition, this program "uses" gets(), although it will never
*execute* it, and thus does not invoke UB. And that's why it needs to
#include <stdio.h>.
By your definition, it does not "use" gets(). Why, then, does it
need to "use" stdio.h?
What if a construct is optimized out? How can you tell whether it is
"used" or not?
What if you compile the program, but never actually run it? Does it
"use" nothing, then? What if you only run it if a radioactive decay
event causes a cat to die in an underground lab somewhere? What if
you run 3000 copies in parallel? Does your program "use" main() 3000
times, then (but only while they're running)?
Your definition amounts to "executes the construct", mine amounts to
"contains the construct". Yours is awkward, and is *fundamentally
undecideable* in the general case; and it loses the distinction between
"contains" and "executes"; and, it falls down completely for constructs
that simply are *not* executable, such as comments, preprocessor
directives, etc.. So, although neither of us knows for certain which
sense the Standard intended in the original quote, I think my sense
of the word makes more sense, is more decideable, and has more value,
in general.
But as they say, YMMV. The real question is what the *Standard* means
by "use", and all we have shown here is that two non-experts disagree.
--Ben
--
That's actually a matter which has been hotly debated. For C90 DR
decisions which provide official interpretations of the wording of the
standard, if none of the relevant text changed in C99, than neither did
the interpretation.
However, many DR decisions are based upon text which did change. Those
decisions can't be carried over to C99. For example, the resolution to
DR005 says in part "No, a strictly conforming program may not contain a
pragma directive." With the addition of the new standard pragmas in C99,
this conclusion has to be false.
It can sometimes be difficult to determine precisely which text the
committee based it's decisions on - when that's the case, it's
correspondingly difficult to guess whether or not the decision should
carry over.
Of course, some DR decisions went beyond the actual text of the
standard. The committee had a chance to add supporting text for those
decisions into the new version of the standard; if it failed to do so,
that should be considered as evidence that the C90 decision does not
apply to C99.
However, this is a very controversial area; primarily because there's
disagreement about which DR decisions fall into this category.
No, it must not be.
Even if we replace this trivial example by something like:
int main(int argc, char **argv) { return argc && (1 / 0); }
Who's the idiot here, Dan?
regards,
alexander.
An implementation can evaluate the 1/0 at translation time in this case,
only if the implementation can guarantee that argc is non-zero. Since
it's up to the implementation how argc acquires it's non-negative value,
it's quite possible that the implementation can guarantee this.
> No, *you* are saying A doesn't use B, and *I* am making the point that it
> *must* use B, because if B is removed, the behaviour changes. It's all
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Wrong. The removal of B does not change the behavior of A:
the behavior of ``if (1) f(); else g();'' is exactly the same as
the behavior of ``if (1) f();''.
> No, it is a proof that a program can "use" a construct,
You have not proved anything yet. What you have done so
far, is to invent a definition of your own, which the standard
disagrees with.
> Yes, I can, and in fact I did tell you so. Please read my quoted text
You did not. Does it ``use'' s[5]? I found no answer in
your previous posts.
> #include <stdio.h>
>
> int main(void)
> {
> char * p;
>
> if(0) {
> gets(p);
> }
> return 0;
> }
>
> By my definition, this program "uses" gets(), although it will never
> *execute* it, and thus does not invoke UB. And that's why it needs to
> #include <stdio.h>.
It does not follow.
> By your definition, it does not "use" gets(). Why, then, does it
> need to "use" stdio.h?
It does not need to. Please read the relevant DRs (see
Jun Woong's posts). According to the DR responses, the following
translation unit *must* be successfully translated:
int checkup()
{
if (0) printf("Printing.\n"); /* Missing prototype */
return 2 || 1 / 0; /* Division by zero */
}
Clearly, printf() is not used here, otherwise it contradicts
with the committee's official response.
Tak-Shing
> Dan Pop wrote:
>> int main(int argc, char **argv) { return argc && (1 / 0); }
>
> An implementation can evaluate the 1/0 at translation time in this case,
> only if the implementation can guarantee that argc is non-zero. Since
> it's up to the implementation how argc acquires it's non-negative value,
> it's quite possible that the implementation can guarantee this.
Since the as-if rule is located under ``program execution'',
I believe that precomputation does not count as evaluation.
Tak-Shing
Under the as-if rule, pre-computation is allowed only if it produces
effects which are indistinguishable from effects which would also be
legal if it performed the evaluation at run-time, instead. It's not
allowed to make the program's translation and execution fail because of
that pre-computation, unless and until it can guarantee that argc is
non-zero. That could be guaranteed at compile time, for some
implementations.
However, consider an implementation that documents a way of invoking the
program which guarantees argc==0. Such an implementation cannot fail to
correctly translate and execute this program, if it is actually invoked
in that fashion (modulo implementation limits issues).
I agree, but I suspect that fewer will agree if we modify the example
slightly:
int main(int argc, char **argv) { return argc && (*0 = 1); }
^
assignment
Assuming that argc can be zero, must the implementation translate this
program? I.e., is "*0" a legitimate lvalue here?
Tom Payne
No, it's a constraint violation. '0' doesn't have pointer type.
Jeremy.
The term "use" in the definition of "undefined behavior" cannot refer
exclusively to static attributes of the program. For two reasons,
there is no way that static analysis can reliably determine whether or
not division-by-zero will occur at run time.
Reason #1: There is no way to predict input:
int n;
n = user_input();
1/n;
Reason #2: Even when there is no external input, determining whether
or not a given variable will ever take on the value zero at a given
point in the program is equivalent to the halting problem, i.e., there
is no algorithm for determining whether or not this situaiton will
occur.
Tom Payne
If argc might be zero, then the division must not be evaluated
at translation time (or at any rate the program must be
translated and made ready for execution). If the implementation
is such that argc can never be zero at run time, then the
division may be performed at translation time, and among the
possible allowed behaviors would be failure to produce an
executable image, plus warning about the division by zero.
I don't see what is so hard to understand: execution occurs
at run time, not translation time (except for constant
expressions, which are such only in contexts where they are
required to be such); and if an implementation can determine
with certainty that undefined behavior would occur when the
program is executed, it has the option to not execute it.
Why? The program had perfectly reasonable, well-defined
properties, but that compiler refused to accept it. Now,
a *warning* about possible division by zero might be
useful, but not an outright refusal to compile.
But it's not the same as "if(1) f(); g();", which was what was under
discussion. "B" is "else". Read the thread.
>> No, it is a proof that a program can "use" a construct,
>
> You have not proved anything yet. What you have done so
>far, is to invent a definition of your own, which the standard
>disagrees with.
You have not shown that the standard disagrees. If you can do so,
do it now.
>> Yes, I can, and in fact I did tell you so. Please read my quoted text
>
> You did not. Does it ``use'' s[5]? I found no answer in
>your previous posts.
I answered that *twice* already.
>> #include <stdio.h>
>>
>> int main(void)
>> {
>> char * p;
>>
>> if(0) {
>> gets(p);
>> }
>> return 0;
>> }
>>
>> By my definition, this program "uses" gets(), although it will never
>> *execute* it, and thus does not invoke UB. And that's why it needs to
>> #include <stdio.h>.
>
> It does not follow.
>
>> By your definition, it does not "use" gets(). Why, then, does it
>> need to "use" stdio.h?
>
> It does not need to. Please read the relevant DRs (see
>Jun Woong's posts). According to the DR responses, the following
>translation unit *must* be successfully translated:
>
> int checkup()
> {
> if (0) printf("Printing.\n"); /* Missing prototype */
> return 2 || 1 / 0; /* Division by zero */
> }
>
> Clearly, printf() is not used here, otherwise it contradicts
>with the committee's official response.
Well, I guess the gcc people haven't read that DR (which DR is it,
please?):
gemini[pts/3]:~/tmp$ cat checkup.c
int checkup()
{
if (0) printf("Printing.\n"); /* Missing prototype */
return 2 || 1 / 0; /* Division by zero */
}
gemini[pts/3]:~/tmp$ gcc -ansi -pedantic -Wall -W -Werror -O2 -c checkup.c -o checkup.o
cc1: warnings being treated as errors
checkup.c: In function `checkup':
checkup.c:3: warning: implicit declaration of function `printf'
gemini[pts/3]:~/tmp$ ls -l checkup.*
-rw-r--r-- 1 bketcham bketcham 157 Nov 7 13:34 checkup.c
--Ben
--
It was merely warning about the missing prototype, but you made use of an
option that tells it to treat warning-level situations as errors (it even
told you that it was doing that). Why would you expect standard-conforming
behavior when you've specifically told it to do this?
--
Barry Margolin, bar...@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
Check back on the other branch of this thread - the one that doesn't
have you and Tak-Shing arguing back and forth. Jun Woong's message dated
"Thu, 7 Nov 2002 00:56:16 +0900" contains links to the relevant DRs.
> gemini[pts/3]:~/tmp$ cat checkup.c
> int checkup()
> {
> if (0) printf("Printing.\n"); /* Missing prototype */
> return 2 || 1 / 0; /* Division by zero */
> }
> gemini[pts/3]:~/tmp$ gcc -ansi -pedantic -Wall -W -Werror -O2 -c checkup.c -o checkup.o
> cc1: warnings being treated as errors
> checkup.c: In function `checkup':
> checkup.c:3: warning: implicit declaration of function `printf'
You told it to treat warnings as errors; that's not one of the options
you can use if you want to invoke gcc in a fully conforming mode. With
that option turned off, it warns about a great many things, some of
which are perfectly legal; but it goes ahead and compiles the code
anyway, so it is conforming. With that option turned on, it refuses to
compile those same things, including the ones that are perfectly legal,
making it no longer a conforming implementation.
These are both valid general-case arguments, i.e., there exist
programs and inputs for which they are true. They are not valid
specific-case arguments, i.e., there may exist other programs (for
case #2) or implementations (for case #1) where the results *can*
be predicted in advance, even as far ahead as compile-time.
(For instance, a program that uses scanf("%d", &n) to read an
integer from stdin might be absolutely guaranteed to get EOF every
time, leaving "n" unchanged, on some implementation. The
compiler implementor simply "knows" that stdin is always connected
to the equivalent of /dev/null, and all attempts to freopen() it
fail, and so on.)
Since the standard has to talk about general cases, this is just
a nit. :-)
--
In-Real-Life: Chris Torek, Wind River Systems (BSD engineering)
Salt Lake City, UT, USA Domain: to...@bsdi.com
http://67.40.109.61/torek/ (for the moment)
(you probably cannot email me -- spam has effectively killed email)
The point is that this conclusion cannot be drawn *exclusively* from the
text of the standard (you also have to read the minds of the committee
members or some DRs filed against an obsolete version of the standard).
The purpose of my questions was to get the issue fully clarified,
for the benefit of everyone following this discussion.
The above-mentioned DRs have provided ample proof that the text of C89
was defective in that area. Yet, the committee decided not to take any
corrective action(s) in C99. Any good reasons for this decision?
>Well, I guess the gcc people haven't read that DR (which DR is it,
>please?):
>
>gemini[pts/3]:~/tmp$ cat checkup.c
> int checkup()
> {
> if (0) printf("Printing.\n"); /* Missing prototype */
> return 2 || 1 / 0; /* Division by zero */
> }
>gemini[pts/3]:~/tmp$ gcc -ansi -pedantic -Wall -W -Werror -O2 -c checkup.c -o checkup.o
>cc1: warnings being treated as errors
>checkup.c: In function `checkup':
>checkup.c:3: warning: implicit declaration of function `printf'
>gemini[pts/3]:~/tmp$ ls -l checkup.*
>-rw-r--r-- 1 bketcham bketcham 157 Nov 7 13:34 checkup.c
What makes you think that gcc is (or should be) a conforming translator
when using the -Werror option?
The semantics of the -Werror option are: "don't complete the
translation if a diagnostic was produced" and NOT "don't complete
the translation if a *required* diagnostic is produced". And both
-Wall and -W ask gcc to generate even more diagnostics that are NOT
required by the standard.
If you still don't get it, consider the following strictly conforming
program:
fangorn:~/tmp 28> cat test.c
int main()
{
int a = 'ab';
return 0;
}
fangorn:~/tmp 29> gcc -c -Werror test.c
cc1: warnings being treated as errors
test.c: In function `main':
test.c:3: warning: multi-character character constant
fangorn:~/tmp 30> echo $status
1
fangorn:~/tmp 31> ls test.o
ls: test.o: No such file or directory
Remove -Werror and the program is correctly translated.
But it's a null pointer constant, so can be used as a pointer in at
least some cases.
--
Clive D.W. Feather, writing for himself | Home: <cl...@davros.org>
Tel: +44 20 8371 1138 (work) | Web: <http://www.davros.org>
Fax: +44 870 051 9937 | Work: <cl...@demon.net>
Written on my laptop; please observe the Reply-To address
Does this mean that WG14 has reached a conclusion on DR 261?
They aren't.
However, in a number of cases, if the question were asked again WG14
would be likely to give the same response. [Somewhere deep in my ToDo
list is an attempt to document these.]
I replied:
>> No, it's a constraint violation. '0' doesn't have pointer type.
Clive D. W. Feather wrote:
> But it's a null pointer constant, so can be used as a pointer in at
> least some cases.
Yes, but not this one. Look at the context (reinstated).
Jeremy.
Not least because (if I understand the rules correctly) division by zero
could result in a NaN and no reason for the program to be aborted.
--
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
> t...@cs.ucr.edu wrote:
> >>> int main(int argc, char **argv) { return argc && (*0 = 1); }
> >>> ^
> >>> assignment
> >>>
> >>> Assuming that argc can be zero, must the implementation translate this
> >>> program? I.e., is "*0" a legitimate lvalue here?
>
> >> No, it's a constraint violation. '0' doesn't have pointer type.
>
> > But it's a null pointer constant, so can be used as a pointer in at
> > least some cases.
>
> Yes, but not this one. Look at the context (reinstated).
That's the one-but-inner context. The _actual_ context is the
dereference operator; _it_ requires a pointer context, and thus the 0 is
interpreted as a null pointer constant, which is converted (at compile
time, most likely) to an actual null pointer. This null pointer is then
dereferenced.
As a result, this program is syntactically correct, and invokes
undefined behaviour only when argc != 0. If the compiler can determine
that argc will always be positive (for example, if it knows it will
always be able to supply argv[0]), it is AFAICT allowed not to compile
it; but if the compiler cannot be sure argc will never be 0, it _must_
compile this program.
Richard
That's exactly how I see it. It follows, however, that "*0" is an
lvalue even though it does not designate an object. Lots of folks
seems to balk at that.
Tom Payne
I do not understand this. How can *0 be a syntactically and
contextually valid expression? The unary * operator means the thing
that is stored at the address given by the operand. Here, it's clear
that the thing is stored at address 0, but no one bothered to tell what
type of thing it is: char, int, long...
--
/-- Joona Palaste (pal...@cc.helsinki.fi) ---------------------------\
| Kingpriest of "The Flying Lemon Tree" G++ FR FW+ M- #108 D+ ADA N+++|
| http://www.helsinki.fi/~palaste W++ B OP+ |
\----------------------------------------- Finland rules! ------------/
"It sure is cool having money and chicks."
- Beavis and Butt-head
Richard Bos wrote:
> Jeremy Yallop <jer...@jdyallop.freeserve.co.uk> wrote:
>
>
>>t...@cs.ucr.edu wrote:
>>
>>>>> int main(int argc, char **argv) { return argc && (*0 = 1); }
>>>>> ^
>>>>> assignment
>>>>>
>>>>>Assuming that argc can be zero, must the implementation translate this
>>>>>program? I.e., is "*0" a legitimate lvalue here?
>>>>
>>>>No, it's a constraint violation. '0' doesn't have pointer type.
>>>
>>>But it's a null pointer constant, so can be used as a pointer in at
>>>least some cases.
>>
>>Yes, but not this one. Look at the context (reinstated).
>
>
> That's the one-but-inner context. The _actual_ context is the
> dereference operator; _it_ requires a pointer context, and thus the 0 is
> interpreted as a null pointer constant, which is converted (at compile
> time, most likely) to an actual null pointer. This null pointer is then
> dereferenced.
Nice theory. Do you have supporting text for this interpretation?
There is no general rule that makes '0' convert to a pointer type just
because one is needed. There's just a few individual rules for
particular cases; and this isn't one of them. Those cases include
6.5.9p2 for == and !=, 6.5.15p3 for ?:, and 6.5.16p2 for the assignment
operators. Each of those paragraphs has a special option to allow for
one of the operands to be a null pointer constant while another operand
is a pointer.
Sections 6.5.9p5 and 6.5.15p3 each contain special wording describing
the kind of conversion you're talking about, to make the two operands
have the same type. Section 6.5.16 doesn't need special wording for null
pointer constants; the right operand is always converted to the type of
the left operand for assignment operators.
That's not a coincidence, or an oversight: there's no other operators
where it serves any useful purpose to allow null pointer constants as
operands. That's not quite true: function call operators also need to
convert null pointer constants to the appropriate pointer type, but in
that case conversion to the parameter's type is specified for all
prototyped arguments, whether or not they are null pointer constants.
Such implicit conversions most emphatically do NOT occur for
unprototyped arguments, including the variable arguments at the end of a
function declared as allowing such arguments. The expression
printf("%p", NULL) is not guaranteed to work as you might expect it to.
If the expansion of NULL is 0, it will be treated as an 'int', which is
the wrong type for %p (though it will work, by accident, on many platforms).
Section 6.5.3.2 describes the indirection operator, and the constraints
do not have an option allowing for general null pointer constants. Only
the ones (such as (void*)0) which actually have a pointer type are
allowed by the constraints (of course, the behavior is still undefined
if the operation is actually performed in that case). There is no
wording for that operator specifying the same kind of special handling
for null pointer constants that is described in 6.5.9p5 and 6.5.15p3.
Good point. So, let's make that "*(int*)0" rather than "*0".
Tom Payne
Incorrect. You can never find the wording supporting your argument.
The unary operator doesn't qualify the context where a null pointer
constant is converted to a null pointer.
--
Jun, Woong (myco...@hanmail.net)
Dept. of Physics, Univ. of Seoul
So.. what's your point? I don't see the connection between the subject
being discussed and your example. The interpretation of the Standard
doesn't change because of your example in this case.
> That's the one-but-inner context. The _actual_ context is the
> dereference operator; _it_ requires a pointer context, and thus the 0 is
> interpreted as a null pointer constant, which is converted (at compile
> time, most likely) to an actual null pointer. This null pointer is then
> dereferenced.
Unless this has just been changed, null pointer constants become null
pointers only in a comparison using == or != (but not < <= > >=) and in
an assignment.
I'm sorry, but this is just plain wrong. The short proof is
What type does the expression `*0' have?
The long proof is:
A null pointer constant does not necessarily have pointer type. In
order to be used as a pointer it must be converted to a pointer
type(6.3.2.3). Such conversions take place either explicitly as a
result of casting (6.5.4), or implicitly as a result of assignment
(or equivalent) (6.5.16.1) or (in)equality comparison (6.5.9). In
contrast, the unary * operator does not induce any type conversion
but its argument is required to have pointer type (6.5.3.2).
I think that the source of your confusion is the expression `pointer
context' (which does not occur in the standard).
> if the compiler cannot be sure argc will never be 0, it _must_
> compile this program.
On the contrary, a diagnostic must be issued and there is no
obligation to complete translation.
Jeremy.
And in ?:
>In message <3DCAC92C...@null.net>, Douglas A. Gwyn
><DAG...@null.net> writes
>>Bill Godfrey wrote:
>>> If reporting the snipped example as an error is broken,
>>> then I'll take broken over "fixed", thank you very much.
>>
>>Why? The program had perfectly reasonable, well-defined
>>properties, but that compiler refused to accept it. Now,
>>a *warning* about possible division by zero might be
>>useful, but not an outright refusal to compile.
>
>Not least because (if I understand the rules correctly) division by zero
>could result in a NaN and no reason for the program to be aborted.
I don't think that this is an option for integer division.
+ So.. what's your point? I don't see the connection between the subject
+ being discussed and your example. The interpretation of the Standard
+ doesn't change because of your example in this case.
I replaced "1/0" with "*(int*)0 = 3", because (IIRC) in past
discussions, some members of the standards committee have held that
any occurrence of "*(int*)0" renders a program incorrect, whether or
not that expression would ever get evaluated. (I don't see similar
adamance about occurrences of "1/0".)
Tom Payne
<t...@cs.ucr.edu> wrote in message news:aqgmdq$f72$1...@glue.ucr.edu...
So some behavior is more undefined than others?
-----= Posted via Newsfeed.Com, Uncensored Usenet News =-----
http://www.newsfeed.com - The #1 Newsgroup Service in the World!
-----== 100,000 Groups! - 19 Servers! - Unlimited Download! =-----
I don't see why they said that the expression which results just in
undefined behavior can prevents the translation of the program even
when it's not evaluated explicitly. Could anyone show me the reason?
Thanks in advance.
Agreed. The mistake was mine -- I should have written "*(int*)0"
rather than "*0".
Tom Payne
It's entirely an issue about what "use" means. If a program that
textually contains a construct is considered to "use" it, then the
behavior is undefined. If a construct is used only if the code
containing it is actually executed, then the behavior is defined, as
long as we can avoid executing that expression. Personally, I'd say the
meaning of "use" is unclear. However, the committee's DR resolution you
cited earlier came down on the side of the second interpretation. Some
people participating in this thread have not paid any attention to that
citation yet.
There's also a legitimate issue about whether a C90 ruling, which was
not implemented by appropriate clarifying textual changes to C99, can be
considered to apply to C99. If you believe that the DR resolution merely
explained the meaning, rather than clarifying something that was
inherently unclear, then it would follow that there was no need for such
textual changes.
Thanks very much for your very comprehensive explanation.
I beleve that the DR resolution explained the meaning as many other
DRs do so. As I know, there are more DRs just which explain the
meaning of the wording in question than which change the text of the
Standard via TCs.
Anyway, according to your and Clive's posting, we have to ask the
Committee validity of all C90 DRs not to make the change of the text
before we decide whether it can apply to C99. :(
Why didn't they clarify those things even if the charter says that
the revision reflects them? This is what I always complain of.
Thanks.
>There's also a legitimate issue about whether a C90 ruling, which was
>not implemented by appropriate clarifying textual changes to C99, can be
>considered to apply to C99. If you believe that the DR resolution merely
>explained the meaning, rather than clarifying something that was
>inherently unclear, then it would follow that there was no need for such
>textual changes.
Isn't the very existence of the DR proof enough that the standard was not
clear enough?
In itself, the existence of a DR doesn't prove that there's any
legitimate reason to be unsure about the meaning of the specified aspect
of the standard. Submitters can have an arbitrarily poor excuse for
submitting a DR. In the past, the Committee has sometimes responded to
DRs which it felt were poorly justified by saying "the standard is clear
enough".
I have to say that any such response is inherently insulting; and
correspondingly inappropriate unless the insult is richly deserved (and
questionable even in that case). Particularly when, as has sometimes
been the case, the response fails to identify which answer it is that
the committee considers so obvious. Even if the standard is perfectly
clear on an issue, and the submitter is a complete idiot for not
recognizing that fact, the submitter did in fact fail to recognize that
fact, and therefore needs help.
I don't think so. AFAICK, the Committee is obliged to discuss and to
response all of DRs submitted. From C90 DRs which have been responsed,
we can know the fact that the submitters sometimes misunderstand the
clear wording which can be obtained by more elaborated reading. Of
course, it'd be useful to reflect those DRs which disclose the obscure
aspects of the Standard into the next revision even if they don't
qualify TCs, I believe.
FWIW, I found the thread to which I referred. It was Oct 29, 2001, in
comp.std.c++, and entitled "Is *(int*)0 an lvalue?".
Tom Payne
After reading it roughly, I can see no more authoritative and
persuasive answer in the thread than the DRs I cited.
I don't think we allowed for NaNs in integer arithmetic,
but in some hypothetical implementation padding bits
could be used to encode them. Being undefined behavior,
it could happen.
You're right: it was a spurious argument, and noting that
there is no specific associated type is one way to see that.
So what? It's no different from 1/0: undefined behavior
occurs if and only if that (sub)expression is evaluated.
No. There are many causes for DR questions, and some of
them clearly come under the category of nit-picking where
nobody argues that responsible programmers or implementors
would misunderstand the intent.
That originated in the one-line summary macros I used when
I was the keeper of the DRs. During my reign the policy
was to follow the summary by a more detailed explanation.
But in cases where the DR submitter has not indicated any
misunderstanding of the intent and merely suggests wording
that he prefers, no additional explanation is necessary
once we indicate that we reconsidered the wording and
still find no need to make any change.
I agree. The "*(int*)0" version, however, has drawn adamant and
authoritative insistance that conforming implementations should not,
under any circumstance, be required to accept such constructs, e.g.,
Francis Glassborow wrote:
... You seem to be claiming that a compiler must accept:
if(foo()) *(int *)0 = 1;
even though it can only determine that either the line is a waste of
space or it generates undefined behaviour depending on the value of
foo(). I maintain that is unreasonable.
FWIW, I maintain the total opposite, i.e., that it would be
unreasonable not to require the "acceptance" of
if(foo()) *(int *)0 = 1;
if there's any possibility that foo() might return zero. The
same goes for "if(foo()) 1/0;".
Tom Payne
This is a purely subjective judgment. The kind of judgment that is
responsible for the very existence of this thread: if the answers to
the relevant C89 DRs had been incorporated in the text of C99, the issue
would have been clarified for anyone having access to the standard.
Instead of that, to find the answer, one has to dig into the DRs submitted
against C89, whose relevance to C99 is highly debatable and debated.
> In comp.std.c Richard Bos <r...@hoekstra-uitgeverij.nl> wrote:
> + That's the one-but-inner context. The _actual_ context is the
> + dereference operator; _it_ requires a pointer context, and thus the 0 is
> + interpreted as a null pointer constant, which is converted (at compile
> + time, most likely) to an actual null pointer. This null pointer is then
> + dereferenced.
> + As a result, this program is syntactically correct, and invokes
> + undefined behaviour only when argc != 0. If the compiler can determine
> + that argc will always be positive (for example, if it knows it will
> + always be able to supply argv[0]), it is AFAICT allowed not to compile
> + it; but if the compiler cannot be sure argc will never be 0, it _must_
> + compile this program.
>
> That's exactly how I see it. It follows, however, that "*0" is an
> lvalue even though it does not designate an object. Lots of folks
> seems to balk at that.
Syntactically, it _is_ an lvalue. Syntactically, *garbage_pointer is
also an lvalue. An lvalue does not necessarily designate a _valid_
object; only lvalues whose generation does not invoke UB are guaranteed
to do that. An lvalue which is invalid can designate something that
_looks_ like an object, but is invalid.
Richard
[BillG]
> > If reporting the snipped example as an error is broken,
> > then I'll take broken over "fixed", thank you very much.
> Why? The program had perfectly reasonable, well-defined
> properties, but that compiler refused to accept it. Now,
> a *warning* about possible division by zero might be
> useful, but not an outright refusal to compile.
IMO, code snippets like 1/0 are "bad", and even if the code will never run,
the fact it is there is still a bad thing.
A good compiler should detect a constant expression with a value of zero
appearing on the right of a / operator. For a compiler to let (1/0) pass,
even with just a warning, it would have to generate some code for the
result of this expression. It's already done the workload of detecting the
x/0 and now it needs to do some more.
Runtime error message? Core dump? 0?
Nah, upgrade the warning to an error and abandon the compile. Let the
programmer sort out the bad code.
I'd rather the compiler makers spent thier time on useful stuff.
All IMO.
Bill, no really, all IMO.
> "Douglas A. Gwyn" <DAG...@null.net> wrote:
>
> [BillG]
> > > If reporting the snipped example as an error is broken,
> > > then I'll take broken over "fixed", thank you very much.
>
> > Why? The program had perfectly reasonable, well-defined
> > properties, but that compiler refused to accept it. Now,
> > a *warning* about possible division by zero might be
> > useful, but not an outright refusal to compile.
>
> IMO, code snippets like 1/0 are "bad", and even if the code will never run,
> the fact it is there is still a bad thing.
>
> A good compiler should detect a constant expression with a value of zero
> appearing on the right of a / operator. For a compiler to let (1/0) pass,
> even with just a warning, it would have to generate some code for the
> result of this expression. It's already done the workload of detecting the
> x/0 and now it needs to do some more.
Example:
Somewhere in a header file I wrote either:
#define NUMBER_OF_ITEMS 10
or
#define NUMBER_OF_ITEMS 0
Shouldn't I be allowed to write this without a warning:
if (NUMBER_OF_ITEMS > 0) {
int x = 100 / NUMBER_OF_ITEMS;
int y = x * NUMBER_OF_ITEMS;
printf ("%d %d\n", x, y);
}
After all, if I changed the header file to
extern int NUMBER_OF_ITEMS;
then my code snippet would be perfectly fine (unless there is some
stupid mistake in it).
Because the committee is loathe to spend time on wording issues.
History shows that whenever we do, we end up spending an inordinate
amount of time for little, if any, improvement. During the revision
process, if someone proposed "better" wording and the committee (or at
least the editor) agreed that it was, indeed, better (remember that
clarity is, in general, subjective, not objective), it got used. In
cases where the standard just doesn't say what we meant and doesn't
contain sufficient hints for a highly-motivated reader to figure out the
intent we'll revise the wording, but otherwise we tend to leave it
alone.
-Larry Jones
Fortunately, that was our plan from the start. -- Calvin
The quality of the responses has unfortunately been quite variable.
We're trying to do a better job now than we have in the past, I hope
we're succeeding. "The standard is clear enough" may be poorly phrased;
the intended meaning is not "you're an idiot" but rather "it doesn't
seem to be causing mass confusion and we can't think of any better way
to say it right now".
-Larry Jones
I've got to start listening to those quiet, nagging doubts. -- Calvin
Not when you're trying to test your divide-by-zero
exeption handler.
> Shouldn't I be allowed to write this without a warning:
> if (NUMBER_OF_ITEMS > 0) {
> int x = 100 / NUMBER_OF_ITEMS;
> int y = x * NUMBER_OF_ITEMS;
Damn! (I humbly withdraw the objection.)
Bill, reading his new copy of "101 ways to not look like an idiot".
And as such, it's a useful thing to know; but as the sole answer to a DR
(which it has been, on occasion), it doesn't qualify as actually
answering the question.
--
James Kuyper
MODIS Level 1 Lead
Science Data Support Team
(301) 352-2150
Indeed; which is why a number of committee members are now insisting
that, in general, when a DR asks specific questions, the committee
response contain answers to those questions. This is only "in general"
because there are lots of extenuating circumstances. For example, the
committee has in the past received DR's along the lines of:
Consider the following code:
...
Is there any undefined behavior here?
with no indication of what the actual issue is or what the author finds
suspect. Such DRs seem more like a game of "stump the committee" than
legitimate questions, although that was probably not the authors' intent
(we recognize that asking good questions is as difficult as providing
good answers). Nonetheless, the committee reserves the right to refuse
to play the game.
-Larry Jones
I keep forgetting that rules are only for little nice people. -- Calvin