Extent of the "as-if" rule

W

Wojtek Lerch

Sidney said:
>
>
> Not at all. It's just that I would have appreciated a disclaimer stating
> something like this:
>
> "The execution environment may impose semantics / side effects in
> addition to (but not contradicting) the semantics and side effects as
> defined in this standard, for the following I/O related functions: ..."
>
> Is that so much to ask?

The standard has a tendency to avoid saying things that are obvious.
Implementations may do whatever they want, as long as they meet all the
requirements defined in the standard. In particular, implementations
are free to give promises to their users that go beyond what the C
standard requires. If a compiler is sold in a box that claims to
contain 97% recycled paper but in reality only contains 95%, isn't it
quite obvious that that little lie doesn't affect the compiler's
conformance to the C standard?

....
>
> That is answerable. A call has a side effect if there is a detectable
> effect in the outside world of the call having been made, i.e., there is
> a detectable difference between the effects on the execution environment
> of a run of the program and a run of a derived program that is equal to
> the original, except that this one call did not take place.

That's a circular definition. If an implementation optimizes the call
away, there's no detectable difference, and therefore it was OK to
optimize it away.

On the other hand, if another implementation doesn't optimize it away,
the derived program takes up less space in the execution environment --
isn't that a detectable difference?
>
> Which definition of "side effect" are you using here?

Any definition you can think of. Since the call has been optimized
away, it can't have any effects whatsoever.
>
> He couldn't, and doesn't have to. What is important is that he knows
> from the standard that the call *may* have a side effect.

You seem to have it backwards. The implementor *implements* the call.
He writes the code. The standard tells him what the call must do and
what it mustn't do, and then he decides how to write code that does all
the required things but doesn't do any of the forbidden things. If the
standard says that the call may but may not have side effects (by
whatever definition of "side effects"), the implementor is free to
choose whether to write code that produces such side effects or code
that can be optimized away.

....
> The standard tries quite hard to provide a situation where compliancy of
> an implementation can be judged based on the text of the standard alone,
> i.e., it is (or attempts to be) self-contained.

Yes. That's the purpose of having a standard.
> I would argue that this fails (where else) with its handling of the
> concept of side effects. These cannot be properly defined without
> references to "outside the C box", I think; so then you're left with a
> number of options. My preference would be to be "intentionally vague"
> rather than an (IMHO) misguided attempt to provide a definition.

The authors of the standard described a concept they wanted to use in
the text, and decided to name it "side effects". They excluded things
like file attributes from their definition because they didn't want
implementation conformance to the C standard to depend on how
implementations define and handle file attributes.

If you ask whether the C standard requires fopen() to change file
attributes, the answer is a clear no. You might not like that answer;
but it doesn't make much sense to say that instead the standard should
say, "no, except when the implementation says yes and maybe perhaps in
some other circumstances that we can't think of right now". Just like
it wouldn't make sense for the C standard to say that if the box
specifies how much recycled paper it contains, or if the country it's
sold in requires all boxes to contain a certain minimum of recycled
paper, then it must indeed contain at least that much.
> Of course I've said the same thing in different words a dozen times now.
> Please note that I'm perfectly happy to accept that some things are
> defined outside the C scope (e.g. in POSIX), but the C standard should
> write a couple of lines about that and its relation to such things,
> that's all I ask.

Isn't it good enough that those other things explain their relation to
C?

....
>
> No. You are hitting on precisely the reason why it is so hard to define
> "side effects" in the way that one would like. But you and I have an
> intuitive idea. I'd prefer a sound definition, but on the other hand I
> prefer a somewhat vague notion over a faulty definition.

I don't know about you, but my intuitive idea is that it depends on the
context. For instance, when I'm talking about medicine (or even about
POSIX), I don't necessarily try to apply the C standard's definition
when someone says "side effects". But when I'm reading the C standard,
my intuition tells me that I should interpret it exactly the way the
definition says. If the result didn't seem to make sense, then I could
suspect that something were wrong either with the definition or with how
the term is used. But my intuituion tells me that everything is OK.

....
>
>
> I presume your question is designed to convey the idea that the C
> standard doesn't have the notion of a platform, as currently written?

Correct. There is not a single occurence of the word "platform" in the
C standard.

....
>
> That's funny, indeed. What has POSIX to say of the access timestamp?

"The fgetc() function may mark the st_atime field of the file associated
with stream for update. The st_atime field shall be marked for update by
the first successful execution of fgetc(), fgets(), fgetwc(), fgetws(),
fread(), fscanf(), getc(), getchar(), gets(), or scanf() using stream
that returns data not supplied by a prior call to ungetc() or ungetwc()."
 
D

Dan Pop

In said:
If I can read a C program
and pronounce all of the required diagnostics for translation,
and then when it's time to run the program
if I claim that all my streams are closed,
can I be a conforming implementation ?

No, your stdin, stdout and stdout must be open, but any attempt to get
input from the first one or send output to the last two ones may fail.
Of course, no fopen() call need succeed. With this implementation, no
C program can produce any output and you can optimise each correct C
program to the equivalent of:

int main() { while(1); }

because the standard doesn't specify how long it takes to a program to
complete its execution.

Your translator will only have to implement translation phases 1 to 4
(only to the extent necessary for properly handling any active #error
directive), after that you can display "hello world" (just in case
the program needed a diagnostic during translation phase 7) and then
generate the code for the optimised version shown above.

Dan
 
J

James Kuyper

pete said:
If I can read a C program
and pronounce all of the required diagnostics for translation,
and then when it's time to run the program
if I claim that all my streams are closed,
can I be a conforming implementation ?

Of course, so long as you implement the required semantics of the
program. There's nothing in the standard that prohibits the use of a
wet-ware implementation. As a matter of QoI, you'd want to have some
suitable storage medium, such as paper, to implement files.
 
D

Douglas A. Gwyn

pete said:
If I can read a C program
and pronounce all of the required diagnostics for translation,
and then when it's time to run the program
if I claim that all my streams are closed,
can I be a conforming implementation ?

No, for a hosted conforming implementation the standard
I/O streams must be open at program startup. However,
they are allowed to fail upon use, provided they report
that in the specified way.
 
D

Douglas A. Gwyn

Dan said:
No, your stdin, stdout and stdout must be open, but any attempt to get
input from the first one or send output to the last two ones may fail.
Of course, no fopen() call need succeed. With this implementation, no
C program can produce any output and you can optimise each correct C
program to the equivalent of:
int main() { while(1); }
because the standard doesn't specify how long it takes to a program to
complete its execution.

A conforming implementation is obliged to perform the
actions specified for the abstract C machine, although
if no side efects are visible in the environment then
there might be no way to *test* whether it conforms.
I think an infinite execution time for a finite-time
program is qualitatively wrong, although again one
could never be sure by external testing.
Note that nonconformance could be detected with
programs that aren't strictly conforming, e.g. writing
to device register or screen pixel.
 
D

Dan Pop

In said:
A conforming implementation is obliged to perform the
actions specified for the abstract C machine,

Nonsense! In this case, no optimisation would be possible. Fortunately,
the standard proves you wrong:

3 In the abstract machine, all expressions are evaluated as
specified by the semantics. An actual implementation need not
^^^^^^^^
evaluate part of an expression if it can deduce that its value is
not used and that no needed side effects are produced (including
any caused by calling a function or accessing a volatile object).
although
if no side efects are visible in the environment then
there might be no way to *test* whether it conforms.

According to the as-if rule, it conforms.
I think an infinite execution time for a finite-time
program is qualitatively wrong, although again one
could never be sure by external testing.

There is no way to tell whether the program will terminate in 1 million
years or never. And the standard allows a finite-time program to
terminate in 1e6 years.
Note that nonconformance could be detected with
programs that aren't strictly conforming, e.g. writing
to device register or screen pixel.

Nope: such programs invoke undefined behaviour, so they cannot be used
in testing the conformance. Even if this weren't the case, the action
in question could be performed 1e6 years after the program startup...

Dan
 
D

Douglas A. Gwyn

Dan said:
Nope: such programs invoke undefined behaviour, so they cannot be used
in testing the conformance. Even if this weren't the case, the action
in question could be performed 1e6 years after the program startup...

Your "logic" leads to the conclusion that conformance
testing is impossible. Fortunately, you are wrong.
 
P

pete

Douglas said:
No, for a hosted conforming implementation the standard
I/O streams must be open at program startup. However,
they are allowed to fail upon use, provided they report
that in the specified way.

Suppose that all my streams are open on program startup,
but that they all close upon first attempted access.
You wind up with a program run that has no output
and takes no input.
 
P

pete

James said:
Of course, so long as you implement the required semantics of the
program. There's nothing in the standard that prohibits the use of a
wet-ware implementation. As a matter of QoI, you'd want to have some
suitable storage medium, such as paper, to implement files.

If all my streams are closed, then I can't use any files.
 
D

Dan Pop

In said:
Your "logic" leads to the conclusion that conformance
testing is impossible. Fortunately, you are wrong.

Nope, that's merely the conclusion you have drawn and, fortunately, you
are wrong.

I was merely pointing out that, for the implementation I was describing,
conformance testing is impossible. I can give you another such example:
a translator that doesn't produce any output for the first 1e6 years.

There is no logical way to infer, from these examples, that conformance
testing is impossible for each and every implementation.

Dan
 
D

Dan Pop

In said:
Suppose that all my streams are open on program startup,
but that they all close upon first attempted access.

That is not allowed by the semantics of a streams. A stream can only
be closed by an fclose() call or as part of program termination.
You wind up with a program run that has no output
and takes no input.

As I have already explained you, this is perfectly possible even if the
streams are open: no I/O request to an open stream is guaranteed to
succeed.

Dan
 
P

pete

Dan said:
That is not allowed by the semantics of a streams. A stream can only
be closed by an fclose() call or as part of program termination.


As I have already explained you,
this is perfectly possible even if the
streams are open: no I/O request to an open stream is guaranteed to
succeed.

I had confused stream failure with closure.

That there is no requirement that a conforming implementation
must deal with input and output files for a running program,
is what I was interested in.

I think that a running program must return a status to the
operating system on termination, if the system expects one,
but I don't think that it has to do anything else.
 
D

Dan Pop

In said:
I think that a running program must return a status to the
operating system on termination, if the system expects one,

And even this may happen after an arbitrarily long amount of time: the
standard doesn't place any upper limit on the running time of the
following program:

int main() { return 0; }

Dan
 
J

James Kuyper

Douglas A. Gwyn said:
Your "logic" leads to the conclusion that conformance
testing is impossible. Fortunately, you are wrong.

Testing implementation conformance by reasonable measures of
conformance is quite feasible. Testing conformance to according to the
C standard's measures of conformance is much more difficult, because
it gives implementors too many "outs".
 
J

James Kuyper

pete said:
If all my streams are closed, then I can't use any files.

Streams aren't closed or opened, files are opened. Opening a file
associates it with a stream (7.19.3p1).

My best guess is that you actually mean something that I would
describe differently: a conforming implementation can have every
single I/O function call fail, as long as it uses the prescribed
methods of reporting that failure. Hardware problems can happen any
time you run the program; that doesn't make it non-conforming.

I just realized that I have no idea what the connection is between
this hypothetical wet-ware implementation of C, and the original
question about file attribute setting. Would you care to explain the
connection? If a wet-ware implementation of C promises that successful
fopen() calls will result in the setting of appropriate file
attributes, and then "optimizes" the fopen() calls away, so that it
doesn't actually happen, then it's the wet-ware implementation's own
promises that are being broken. It's not anything that is, or should
be, in scope for the C standard. That's exactly the same thing that
can be said about a more conventional implementation of C.

Whether the files all start out closed is irrelevant. Whether the
files can be successfully opened is a different issue, but also
irrelevant. What happens if they are successfully opened is relevant,
but still not in-scope for the C standard.
 
P

pete

James Kuyper wrote:
I just realized that I have no idea what the connection is between
this hypothetical wet-ware implementation of C, and the original
question about file attribute setting.

The standard describes a conforming implementation
in terms of it's behavior during the translation and
execution of a C program.

I had confused stream closing, with any situation
which might cause a return value of EOF.
But what I was getting at, was what Dan Pop is saying
in other strands of this thread, that because streams are unreliable,
a conforming implementation need only
be able to produce the correct diagnostics during translation.
A conforming implementation need not produce any output
nor accept any input during the execution of a program.
 
P

pete

Douglas said:
Your "logic" leads to the conclusion that conformance
testing is impossible. Fortunately, you are wrong.

I believe that the claim, "Excedes ANSI Specifications",
is already in use. I always interpreted it to mean that the
compiler did not self destruct after translating one program.

And now if I may change change the topic to some old business:

Is it legitimate for a function like puts,
to return at the first sign of EOF, like this:

int puts(const char *s)
{
while (*s != '\0') {
if (putchar(*s) == EOF) {
return EOF;
}
++s;
}
return putchar('\n');
}

or must it keep hammering away, regardless of EOF, like this:

int puts(const char *s)
{
int eof = 0;

while (*s != '\0') {
if (putchar(*s) == EOF) {
eof = 1;
}
++s;
}
return putchar('\n') == EOF || eof != 0 ? EOF : 1;
}

?
 
C

CBFalconer

pete said:
.... snip ...

And now if I may change change the topic to some old business:

Is it legitimate for a function like puts,
to return at the first sign of EOF, like this:

int puts(const char *s)
{
while (*s != '\0') {
if (putchar(*s) == EOF) {
return EOF;
}
++s;
}
return putchar('\n');
}

or must it keep hammering away, regardless of EOF, like this:

int puts(const char *s)
{
int eof = 0;

while (*s != '\0') {
if (putchar(*s) == EOF) {
eof = 1;
}
++s;
}
return putchar('\n') == EOF || eof != 0 ? EOF : 1;
}

I would recommend the immediate return. Once the EOF has been
received something has gone wrong, and the string will not be
properly output. Any further 'hammering' may disturb the error
status, which in turn will prevent proper diagnostics.
 
M

mahe

Tristan said:
Greetings.




I don't know what the standard says, but from an implementation point of
view it might make sense not to optimize the fopen() away. Many file
systems maintain a "last accessed" timestamp for files; therefore, the
seemingly useless fopen() does indeed modify the environment.

Well when I write a program, I expect the compiler to transpose what I
asked him to do, if it could optimize his transcription to make his
internal stuff easier or to run things faster, thats not my concern, as
in ur example its none of the compilers business to predict from what I
am expecting from it.

Well let me take a completely meaningless example, because there are lot
of good ways to do the same.
Suppose I run the program with the above fopen variant:
assuming that I run it on a unix flavored OS per example:
while true;do ./fopen_variant;done

On the other hand I would like to check how many current processes has a
descriptor assciated to the "somefile" using lsof for example.I may not
have the same results, if the as-if rule is applied here!!

I shall say that the as-if rule should be applied only to those vars or
routines that deals strictly with internals of the same program. For
example routines that deals with shared memory could not be involved!!

regards,
magesh
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top