Type interchange

K

Keith Thompson

pete said:
Whether or not a program is a "strictly conforming program"
is not dependant on the implementation,
Agreed.

and whether or not a program is a correct program
is not dependant on the implementation.

Depends on what you mean by "correct".
The code that you posted was intended to have a portability issue.

Of course.
A non portable program construct
for which the standard imposes no requirements,
is what the standard says yields undefined behavior.

But it doesn't say that *all* uses of nonportable program constructs
yield undefined behavior.

Let's make it a complete program:

int main(void) {
int n = 32767;
n ++;
return 0;
}

I say that this program (specifically its execution) has undefined
behavior under an implementation with INT_MAX == 32767, and has
well defined behavior under an implementation with INT_MAX > 32767.

What do you say? Are you claiming that the program's behavior is
undefined regardless of the implementation?
 
J

James Kuyper

....
Furthermore, under your interpretation, I see nothing saying that
int and long must have the same representation to be compatible.
An implementation could have 32-bit int and 64-bit long, but if
it decides that int and long are compatible types, then the above
declarations may be accepted without so much as a warning.

Keep in mind that if an implementation treats a 32-bit int and a 64-bit
long as being compatible, it can't hide behind the clauses that give
undefined behavior to code which treats them as compatible. I personally
don't see how they could conform to those requirements, but if they did
somehow manage to do so, I don't see any need for a warning except for
purposes of writing portable code - and as a matter of QoI, I would
consider providing such warnings to be essential.
 
K

Keith Thompson

James Kuyper said:
Keep in mind that if an implementation treats a 32-bit int and a 64-bit
long as being compatible, it can't hide behind the clauses that give
undefined behavior to code which treats them as compatible. I personally
don't see how they could conform to those requirements, but if they did
somehow manage to do so, I don't see any need for a warning except for
purposes of writing portable code - and as a matter of QoI, I would
consider providing such warnings to be essential.

One question.

Under what circumstances, if any, may a conforming implementation *not*
issue a diagnostic for the following program?

int main(void) {
int *ip = 0;
long *lp = ip;
return 0;
}

(My answer is that a diagnostic is always required.)
 
J

James Kuyper

Keith Thompson wrote: .... ....
That's an example of a non portable construct
for which the standard imposes no requirements.

It's a non-portable construct for which the standard imposes
requirements on every implementation where INT_MAX > 32767, and no
requirements on any implementation where INT_MAX == 32767.

Otherwise there's no safe way to use any number bigger than the
documented minimal ranges for each type; there would be no point in
allowing those ranges to be bigger than that minimum, because every
program that makes use of the extra range has undefined behavior, anyway.
 
J

James Kuyper

One question.

Under what circumstances, if any, may a conforming implementation *not*
issue a diagnostic for the following program?

int main(void) {
int *ip = 0;
long *lp = ip;
return 0;
}

With the standard as currently written, I don't believe that 'int' and
'long' are required to be incompatible, thought they do have to be
different types. If that's correct, then as you suggest, it implies that
an implementation where they are incompatible is not required to issue a
diagnostic for such code. As a matter of QoI, such an implementation
would be sufficiently bizarre that it should provide a warning anyway,
but I don't think it would be unconforming if it did not.

However, they must actually be compatible: if you write any valid long
value using *lp, and read it back using *ip, you must read the same
value (and not cause any other problems as a side effect). How that
could possibly be true in the particular case you mentioned, with a
32-bit int and a 64-bit long, I have no idea - but if someone figured
out how to make it work, it would be a conforming implementation of C.
 
J

James Kuyper

One question.

Under what circumstances, if any, may a conforming implementation *not*
issue a diagnostic for the following program?

int main(void) {
int *ip = 0;
long *lp = ip;
return 0;
}

With the standard as currently written, I don't believe that 'int' and
'long' are required to be incompatible, thought they do have to be
different types. If that's correct, then as you suggest, it implies that
an implementation where they are compatible is not required to issue a
diagnostic for such code. As a matter of QoI, such an implementation
would be sufficiently bizarre that it should provide a warning anyway,
but I don't think it would be fail to conform for that reason.

However, they must actually be compatible: if you write any valid long
value using *lp, and read it back using *ip, you must read the same
value (and not cause any other problems as a side effect). How that
could possibly be true in the particular case you mentioned, with a
32-bit int and a 64-bit long, I have no idea - but if someone figured
out how to make it work, it would be a conforming implementation of C.
 
K

Keith Thompson

pete said:
Keith Thompson wrote: [...]
Let's make it a complete program:

int main(void) {
int n = 32767;
n ++;
return 0;
}

I say that this program (specifically its execution) has undefined
behavior under an implementation with INT_MAX == 32767, and has
well defined behavior under an implementation with INT_MAX > 32767.

What do you say? Are you claiming that the program's behavior is
undefined regardless of the implementation?

Yes.

That's an example of a non portable construct
for which the standard imposes no requirements.

And that's where I think you're wrong. It's a non-portable construct
for which the standard *does* impose a requirement. The requirement is
that if INT_MAX > 32767, then the value of n after "n ++;" shall be
32768.

Given a revised program:

#include <stdio.h>
int main(void) {
int n = 32767;
n ++;
printf("n = %d\n", n);
return 0;
}

do you believe that, on an implementation with INT_MAX > 32767,
and in the absence of I/O errors, that program can do anything
other than printing "n = 32768"?

A conditional requirement is still a requirement.
 
K

Keith Thompson

James Kuyper said:
It's a non-portable construct for which the standard imposes
requirements on every implementation where INT_MAX > 32767, and no
requirements on any implementation where INT_MAX == 32767.

Otherwise there's no safe way to use any number bigger than the
documented minimal ranges for each type; there would be no point in
allowing those ranges to be bigger than that minimum, because every
program that makes use of the extra range has undefined behavior, anyway.

That's not *quite* true:

#include <stdio.h>
#include <limits.h>
int main(void) {
int n = 32767;
if (INT_MAX > 32767) {
n ++;
}
printf("n = %d\n", n);
return 0;
}

Programs that use arrays to do multi-precision arithmetic, for example,
can reasonably choose the range used for each "digit" based on constants
in <limits.h>; the same code might use 16 bits for element on one
implementation, 32 on another, and 64 on another.
 
K

Keith Thompson

James Kuyper said:
With the standard as currently written, I don't believe that 'int' and
'long' are required to be incompatible, thought they do have to be
different types. If that's correct, then as you suggest, it implies that
an implementation where they are compatible is not required to issue a
diagnostic for such code. As a matter of QoI, such an implementation
would be sufficiently bizarre that it should provide a warning anyway,
but I don't think it would be fail to conform for that reason.

However, they must actually be compatible: if you write any valid long
value using *lp, and read it back using *ip, you must read the same
value (and not cause any other problems as a side effect). How that
could possibly be true in the particular case you mentioned, with a
32-bit int and a 64-bit long, I have no idea - but if someone figured
out how to make it work, it would be a conforming implementation of C.

Do you believe that the committee *deliberately* left the
compatibility or incompatibility of int and long unspecified,
*and* failed to require implementations to document their choice?
Do you think they meant to allow implementations to make int and
long compatible even if they're of different sizes? If so, do you
think it made sense to do so?

I don't. I think they intended int and long to be incompatible in
all cases, but neglected to state explicitly that the definition
of "compatible types" is exhaustive. And I think that the only
*sensible* interpretation of what the standard says (even if it
fails to state it explicitly) is that two types are compatible if
and only if the standard says they are.

Similarly, I assume that 42 is an expression (even though the
definition of "expression" in 6.5p1 implies that it isn't),
that it's not an lvalue (even though the definition of "lvalue"
in 6.3.2.1p1 implies that it is), and that it has a value (even
though the definition of "value" in 3.17 implies that it doesn't).
 
Q

Quentin Pope

Nothing in the standard allows you to assume that 'long'
and 'int' are compatible types, not even if INT_MAX == LONG_MAX. 'int'
could be a one's complement type, while 'long' is a 2's complement type.
'int' could be little-endian, while 'long' could be big-endian. When
used as function arguments, 'int' and 'long' values could be passed in
different registers.

I don't think any of those would be a problem because the result is cast
to (void) in the OP's code.

However problems would arise on those implementations where although
LONG_MAX==INT_MAX, nonetheless sizeof(int)!=sizeof(long) because one of
them has extra padding bits that the other does not.

//QP
 
J

James Kuyper

Do you believe that the committee *deliberately* left the
compatibility or incompatibility of int and long unspecified,
*and* failed to require implementations to document their choice?

I don't know - their intent has often significantly differed from the
words they wrote, but I think it's a definite possibility that they
intended to leave it unspecified. For convenience, I'd like to
distinguish this concept of compatibility from my own concept. I'll call
this concept ISO compatibility, or I-compatibility for short. If two
types are specified by the standard to be compatible, then they are
I-compatible, otherwise they are I-incompatible.
Do you think they meant to allow implementations to make int and
long compatible even if they're of different sizes? If so, do you
think it made sense to do so?

You keep bringing up compatibility of int and long of different sizes. I
can't imagine any way to implement them so they're compatible, so the
issue shouldn't come up. However, it's been proven time and again that
my imagination is inadequate, so I try to cover all possibilities. If,
by some magic, they actually figure out how to make a 32-bit int
compatible with a 64-bit long, then I don't see anything wrong with
handling them as compatible types as far as the standard is concerned.

The case I'm much more concerned about is an int and a long that are
both handled identically by a given implementation. For example, they
could both be 32-bit big-endian types with no padding bits and no trap
representations. If you think that such an implementation is still
required to diagnose them as incompatible, assume that it does so - but
after issuing the diagnostic, it then generates an executable that
behaves exactly the way it would have if 'int' and 'long' were merely
two synonyms for the same type.

Would it be reasonable to describe such an implementation by saying that
'int' and 'long' were incompatible?

That's basically what "compatible" means to me - for the sake of
discussion, I'll call my concept Kuyper-compatibility, or
K-compatibility for short. If the actual behavior (not including the
generated diagnostics) for two different types is the same as it would
have been if they had been two synonyms for the same type, then they are
K-compatible types; otherwise, they are K-incompatible types.

After a long frustrating thread on some related topic a couple of years
ago, I reached the conclusion that when the standard says two types are
compatible, what it means is only that it guarantees that the two types
are K-compatible. When it leaves the question open, the two types might
be K-compatible, or they might be K-incompatible. On that basis, I
concluded that the standard meant "K-compatible" when it said "compatible".

What this means, therefore, is that I-compatible is essentially
equivalent to "guaranteed by the standard to be K-compatible", while
I-incompatible means "not guaranteed by the standard to be
K-compatible". I consider it plausible that the committee may have
intended "compatible" to mean I-compatible rather than K-compatible.
However, that has not been my understanding, and I'm not yet convinced
of it. Any authoritative statement on that matter from someone like
Larry Jones would convince me. I don't see any problems (other than
footnote 35) associated with the possibility that the committee meant
K-compatible when they used the term "compatible".
 
K

Keith Thompson

James Kuyper said:
I don't know - their intent has often significantly differed from the
words they wrote, but I think it's a definite possibility that they
intended to leave it unspecified.

[big snip]
Any authoritative statement on that matter from someone like
Larry Jones would convince me. I don't see any problems (other than
footnote 35) associated with the possibility that the committee meant
K-compatible when they used the term "compatible".

Hmm. I'll post to comp.std.c and see if we can get a more or less
definitive answer.
 
J

James Kuyper

I don't think any of those would be a problem because the result is cast
to (void) in the OP's code.

I already addressed that issue when I mentioned, in the part that you
clipped, that "In all probability, your code will work fine,
particularly since it discards the return value,".
However problems would arise on those implementations where although
LONG_MAX==INT_MAX, nonetheless sizeof(int)!=sizeof(long) because one of
them has extra padding bits that the other does not.

Yes, that's another unlikely possibility, and just like the other three,
it probably won't matter because of the return value is discarded.
 
T

Tim Rentsch

Keith Thompson said:
[snip]

My position: The definition of "compatible type" is intended to
be exhaustive, even though the standard doesn't say so explicitly.

In fact, I believe all definitions in the Standard are intended
to be exhaustive; it's just that some do a better job at that
than others.
int and long are incompatible for every implementation (and therefore
int* and long* are incompatible for every implementation).

Of course. The idea that int and long might be compatible
types is completely nonsensical. And, more to the point,
not supported to any degree whatsoever by any text in the
Standard. The burden of proof is on those who say they might
be compatible to find some supporting text that suggests
they might be. No such evidence has been offered.
 
T

Tim Rentsch

James Kuyper said:
On 11/30/2011 10:17 PM, Keith Thompson wrote:
...

The implementation always knows whether two types are incompatible; it
has to implement them. For the developer, I don't think that it is
possible to demonstrate from the standard that any pair of basic types
is incompatible. I think it's very bad practice to write code which
assumes that two types are compatible without being able to prove that
they are; but I think it's entirely possible that they are in fact
compatible for a given implementation, without being able to prove that
fact from the standard.

You have this backwards. Since the Standard defines the term
'compatible', if you want to allege that int and long might
be compatible then the onus is on you to provide citations
for text in the Standard that might support that thesis.
Just saying you can't find anything that disproves it is
irrelevant; there must be positive evidence.
 
T

Tim Rentsch

James Kuyper said:
On 12/01/2011 10:15 AM, Keith Thompson wrote:
...

Mainly it's because 6.2.7p1 says "Additional rules for determining
whether two types are compatible are described in ...". It does not
describe them as rules for determining whether types are incompatible.
If the meaning is as you imply, it would have been clearer to say
something to the effect that two types not covered by any of these rules
are incompatible.

Notice how he didn't answer your first question. :)
 

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
474,082
Messages
2,570,589
Members
47,211
Latest member
Shamestone

Latest Threads

Top