The undefinedness of a common expression.

L

lawrence.jones

In comp.std.c Wojtek Lerch said:
When the object is a regular non-volatile variable, sure; but the
interesting case is an assignment to a volatile object. Imagine that you're
assigning 1 to a hardware register that always returns zero when read? What
is "the value of the object after the assignment" in that case -- is it one
or is it zero?

Yes. :)
 
L

lawrence.jones

In comp.std.c Francis Glassborow said:
However the question is whether that value will be 1. I stick with my
view that requiring (or even allowing) a read after writing will be a
code breaker if i is volatile. I have always understood that the
returned value is the value (rvalue)assigned cast to the type being
assigned to.

It has to be more than that, because there's no way to cast to the type
of a bit field. As far as I'm concerned, anyone who uses the result of
an assignment to a volatile variable deserves whatever value they happen
to get. It's easy enough to write the code to explicitly do a read back
or not, whichever you want.
 
L

lawrence.jones

In comp.std.c Kaz Kylheku said:
If an access unambiguously reads either the prior or new value,
that means it is well-defined.

That's essentially what the rewritten rule in C1X says. It's not what
the rule said previously, although it's what was intended all along.
 
L

lawrence.jones

In comp.std.c Wojtek Lerch said:
I vaguely remember that the formal model of sequence points that ended up
not making into the standard a while ago seemed to allow the left argument
to an assignment to read the value, like the a[a[0]]=1 example does. Was
that one of the reasons why it was dropped?

No, that was generally considered to be a good thing.

The primary reason it was dropped was that there was insufficient time
and tutorial material for the committee to be sure that they understood
it and that it matched the intended behavior. There were also holes in
it involving floating-point exception flags (which are somewhat similar
to regular objects but with significant differences). A final objection
was that it formalized accesses in the abstract machine and required
those access to occur for volatile variables and some committee members
were strongly opposed to that.
 
W

Wojtek Lerch

It has to be more than that, because there's no way to cast to the type
of a bit field.

It's the value that gets stored in the object. What THAT value is is
specified somewhere, somehow, right?
As far as I'm concerned, anyone who uses the result of
an assignment to a volatile variable deserves whatever value they happen
to get. It's easy enough to write the code to explicitly do a read back
or not, whichever you want.

Well, no -- if the standard allows (requires?) the assigment to do the read
back for you, then it's impossible to write code that does a write without a
read back. As a minimum, the standard should make it clear whether the
implicit read back is required, forbidden, or unspecified.
 
K

Keith Thompson

It has to be more than that, because there's no way to cast to the type
of a bit field.

Converted, then.
As far as I'm concerned, anyone who uses the result of
an assignment to a volatile variable deserves whatever value they happen
to get. It's easy enough to write the code to explicitly do a read back
or not, whichever you want.

As far as I'm concerned, anyone who does so should be able to
determine, by reading the standard, whether the behavior is defined,
unspecified, undefined, or whatever. (You said elsethread that this
is clarified in C201X.)
 
W

Wojtek Lerch

Keith Thompson said:
As far as I'm concerned, anyone who does so should be able to
determine, by reading the standard, whether the behavior is defined,
unspecified, undefined, or whatever.

It seems that it's the last one. ;-)
 
L

lawrence.jones

In comp.std.c Wojtek Lerch said:
Well, no -- if the standard allows (requires?) the assigment to do the read
back for you, then it's impossible to write code that does a write without a
read back.

I think it's safe to assume that any useful implementation of volatile
would not do a readback when the value of the assignment is not used.
But I can see reasonable implementors (and users) disagreeing on whether
a readback is desirable or not when the value *is* used.
As a minimum, the standard should make it clear whether the
implicit read back is required, forbidden, or unspecified.

Agreed.
 
L

lawrence.jones

In comp.std.c Keith Thompson said:
As far as I'm concerned, anyone who does so should be able to
determine, by reading the standard, whether the behavior is defined,
unspecified, undefined, or whatever. (You said elsethread that this
is clarified in C201X.)

No, what is clarified in C1X is that the values of the left and right
operands have to be fully determined before the assignment can take
place, which makes a[a[0]] = 1 well-defined regardless of the value of
a[0]. That doesn't affect whether a readback occurs or not.
 
L

lawrence.jones

In comp.std.c Wojtek Lerch said:
Do you mean the Standard leaves the definition of "the value" unspecified
for volatile objects?

That's my position: it is unspecified whether the value of an assignment
to a volatile object is the value actually stored in the object or the
value obtained by reading the object after storing the value.
 
W

Wojtek Lerch

I think it's safe to assume that any useful implementation of volatile
would not do a readback when the value of the assignment is not used.

Agreed. If the standard indeed requires a readback, an implementation that
violates that requirement is more useful than a conforming one.
But I can see reasonable implementors (and users) disagreeing on whether
a readback is desirable or not when the value *is* used.

Sure; but that has nothing to do with whether it's actually required or
forbidden by the standard. But I can't find any evidence in the standard
suggesting that the presence or absence of the readback in the abstract
machine depends on the context surrounding the assignment expression, or
that it's meant to be unspecified or implementation-defined. I'd like to
believe that "the value after the assignment" was intended to mean the value
being stored in the object. I can accept that it was, instead, intended to
mean a value read from the object immediately after the store. But it's
really hard for me to believe that those words were chosen to reflect the
intent that it should be unspecified whether it's one or the other.
 
W

Wojtek Lerch

That's my position: it is unspecified whether the value of an assignment
to a volatile object is the value actually stored in the object or the
value obtained by reading the object after storing the value.

"Unspecified" means that the standard offers two or more possibilities and
allows the implementation to chose one of them. That's not the same as
offering an unclear description that can be interpreted in two different
ways. The description in the standard does *not* sound like it was meant to
offer a choice, does it? Doesn't "the value after the assignment" sound
like there's exactly one possibility? The only problem is the standard
forgets to explain is how we're supposed to interpret those words when the
object refuses to be modified by the assignment.

The way I see it, the standard doesn't even consider the possibility of such
objects -- the only way to make them fit into the standard's model seems to
be by pretending that the write actually stores the new value in the object,
but then immediately something restores the old value in a way unknown to
the implementation. If you agree to that interpretation, I think "the value
after the assignment" should be taken as the value *immediately* after the
store, rather than the value at some point later, after the object may have
been modified in ways known or unknown to the implementations. But, of
course, that does not necessarily reflect the intended meaning of the
standard -- as we already know, the words of the standard do not seem to
clearly reflect the intent in this area.
 
K

Kaz Kylheku

["Followup-To:" header set to comp.std.c.]
I think it's safe to assume that any useful implementation of volatile
would not do a readback when the value of the assignment is not used.

Dragons be down that path.

Would a useful implementation of volatile also not read *x in the expressions:

*x;
(void) *x;

where x is a pointer to some volatile type?

If a read is required, it can't be elided just because it is thrown away,
when the object is volatile.

That's separate issue from whether or not assignment requires a read-back,
surely.
But I can see reasonable implementors (and users) disagreeing on whether
a readback is desirable or not when the value *is* used.

I've been arguing that the text in fact requires the readback, but I perhaps
haven't made it clear that I don't agree that it's a good requirement.

For one thing, it leaves the programmer with no obvious way to express a pure
assignment (write only, no read).

That's very bad when a read may have a side effects on the hardware.
 
L

lawrence.jones

In comp.std.c Wojtek Lerch said:
Sure; but that has nothing to do with whether it's actually required or
forbidden by the standard. But I can't find any evidence in the standard
suggesting that the presence or absence of the readback in the abstract
machine depends on the context surrounding the assignment expression, or
that it's meant to be unspecified or implementation-defined.

6.7.3p6: What constitutes an access to an object that has
volatile-qualified type is implementation-defined.

That loophole was intended to be large enough to drive an arbitrary size
vehicle through.
 
W

Wojtek Lerch

6.7.3p6: What constitutes an access to an object that has
volatile-qualified type is implementation-defined.

That loophole was intended to be large enough to drive an arbitrary size
vehicle through.

But it has nothing to do with what kind of accesses assignment operators are
required or allowed to perform in the abstract machine. Only after that
question is answered, you can use your loophole to translate the read and/or
write accesses in the abstract machine to whatever the implementation says
consitutes those accesses for volatile objects. If the correct
interpretation of 6.5.16#3 is that an assignment operator returns the
converted value of its right operand that it also stores in the object, I
don't see how your loophole could possibly justify reading back and
returning a different value.
 
R

Richard Bos

The primary reason it was dropped was that there was insufficient time
and tutorial material for the committee to be sure that they understood
it and that it matched the intended behavior. There were also holes in
it involving floating-point exception flags (which are somewhat similar
to regular objects but with significant differences). A final objection
was that it formalized accesses in the abstract machine and required
those access to occur for volatile variables and some committee members
were strongly opposed to that.

I presume you mean that they were opposed to the specific kind of
accesses that were formalised, rather than to the principle as such?
After all, the principle is present in the current Standard as well, and
besides, it is volatile's main purpose.

Richard
 
L

lawrence.jones

In comp.std.c Richard Bos said:
I presume you mean that they were opposed to the specific kind of
accesses that were formalised, rather than to the principle as such?
After all, the principle is present in the current Standard as well, and
besides, it is volatile's main purpose.

No, they were opposed to the principle. They liked the current
Standard's loophole (what constitutes an access to a volatile object is
implementation-defined) and didn't want any tighter requirements for
fear that it would prevent them from doing what they considered to be
"the right thing".
 
A

André Gillibert

Wojtek Lerch said:
Is it? I've always believed that an assignment operator returns the value
that it also stores in the object, *without* reading it back from the
object, and that the Standard's description in terms of "the value of the
object after the assignment" was just a clumsy wording (no offense) that
wasn't meant to imply a read access.

I agree.
I think a "read" access to an object involves a lvalue-to-value
conversion (6.3.2.1-1), even though that's not clearly stated.

Moreover 6.5.16-3 indicates that assignment expressions aren't lvalues.
They have the value (rvalue) of the left operand after the assignment.
I think it implies that a volatile variable is not read twice.

Note that this is different from C++ where assignments are lvalues
which makes

int a,b,c=0;
a=b=c;

Well-defined in C but undefined in C++ (I think it's a C++ standard
defect but fortunately not a C defect).

I can think of an hypotetical implementations giving strange behavior
to a[0]=0;a[a[0]]=1;

#include <stdio.h>

int main(void) {
int a[3];
a[0]=0; /* line 1 */
a[a[0]]=1; /* line 2 */
printf("%d\n", a[0]); /* line 3 */
return 0;
}

The C implementation may notice that a[0] is read on line 3 and
assigned a constant value on line 2, and so, its
constant-value-optimization module may test if the value of a[0] may have been
modified between line 1 and line 3. If it proves it cannot be modified,
it will be able to optimize the printf statement to:
printf("%d\n", 0);

Now, the C implementation analyzes line 2. It notices that the a array
is written, but, due to the fact a[0] is read for something that's not
needed to compute the value (the 1 literal), it cannot be
modified before the next sequence point (at end of this statement).

Thus, the compiler may optimize the printf statement, and output zero.
As n1124 is worded, this optimization is legal.
 
W

Wojtek Lerch

André Gillibert said:
I agree.
I think a "read" access to an object involves a lvalue-to-value
conversion (6.3.2.1-1), even though that's not clearly stated.

It's the other way around: a lvalue-to-value conversion involves a read
access, but that doesn't mean that every read access is part of an
lvalue-to-value conversion.
Moreover 6.5.16-3 indicates that assignment expressions aren't lvalues.
They have the value (rvalue) of the left operand after the assignment.
I think it implies that a volatile variable is not read twice.

Twice? This is about whether an assignment to a volatile variable reads it
*once* or not at all. I don't think anybody has claimed that it reads is
twice.
Note that this is different from C++ where assignments are lvalues
which makes

int a,b,c=0;
a=b=c;

Well-defined in C but undefined in C++ (I think it's a C++ standard
defect but fortunately not a C defect).

It's undefined in C++? Why?
 

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

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,815
Latest member
treekmostly22

Latest Threads

Top