Type interchange

E

Edward Rutherford

Hello:

Can the following program generate an undefined behavior?

Thank you.

#include<limits.h>
int main()
{
#if INT_MAX == LONG_MAX
long getchar();
#else
int getchar();
#endif
(void) getchar();
return 0;
}
 
J

James Kuyper

Hello:

Can the following program generate an undefined behavior?

Thank you.

#include<limits.h>
int main()
{
#if INT_MAX == LONG_MAX
long getchar();
#else
int getchar();
#endif
(void) getchar();
return 0;
}

Programs do not generate undefined behavior. "undefined behavior" does
not mean a particular kind of behavior. It means that the behavior is
NOT defined - it can be anything. Code with undefined behavior is
perfectly capable of behaving in precisely the fashion you incorrectly
thought it was required to behave.

Section 7.1.4 says "Provided that a library function can be declared
without reference to any type defined in a header, it is also
permissible to declare the function and use it without including its
associated header.", so the declaration using 'int' is permissible.
However, 6.5.2.2p9 says " If the function is defined with a type that is
not compatible with the type (of the expression) pointed to by the
expression that denotes the called function, the behavior is
undefined."

A function declaration that is not a function prototype, and is used in
a function call expression with no arguments, can be compatible with a
function defined as taking no parameters - but only if the return types
are compatible. 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.

None of those possibilities is very likely. In all probability, your
code will work fine, particularly since it discards the return value,
unused. However, the fact that it will probably work is NOT guaranteed
by the standard, the correct wording to describe that absence of a
guarantee is "the behavior of your program is undefined".
 
K

Keith Thompson

James Kuyper said:
A function declaration that is not a function prototype, and is used in
a function call expression with no arguments, can be compatible with a
function defined as taking no parameters - but only if the return types
are compatible. 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.
[...]

The standard defines the term "compatible type". It's clear from
the standard that int and long are *not* compatible types.

I think what you mean is that you can't assume that int and
long have the same representation, alignment requirements, and
so forth. But even if they do, the behavior of a program that
declares getchar() as a function returning long is undefined.
Representation is relevant to how the program is likely to behave
in practice, but it's not relevant to the question of whether the
standard define's the code's behavior.
 
J

James Kuyper

James Kuyper said:
A function declaration that is not a function prototype, and is used in
a function call expression with no arguments, can be compatible with a
function defined as taking no parameters - but only if the return types
are compatible. 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.
[...]

The standard defines the term "compatible type". It's clear from
the standard that int and long are *not* compatible types.

The standard says that two types are compatible if they're the same. The
standard has several locations where it specifies that particular pairs
of types that are not the same are still compatible. There are rules for
derived types such as arrays, functions, structures, and unions, which
allow them to be compatible only if the derivations are sufficiently
similar. However, I couldn't find any place in the normative text where
it specifies that any particular pair of basic types must be
incompatible. Footnote 35 says that char, signed char, and unsigned char
are incompatible types, but I can't find supporting normative text.

I think that a particular pair of types are in fact compatible for a
given implementation, if everything that the standard says must work
when two types are compatible, does in fact work for that pair of types,
when using that implementation, even if the standard fails to guarantee
that those types are compatible.

Could you cite any specific requirement that would be violated by an
implementation that treated 'int' and 'long' as compatible types?
 
B

Ben Bacarisse

James Kuyper said:
James Kuyper said:
A function declaration that is not a function prototype, and is used in
a function call expression with no arguments, can be compatible with a
function defined as taking no parameters - but only if the return types
are compatible. 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.
[...]

The standard defines the term "compatible type". It's clear from
the standard that int and long are *not* compatible types.

The standard says that two types are compatible if they're the same. The
standard has several locations where it specifies that particular pairs
of types that are not the same are still compatible. There are rules for
derived types such as arrays, functions, structures, and unions, which
allow them to be compatible only if the derivations are sufficiently
similar. However, I couldn't find any place in the normative text where
it specifies that any particular pair of basic types must be
incompatible. Footnote 35 says that char, signed char, and unsigned char
are incompatible types, but I can't find supporting normative text.

I think that a particular pair of types are in fact compatible for a
given implementation, if everything that the standard says must work
when two types are compatible, does in fact work for that pair of types,
when using that implementation, even if the standard fails to guarantee
that those types are compatible.

Could you cite any specific requirement that would be violated by an
implementation that treated 'int' and 'long' as compatible types?

I think that is technically impossible since the only things that can
result from incompatibility are diagnostics and undefined behaviour. If
an implementation always reported "hey, I treat int and long as
compatible" it would cover the first, and the second needs no special
treatment.

From a quality of implementation point of view, I'd want a diagnostic
here:

long l = 0;
int *ip;
ip = &l;

and I'd want it to say that the assignment was the problem due to the
incompatibility of the pointed-to types (long and int). But, as we
know, any diagnostic at all (such as the daft one above) will do.

Types are compatible or not as defined by the standard, and I think it
muddies the water to talk about types being "compatible for a given
implementation".
 
K

Keith Thompson

James Kuyper said:
James Kuyper said:
A function declaration that is not a function prototype, and is used in
a function call expression with no arguments, can be compatible with a
function defined as taking no parameters - but only if the return types
are compatible. 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.
[...]

The standard defines the term "compatible type". It's clear from
the standard that int and long are *not* compatible types.

The standard says that two types are compatible if they're the same. The
standard has several locations where it specifies that particular pairs
of types that are not the same are still compatible. There are rules for
derived types such as arrays, functions, structures, and unions, which
allow them to be compatible only if the derivations are sufficiently
similar. However, I couldn't find any place in the normative text where
it specifies that any particular pair of basic types must be
incompatible. Footnote 35 says that char, signed char, and unsigned char
are incompatible types, but I can't find supporting normative text.

As I understand it, there doesn't have to be any text that says int
and long aren't compatible. The fact that nothing says they *are*
compatible means that they aren't. The definition of "compatible
types" is spread across 4 different sections, but I believe it's
intended to be exhaustive. Otherwise, how would you demonstrate
that *any* pair of types are incompatible?
I think that a particular pair of types are in fact compatible for a
given implementation, if everything that the standard says must work
when two types are compatible, does in fact work for that pair of types,
when using that implementation, even if the standard fails to guarantee
that those types are compatible.

Could you cite any specific requirement that would be violated by an
implementation that treated 'int' and 'long' as compatible types?

If int and long are compatible (even for a particular implementation),
then int* and long* are also compatible by 6.7.5.1p2. This implies that
this:

int *pi = NULL;
long *pl = pi;
ptrdiff_t pd = pl - pi;

does not necessarily violate a constraint. I don't believe that's
the case; I believe the initialization of pl and the subtraction are
both constraint violations on all implementations. (That doesn't
prove that int and long are incompatible, it merely demonstrates
a consequence if they were.)

And for what it's worth, gcc agrees with me; it complains

warning: initialization from incompatible pointer type
error: invalid operands to binary - (have 'long int *' and 'int *')
 
J

James Kuyper

On 11/30/2011 10:17 PM, Keith Thompson wrote:
....
As I understand it, there doesn't have to be any text that says int
and long aren't compatible. The fact that nothing says they *are*
compatible means that they aren't. The definition of "compatible
types" is spread across 4 different sections, but I believe it's
intended to be exhaustive. Otherwise, how would you demonstrate
that *any* pair of types are incompatible?

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.
 
J

James Kuyper

I think that is technically impossible since the only things that can
result from incompatibility are diagnostics and undefined behaviour. If
an implementation always reported "hey, I treat int and long as
compatible" it would cover the first, and the second needs no special
treatment.

From a quality of implementation point of view, I'd want a diagnostic
here:

long l = 0;
int *ip;
ip = &l;

and I'd want it to say that the assignment was the problem due to the
incompatibility of the pointed-to types (long and int). But, as we
know, any diagnostic at all (such as the daft one above) will do.

Types are compatible or not as defined by the standard,

Yes, and the standard leaves the compatibility of types A and B
unspecified for a very large number of possible pairs of A and B,
including A='int' and B='long'.
... and I think it
muddies the water to talk about types being "compatible for a given
implementation".

It's no muddier than the fact that a given piece of code may have
undefined behavior on one implementation, and not on another, simply
because the condition that determines whether or not the behavior is
undefined depends upon something that's implementation defined:

unsigned char array[sizeof(long double)];
array[1] = 1;

Whether or not that code has undefined behavior depends upon whether or
not sizeof(long double) > 1, something that is implementation-defined.

Some people prefer to describe this by saying that if there's any
possible conforming implementation where the behavior is undefined, then
the behavior is undefined, period, even on other implementations where
the condition that makes it undefined isn't true - but I find that
interpretation incoherent.
 
K

Keith Thompson

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.

Do you believe that this:

int *ip = 0;
long *lp = ip;

is valid (not a constraint violation) on *any* conforming
implementation?

"Compatible types" is a term defined by the standard; it needn't
conform to the English meanings of the words. What is your basis
for assuming that the definition is not meant to be exhaustive, and
that types can be compatible when the standard doesn't say they are?
 
J

James Kuyper

On 12/01/2011 10:15 AM, Keith Thompson wrote:
....
Do you believe that this:

int *ip = 0;
long *lp = ip;

is valid (not a constraint violation) on *any* conforming
implementation?

"Compatible types" is a term defined by the standard; it needn't
conform to the English meanings of the words. What is your basis
for assuming that the definition is not meant to be exhaustive, and
that types can be compatible when the standard doesn't say they are?

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.
 
B

Ben Bacarisse

James Kuyper said:
On 11/30/2011 10:09 PM, Ben Bacarisse wrote:

Yes, and the standard leaves the compatibility of types A and B
unspecified for a very large number of possible pairs of A and B,
including A='int' and B='long'.

I don't think it does. For your interpretation to be correct the
standard must fail to say if int and long are the same type. The rule
is simple:

"Two types have compatible type if their types are the
same. Additional rules for determining whether two types are
compatible..."

None of the additional rules apply, so all that matters is if we can
determine whether int and long are or are not the same type. I think it
is clear that they can't ever be the same type. For example, the
conversion rank of long is greater than that of int (always). How could
two types be "the same type" if they differ in such a way? Furthermore,
we read (in 6.2.5 p4) that

There are five standard signed integer types, designated as signed
char, short int, int, long int, and long long int.[1]

This is not the natural wording to describe these types if two (or more)
of them may be the same type. Your supposed conforming implementation
would only have four standard signed integer types.

<snip>

[1] 6.7.2 p5 tell us that "long int" and "long" designate the same
type.
 
J

James Kuyper

I don't think it does. For your interpretation to be correct the
standard must fail to say if int and long are the same type. The rule
is simple:

"Two types have compatible type if their types are the
same. Additional rules for determining whether two types are
compatible..."

I never claimed that 'int' and 'long' could be compatible by reason of
being the same type. When I said that they could be compatible for a
particular implementation, I meant that this possibility exists despite
their being different types.

Some of the other rules referred to by 6.2.7p1 are worded in such a way
that any pair of types that they apply to which aren't identified as
compatible by that rule, must be incompatible. For instance, 6.7.3p9
says "For two qualified types to be compatible, both shall ..."; which
implies quite clearly that any pair of qualified types that don't match
the description following the "shall" are not compatible. However,
neither 'int' nor 'long' is a qualified type, so that doesn't apply.
Neither do any of the other rules referenced by 6.2.7p1.

6.2.7p1 itself contains no such wording. It could have been written as:
For two types to be compatible, they must either be the same type, or
covered by one of the additional rules in ...". It wasn't written that way.
 
J

James Kuyper

That way,
whether or not a program contains undefined behavior
is a property of the C program,
and not a property of the
combination of the implementation and C program.

Yes, that's a nice feature to have, but it seems incompatible with the
descriptions given in the standard of the reasons why a given piece of
code has undefined behavior.

For example, any attempt to dereference a pointer one-past-the end of an
array has undefined behavior; but in the example I gave, on any
implementation where sizeof(long double) > 1, array[1] is NOT an attempt
to dereference a pointer one past the end of the array - so how could
such a program qualify has having undefined behavior when translated by
such an implementation?
 
J

James Kuyper

The example of char being compatible
neither with signed char nor with unsigned char,
should have made it clear enough.

It makes the intent clear; but since that footnote is not normative, if
there's no supporting statement in the normative text, that merely means
that the footnote was wrong, and that the normative words of the
standard failed to implement the intent of the authors.
 
B

Ben Bacarisse

James Kuyper said:
I never claimed that 'int' and 'long' could be compatible by reason of
being the same type. When I said that they could be compatible for a
particular implementation, I meant that this possibility exists despite
their being different types.

Some of the other rules referred to by 6.2.7p1 are worded in such a way
that any pair of types that they apply to which aren't identified as
compatible by that rule, must be incompatible. For instance, 6.7.3p9
says "For two qualified types to be compatible, both shall ..."; which
implies quite clearly that any pair of qualified types that don't match
the description following the "shall" are not compatible. However,
neither 'int' nor 'long' is a qualified type, so that doesn't apply.
Neither do any of the other rules referenced by 6.2.7p1.

6.2.7p1 itself contains no such wording. It could have been written as:
For two types to be compatible, they must either be the same type, or
covered by one of the additional rules in ...". It wasn't written that
way.

Leaving open the possibility that a conforming implementation might have
"const char" and "double" as compatible types, or maybe having
"void (int)" (a function type) be compatible with _Bool *?

Your point would have been clearer had you chosen one of these more
dramatic examples.
 
J

James Kuyper

Leaving open the possibility that a conforming implementation might have
"const char" and "double" as compatible types, or maybe having
"void (int)" (a function type) be compatible with _Bool *?

Your point would have been clearer had you chosen one of these more
dramatic examples.

The OP asked about 'int' and 'long'. Also, I wasn't sure whether or it
could be proven, on purely functional grounds, that radically different
types like those are incapable of being compatible. The standard says
that a variety of things should work if two types are compatible, and
I'm not sure that they all could be made to work if, for instance, one
is a function type and the other is _Bool. It's quite clear to me that
all of those things could be made to work for int and long, since those
two types could be identically implemented.
 
K

Kaz Kylheku

That way,
whether or not a program contains undefined behavior
is a property of the C program,
and not a property of the
combination of the implementation and C program.

That's fair. A program can contain latent undefined behavior (i.e. the
potential for it) yet depend on the parameters of the implementation
such that the behavior can be avoided.
 
K

Keith Thompson

pete said:
That way,
whether or not a program contains undefined behavior
is a property of the C program,
and not a property of the
combination of the implementation and C program.

int n = 32767;
n ++;
 
K

Keith Thompson

James Kuyper said:
I never claimed that 'int' and 'long' could be compatible by reason of
being the same type. When I said that they could be compatible for a
particular implementation, I meant that this possibility exists despite
their being different types.

Some of the other rules referred to by 6.2.7p1 are worded in such a way
that any pair of types that they apply to which aren't identified as
compatible by that rule, must be incompatible. For instance, 6.7.3p9
says "For two qualified types to be compatible, both shall ..."; which
implies quite clearly that any pair of qualified types that don't match
the description following the "shall" are not compatible. However,
neither 'int' nor 'long' is a qualified type, so that doesn't apply.
Neither do any of the other rules referenced by 6.2.7p1.

6.2.7p1 itself contains no such wording. It could have been written as:
For two types to be compatible, they must either be the same type, or
covered by one of the additional rules in ...". It wasn't written that way.

We know the standard is not 100% internally consistent.

I think that 6.2.7p1 should have been written as you suggest.
I think that your suggested wording is consistent with the intent.

The consequences of another interpretation are, IMHO, quite painful:

For an implementation where int and long have the same
representation, they *may or may not* be compatible, depending on
the implementation. There is no requirement for this choice to
be documented. Which means that this:

int *ip = 0;
long *lp = ip;

either does or does not violate a constraint, depending on an
undocumented characteristic of the implementation.

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.

My position: The definition of "compatible type" is intended to
be exhaustive, even though the standard doesn't say so explicitly.
int and long are incompatible for every implementation (and therefore
int* and long* are incompatible for every implementation).
 
K

Keith Thompson

pete said:
The behavior of that code isn't limited by the standard, is it?

If INT_MAX == 32767, then its behavior is undefined. Otherwise, its
behavior is well defined (it sets n to 32768).

The point is that whether or not a program contains undefined
behavior already is "a property of the combination of the
implementation and C program".
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top