Discussion:
Call to a function
(too old to reply)
Stefan Ram
2023-09-22 12:24:28 UTC
Permalink
When "1" is cast to a function type and then this is called,
one would expect this call to have undefined behavior. But
I can only find this in the C specification:

|If a converted pointer is used to call a function whose type
|is not compatible with the referenced type, the behavior is
|undefined.

. At the address "1" there is not "a function whose type is not
compatible", but no function at all. Is "no function at all" also
"a function whose type is not compatible with the referenced type"
or is there some other part of the specification by which a call
to an address where there is no function at all has UB?

Then, when "f()" is being evaluated and there is no UB, I'd
expect the control to temporarily be transfered to f. The C
spec has a section "Function Calls" under "Postfix Operators",
but I cannot find anything there that actually says this.
Kaz Kylheku
2023-09-22 15:21:48 UTC
Permalink
Post by Stefan Ram
When "1" is cast to a function type and then this is called,
one would expect this call to have undefined behavior. But
|If a converted pointer is used to call a function whose type
|is not compatible with the referenced type, the behavior is
|undefined.
Because ISO C supports conversions between function pointer types,
above, the document is addressing what happens in the situation
when the address of a function is converted to a different pointer type,
which is then called. E.g. int puts(const char *) is misused as
a void (double) function:

void (*fptr)(double) = (void (*)(double)) puts;

So far, the behavior is defined: the conversion is valid.
The pointer could be converted to the correct type and used:

So, the above remarks make it clear that

fptr(3.14);

isn't defined.
Post by Stefan Ram
. At the address "1" there is not "a function whose type is not
compatible", but no function at all.
The conversion is not supported by ISO C, and so itself has
undefined behavior:

void (*fptr)(double) = (void (*)(double)) 1;

There is no need to make remarks about the consequences of
using a pointer which was obtained by undefined behavior.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
James Kuyper
2023-09-22 15:39:40 UTC
Permalink
Post by Kaz Kylheku
Post by Stefan Ram
When "1" is cast to a function type and then this is called,
one would expect this call to have undefined behavior. But
|If a converted pointer is used to call a function whose type
|is not compatible with the referenced type, the behavior is
|undefined.
Because ISO C supports conversions between function pointer types,
above, the document is addressing what happens in the situation
when the address of a function is converted to a different pointer type,
which is then called. E.g. int puts(const char *) is misused as
void (*fptr)(double) = (void (*)(double)) puts;
So far, the behavior is defined: the conversion is valid.
So, the above remarks make it clear that
fptr(3.14);
isn't defined.
Post by Stefan Ram
. At the address "1" there is not "a function whose type is not
compatible", but no function at all.
The conversion is not supported by ISO C, and so itself has
void (*fptr)(double) = (void (*)(double)) 1;
"An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be correctly
aligned, might not point to an entity of the referenced type, and might
be a trap representation." (6.3.2.3p5).

The conversion itself is supported, it's just not guaranteed to result
in a pointer to "an entity of the referenced type". I would expect that
calling a function through a pointer that does not point at a function
would result in undefined behavior - but I think Stefan has a point -
I'm having trouble locating the part of the standard that explicitly
says so. I suppose that such a pointer could be considered to be a trap
representation. But even so, it's what you do with the resulting pointer
value that has undefined behavior, not the conversion itself.
Kaz Kylheku
2023-09-22 16:47:16 UTC
Permalink
Post by James Kuyper
Post by Kaz Kylheku
The conversion is not supported by ISO C, and so itself has
void (*fptr)(double) = (void (*)(double)) 1;
"An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be correctly
aligned, might not point to an entity of the referenced type, and might
be a trap representation." (6.3.2.3p5).
The conversion itself is supported, it's just not guaranteed to result
in a pointer to "an entity of the referenced type". I would expect that
calling a function through a pointer that does not point at a function
would result in undefined behavior - but I think Stefan has a point -
I'm having trouble locating the part of the standard that explicitly
says so. I suppose that such a pointer could be considered to be a trap
representation. But even so, it's what you do with the resulting pointer
value that has undefined behavior, not the conversion itself.
Thanks for the correction.

You would think that the description of function call expressions covers
this. E.g. in C99 we had the wording:

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.

Whatever is at the address arising from the conversion of 1, if that is
an address, it is not a function defined with a type pointed to by the
pointer expression.

Also, this is tangentially relevant from Address and indirection
operators:

The unary * operator denotes indirection. If the operand points to a
function, the result is a function designator; if it points to an
object, the result is an lvalue designating the object. If the operand
has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an
invalid value has been assigned to the pointer, the behavior of the
unary * operator is undefined.

An invalid function pointer cannot be deferenced using * in order
to designate a function. So that rules out (*f)() from being
well-defined, which casts doubt on f().
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
James Kuyper
2023-09-23 05:26:09 UTC
Permalink
Post by Kaz Kylheku
Post by James Kuyper
Post by Kaz Kylheku
The conversion is not supported by ISO C, and so itself has
void (*fptr)(double) = (void (*)(double)) 1;
"An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be correctly
aligned, might not point to an entity of the referenced type, and might
be a trap representation." (6.3.2.3p5).
The conversion itself is supported, it's just not guaranteed to result
in a pointer to "an entity of the referenced type". I would expect that
calling a function through a pointer that does not point at a function
would result in undefined behavior - but I think Stefan has a point -
I'm having trouble locating the part of the standard that explicitly
says so. I suppose that such a pointer could be considered to be a trap
representation. But even so, it's what you do with the resulting pointer
value that has undefined behavior, not the conversion itself.
Thanks for the correction.
You would think that the description of function call expressions covers
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.
That's 6.5.2.2p9 in the latest version of the standard that I have
access to.
Post by Kaz Kylheku
Whatever is at the address arising from the conversion of 1, if that is
an address, it is not a function defined with a type pointed to by the
pointer expression.
Correct. What the pointer points at is NOT a function, and is therefore,
in particular, not a function "defined with a type that is not
compatible ...". Therefore, 6.5.2.2p9 doesn't apply, and is therefore
not the reasons the behavior is undefined.

I've come to the conclusion that it's undefined by the omission of any
applicable explicit definition of the behavior (4p2).
Tim Rentsch
2023-09-23 14:38:30 UTC
Permalink
Post by Kaz Kylheku
Post by Kaz Kylheku
The conversion is not supported by ISO C, and so itself has
void (*fptr)(double) = (void (*)(double)) 1;
"An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be correctly
aligned, might not point to an entity of the referenced type, and might
be a trap representation." (6.3.2.3p5).
The conversion itself is supported, it's just not guaranteed to result
in a pointer to "an entity of the referenced type". I would expect that
calling a function through a pointer that does not point at a function
would result in undefined behavior - but I think Stefan has a point -
I'm having trouble locating the part of the standard that explicitly
says so. I suppose that such a pointer could be considered to be a trap
representation. But even so, it's what you do with the resulting pointer
value that has undefined behavior, not the conversion itself.
Thanks for the correction.
You would think that the description of function call expressions covers
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.
Whatever is at the address arising from the conversion of 1, if that is
an address, it is not a function defined with a type pointed to by the
pointer expression.
It _could_ be a function. The mapping from integers to pointers
is implementation defined. There is no reason an implementation
couldn't define the conversion of integers to function pointers
as the nth function in the overall program, with functions being
listed alphabetically. So if a program defines a function

void aardvark( double d ){ (void)&d; }

then that program, with this definition of main(),

int main( void ){
((void (*)( double )) 1) ( 3.14 );
return 0;
}

could be a well-defined (as well as well-formed) program for that
implementation.
Keith Thompson
2023-09-22 18:54:24 UTC
Permalink
Post by Kaz Kylheku
Post by Stefan Ram
When "1" is cast to a function type and then this is called,
one would expect this call to have undefined behavior. But
|If a converted pointer is used to call a function whose type
|is not compatible with the referenced type, the behavior is
|undefined.
Because ISO C supports conversions between function pointer types,
above, the document is addressing what happens in the situation
when the address of a function is converted to a different pointer type,
which is then called. E.g. int puts(const char *) is misused as
void (*fptr)(double) = (void (*)(double)) puts;
So far, the behavior is defined: the conversion is valid.
So, the above remarks make it clear that
fptr(3.14);
isn't defined.
Post by Stefan Ram
. At the address "1" there is not "a function whose type is not
compatible", but no function at all.
The conversion is not supported by ISO C, and so itself has
void (*fptr)(double) = (void (*)(double)) 1;
The original post used "1", a string literal. You assumed it was an
integer constant in quotes. The point is the same either way, but
Stefan, it might have been clearer if you had shown sample code.
Post by Kaz Kylheku
There is no need to make remarks about the consequences of
using a pointer which was obtained by undefined behavior.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Tim Rentsch
2023-10-03 13:34:26 UTC
Permalink
Post by Keith Thompson
Post by Kaz Kylheku
Post by Stefan Ram
When "1" is cast to a function type and then this is called,
one would expect this call to have undefined behavior. But
|If a converted pointer is used to call a function whose type
|is not compatible with the referenced type, the behavior is
|undefined.
Because ISO C supports conversions between function pointer types,
above, the document is addressing what happens in the situation
when the address of a function is converted to a different pointer type,
which is then called. E.g. int puts(const char *) is misused as
void (*fptr)(double) = (void (*)(double)) puts;
So far, the behavior is defined: the conversion is valid.
So, the above remarks make it clear that
fptr(3.14);
isn't defined.
Post by Stefan Ram
. At the address "1" there is not "a function whose type is not
compatible", but no function at all.
The conversion is not supported by ISO C, and so itself has
void (*fptr)(double) = (void (*)(double)) 1;
The original post used "1", a string literal. You assumed it was an
integer constant in quotes.
I would say inferred rather than assumed. Clearly the OP was
using double quotes as a way of delimiting code, in much the same
way that some people use back tick (backward facing single
quotes); note that the original text also says "f()" to indicate
a function call, and there is no doubt that "f()" is not meant to
include the quotes as part of the code.
Post by Keith Thompson
The point is the same either way, [...]
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.

[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
Keith Thompson
2023-10-03 22:13:51 UTC
Permalink
Tim Rentsch <***@z991.linuxsc.com> writes:
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)

Concretely, I believe that this program violates no syntax error or
constraint. It includes code whose behavior would be undefined if it
were executed, but the `if (0)` prevents that.

On what basis do you think a conforming implementation may reject it?
Do you see a syntax rule or constraint that it violates? Do you see
some other basis for rejecting it?

int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Kaz Kylheku
2023-10-04 01:52:06 UTC
Permalink
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error or
constraint. It includes code whose behavior would be undefined if it
were executed, but the `if (0)` prevents that.
The problem is that the is not required to be translatable.

The code being dead inside an if (0) doesn't rescue it.

Conversion between function and object pointers isn't something that is
only undefined at run time, but otherwise translatable.
Post by Keith Thompson
On what basis do you think a conforming implementation may reject it?
On the basis that it's not able to convert between object and
function pointers, and on the basis that it only eliminates dead
code that is first generated by its translation scheme.
Post by Keith Thompson
Do you see a syntax rule or constraint that it violates? Do you see
some other basis for rejecting it?
The implementation can add its own constraint rule, and then
stop translation.

Undefined can cause the program to be terminated during translation or
execution, with or without a diagnostic.
Keith Thompson
2023-10-04 02:13:55 UTC
Permalink
Post by Kaz Kylheku
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error or
constraint. It includes code whose behavior would be undefined if it
were executed, but the `if (0)` prevents that.
The problem is that the is not required to be translatable.
You left out a word, but I disagree.
Post by Kaz Kylheku
The code being dead inside an if (0) doesn't rescue it.
Conversion between function and object pointers isn't something that is
only undefined at run time, but otherwise translatable.
The standard doesn't talk about translatability. It says what is
allowed, and defines the behavior of *some* things that are allowed.
A cast from a function pointer type to an object pointer type,
or vice versa, satisfies the constraints for a cast operator.
(Conversions between pointer types and floating-point types are
explicitly banned. Function/object pointer conversions *could*
have been.)

Perhaps the standard would be improved by making object/function pointer
conversions not involving a null pointer constant a constraint
violation. That might break some working but non-portable code that
converts between function pointers and void*. Or perhaps the standard
could be changed to state explicitly that such conversions have
undefined behavior, but they already do due to the lack of any
definition of the behavior.
Post by Kaz Kylheku
Post by Keith Thompson
On what basis do you think a conforming implementation may reject it?
On the basis that it's not able to convert between object and
function pointers, and on the basis that it only eliminates dead
code that is first generated by its translation scheme.
For most implementations, object and function pointers have the same
size and representation, and a conversion can just copy or reinterpret
the bits. For others, perhaps it could extend or truncate the result,
yielding garbage, just as (void*)(char)'x' yields garbage. Or it could
just yield a null pointer regardless of the operand. It's not plausible
that an implementation would not be *able* to perform such a conversion.
Post by Kaz Kylheku
Post by Keith Thompson
Do you see a syntax rule or constraint that it violates? Do you see
some other basis for rejecting it?
The implementation can add its own constraint rule, and then
stop translation.
What do you mean by "add its own constraint rule"? Constraints are
defined by the standard, not by an implementation. Are you suggesting
an implementation can add its own constraint for some code that the
implementer just found too difficult to handle correctly?
Post by Kaz Kylheku
Undefined can cause the program to be terminated during translation or
execution, with or without a diagnostic.
*If* the undefined behavior can occur.

int a = 1;
const int b = 0;
if (b != 0) a /= b;

A compiler can prove that the division has undefined behavior if it's
executed. Can it reject the code because of that?
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Chris M. Thomasson
2023-10-04 03:41:39 UTC
Permalink
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error or
constraint. It includes code whose behavior would be undefined if it
were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
Do you see a syntax rule or constraint that it violates? Do you see
some other basis for rejecting it?
int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
"Saved by Zero" by the Fixx? lol



;^)
Tim Rentsch
2023-10-25 03:54:54 UTC
Permalink
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error
or constraint. It includes code whose behavior would be undefined
if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
Richard Damon
2023-10-25 04:27:40 UTC
Permalink
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error
or constraint. It includes code whose behavior would be undefined
if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
No, to reject a program it must violate some constraint.

Not being 'strictly conforming' (a technical term in the Standard) is
not a requirement for a program to be accepted.

Now, there is the escape clause, that allows an implementation to fail
to actually be able to run anything but the one specified program that
meets all the minimum requirements, but that isn't grounds to 'reject'
programs.
Tim Rentsch
2023-10-29 16:01:06 UTC
Permalink
Post by Richard Damon
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error
or constraint. It includes code whose behavior would be undefined
if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
No, to reject a program it must violate some constraint.
Not being 'strictly conforming' (a technical term in the Standard)
is not a requirement for a program to be accepted.
In 4p6 the C standard says conforming implementations shall
accept any strictly conforming program. (There are some minor
differences between hosted implementations and freestanding
implementations but that's not important to the question.)
Does the C standard make any other statement about what programs
must be accepted? If so where are those statements? If not,
what is the basis for your statement that for an implementation
to reject a program the program must violate some constraint?

I'm assuming by the way that "reject" is being read as the
opposite of "accept", which is how I meant it: an implementation
rejects a program if and only if the implementation doesn't
accept the program. Come to think of it, "decline" might be a
better word choice for the opposite of "accept". Either way
though, the principle is that a program is either "accepted"
or "not accepted"; these is nothing in between.

(I should add that it is possible that a failure could occur
before an implementation could tell whether it would accept it or
not. I think it's logically consistent to count such cases as
accepted: if the program turns out to be strictly conforming,
the implementation has accepted it, and if the program turns out
to have an unskipped #error directive, the implementation has not
successfully translated it. So the requirements of the standard
are satisfied.)
Post by Richard Damon
Now, there is the escape clause, that allows an implementation to
fail to actually be able to run anything but the one specified
program that meets all the minimum requirements, but that isn't
grounds to 'reject' programs.
Accepting a program has nothing to do with actually running the
program; producing an executable is enough. Trying to produce
an executable but not succeeding because of one of the factors
mentioned in section 1 paragraph 2 still counts as accepting.

Implementation limits (as the term is used in the C standard)
fall in a different category. The standard requires an
implementation to accept a program if the program is strictly
conforming, but a program is not considered strictly conforming
if it exceeds a minimum implementation limit. Consider this
source file:

int one =
((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((
1
))))))))))))))))))))))))))))))))))))))))))))))))))
))))))))))))))))))))))))))))))))))))))))))))))))))
;

Can an implementation decline to translate this source file (and
so not accept it) and still be conforming? There are 100 nested
levels of parentheses.

Or consider this source file:

extern char big[ 2305843009213693952 ];

Can an implementation decline to translate this source file (and
so not accept it) and still be conforming? (The value of SIZE_MAX
may be assumed to be 2**64-1.)

For both of these questions, it may be assumed that the concerns
mentioned in 1p2 do not affect the answers.
Keith Thompson
2023-10-25 04:58:38 UTC
Permalink
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error
or constraint. It includes code whose behavior would be undefined
if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
Not just for that reason.

This program:

#include <stdio.h>
int main(void) {
printf("%zu\n", sizeof (int));
}

is not strictly conforming, since it depends on the
implementation-defined size of int. A hosted implementation that
rejected it would violate 4p3:

A program that is correct in all other aspects, operating on correct
data, containing unspecified behavior shall be a correct program and
act in accordance with 5.1.2.3.

(A perverse implementation could claim that it exceeds its capacity, but
I think we can ignore that.)

What is your answer to your own question?

And I hope we're not going to go back and forth too many times before
you answer mine.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Tim Rentsch
2023-11-10 11:37:31 UTC
Permalink
Post by Keith Thompson
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax
error or constraint. It includes code whose behavior would
be undefined if it were executed, but the `if (0)` prevents
that.
On what basis do you think a conforming implementation may
reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
Not just for that reason.
#include <stdio.h>
int main(void) {
printf("%zu\n", sizeof (int));
}
is not strictly conforming, since it depends on the
implementation-defined size of int. A hosted implementation
A program that is correct in all other aspects, operating
on correct data, containing unspecified behavior shall be a
correct program and act in accordance with 5.1.2.3.
We have talked before about 4p3. Your understandings of the
meaning and consequences of this sentence are mistaken, which if
you would take the time to read the published discussion notes
that led up to it being added to the standard I expect you would
realize. In any case 4p3 is moot, because we are addressing the
qualifying condition: if an implementation has a legitimate
reason not to accept a given program, then that program is not
"correct in all other aspects", and 4p3 doesn't apply.

Let's consider a concrete example. This program

#include <stdio.h>
#include <stdint.h>

extern char large[2305843009213693952];

int
main( void ){
printf( "Hello, world\n" );
printf( "\n" );
printf( "SIZE_MAX is %21zu\n", SIZE_MAX );
printf( " large has %21zu bytes\n", sizeof large );
printf( "\n" );
printf( "(... that's all, folks! ...)\n" );
return 0;
}

is, if I am not mistaken, free of any undefined behavior. It can
be compiled and run, for example by gcc on a 64-bit linux system.
Yet if we ask clang to compile it on the same system, clang gives
an error complaining that the array is too large. Note that the
same program, except with a one-smaller value of the array
dimension, can be compiled by clang without complaint, and run
successfully. Clearly the clang implementors feel that the C
standard allows them not to accept this program, and the only
reason I can see for that is that the program is not strictly
conforming, because it exceeds a minimum implementation limit.

Long story short: in how it treats this program, clang agrees
with my reading of the C standard. Incidentally, gcc has the
same behavior as clang, only with a larger value of the array
dimension.
Post by Keith Thompson
(A perverse implementation could claim that it exceeds its
capacity, but I think we can ignore that.)
Per the C standard, "capacity" applies to data-processing systems
or to processors, not implementations. Implementations have
implementation limits, also referred to in the standard as
environmental limits or translation limits.
Post by Keith Thompson
What is your answer to your own question?
The C standard requires implementations to accept any strictly
conforming program. There is no statement in the standard that
requires implementations to accept any program other than those
that are strictly conforming (except perhaps the "one program"
of 5.2.4.1 p1, but implementations are free to choose a strictly
conforming program to satisfy this requirement, so there is no
conflict there). Hence the C standard allows implementations not
to accept any program that is not strictly conforming.
Post by Keith Thompson
And I hope we're not going to go back and forth too many times
before you answer mine.
Having a shared understanding on this threshold question is a
necessary prerequisite to addressing the larger question. If you
can't or won't agree that not being strictly conforming is by
itself sufficient basis for not accepting a program, then there
seems little point in continuing, as my answer depends on that
premise for its conclusion.
Kaz Kylheku
2023-11-10 22:04:52 UTC
Permalink
Post by Tim Rentsch
Having a shared understanding on this threshold question is a
necessary prerequisite to addressing the larger question. If you
can't or won't agree that not being strictly conforming is by
itself sufficient basis for not accepting a program, then there
seems little point in continuing, as my answer depends on that
premise for its conclusion.
But the following isn't strictly confroming:

int x = 65536;

do you think that an implementation whose int is 32 bits wide has
grounds for rejecting it because it's not strictly conforming?

We generally expect correct programs that make use of
implementation-defined characteristics to be translated.

The requirement to accept strictly conforming programs does not add up
to a blanket permission to reject non-strictly conforming programs.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Tim Rentsch
2023-11-13 16:14:52 UTC
Permalink
Post by Kaz Kylheku
Post by Tim Rentsch
Having a shared understanding on this threshold question is a
necessary prerequisite to addressing the larger question. If you
can't or won't agree that not being strictly conforming is by
itself sufficient basis for not accepting a program, then there
seems little point in continuing, as my answer depends on that
premise for its conclusion.
int x = 65536;
do you think that an implementation whose int is 32 bits wide has
grounds for rejecting it because it's not strictly conforming?
We generally expect correct programs that make use of
implementation-defined characteristics to be translated.
The requirement to accept strictly conforming programs does not add up
to a blanket permission to reject non-strictly conforming programs.
I think you are in effect asking two question. The first question
is does the C standard allow non-strictly-conforming code not to be
accepted, even if the code is reasonable for the system being
targeted? As I read the C standard, the answer to that question is
an unequivocal yes.

The second question is something like, is this a good idea?

Let me give a personal answer. It doesn't bother me that the C
standard allows such programs not to be accepted (with implied
clause, by conforming implementations). I say it doesn't bother me
in the same way that the C standard requires implementations to be
able to translate and execute only one program, and still be
conforming. No compiler worth using is going to reject the
statement 'int x = 65536;' on a 32-bit platform. Sure, the C
standard allows it, but nobody would use such a compiler, and it's
so much effort to write a C compiler that no one would do that,
except maybe as a joke. If that's what they want, more power to
them - it will be funny for about 5 minutes.

On the other hand, consider this statement

long pbits = 0240404242004042424254;

This statement compiles fine on my colo server, which runs linux,
and has 64-bit longs. But if I were to compile it on a system with
32-bit longs, the compiler is within its rights to say "oops, bad
program, I'm not going to translate this garbage". Personally I
view that behavior as a positive, so I'm glad the C standard allows
it.

The C standard deliberately sets a low bar. In practice the low
bar doesn't matter at all, because compiler writers want to produce
a useful, above and beyond what the C standard might require. And
in those cases like the 'long' assignment example above, there is a
benefit to compilers having the option to turn down the program.
The key phrase there is "having the option". Different people have
different requirements and want different behaviors. It's good
that the C standard recognizes that and allows a wide range of
choices, while still setting a reasonable bar for what programs
must be accepted.
Phil Carmody
2023-11-13 19:35:42 UTC
Permalink
Post by Tim Rentsch
Post by Keith Thompson
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax
error or constraint. It includes code whose behavior would
be undefined if it were executed, but the `if (0)` prevents
that.
On what basis do you think a conforming implementation may
reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
Not just for that reason.
#include <stdio.h>
int main(void) {
printf("%zu\n", sizeof (int));
}
is not strictly conforming, since it depends on the
implementation-defined size of int. A hosted implementation
A program that is correct in all other aspects, operating
on correct data, containing unspecified behavior shall be a
correct program and act in accordance with 5.1.2.3.
We have talked before about 4p3. Your understandings of the
meaning and consequences of this sentence are mistaken, which if
you would take the time to read the published discussion notes
that led up to it being added to the standard I expect you would
realize. In any case 4p3 is moot, because we are addressing the
qualifying condition: if an implementation has a legitimate
reason not to accept a given program, then that program is not
"correct in all other aspects", and 4p3 doesn't apply.
Let's consider a concrete example. This program
#include <stdio.h>
#include <stdint.h>
extern char large[2305843009213693952];
int
main( void ){
printf( "Hello, world\n" );
printf( "\n" );
printf( "SIZE_MAX is %21zu\n", SIZE_MAX );
printf( " large has %21zu bytes\n", sizeof large );
printf( "\n" );
printf( "(... that's all, folks! ...)\n" );
return 0;
}
is, if I am not mistaken, free of any undefined behavior. It can
be compiled and run, for example by gcc on a 64-bit linux system.
Yet if we ask clang to compile it on the same system, clang gives
an error complaining that the array is too large. Note that the
same program, except with a one-smaller value of the array
dimension, can be compiled by clang without complaint, and run
successfully. Clearly the clang implementors feel that the C
standard allows them not to accept this program, and the only
reason I can see for that is that the program is not strictly
conforming, because it exceeds a minimum implementation limit.
Long story short: in how it treats this program, clang agrees
with my reading of the C standard. Incidentally, gcc has the
same behavior as clang, only with a larger value of the array
dimension.
Is your conclusion the same if the variable is scrapped and the printf
is given the type instead? clang retains its stance on the concept:

"""
***@dovespaz:~$ clang -Wall -o crap crap.c
crap.c:11:59: error: array is too large (2305843009213693952 elements)
printf( " large has %21zu bytes\n", sizeof(char[2305843009213693952]));
^~~~~~~~~~~~~~~~~~~
1 error generated.
"""

Is there an "array", /per se/? I only see a type.

clang does seem to agree with me that it's a type it's looking at, as if
we try to feed it the expression-only version of sizeof, it notices that
you've given it a type name, not an expression:

"""
***@dovespaz:~$ clang -Wall -o crap crap.c
crap.c:11:53: error: expected parentheses around type name in sizeof expression
printf( " large has %21zu bytes\n", sizeof char[2305843009213693952]);
^
( )
1 error generated.
"""

Phil
--
We are no longer hunters and nomads. No longer awed and frightened, as we have
gained some understanding of the world in which we live. As such, we can cast
aside childish remnants from the dawn of our civilization.
-- NotSanguine on SoylentNews, after Eugen Weber in /The Western Tradition/
Keith Thompson
2023-11-13 20:48:21 UTC
Permalink
[...]
Post by Phil Carmody
Post by Tim Rentsch
Long story short: in how it treats this program, clang agrees
with my reading of the C standard. Incidentally, gcc has the
same behavior as clang, only with a larger value of the array
dimension.
Is your conclusion the same if the variable is scrapped and the printf
"""
crap.c:11:59: error: array is too large (2305843009213693952 elements)
printf( " large has %21zu bytes\n", sizeof(char[2305843009213693952]));
^~~~~~~~~~~~~~~~~~~
1 error generated.
"""
Is there an "array", /per se/? I only see a type.
I'm not sure what distinction you're making.

I find it useful to think of "array" as an adjective, not a noun. Of
course there's no array *object* or array *expression* in
`sizeof(char[2305843009213693952]`. `char[2305843009213693952]` is
obviously an array *type*. There's no ambiguity here.
Post by Phil Carmody
clang does seem to agree with me that it's a type it's looking at, as if
we try to feed it the expression-only version of sizeof, it notices that
"""
crap.c:11:53: error: expected parentheses around type name in sizeof expression
printf( " large has %21zu bytes\n", sizeof char[2305843009213693952]);
^
( )
1 error generated.
"""
Yes, of course it's a type name. And?
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Tim Rentsch
2023-11-14 03:17:26 UTC
Permalink
Post by Phil Carmody
Post by Tim Rentsch
[...] Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
Post by Phil Carmody
Post by Tim Rentsch
Let's consider a concrete example. This program
#include <stdio.h>
#include <stdint.h>
extern char large[2305843009213693952];
int
main( void ){
printf( "Hello, world\n" );
printf( "\n" );
printf( "SIZE_MAX is %21zu\n", SIZE_MAX );
printf( " large has %21zu bytes\n", sizeof large );
printf( "\n" );
printf( "(... that's all, folks! ...)\n" );
return 0;
}
is, if I am not mistaken, free of any undefined behavior. It can
be compiled and run, for example by gcc on a 64-bit linux system.
Yet if we ask clang to compile it on the same system, clang gives
an error complaining that the array is too large. Note that the
same program, except with a one-smaller value of the array
dimension, can be compiled by clang without complaint, and run
successfully. Clearly the clang implementors feel that the C
standard allows them not to accept this program, and the only
reason I can see for that is that the program is not strictly
conforming, because it exceeds a minimum implementation limit.
Long story short: in how it treats this program, clang agrees
with my reading of the C standard. Incidentally, gcc has the
same behavior as clang, only with a larger value of the array
dimension.
Is your conclusion the same if the variable is scrapped and the
printf is given the type instead?
The short answer is yes.

The lower bound for SIZE_MAX is 65535. As I read the C standard,
any object or any type whose 'sizeof' is bigger than 65535 means
the program exceeds a minimum implementation limit, and is
therefore not strictly conforming. Because it is not strictly
conforming, implementations are free not to accept it.
Post by Phil Carmody
"""
crap.c:11:59: error: array is too large (2305843009213693952 elements)
printf( " large has %21zu bytes\n", sizeof(char[2305843009213693952]));
^~~~~~~~~~~~~~~~~~~
1 error generated.
"""
Is there an "array", /per se/? I only see a type.
An array type, but no array object. I think most people would say
there isn't an array, but just a type.
Post by Phil Carmody
clang does seem to agree with me that it's a type it's looking at,
[...]
Right, the operand of that sizeof is syntactically a type, not an
expression (or more specifically, what the C standard calls a "type
name").
Phil Carmody
2023-11-16 11:47:45 UTC
Permalink
Post by Tim Rentsch
Post by Phil Carmody
Post by Tim Rentsch
[...] Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
Post by Phil Carmody
Post by Tim Rentsch
Let's consider a concrete example. This program
#include <stdio.h>
#include <stdint.h>
extern char large[2305843009213693952];
int
main( void ){
printf( "Hello, world\n" );
printf( "\n" );
printf( "SIZE_MAX is %21zu\n", SIZE_MAX );
printf( " large has %21zu bytes\n", sizeof large );
printf( "\n" );
printf( "(... that's all, folks! ...)\n" );
return 0;
}
is, if I am not mistaken, free of any undefined behavior. It can
be compiled and run, for example by gcc on a 64-bit linux system.
Yet if we ask clang to compile it on the same system, clang gives
an error complaining that the array is too large. Note that the
same program, except with a one-smaller value of the array
dimension, can be compiled by clang without complaint, and run
successfully. Clearly the clang implementors feel that the C
standard allows them not to accept this program, and the only
reason I can see for that is that the program is not strictly
conforming, because it exceeds a minimum implementation limit.
Long story short: in how it treats this program, clang agrees
with my reading of the C standard. Incidentally, gcc has the
same behavior as clang, only with a larger value of the array
dimension.
Is your conclusion the same if the variable is scrapped and the
printf is given the type instead?
The short answer is yes.
The lower bound for SIZE_MAX is 65535. As I read the C standard,
any object or any type whose 'sizeof' is bigger than 65535 means
the program exceeds a minimum implementation limit, and is
therefore not strictly conforming. Because it is not strictly
conforming, implementations are free not to accept it.
There's wording in the standard to support the "object" claim, but
I'm not seeing direct support for the "type" claim. Of course, there
might be a chain of deductions that leads there.
Post by Tim Rentsch
Post by Phil Carmody
"""
crap.c:11:59: error: array is too large (2305843009213693952 elements)
printf( " large has %21zu bytes\n", sizeof(char[2305843009213693952]));
^~~~~~~~~~~~~~~~~~~
1 error generated.
"""
Is there an "array", /per se/? I only see a type.
An array type, but no array object. I think most people would say
there isn't an array, but just a type.
The standard uses the word "array" as a noun to refer to an array
object, so I was using the same usage as the standard there. And clang
was using that same word, and it was that usage of the word I was
calling into question. Given your similar view, I definitely think the
clang diagnostic is sloppily worded.
Post by Tim Rentsch
Post by Phil Carmody
clang does seem to agree with me that it's a type it's looking at,
[...]
Right, the operand of that sizeof is syntactically a type, not an
expression (or more specifically, what the C standard calls a "type
name").
Syntactically, yes.

Phil
--
We are no longer hunters and nomads. No longer awed and frightened, as we have
gained some understanding of the world in which we live. As such, we can cast
aside childish remnants from the dawn of our civilization.
-- NotSanguine on SoylentNews, after Eugen Weber in /The Western Tradition/
Tim Rentsch
2023-11-16 14:39:46 UTC
Permalink
Post by Phil Carmody
Post by Tim Rentsch
Post by Phil Carmody
Post by Tim Rentsch
[...] Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
Post by Phil Carmody
Post by Tim Rentsch
Let's consider a concrete example. This program
#include <stdio.h>
#include <stdint.h>
extern char large[2305843009213693952];
int
main( void ){
printf( "Hello, world\n" );
printf( "\n" );
printf( "SIZE_MAX is %21zu\n", SIZE_MAX );
printf( " large has %21zu bytes\n", sizeof large );
printf( "\n" );
printf( "(... that's all, folks! ...)\n" );
return 0;
}
is, if I am not mistaken, free of any undefined behavior. It can
be compiled and run, for example by gcc on a 64-bit linux system.
Yet if we ask clang to compile it on the same system, clang gives
an error complaining that the array is too large. Note that the
same program, except with a one-smaller value of the array
dimension, can be compiled by clang without complaint, and run
successfully. Clearly the clang implementors feel that the C
standard allows them not to accept this program, and the only
reason I can see for that is that the program is not strictly
conforming, because it exceeds a minimum implementation limit.
Long story short: in how it treats this program, clang agrees
with my reading of the C standard. Incidentally, gcc has the
same behavior as clang, only with a larger value of the array
dimension.
Is your conclusion the same if the variable is scrapped and the
printf is given the type instead?
The short answer is yes.
The lower bound for SIZE_MAX is 65535. As I read the C standard,
any object or any type whose 'sizeof' is bigger than 65535 means
the program exceeds a minimum implementation limit, and is
therefore not strictly conforming. Because it is not strictly
conforming, implementations are free not to accept it.
There's wording in the standard to support the "object" claim, but
I'm not seeing direct support for the "type" claim. Of course, there
might be a chain of deductions that leads there.
Here is my reasoning.

The sizeof operator works on both expressions (including expressions
that designate objects) and type names.

The result of sizeof has type size_t.

In <stdint.h>, the preprocessor symbol SIZE_MAX gives the maximum
value (for that implementation) of a value of type size_t. SIZE_MAX
has a lower bound of 65535 (a minimum implementation limit). So any
type whose size exceeds this value is violating the restriction that
strictly conforming programs not exceed a minimum implementation
limit. (Sorry if that was belaboring the obvious.)

Or, here is another way to look at it.

Implemenations are allowed to have size_t be a 16-bit type. If we
wanted to compile a declaration like

struct foo { char a[20000], b[20000], c[20000], d[20000]; };

on such an implementation, the implementation would have to refuse
the program, because the size cannot be represented in a 16-bit
size_t (types have sizes even if they are never an operand of a
sizeof expressions). Strictly conforming programs are intended to
be maximally portable. Because a program with a type whose size
is more than 65535 cannot be accepted by an implementation with
a 16-bit size_t, the program must not be strictly conforming.
Post by Phil Carmody
Post by Tim Rentsch
Post by Phil Carmody
"""
crap.c:11:59: error: array is too large (2305843009213693952 elements)
printf( " large has %21zu bytes\n", sizeof(char[2305843009213693952]));
^~~~~~~~~~~~~~~~~~~
1 error generated.
"""
Is there an "array", /per se/? I only see a type.
An array type, but no array object. I think most people would say
there isn't an array, but just a type.
The standard uses the word "array" as a noun to refer to an array
object, so I was using the same usage as the standard there.
Looking through the standard, I see that the word array is used both
as an adjective and as a noun.
Post by Phil Carmody
And clang
was using that same word, and it was that usage of the word I was
calling into question. Given your similar view, I definitely think the
clang diagnostic is sloppily worded.
I am shocked, shocked to discover that a C compiler has a sloppily
worded diagnostic message.

But I agree with your assessment. :)
Phil Carmody
2023-11-20 23:29:41 UTC
Permalink
Post by Tim Rentsch
Post by Phil Carmody
There's wording in the standard to support the "object" claim, but
I'm not seeing direct support for the "type" claim. Of course, there
might be a chain of deductions that leads there.
Here is my reasoning.
The sizeof operator works on both expressions (including expressions
that designate objects) and type names.
The result of sizeof has type size_t.
In <stdint.h>, the preprocessor symbol SIZE_MAX gives the maximum
value (for that implementation) of a value of type size_t. SIZE_MAX
has a lower bound of 65535 (a minimum implementation limit). So any
type whose size exceeds this value is violating the restriction that
strictly conforming programs not exceed a minimum implementation
limit. (Sorry if that was belaboring the obvious.)
Yup, that has no holes, AFAICS.
Post by Tim Rentsch
Post by Phil Carmody
Post by Tim Rentsch
Post by Phil Carmody
"""
crap.c:11:59: error: array is too large (2305843009213693952 elements)
printf( " large has %21zu bytes\n", sizeof(char[2305843009213693952]));
^~~~~~~~~~~~~~~~~~~
1 error generated.
"""
Is there an "array", /per se/? I only see a type.
An array type, but no array object. I think most people would say
there isn't an array, but just a type.
The standard uses the word "array" as a noun to refer to an array
object, so I was using the same usage as the standard there.
Looking through the standard, I see that the word array is used both
as an adjective and as a noun.
I only looked through about a third of 1570, but I only saw noun uses.
I saw noun+noun compound nouns, with the first noun being "array", but
that doesn't make the word an adjective.

An actual adjectival use could be:
*The object is array.
(as per "The bus is red.") the likes of which I couldn't find.

Yes, this is more a.u.e. than c.l.c.

Phil
--
We are no longer hunters and nomads. No longer awed and frightened, as we have
gained some understanding of the world in which we live. As such, we can cast
aside childish remnants from the dawn of our civilization.
-- NotSanguine on SoylentNews, after Eugen Weber in /The Western Tradition/
James Kuyper
2023-11-22 04:21:41 UTC
Permalink
...
Post by Phil Carmody
Post by Tim Rentsch
Looking through the standard, I see that the word array is used both
as an adjective and as a noun.
I only looked through about a third of 1570, but I only saw noun uses.
I saw noun+noun compound nouns, with the first noun being "array", but
that doesn't make the word an adjective.
In such usages, "array" is technically referred to as a noun adjunct,
attributive noun, qualifying noun, noun (pre)modifier, or apposite noun
- if there are subtle difference between the meanings of those phrases,
Wikipedia fails to explain them. Like an adjective, such a noun is
defined as modifying the following noun, rather than serving as a noun
in it's own right.

I was completely unaware of any of those terms until I studied what
Wikipedia has to say on the issue just now. For most of my life, that is
precisely what I have described as "a noun serving as an adjective",
because at some point someone taught me to refer to it that way. I
suspect that this is precisely what Tim was referring to. Apparently, a
more correct way to to say what he said would be "Looking through the
standard, I see that the word array is used both to modify other nouns,
and as a noun in its own right."

It doesn't matter what grammar term we use to describe such things.
What's important is that we agree on what the meaning is. A train
station is a station for trains - but it is not in any sense itself a
train. Similarly, an array type is a type that describes an array, but
is not in any sense itself an array. Whether we refer to "array" in the
term "array type" as a noun acting as a verb, or as a noun object is
unimportant.
Tim Rentsch
2023-12-24 19:12:16 UTC
Permalink
[...]
Post by Phil Carmody
Post by Tim Rentsch
Post by Phil Carmody
The standard uses the word "array" as a noun to refer to an array
object, so I was using the same usage as the standard there.
Looking through the standard, I see that the word array is used both
as an adjective and as a noun.
I only looked through about a third of 1570, but I only saw noun
uses. I saw noun+noun compound nouns, with the first noun being
"array", but that doesn't make the word an adjective.
I concede your point of grammar (noting that there is some
specialized terminology related to noun+noun phrases, but
I won't quibble about that).

That said, your earlier statement "The standard uses the word
'array' as a noun to refer to an array object" isn't exactly right
either, as for example the phrase "array type". The word array is
functioning as a modifier, whether we want to call it a noun or an
adjective, and it does not refer to any array object. Furthermore
using an array type (in a way that is well-defined in C) doesn't
necessarily mean that that there is an array object somewhere. I
think that is the key point here.
Post by Phil Carmody
*The object is array.
(as per "The bus is red.") the likes of which I couldn't find.
Not all adjectives are usable in a "The bus is red" construction.
Many and probabaly even most are, but not all are.
Post by Phil Carmody
Yes, this is more a.u.e. than c.l.c.
Noted. I have tried to focus on the C aspects in my response.
James Kuyper
2023-10-25 05:13:31 UTC
Permalink
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error
or constraint. It includes code whose behavior would be undefined
if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
"A strictly conforming program shall use only those features of the
language and library specified in this International Standard.3) It
shall not produce output dependent on any unspecified, undefined, or
implementation-defined behavior, and shall not exceed any minimum
implementation limit." (4p5)

DR 109 addresses a function named foo() which contains the line "1/0;",
but there was no guarantee that the function would ever be called - in
fact, the function definition was the only part of the program mentioned
in the DR. The committee ruled that:

"... if every possible execution of a given program would result in
undefined behavior, the given program is not strictly conforming.
A conforming implementation must not fail to translate a strictly
conforming program simply because some possible execution of that
program would result in undefined behavior. Because foo might never be
called, the example given must be successfully translated by a
conforming implementation. "

In DR 109, the program was not guaranteed to execute 1/0. The program
that Keith provided (which you snipped) is much safer - it uses an if(0)
to guarantee that the code that might have undefined behavior will NOT
be executed. If the DR109 code doesn't prevent the program from being
strictly conforming, this certainly should not.

The standard never talks about rejection. It requires that "A conforming
hosted implementation shall accept any strictly conforming program.". No
such requirement applies to any program which is not strictly
conforming. (4p6)

Therefore, this code must be accepted.
Kaz Kylheku
2023-10-25 06:46:04 UTC
Permalink
Post by James Kuyper
The standard never talks about rejection. It requires that "A conforming
hosted implementation shall accept any strictly conforming program.". No
such requirement applies to any program which is not strictly
conforming. (4p6)
Therefore, this code must be accepted.
What does "accept" mean? I don't see it defined. If it means
"successfully translate and execute", then what is the point of the
requirement that a conforming implementaton must successfully translate
and execute "a" program that contains an instance of every limit? If
"accept" means "translate and execute", then the above quoted
requirement means that the implementation must accept every possible
such program, not just "a" program.

Might "accept" might mean, roughly, parse the syntax, check constraints
and report no issues that would stop further translation?

Or possibly, successfully translate and link the program, without
assurance that it can be executed?

The thing is, if it can't execute, that's as good as rejected.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
James Kuyper
2023-10-26 03:51:27 UTC
Permalink
Post by James Kuyper
The standard never talks about rejection. It requires that "A conforming
hosted implementation shall accept any strictly conforming program.". No
such requirement applies to any program which is not strictly
conforming. (4p6)
Therefore, this code must be accepted.
What does "accept" mean? I don't see it defined. ...
Correct. In the absence of a standard-defined meaning, we're left with
the ordinary English meaning of the word. Since I can accept a gift
without, in any way, being obligated to do anything with it, I've argued
that a implementation could meet that requirement by issuing the message
"Program accepted", and doing nothing else with it. The message, not
being a mandatory diagnostic, is optional.

The ordinary English meaning of accept is opposite to the meaning of
reject (which is neither defined nor even used in the standard). As
such, citing the text requiring strictly conforming code to be accepted,
is as close as I can come to supporting Keith's skepticism about a
conforming implementation being allowed to reject this code.
... If it means
"successfully translate and execute", then what is the point of the
requirement that a conforming implementaton must successfully translate
and execute "a" program that contains an instance of every limit?
I fully agree. Whatever "accept" means, it must be something less strong
than "translate and execute".

...
Might "accept" might mean, roughly, parse the syntax, check constraints
No, not entirely. A minimally-conforming implementation need only
determine that no #error directive survives conditional compilation,
something that only requires processing through translation phase 4. It
can meet the requirement by accepting all code that passes that check,
without further analysis (unless it identifies the "one program" that
it's required to do more with).
and report no issues that would stop further translation?
Everything that would render such a report mandatory would also prevent
the code from qualifying as strictly conforming.
Or possibly, successfully translate and link the program, without
assurance that it can be executed?
Each implementation is required to translate and execute only one
program. That alone would be sufficient to render it unlikely that any
particular program must be executed, but the "one program" that must be
translated and executed has to meet minimum complexity requirements that
render it EXTREMELY unlikely to be the same as any particular program
that you actually want executed.
The thing is, if it can't execute, that's as good as rejected.
Users are what requires an implementation of C to do anything useful;
the standard does not. They won't use one that doesn't. The C standard
only determines which useful things it should do, if it chooses to do them.
Keith Thompson
2023-10-26 07:10:22 UTC
Permalink
Post by James Kuyper
Post by James Kuyper
The standard never talks about rejection. It requires that "A conforming
hosted implementation shall accept any strictly conforming program.". No
such requirement applies to any program which is not strictly
conforming. (4p6)
Therefore, this code must be accepted.
What does "accept" mean? I don't see it defined. ...
Correct. In the absence of a standard-defined meaning, we're left with
the ordinary English meaning of the word. Since I can accept a gift
without, in any way, being obligated to do anything with it, I've argued
that a implementation could meet that requirement by issuing the message
"Program accepted", and doing nothing else with it. The message, not
being a mandatory diagnostic, is optional.
The ordinary English meaning of accept is opposite to the meaning of
reject (which is neither defined nor even used in the standard). As
such, citing the text requiring strictly conforming code to be accepted,
is as close as I can come to supporting Keith's skepticism about a
conforming implementation being allowed to reject this code.
... If it means
"successfully translate and execute", then what is the point of the
requirement that a conforming implementaton must successfully translate
and execute "a" program that contains an instance of every limit?
I fully agree. Whatever "accept" means, it must be something less strong
than "translate and execute".
I don't agree (though I do agree that the standard is unclear on what it
means).

I don't think that printing "Program accepted" and doing nothing else
would satisfy the standard's requirements. Why would the standard have
such a completely useless requirement?

I suggest that "accept" *does* (or at least should) mean "successfully
translate and execute".

The standard does not specify "the size or complexity of a program and
its data that will exceed the capacity of any specific data-processing
system or the capacity of a particular processor" (Scope, section 1
paragraph 2) -- so *some* programs will exceed an implementation's
capacity.

5.2.4.1 says that "The implementation shall be able to translate and
execute at least one program that contains at least one instance of
every one of the following limits". That imposes a restriction on an
implementation's capacity limits. (And it does it in a way that's
straightforwardly specified, but such that the easiest non-perverse way
to conform is to have *reasonable* capacity limits.)

I suggest that the requirements in section 4, Conformance, are all
subject to any capacity limits (the ones the standard doesn't cover).
That includes 4p3:

A program that is correct in all other aspects, operating on correct
data, containing unspecified behavior shall be a correct program and
act in accordance with 5.1.2.3.

and 4p6:

The two forms of *conforming implementation* are hosted and
freestanding. A *conforming hosted implementation* shall accept any
strictly conforming program. A *conforming freestanding
implementation* shall accept [snip]

My reading of all that is that a conforming implementation must
translate and execute any *correct* program and any *strictly
conforming* program **that does not exceed its capacity limits**.
Capacity limits are a valid excuse to:
- reject
- not accept
- fail to translate
- fail to execute
any program other than the "one program" of 5.2.4.1.

The program I posted upthread was:

int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}

I assert that a conforming hosted implementation must successfully
translate and execute it (it has no visible runtime behavior other
than returning a successful status) subject to capacity limits.
An implementation is no more or less permitted to reject it than
it is to reject "hello, world".

[...]
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Kaz Kylheku
2023-10-26 20:40:33 UTC
Permalink
Post by Keith Thompson
I don't think that printing "Program accepted" and doing nothing else
would satisfy the standard's requirements. Why would the standard have
such a completely useless requirement?
Because it forgot to define what "accept" means. Stuff happens!

ISO 2382-1 1993 doesn't define "accept" but uses the word in a way that
means receiving input before processing it:

01.03.05
analog computer
A computer whose operations are analogous to
the behaviour of another system and that accepts,
processes, and produces analog data.

To accept an input in this sense, all you have to do is buffer it and
generate any required acknowledgement signal, and then throw it away.

A definition of "accept" that is compatible with everything else in the
standard is "do not abruptly terminate without having completed
translation, and combination of translation units into a program,
regardless of the issuance of any diagnostics".

Now the host environment where translated programs execute is usually
not considered part of the implementation. The implementation may add
something to the program (like a library) but host environments exist
independently of the C language. Some host environments are produced
using tools having nothing to do with C, but are targeted by C
implementations.

The limits in what program may be executed, having been translated and
linked, are not coming from the C implementation.

Therefore, it is not reasonable to have requirements that the
implementaton must execute a program, since there are limitations in
the host environment to which the ISO C document doesn't speak,
and which the implementors may not be able to do anything about.

A C program could be compiled for a family of host systems. Some models
or installations of them may be equipped without enough resources to run
it, others not.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Keith Thompson
2023-10-26 23:27:05 UTC
Permalink
Post by Kaz Kylheku
Post by Keith Thompson
I don't think that printing "Program accepted" and doing nothing else
would satisfy the standard's requirements. Why would the standard have
such a completely useless requirement?
Because it forgot to define what "accept" means. Stuff happens!
Yes, they forgot to define what "accept" means. I'm expressing skepticism
that they intended to define it in such a useless manner.
Post by Kaz Kylheku
ISO 2382-1 1993 doesn't define "accept" but uses the word in a way that
01.03.05
analog computer
A computer whose operations are analogous to
the behaviour of another system and that accepts,
processes, and produces analog data.
To accept an input in this sense, all you have to do is buffer it and
generate any required acknowledgement signal, and then throw it away.
A definition of "accept" that is compatible with everything else in the
standard is "do not abruptly terminate without having completed
translation, and combination of translation units into a program,
regardless of the issuance of any diagnostics".
That sounds about right -- and is not consistent with printing "Program
accepted" and doing nothing else.
Post by Kaz Kylheku
Now the host environment where translated programs execute is usually
not considered part of the implementation. The implementation may add
something to the program (like a library) but host environments exist
independently of the C language. Some host environments are produced
using tools having nothing to do with C, but are targeted by C
implementations.
Perhaps not part of the implementation, but it's within the scope of the
standard.

Section 5 (Environment):

An implementation translates C source files and executes C
programs in two data-processing-system environments, which
will be called the *translation environment* and the *execution
environment* in this International Standard. Their characteristics
define and constrain the results of executing conforming C
programs constructed according to the syntactic and semantic
rules for conforming implementations.
Post by Kaz Kylheku
The limits in what program may be executed, having been translated and
linked, are not coming from the C implementation.
Therefore, it is not reasonable to have requirements that the
implementaton must execute a program, since there are limitations in
the host environment to which the ISO C document doesn't speak,
and which the implementors may not be able to do anything about.
Much of the standard defines the execution-time behavior of programs.
There is no such behavior without a working execution environment.
If `puts("hello")` prints "good-bye" because of something in the
environment, the implementation is non-conforming because it failed
to arrange for the right thing to happen at run time.
Post by Kaz Kylheku
A C program could be compiled for a family of host systems. Some models
or installations of them may be equipped without enough resources to run
it, others not.
True. I'd say that there has to be at least one target system that can
execute the program for the implementation to be conforming.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
James Kuyper
2023-10-27 05:42:02 UTC
Permalink
...
Post by Keith Thompson
Post by James Kuyper
... If it means
"successfully translate and execute", then what is the point of the
requirement that a conforming implementaton must successfully translate
and execute "a" program that contains an instance of every limit?
I fully agree. Whatever "accept" means, it must be something less strong
than "translate and execute".
I don't agree (though I do agree that the standard is unclear on what it
means).
I don't think that printing "Program accepted" and doing nothing else
would satisfy the standard's requirements. Why would the standard have
such a completely useless requirement?
It has a completely useless definition of "conforming". It has 5.2.4.1,
cited by you below, which is flawed in a way that renders it essentially
useless. Why shouldn't it have other useless clauses? The committee can
make mistakes, and has even doubled down on many of them.

It has been decades since the first time I saw someone point out that
"accept" is not defined by the standard, with active committee members
participating in the discussion. I have to accept that the committee
does not feel that this is a flaw urgently in need of correction - which
I consider a mistake by the committee.
Post by Keith Thompson
I suggest that "accept" *does* (or at least should) mean "successfully
translate and execute".
If the "one program" for a given implementation of C is strictly
conforming, that would render 5.2.4.1 redundant.
If the "one program" is not strictly conforming, then 5.2.4.1 serves
merely to mandate something for one program which was not strictly
conforming, which was already mandated for all strictly conforming ones.

The combination of those clauses makes sense to me only if "accept" were
meant to be somewhat less strong of a requirement than "successfully
translate and execute".
Post by Keith Thompson
5.2.4.1 says that "The implementation shall be able to translate and
execute at least one program that contains at least one instance of
every one of the following limits". That imposes a restriction on an
implementation's capacity limits. (And it does it in a way that's
straightforwardly specified, but such that the easiest non-perverse way
to conform is to have *reasonable* capacity limits.)
It does so by mandating the existence of only one program that must be
constrained by those requirements, which are minima for that program,
not maxima. That's a lousy way to specify maxima that were intended to
apply to programs other than the "one program". I agree that this seems
to have been the intent, but it fails to express that intent.

...
Post by Keith Thompson
My reading of all that is that a conforming implementation must
translate and execute any *correct* program and any *strictly
conforming* program **that does not exceed its capacity limits**.
- reject
- not accept
- fail to translate
- fail to execute
any program other than the "one program" of 5.2.4.1.
And as a result of the way that the standard expressed those
requirements, an implementation could have capacity limits that prevent
it from translating and executing any program other than the "one
program" that it's required to translate and execute, without failing to
conform to the standard.
Post by Keith Thompson
int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
I assert that a conforming hosted implementation must successfully
translate and execute it (it has no visible runtime behavior other
than returning a successful status) subject to capacity limits.
An implementation is no more or less permitted to reject it than
it is to reject "hello, world".
I can agree that a conforming implementation is required to accept that
code, whatever it is that "accept" means - your conclusion follows from
your beliefs about what "accept" means. For myself, I believe that after
accepting such code, a fully conforming implementation can then fail to
translate and execute that code, due to it exceeding that
implementation's capacity.
Keith Thompson
2023-10-27 08:53:25 UTC
Permalink
Post by James Kuyper
...
Post by Keith Thompson
Post by James Kuyper
... If it means
"successfully translate and execute", then what is the point of the
requirement that a conforming implementaton must successfully translate
and execute "a" program that contains an instance of every limit?
I fully agree. Whatever "accept" means, it must be something less strong
than "translate and execute".
I don't agree (though I do agree that the standard is unclear on what it
means).
I don't think that printing "Program accepted" and doing nothing else
would satisfy the standard's requirements. Why would the standard have
such a completely useless requirement?
It has a completely useless definition of "conforming". It has 5.2.4.1,
cited by you below, which is flawed in a way that renders it essentially
useless. Why shouldn't it have other useless clauses? The committee can
make mistakes, and has even doubled down on many of them.
"Conforming" (defined as "acceptable to a conforming implementation") is
not particularly useful, but it's a definition of a term, not a
requirement. I don't think anything in the standard depends on any
program being "conforming".

I don't agree that 5.2.4.1 is as flawed as you suggest it is.
Post by James Kuyper
It has been decades since the first time I saw someone point out that
"accept" is not defined by the standard, with active committee members
participating in the discussion. I have to accept that the committee
does not feel that this is a flaw urgently in need of correction - which
I consider a mistake by the committee.
Post by Keith Thompson
I suggest that "accept" *does* (or at least should) mean "successfully
translate and execute".
If the "one program" for a given implementation of C is strictly
conforming, that would render 5.2.4.1 redundant.
If the "one program" is not strictly conforming, then 5.2.4.1 serves
merely to mandate something for one program which was not strictly
conforming, which was already mandated for all strictly conforming ones.
My interpretation (perhaps not fully supported by the wording of the
standard) is that the requirement to accept any strictly conforming
program is conditional on that program not exceeding the
implementation's capacity. You could construct a strictly conforming
program (which does not exceed any of the minimum translation limits)
that's so huge and complex that an implementation could not reasonably
be expected to translate it. A compiler might run out of memory trying
to compile it, and either crash or die with an error message before it
could even determine that the program is strictly conforming. I don't
think it's reasonable to call that "accepting" the program.

The "one program" must be one that does not exceed the implementation's
capacity. It's a way of strongly encouraging implementers to have
reasonable capacity limits.
Post by James Kuyper
The combination of those clauses makes sense to me only if "accept" were
meant to be somewhat less strong of a requirement than "successfully
translate and execute".
Post by Keith Thompson
5.2.4.1 says that "The implementation shall be able to translate and
execute at least one program that contains at least one instance of
every one of the following limits". That imposes a restriction on an
implementation's capacity limits. (And it does it in a way that's
straightforwardly specified, but such that the easiest non-perverse way
to conform is to have *reasonable* capacity limits.)
It does so by mandating the existence of only one program that must be
constrained by those requirements, which are minima for that program,
not maxima. That's a lousy way to specify maxima that were intended to
apply to programs other than the "one program". I agree that this seems
to have been the intent, but it fails to express that intent.
...
Post by Keith Thompson
My reading of all that is that a conforming implementation must
translate and execute any *correct* program and any *strictly
conforming* program **that does not exceed its capacity limits**.
- reject
- not accept
- fail to translate
- fail to execute
any program other than the "one program" of 5.2.4.1.
And as a result of the way that the standard expressed those
requirements, an implementation could have capacity limits that prevent
it from translating and executing any program other than the "one
program" that it's required to translate and execute, without failing to
conform to the standard.
Yes, of course. A very perverse implementation could detect the "one
program" and generate code for it (the "one program" might print "hello,
world", for example), and print a fatal capacity error for anything
else. I've been tempted to create such an implementation myself.
(Yes, I have odd ideas of fun.)

But it would have to be a deliberately useless implementation. If
you're trying to create a *useful* implementation, the easiest way to
satisfy the "one program" requirement is to have reasonably large
capacity limits, for example by having all compiler internal data
structures be dynamic so the capacity is exceeded only when there's not
enough memory.
Post by James Kuyper
Post by Keith Thompson
int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
I assert that a conforming hosted implementation must successfully
translate and execute it (it has no visible runtime behavior other
than returning a successful status) subject to capacity limits.
An implementation is no more or less permitted to reject it than
it is to reject "hello, world".
I can agree that a conforming implementation is required to accept that
code, whatever it is that "accept" means - your conclusion follows from
your beliefs about what "accept" means. For myself, I believe that after
accepting such code, a fully conforming implementation can then fail to
translate and execute that code, due to it exceeding that
implementation's capacity.
Sure, capacity is not clearly defined, and is explicitly outside the
scope of the standard. But no real compiler is going to run out of
resources trying to compile that 8-line program. My whole point is
that, in my opinion, a compiler may not use the conversion from int* to
func* as a justification for rejecting it. Capacity limits, interesting
as they are, are a distraction from the point I was making with that
program.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
James Kuyper
2023-10-27 16:35:23 UTC
Permalink
...
Post by Keith Thompson
Post by James Kuyper
And as a result of the way that the standard expressed those
requirements, an implementation could have capacity limits that prevent
it from translating and executing any program other than the "one
program" that it's required to translate and execute, without failing to
conform to the standard.
Yes, of course. A very perverse implementation could detect the "one
program" and generate code for it (the "one program" might print "hello,
world", for example), and print a fatal capacity error for anything
else. I've been tempted to create such an implementation myself.
(Yes, I have odd ideas of fun.)
Now that I'm retired, I've actually started working on such an
implementation. I call it MinimalC. I decided to make it a more
interesting project by including support for multiple different versions
of the C standard, starting with C90. I decided to use it as an excuse
to get familiar with several development tools that I didn't have much
time to learn while I was employed, such as flex, bison, Doxygen,
BibTeX, and the Eclipse IDE, as well as the new Unicode support provided
by recent versions of C and C++. While I've used SVN before, this is the
first time I've been the person who sets up SVN. I'm learning a lot. I'm
also getting stuck a lot - but nothing depends upon me meeting a
schedule anymore.
However, due to taking care of twin 8-year olds and being a househusband
while my (younger) wife continues working, I've found that my retirement
doesn't leave me with anywhere near as much free time as I'd hoped.
Don't hold your breath waiting for me to release MinimalC.
Vir Campestris
2023-10-29 21:05:35 UTC
Permalink
Post by James Kuyper
However, due to taking care of twin 8-year olds and being a househusband
while my (younger) wife continues working, I've found that my retirement
doesn't leave me with anywhere near as much free time as I'd hoped.
Don't hold your breath waiting for me to release MinimalC.
James, my wife quit before I did (and acted as project manager for our
house, which needed lots of work. Our house is older than your country).

We have no grandchildren.

And yet still I don't have free time. I've got a lot fitter though, and
the garden is much neater!

Andy
Tim Rentsch
2023-11-09 21:25:22 UTC
Permalink
Post by Keith Thompson
The standard never talks about rejection. It requires that "A
conforming hosted implementation shall accept any strictly
conforming program.". No such requirement applies to any program
which is not strictly conforming. (4p6) [...]
What does "accept" mean? I don't see it defined. ...
I would like to address the meaning of "accept". Please bear with
me as I work through excerpts from two postings, the second one
the grandchild of this first one. (Some text may be pruned or
reordered, but I have made an effort to keep the attributions
intact and correct.)

The term "accept" is commonly used in the field of automata theory.
It means something like "to admit as satisfying the criteria" of the
automata in question. For example, if an automata to determine
primality accepts an input it means the input is a prime number.
This sense of accept is close to what the C standard means.
Post by Keith Thompson
Correct. In the absence of a standard-defined meaning, we're left
with the ordinary English meaning of the word. Since I can accept
a gift without, in any way, being obligated to do anything with it,
I've argued that a implementation could meet that requirement by
issuing the message "Program accepted", and doing nothing else with
it. The message, not being a mandatory diagnostic, is optional.
It's more likely that the ISO C committee meant "accept" in some
more technical sense (probably similar to the usage explained above)
than in the sense of an ordinary English meaning.
Post by Keith Thompson
The ordinary English meaning of accept is opposite to the meaning of
reject (which is neither defined nor even used in the standard). As
such, citing the text requiring strictly conforming code to be
accepted, is as close as I can come to supporting Keith's skepticism
about a conforming implementation being allowed to reject this code.
The word "accept" has lots of antonyms: deny, veto, snub, shun,
spurn, demur, reject, refuse, resist, rebuff, oppose, decline,
dismiss, disallow, renounce, repudiate, blackball, and disapprove
are just some of the dozens of easily findable antonyms. Looking
over the list, I think a better opposite word is not "reject" but
"dismiss", which suggests these definitions:

accept: admit as suitable for translation (by this implementation)
(opposite): dismiss as unsuitable for translation (by this implementation)

These definitions provide a good fit to how the word "accept" is
used in the C standard, including other parts of the standard that
talk about translation or execution but don't use the word "accept"
explicitly.

Note in particular that "accept" is a prerequisite for translation,
not the other way around, which is consistent with how the standard
uses these terms.
Post by Keith Thompson
[it seems inconsistent for "accept" to mean "successfully
translate and execute"]
I fully agree. Whatever "accept" means, it must be something less
strong than "translate and execute".
I don't agree (though I do agree that the standard is unclear on
what it means).
I don't think that printing "Program accepted" and doing nothing
else would satisfy the standard's requirements. [...]
This statement implicitly offers a false dichotomy. What "accept"
means can be less stringent than "translate and execute" but still
imply more than just printing a "program accepted" message.
Post by Keith Thompson
I suggest that "accept" *does* (or at least should) mean
"successfully translate and execute".
Obviously this statement cannot be right. For one thing, it's not
possible. Perhaps more to the point, it is well established that
the C standard allows an implementation to be conforming even if
it can translate and execute only one program. There were heated
debates on the topic when the original C standard was formed. The
meaning of "accept" needs to be determined by what the standard
actually says about its requirements, not what we might want it to
say.
Post by Keith Thompson
The standard does not specify "the size or complexity of a program
and its data that will exceed the capacity of any specific
data-processing system or the capacity of a particular processor"
(Scope, section 1 paragraph 2) -- so *some* programs will exceed
an implementation's capacity.
5.2.4.1 says that "The implementation shall be able to translate
and execute at least one program that contains at least one
instance of every one of the following limits". That imposes a
restriction on an implementation's capacity limits. (And it does
it in a way that's straightforwardly specified, but such that the
easiest non-perverse way to conform is to have *reasonable*
capacity limits.)
These paragraphs confuse several distinct notions and entities. The
term "limits" is used (in many places) in relation to implementations.
The term "capacity" appears in the standard exactly twice, once in
relation to a data-processing system, and once in relation to a
processor. The compound term "capacity limits" is never used. The
last item in 1p2

[the standard does not specify] all minimal requirements of a
data-processing system that is capable of supporting a conforming
implementation.

makes it clear that "implementations" and "data-processing systems"
are two different things (and of course processors occupy yet a third
category). This distinction makes sense: if I run gcc on my colo
server (which has 1GB of RAM), or if I run gcc on my compute server
(which has 128GB of RAM), in both cases the implementation is gcc (and
glibc or similar); it is only the underlying data-processing system
that is different.
Post by Keith Thompson
[...]
My reading of all that is that a conforming implementation must
translate and execute any *correct* program and any *strictly
conforming* program **that does not exceed its capacity limits**.
Implementations have implementation limits (described in many places
in the standard). Data-processing systems (and also processors) have
capacities, about which the standard has nothing to say, not even
implicitly. The compound phrase "capacity limits" is meaningless.

[and now on to the second message]
Post by Keith Thompson
I suggest that "accept" *does* (or at least should) mean
"successfully translate and execute".
If the "one program" for a given implementation of C is strictly
conforming, that would render 5.2.4.1 redundant.
If the "one program" is not strictly conforming, then 5.2.4.1
serves merely to mandate something for one program which was not
strictly conforming, which was already mandated for all strictly
conforming ones.
My interpretation (perhaps not fully supported by the wording of
the standard) is that the requirement to accept any strictly
conforming program is conditional on that program not exceeding
the implementation's capacity.
A. It isn't. The standard means what it says. It's up to us to
find a definition for "accept" that's consistent with what the C
standard does say, not with what we think it ought to say. The
definitions I gave above for "accept" and "dismiss" (meant as
opposite to accept) are consistent with what the standard actually
says, without any problem from what might occur when an attempt is
made to translate a program.

B. Perhaps a better way to understand what "accept" means is to
look at its opposite. This program

int
main(void){
return (((0)));
}

must be accepted (that is, an implementation must deem it suitable
for translation, and attempt to translate it), because it is
strictly conforming. The attempt to translate is key. On the
flip side, this program

int
main(void){
return
((((((((((((((((((((((((((((((((((((((((((((((((((
((((((((((((((((((((((((((((((((((((((((((((((((((
0
))))))))))))))))))))))))))))))))))))))))))))))))))
))))))))))))))))))))))))))))))))))))))))))))))))))
;
}

does not have to be accepted (it isn't strictly conforming, because
it exceeds a minimum implementation limit). An implementation is
free to dismiss the program as being unsuitable for translation (by
that implementation), by virtue of not meeting all the requirements
for a strictly conforming program.

Similarly, consider this source file

extern char big[2305843009213693952];

An implementation is free to dismiss (and so not accept) this source
file, because it exceeds a minimum implementation limit. In fact,
trying to compile it with clang gives an error (under -pedantic),
complaining that the array is too large. Compiling with gcc works
without complaint. Furthermore, this source file

extern char big[2305843009213693951];

is accepted (and successfully translated) by both gcc and clang.
Clearly clang is exercising its "editorial discretion" not to
accept the larger array declaration, which it is entitled to do
because the size of the object 'big' exceeds a specified minimum
implementation limit (for a hosted implementation, which surely gcc
and clang are both meant to be).
Post by Keith Thompson
You could construct a strictly conforming program (which does not
exceed any of the minimum translation limits) that's so huge and
complex that an implementation could not reasonably be expected to
translate it. A compiler might run out of memory trying to
compile it, and either crash or die with an error message before
it could even determine that the program is strictly conforming.
I don't think it's reasonable to call that "accepting" the
program.
The implementation is accepting the program by virtue of trying to
translate it. If during the attempted translation some unsuitable
condition is discovered the program can be dismissed with an error
message. If on the other hand some resource is exhausted and the
compiler has to give up, it is still accepting the program, which
is, as far as the compiler can tell up to that point, suitable for
translation by this implementation. Deeming an input suitable for
translation doesn't mean an attempted translation will succeed
(which is unknowable, and certainly cannot be guaranteed); it
means only that as far as the compiler can tell there is nothing
in the program text seen up to that point that would make it
unsatisfactory under that implementation. The C standard draws
a bright line for what the lower bar is for which programs must
be deemed suitable for translation; anything above that lower
bar is left up to implementation, and is purely a quality-of-
implementation matter, not a conformance question.
Kenny McCormack
2023-10-27 09:18:06 UTC
Permalink
In article <uhfija$249ng$***@dont-email.me>,
James Kuyper <***@alumni.caltech.edu> wrote:
...
Post by James Kuyper
It has been decades since the first time I saw someone point out that
"accept" is not defined by the standard, with active committee members
participating in the discussion. I have to accept that the committee
does not feel that this is a flaw urgently in need of correction - which
I consider a mistake by the committee.
Post by Keith Thompson
I suggest that "accept" *does* (or at least should) mean "successfully
translate and execute".
But if they did that (defined "accept" as shown above), then wouldn't they
then have to define "successfully", "translate", and "execute"?

And so on, and so on...
--
The randomly chosen signature file that would have appeared here is more than 4
lines long. As such, it violates one or more Usenet RFCs. In order to remain
in compliance with said RFCs, the actual sig can be found at the following URL:
http://user.xmission.com/~gazelle/Sigs/RepInsults
Kaz Kylheku
2023-10-27 16:27:55 UTC
Permalink
Post by Kenny McCormack
...
Post by James Kuyper
It has been decades since the first time I saw someone point out that
"accept" is not defined by the standard, with active committee members
participating in the discussion. I have to accept that the committee
does not feel that this is a flaw urgently in need of correction - which
I consider a mistake by the committee.
Post by Keith Thompson
I suggest that "accept" *does* (or at least should) mean "successfully
translate and execute".
But if they did that (defined "accept" as shown above), then wouldn't they
then have to define "successfully", "translate", and "execute"?
It depends. Translate is what the implementation does; it has
translation phases. So it can be understood from that.

Successfully is nuanced, and could benefit from a definition:

successful execution: performance of all requirements in relation to
the semantics of a specific, strictly conforming program,
uninterrupted by an abrupt termination. Note that this concept is
independent of the termination status of the program; a program
whose termination status is unsuccessful is successfully executed
if it passes that point whereupon it indicates that termination status
to the host environment.

Someone should write a program which tabulates all words used in the
standrad, and then go through them one by one, identifying which ones
need a definition.

The standard should also cite a specific edition of a specific
English-language dictionary, as the source of definitions of words
that are not defined in the document, or in that ridiculous ISO 2382-1.

Speaking of which, ISO 2382-1 is replete with embarassing drivel; it
should be discontinued as a normative reference. You will never find a
satisfying answer to any matter of terminology in that reference;
I suspect that including might just some boilerplate imposed by ISO.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
James Kuyper
2023-10-27 16:41:10 UTC
Permalink
Post by Kenny McCormack
...
Post by James Kuyper
It has been decades since the first time I saw someone point out that
"accept" is not defined by the standard, with active committee members
participating in the discussion. I have to accept that the committee
does not feel that this is a flaw urgently in need of correction - which
I consider a mistake by the committee.
Post by Keith Thompson
I suggest that "accept" *does* (or at least should) mean "successfully
translate and execute".
But if they did that (defined "accept" as shown above), then wouldn't they
then have to define "successfully", "translate", and "execute"?
The meaning of "translate" is implicitly adequately defined by the
translation phases section. The meaning of "execute" is adequately clear
from the semantics sections is part 6 and 7. Of those phrases, "accept"
is the only problematic one.
Tim Rentsch
2023-10-29 12:47:14 UTC
Permalink
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error
or constraint. It includes code whose behavior would be undefined
if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
The standard never talks about rejection. It requires that "A
conforming hosted implementation shall accept any strictly
conforming program.". No such requirement applies to any program
which is not strictly conforming. (4p6)
Perhaps I shouldn't have used the word "reject". If the question
were phrased "Does the C standard allow an implementation not to
accept any program that is not strictly conforming?", does your
comment above mean you would say Yes to that question?
Keith Thompson
2023-10-29 21:40:26 UTC
Permalink
Post by Tim Rentsch
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax error
or constraint. It includes code whose behavior would be undefined
if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
The standard never talks about rejection. It requires that "A
conforming hosted implementation shall accept any strictly
conforming program.". No such requirement applies to any program
which is not strictly conforming. (4p6)
Perhaps I shouldn't have used the word "reject". If the question
were phrased "Does the C standard allow an implementation not to
accept any program that is not strictly conforming?", does your
comment above mean you would say Yes to that question?
I've decided not to answer any more of your questions until you
answer mine.

The code we're discussing was snipped at some point, so here it
is again:

int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}

It illustrates the issue that the standard does not define the behavior
of a conversion from an object pointer type to a function pointer type
(other than the special case of a null pointer constant), but does not
make such conversion, expressed as a cast, a constraint violation.

I asked: "On what basis do you think a conforming implementation may
reject it? Do you see a syntax rule or constraint that it violates? Do
you see some other basis for rejecting it?"

To be clear, I'm asking about whether an implementation may reject it
specifically because of the pointer conversion, not about reasons for
rejecting it that would apply equally to a "hello, world" program.

Feel free to disregard the following paragraphs.

The impression I get (very likely a wrong one) is that you are
trying to use a Socratic method, asking me questions that you
think will steer me to the correct conclusion -- a conclusion that
you have not shared. I am not speculating about your motivations
(which you hide very well); rather, I am letting you know about the
impression that I get from what you write. I'm interested in what
you think about relevant technical issues, but I'm not interested
in playing the role of your student.

(We have two similar discussions happening in parallel in different
threads. The other one involved a non-void function falling off
the end, with the caller attempting to use the result. I won't get
into that code in this thread, other than to say that I'm awaiting
your answer there as well. In both threads, it's becoming difficult
to remember what was being discussed in the first place.)
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Tim Rentsch
2023-11-10 14:33:45 UTC
Permalink
Post by Keith Thompson
Post by Tim Rentsch
Post by Keith Thompson
[...]
Post by Tim Rentsch
The point isn't quite the same. The C standard explicitly says
integers may be converted to any pointer type. The C standard
does not say that a pointer to an object type may be converted
to a pointer to function type. Every implementation is within
its rights to reject any program whose static text includes[*] a
cast from a pointer to an object type to a pointer to function
type, regardless of whether the cast has any chance of being
executed.
[*] meaning, still present as source of any preprocessor
conditionals have been processed, etc.
I disagree. (I think we've discussed this before.)
Concretely, I believe that this program violates no syntax
error or constraint. It includes code whose behavior would be
undefined if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may
reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
Let me respond now to the paragraph below, and get back to your
other comments in a later posting.
Post by Keith Thompson
The impression I get (very likely a wrong one) is that you are
trying to use a Socratic method, asking me questions that you
think will steer me to the correct conclusion -- a conclusion that
you have not shared. I am not speculating about your motivations
(which you hide very well); rather, I am letting you know about the
impression that I get from what you write. I'm interested in what
you think about relevant technical issues, but I'm not interested
in playing the role of your student.
To be clear, my question above is meant to elicit your views on
what I asked about. My question was not meant to educate you, to
be a Socratic question, or to steer you in any particular
direction. I was only looking for an answer, nothing more.

I ask the question because it is a threshold question for your
earlier question upthread. My answer to your prior question
depends on this premise for its conclusion. I asked my question
separately because I want to clarify the discussion. As things
stand, my question has gotten a yes answer from James Kuyper and
Kaz, and a no answer from Richard Damon and yourself. I
responded to Richard's posting sometime last week but have not
seen any followup from him on that. I responded to several of
your postings just in the last day, so I'm not sure if you have
had a chance yet to read those. If after reading my comments you
agree that the C standard allows implementations not to accept
any program that is not strictly conforming then I can go ahead
and answer your question. If after reading my comments you still
do not agree to that premise then there really isn't anything
more for me to say other than I believe your understanding of the
C standard is not consistent with what its authors intend. I can
agree to accept (no pun intended) either view as your final
opinion on the matter.
Tim Rentsch
2023-11-18 05:16:56 UTC
Permalink
Keith Thompson <Keith.S.Thompson+***@gmail.com> writes:

[...]
Post by Keith Thompson
(We have two similar discussions happening in parallel in different
threads. The other one involved a non-void function falling off
the end, with the caller attempting to use the result. I won't get
into that code in this thread, other than to say that I'm awaiting
your answer there as well. [...])
I don't remember any outstanding question from the discussion of
falling off the end of a non-void function. Looking back over
postings from you responding to a posting of mine I don't see
any either. I guess it's possible there was a question but I
didn't see it. In any case I have no idea what question in that
thread you want answered, so if you still want an answer please
ask the question again.
Tim Rentsch
2023-11-19 08:09:27 UTC
Permalink
Post by Keith Thompson
The code we're discussing was snipped at some point, so here it
int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
It illustrates the issue that the standard does not define the behavior
of a conversion from an object pointer type to a function pointer type
(other than the special case of a null pointer constant), but does not
make such conversion, expressed as a cast, a constraint violation.
I asked: "On what basis do you think a conforming implementation may
reject it?
The code is not strictly conforming.
Post by Keith Thompson
Do you see a syntax rule or constraint that it violates?
No.
Post by Keith Thompson
Do you see some other basis for rejecting it?"
Yes. The conversion expression can be untranslatable on some
platforms. That means any such expression cannot be part of
a strictly conforming program.
Post by Keith Thompson
To be clear, I'm asking about whether an implementation may reject it
specifically because of the pointer conversion, not about reasons for
rejecting it that would apply equally to a "hello, world" program.
It is the pointer conversion expression that makes the program be
not strictly conforming.
Post by Keith Thompson
Feel free to disregard the following paragraphs.
The impression I get (very likely a wrong one) is that you are
trying to use a Socratic method,
I'm not.
Post by Keith Thompson
asking me questions that you
think will steer me to the correct conclusion -- a conclusion that
you have not shared. I am not speculating about your motivations
(which you hide very well); rather, I am letting you know about the
impression that I get from what you write. I'm interested in what
you think about relevant technical issues, but I'm not interested
in playing the role of your student.
I have no interest in treating you as a student. I'd be much
happier if you would do more thinking for yourself.
Keith Thompson
2023-11-19 21:20:20 UTC
Permalink
Post by Tim Rentsch
Post by Keith Thompson
The code we're discussing was snipped at some point, so here it
int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
It illustrates the issue that the standard does not define the behavior
of a conversion from an object pointer type to a function pointer type
(other than the special case of a null pointer constant), but does not
make such conversion, expressed as a cast, a constraint violation.
I asked: "On what basis do you think a conforming implementation may
reject it?
The code is not strictly conforming.
Post by Keith Thompson
Do you see a syntax rule or constraint that it violates?
No.
Post by Keith Thompson
Do you see some other basis for rejecting it?"
Yes. The conversion expression can be untranslatable on some
platforms. That means any such expression cannot be part of
a strictly conforming program.
I disagree. A conforming implementation could implement a conversion of
an object pointer to a function pointer that always yields a null
pointer, or that yields a pointer whose representation is derived from
the representation of the operand. The latter is typically done for
pointer-to-integer or integer-to-pointer conversions with mismatched
sizes (6.3.2.3 explicitly says that integers may be converted to
pointers and vice versa).

If, hypothetically, the standard said that an otherwise valid program
containing a conversion of an object pointer to a function pointer type
must be successfully translated, then compiler implementers could easily
find a way to implement such conversions, even if the result is not
useful. It is incorrect to claim that such a conversion expression may
be "untranslatable".

[...]
Post by Tim Rentsch
I have no interest in treating you as a student. I'd be much
happier if you would do more thinking for yourself.
I would be happier if you would not assume that I'm not already
doing that. Be less arrogant.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Tim Rentsch
2024-01-19 20:49:57 UTC
Permalink
Post by Keith Thompson
Post by Tim Rentsch
Post by Keith Thompson
The code we're discussing was snipped at some point, so here it
int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
It illustrates the issue that the standard does not define the behavior
of a conversion from an object pointer type to a function pointer type
(other than the special case of a null pointer constant), but does not
make such conversion, expressed as a cast, a constraint violation.
I asked: "On what basis do you think a conforming implementation may
reject it?
The code is not strictly conforming.
Post by Keith Thompson
Do you see a syntax rule or constraint that it violates?
No.
Post by Keith Thompson
Do you see some other basis for rejecting it?"
Yes. The conversion expression can be untranslatable on some
platforms. That means any such expression cannot be part of
a strictly conforming program.
I disagree. A conforming implementation could implement a
conversion of an object pointer to a function pointer that always
yields a null pointer, or that yields a pointer whose representation
is derived from the representation of the operand. The latter is
typically done for pointer-to-integer or integer-to-pointer
conversions with mismatched sizes (6.3.2.3 explicitly says that
integers may be converted to pointers and vice versa).
If, hypothetically, the standard said that an otherwise valid
program containing a conversion of an object pointer to a function
pointer type must be successfully translated, then compiler
implementers could easily find a way to implement such conversions,
even if the result is not useful. It is incorrect to claim that
such a conversion expression may be "untranslatable".
What you mean by translatable is different from what I mean. Of
course it is possible to produce a sequence of bits that conforms
to the bit-level representation of a function pointer. The
problem is that it might not be possible to do that in a way that
is sensible, meaningful, or useful.

The contrast with converting between pointers and integers is
helpful here. Converting a pointer to an integer, even if the
integer type isn't as big as the pointer type, is always
potentially useful, because for example the integer could be used
as a hash function. Integers also have the nice property that
they are dense (not counting padding bits), so producing any old
integer value will never be problematic. (Yes I know about the
rule that if the value cannot be represented the behavior is
undefined, but I'm not talking about that.) Going the other
direction, an integer type whose size is large enough (and almost
always there are such types) can guarantee that converting a
pointer to an integer is invertible. There is a deeper principle
here: the addressing structures used in actual hardware use
integers as the basis for addresses. Converting between integers
and pointers always makes sense at some low level. An exception
to that rule is elaborate function pointers, which can use very
large structured values to represent a pointer to function. This
exception is the primary motivation for the C standard saying
that "The result [of converting a pointer to an integer] need not
be in the range of values of any integer type"; in such cases
the behavior is undefined but it is still always allowed to write
an expression asking for such conversions - it is only trying to
execute these expressions that causes a problem.

Note that the C standard reflects the distinction I'm making
here. The standard specifically specifies that integers may
be converted to pointers, and vice versa. The standard also
specifically specifies that object pointers may be converted to
other object pointers, and that function pointers may be
converted to other function pointers. The standard does NOT
specify that function pointers may be converted to object
pointers, or that object pointers may be converted to function
pointers. This difference corresponds exactly to what I mean
by translatable and untranslatable. This distinction is not
incidental, accidental, or meaningless. On the contrary, it is
certainly deliberate, and goes to the heart of the question here.
Post by Keith Thompson
Post by Tim Rentsch
I have no interest in treating you as a student. I'd be much
happier if you would do more thinking for yourself.
I would be happier if you would not assume that I'm not already
doing that.
It's not assumption but observation. To give an example, not too
long ago you asked a question in comp.std.c asking about undefined
behavior and indeterminate answers. I wrote a long posting in
response, basically going through a thorough and systematic review
of the history in different versions of the C standard. I'm sure
you could have done that yourself if you had tried to do so.

Please note that I'm not saying that you *should* have done that,
only that you *could* have done that.
Post by Keith Thompson
Be less arrogant.
I don't think it's arrogant to think or to say that you are
more capable than your comments would otherwise indicate. If
anything it seems just the opposite, that I have a higher
impression of your abilities than might seem to be the case
to a casual reader.
Lawrence D'Oliveiro
2024-01-22 03:27:12 UTC
Permalink
An exception to that rule is elaborate
function pointers, which can use very large structured values to
represent a pointer to function.
Real-world example: PowerPC/POWER, where a function reference is two
addresses, one for the code and the other for, I think it’s called the GOT
(“Global Object Table”). Every piece of code assumes its GOT register has
been set up with the right value.
Keith Thompson
2024-01-22 04:14:32 UTC
Permalink
Post by Lawrence D'Oliveiro
An exception to that rule is elaborate
function pointers, which can use very large structured values to
represent a pointer to function.
Real-world example: PowerPC/POWER, where a function reference is two
addresses, one for the code and the other for, I think it’s called the GOT
(“Global Object Table”). Every piece of code assumes its GOT register has
been set up with the right value.
According to <https://devblogs.microsoft.com/oldnewthing/20180816-00/?p=99505>:

Since each function requires its table of contents to be set
properly, a function pointer on PowerPC is not a pointer to the
first instruction. Instead, it’s a pointer to a structure
consisting of two pointers: The first pointer points to the
first instruction of the function, and the second pointer is
the table of contents for the function.

A quick experiment with a gcc powerpc cross-compiler
(powerpc-linux-gnu-gcc (Ubuntu 13.2.0-4ubuntu3) 13.2.0) indicates that
void* and function pointers are the same size, 4 bytes.

I suppose compilers *could* have used that two-pointer structure as the
representation of a function pointer. Possibly that would have broken
code that makes invalid assumptions about function pointer sizes.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Working, but not speaking, for Medtronic
void Void(void) { Void(); } /* The recursive call of the void */
Scott Lurndal
2024-01-22 16:36:46 UTC
Permalink
Post by Lawrence D'Oliveiro
An exception to that rule is elaborate
function pointers, which can use very large structured values to
represent a pointer to function.
Real-world example: PowerPC/POWER, where a function reference is two
addresses, one for the code and the other for, I think it’s called the GOT
(“Global Object Table”). Every piece of code assumes its GOT register has
been set up with the right value.
That's called dynamic linking and is only used for inter-library function
calls. Intralibrary and intraapplication function calls don't need the
GOT (Global Offset Table), which is used by all modern Unix and Linux
distributions to support dynamic linking.
Lawrence D'Oliveiro
2024-01-22 22:05:39 UTC
Permalink
.. GOT (Global Offset Table) ...
There had to be actual addresses in there somewhere though, did there not?
Otherwise you would have to ask “offsets to what?”.
James Kuyper
2023-11-19 23:50:23 UTC
Permalink
Post by Tim Rentsch
Post by Keith Thompson
The code we're discussing was snipped at some point, so here it
int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
It illustrates the issue that the standard does not define the behavior
of a conversion from an object pointer type to a function pointer type
(other than the special case of a null pointer constant), but does not
make such conversion, expressed as a cast, a constraint violation.
I asked: "On what basis do you think a conforming implementation may
reject it?
The code is not strictly conforming.
Post by Keith Thompson
Do you see a syntax rule or constraint that it violates?
No.
Post by Keith Thompson
Do you see some other basis for rejecting it?"
Yes. The conversion expression can be untranslatable on some
platforms. That means any such expression cannot be part of
a strictly conforming program.
The committee has officially ruled in DR 109 that code with undefined
behavior renders the program not strictly conforming, only if execution
of that code is an inevitable consequence of running the program. Not
only is execution of this code not inevitable, it is in fact impossible
for it to be executed.

I'm curious - on what platform is it impossible, or at least difficult,
to translate an if(0) block as a no-op? I'd think that simply failing to
generate any corresponding machine code would be sufficient on most, if
not all, platforms.

The code that appeared in DR 109 was not a no-op, but the behavior of
any program that called the function would be undefined, which means
that the standard imposes no requirements on its behavior. On what
platform is it difficult to generate code that has no requirements on
how it behaves? I'd think it would be trivial translate it as, for
instance, the equivalent of

fprintf(stderr, "ISO C does not define the behavior of converting \n"
"a pointer to an object into a pointer to a function.\n");
exit(EXIT_FAILURE);
Tim Rentsch
2024-01-23 02:11:52 UTC
Permalink
I am responding here to two different messages from James Kuyper,
combined for convenience in a single posting. Each segment
starts with an attribution line naming James and giving his email
address, as would appear in a normal followup, and then gives
a Message-ID for that posting.

Incidentally, both of these message seems to be a response to a
posting (or postings) of mine, but they seem not to be linked
in the usual way that newgroup postings. I'm not sure how or
why that happened, but I thought I should mention it.

[..first message..]

James Kuyper <***@alumni.caltech.edu> writes:
[Message-ID: <uje6vv$gei$***@dont-email.me>]

[This message is missing an attribution line that might have
been something like the following line]
Post by James Kuyper
Post by Tim Rentsch
Post by Keith Thompson
The code we're discussing was snipped at some point, so here it
int main(void) {
int obj = 42;
typedef void func(void);
if (0) {
func *fptr = (func*)&obj;
fptr();
}
}
It illustrates the issue that the standard does not define the
behavior of a conversion from an object pointer type to a function
pointer type (other than the special case of a null pointer
constant), but does not make such conversion, expressed as a cast,
a constraint violation.
I asked: "On what basis do you think a conforming implementation
may reject it?
The code is not strictly conforming.
Post by Keith Thompson
Do you see a syntax rule or constraint that it violates?
No.
Post by Keith Thompson
Do you see some other basis for rejecting it?"
Yes. The conversion expression can be untranslatable on some
platforms. That means any such expression cannot be part of
a strictly conforming program.
The committee has officially ruled in DR 109 that code with undefined
behavior renders the program not strictly conforming, only if execution
of that code is an inevitable consequence of running the program. Not
only is execution of this code not inevitable, it is in fact impossible
for it to be executed.
The remarks in DR 109 are irrelevant to what I'm saying. The
program given above fails to be strictly conforming for reasons
that have nothing to do with undefined behavior.
Post by James Kuyper
I'm curious - on what platform is it impossible, or at least
difficult, to translate an if(0) block as a no-op? I'd think that
simply failing to generate any corresponding machine code would be
sufficient on most, if not all, platforms.
I hope you realize that this question is quite irrelevant to the
matter being addressed here.
Post by James Kuyper
The code that appeared in DR 109 was not a no-op, but the behavior
of any program that called the function would be undefined, which
means that the standard imposes no requirements on its behavior.
On what platform is it difficult to generate code that has no
requirements on how it behaves? I'd think it would be trivial
translate it as, for instance, the equivalent of
fprintf(stderr, "ISO C does not define the behavior of converting \n"
"a pointer to an object into a pointer to a function.\n");
exit(EXIT_FAILURE);
You seem to think that the problem has to do with how to compile
program text that has undefined behavior. It doesn't. Please
see also my response to Keith Thompson's message regarding what
I mean by "translatable".


[..second message..]
Post by James Kuyper
Post by Tim Rentsch
Post by Keith Thompson
Most fundamentally, what I an hoping to accomplish is to convince
you to answer Keith's question.
Consider a C compiler for a machine where function pointers are
much bigger than any object pointer type. Functions might even
live in a completely different address space than data (a
so-called Harvard architecture). On such a machine there is no
sensible way to change an object pointer into a function pointer,
or vice versa. Naturally the compiler would prefer to choose not
to translate any source file that contains such a conversion.
But the standard does not allow such behavior -
Yes, it does. There's a flaw in your logic in trying to decide
whether the program is strictly conforming.
Post by James Kuyper
if they wish to
claim conformance to the C standard, they'll have to do something
other than what they'd prefer. Since the code with undefined
behavior is protected by an if(0), an implementation is not
required to generate the impossible conversion code, and is
required to accept the program.
Let me say again that the question of undefined behavior is
not relevant here. The given program fails to be strictly
conforming for reasons that have nothing to do with undefined
behavior.
Post by James Kuyper
Post by Tim Rentsch
The C standard doesn't say function pointers may be converted to
object pointers, or the other way around. The obvious thing to
do is simply give an error message and forego the translation
effort. It doesn't make sense that the standard would insist
that an implementation translate a construct that is nonsensical,
not just in a particular case but in every possible case.
I appreciate that you feel that way - but the committee resolved
DR 109 in conflict with your feeling.
That is your misunderstanding. The remarks in DR 109 have no
bearing on my conclusions.
Post by James Kuyper
The code referred to by DR
109 divided an integer by an integer constant of 0, which wasn't
even protected by an if(0). It was the only line in the only
function defined in that translation unit, which was absolutely
guaranteed, if function were called, to execute that line. The
only reason the committee gave for it being strictly conforming
was that there was no guarantee that the rest of the program ever
actually called the function. But the committee decided that that
fact was sufficient to make the code, in itself, strictly
conforming, and the implementation was therefore required to
accept it. If the program as a whole did call that function, that
fact would be sufficient to justify rejecting the whole program,
but it was not sufficient to justify rejecting that particular
translation unit.
Your phrasing here is slightly off. What was actually said is
that

A conforming implementation must not fail to translate
a strictly conforming program simply because some
possible execution of that program would result in
undefined behavior.

The undefined behavior not being evaluated doesn't guarantee the
program is strictly conforming. If a program /is/ otherwise
strictly conforming then undefined behavior that is potentially
unevaluated doesn't interfere with that. Do you see the
difference? The problem here is that the code in Keith's program
is not strictly conforming, regardless of whether there is
evaluated undefined behavior.
Post by James Kuyper
Basically, code with undefined behavior prevents a program from
being strictly conforming only if execution of such code is an
inevitable consequence of starting the program. If executing that
code can be avoided, then only a failure to avoid it renders the
the behavior undefined. If that failure is not realized at
compile time, then the implementation must still accept it.
If undefined behavior is unavoidable, then the program is not
strictly conforming, and it need not be accepted.

However, if all undefined behavior is potentially not evalauted,
that does not by itself guarantee that the program is strictly
conforming. Furthermore you yourself quoted, in another posting,
the relevant sentence from the C standard that bears on the
question of strict conformance.
Post by James Kuyper
If the committee ruled that way on that code, how could you
possibly expect it to support rejection of this code?
You think the only things that matter in the two programs are
analogous. They aren't.
Post by James Kuyper
Post by Tim Rentsch
Converting between function pointers and object pointers isn't
like dividing by zero.
True - there's implementations which can meaningfully do such a
conversion - there's no meaningful way to divide an integer by 0
(a floating point calculation could reasonably produce infinity,
if infinity can be represented on that hardware). Which makes the
committee's decision on DR 109 even stronger.
Again you miss the point. The question of undefined behavior
has no bearing on the conclusion.
Post by James Kuyper
Post by Tim Rentsch
[.. some remarks unrelated to the primary topic ..]
[...]
However, if you're unable to provide a convincing argument that
your point of view is correct, then it's a point of view that is,
quite frankly, of no interest.
I have very little interest in trying to offer an argument
that convinces you. My conclusions are correct, whether
my comments convince you or not.

[... incidental commentary left out ...]
Post by James Kuyper
Post by Tim Rentsch
Try compiling Keith's program with gcc -pedantic-errors. I expect
you will find, as I did, that gcc flags the conversion with an
error and doesn't produce an output file. The C standard allows
such behavior only if the source file being compiled is not
strictly conforming. I have more confidence in gcc's understanding
of the C standard than I do in yours.
I have considerable trust in gcc, but more trust in the actual
words of the standard. Where there's a disagreement, I favor the
actual words of the standard.
The problem is not the words of the C standard but what you think
they mean.
Post by James Kuyper
And, having seen how you interpret the standard, I certainly
trust my interpretation more than yours.
Of course. You're living in your own language bubble. Unlike you,
I calibrate my understanding of what text in the C standard means
by comparing it with other sources, including especially remarks
written or spoken by C standard committee members in other contexts.
Post by James Kuyper
Post by Tim Rentsch
Post by Keith Thompson
Yes, it does. And I frequently despair of ever figuring out a way
to say what I mean that will be understood as intended by you.
[...]
The problem is not me understanding you, but you not understanding
me. Your mental model for how to use and understand natural
language is peculiar. Also rigid and inflexible. Probably the
most troublesome property is that you are focused on argument more
than on understanding. You read the words but don't understand the
meaning. Instead you insist on playing idiosyncratic word games,
apparently believing that how you interpret the words is universal
and absolute. It isn't. Assigning meaning to natural language is
inherently a subjective process, not an objective one. You're a
lot better at arguing than you are at listening. You might want to
think about that.
Subjectivity is fine for poetry - but we're talking about a
standard. To the extent that the meaning of words is subjective,
they are unsuitable for use in a standard. The whole point of the
standard is to standardize the requirements - that is, to make
them exactly the same for everyone. If you have to use subjective
interpretations to determine what the requirements are, the
standard has failed the purpose for which it was intended. An
implementor could use it's subjective opinion to justify
implementing C in a way that would be inconsistent with the
subjective opinion of a user.
I'm sorry you missed the point of what I was saying.

Just one more item. Here is a quote from the C Rationale document:

Consequences of the treatment of pointer types in the
Standard include:

* [...]

* Even with an explicit cast, it is invalid to convert
a function pointer to an object pointer or a pointer
to void, or vice versa.

Note in particular the word /invalid/. Not undefined, but
invalid. What do you suppose is the basis for that statement?
What bearing does it have on the question being discussed?
What do you think the implications are for the various assertions
made during the discussion?
James Kuyper
2024-01-23 19:19:09 UTC
Permalink
On 2024-01-22 00:08, Tim Rentsch wrote:
...
Post by Tim Rentsch
Incidentally, both of these message seems to be a response to a
posting (or postings) of mine, but they seem not to be linked
in the usual way that newgroup postings. I'm not sure how or
why that happened, but I thought I should mention it.
I currently have Thunderbird configured to delete any usenet messages
from you. This is not intended to punish you, nor does it incur any
obligation on my part to ignore your questions. It's intended solely to
reduce the amount of aggravation I feel as a result of reading your
messages. As a result, if I end up learning about one of your messages
by other means, and decide that I do want to respond, it's inconvenient
to do so. This is both an advantage and a disadvantage - it discourages
me from responding unless strongly motivated, but it also makes it
inconvenient to respond if I am sufficiently motivated.
I've been using Google Groups to post such messages, but when they cut
that off, I've been forced to switched to a couple of other
work-arounds. I'm not sure which one I used in that message, and the
details aren't important, but I'm not surprised if either method messed
up the message links.
Post by Tim Rentsch
[..first message..]
[This message is missing an attribution line that might have
been something like the following line]
The remarks in DR 109 are irrelevant to what I'm saying. The
program given above fails to be strictly conforming for reasons
that have nothing to do with undefined behavior.
And once again, typical Tim Rentsch behavior. This would have been a
perfectly obvious and natural opportunity to insert a statement of what
you think the actual reason is, but you refuse to do so.
Post by Tim Rentsch
Post by James Kuyper
I'm curious - on what platform is it impossible, or at least
difficult, to translate an if(0) block as a no-op? I'd think that
simply failing to generate any corresponding machine code would be
sufficient on most, if not all, platforms.
I hope you realize that this question is quite irrelevant to the
matter being addressed here.
You said that implementing such a conversion would be difficult. I don't
see what's difficult about a conversion that does not in fact need to be
implemented.
Post by Tim Rentsch
Post by James Kuyper
The code that appeared in DR 109 was not a no-op, but the behavior
of any program that called the function would be undefined, which
means that the standard imposes no requirements on its behavior.
On what platform is it difficult to generate code that has no
requirements on how it behaves? I'd think it would be trivial
translate it as, for instance, the equivalent of
fprintf(stderr, "ISO C does not define the behavior of converting \n"
"a pointer to an object into a pointer to a function.\n");
exit(EXIT_FAILURE);
You seem to think that the problem has to do with how to compile
program text that has undefined behavior. It doesn't. Please
see also my response to Keith Thompson's message regarding what
I mean by "translatable".
The relevant code does not need to be translated, and therefore does not
need to be translatable, since it is literally impossible for it to be
executed.

...
Post by Tim Rentsch
Yes, it does. There's a flaw in your logic in trying to decide
whether the program is strictly conforming.
Once again, you passed upon on a perfectly obvious and natural
opportunity to insert a statement explaining what actually renders the
program not strictly conforming.

You could have easily, at any time, short-circuited months of circuitous
arguing by identifying the feature of the program that renders it not
strictly conforming. The only suspect I can find for such a feature is
the fact that the code which never gets executed would have undefined
behavior if it were executed. Since you say that's not the problem, I
have no idea what you think the problem actually is.

...
Post by Tim Rentsch
Let me say again that the question of undefined behavior is
not relevant here. The given program fails to be strictly
conforming for reasons that have nothing to do with undefined
behavior.
Again, you passed up on a perfectly obvious and natural opportunity to
insert a statement identifying what you think the reason actually is.

...
Post by Tim Rentsch
The undefined behavior not being evaluated doesn't guarantee the
program is strictly conforming. If a program /is/ otherwise
strictly conforming then undefined behavior that is potentially
unevaluated doesn't interfere with that. Do you see the
difference?
Not in any way that is applicable. I see no other way in which it fails
to be strictly conforming. I may have missed something, and you might
have noticed it, which is why we've repeatedly asked you to identify it,
but you have repeatedly and steadfastly refused to explain what that
other way is.
Again, you missed a perfectly obvious and natural opportunity to insert
a statement identifying what it is that you think makes the program not
strictly conforming.

...
Post by Tim Rentsch
Post by James Kuyper
If the committee ruled that way on that code, how could you
possibly expect it to support rejection of this code?
You think the only things that matter in the two programs are
analogous. They aren't.
Because you refuse to identify what thing it is you think matters.
Once again, you missed a perfectly obvious and natural opportunity to
insert a statement identifying what it is that you think matters.

...
Post by Tim Rentsch
Again you miss the point. The question of undefined behavior
has no bearing on the conclusion.
Again, you missed a perfectly natural and obvious opportunity to
identify what it is that you think does have a bearing on the conclusion.

...> I have very little interest in trying to offer an argument
Post by Tim Rentsch
that convinces you. My conclusions are correct, whether
my comments convince you or not.
Yes, I've noticed your lack of interest in convince me. But do you have
any idea how frustrating it is to shadow box with someone who refuses to
put his arguments out there so we can decide whether or not they make
any sense. There's a perfectly natural conclusion that can be reached
when someone behaves that way, and that is they are afraid to explain
their arguments, because they know that those arguments are not good
enough to survive public exposure. That might not be your actual reason,
but your behavior entitles us to assume that it is - not because that
would be the most reasonable guess, but as a built-in punishment for
your refusal to communicate.

...
Post by Tim Rentsch
The problem is not the words of the C standard but what you think
they mean.
And, again, you missed a perfectly obvious and natural opportunity to
explain what it is you think they actually mean.

...
Post by Tim Rentsch
I calibrate my understanding of what text in the C standard means
by comparing it with other sources, including especially remarks
written or spoken by C standard committee members in other contexts.
And again, you missed a perfectly obvious and natural opportunity to
insert a citation of the relevant remarks that informed you interpretation.

...
Post by Tim Rentsch
I'm sorry you missed the point of what I was saying.
That's because you refuse to explain it. You just criticize what I say
without bothering to explain your criticism. How could I possibly have
any idea what your point is when you won't explain it?
Again, you missed a perfectly obvious and natural opportunity to insert
an explanation of what your point is.
Post by Tim Rentsch
Consequences of the treatment of pointer types in the
* [...]
* Even with an explicit cast, it is invalid to convert
a function pointer to an object pointer or a pointer
to void, or vice versa.
Note in particular the word /invalid/. Not undefined, but
invalid. What do you suppose is the basis for that statement?
Since the standard doesn't use the term "invalid" in any applicable text
that I could find, I would assume that the basis for this statement is
the fact that the behavior of such a conversion is undefined. The terms
"syntax error", "constraint violation", "unspecified behavior",
"implementation-defined behavior", "undefined behavior", and
"locale-specific", among others, all have requirements (or the lack
thereof) attached to them by the C standard. "invalid" does not. The
behavior of such a conversion is undefined "by omission of any explicit
definition of the behavior", and as far as I know that is the only
relevant problem with it. It doesn't violate any applicable syntax rule
or constraint that I'm aware of. And you've repeatedly refused to
identify one.
Post by Tim Rentsch
What bearing does it have on the question being discussed?
What do you think the implications are for the various assertions
made during the discussion?
I don't think it has a bearing, because I think it is using "invalid" as
an informal reference to the fact that the behavior is undefined. We
appear to both be in agreement that the undefined behavior of that
statement, if it were executed, is irrelevant to the strict conformance
of this code, since it cannot be executed, so I would expect that to
have no implications for the various assertions.

Again, you passed upon on a perfectly obvious and natural opportunity
for inserting statements explaining what bearing you think it has on the
discussion and what the implications are for the various statements made
during the discussion.

If you've not willing to explain, why do you even bother posting a
message? A message that merely asserts that there is a problem, while
failing to identify it, does nothing except raise the suspicion that you
don't actually have a valid reason for thinking that there's a problem.
James Kuyper
2023-10-29 16:39:45 UTC
Permalink
...
Post by James Kuyper
Post by Keith Thompson
Concretely, I believe that this program violates no syntax error
or constraint. It includes code whose behavior would be undefined
if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
The standard never talks about rejection. It requires that "A
conforming hosted implementation shall accept any strictly
conforming program.". No such requirement applies to any program
which is not strictly conforming. (4p6)
Perhaps I shouldn't have used the word "reject". If the question
were phrased "Does the C standard allow an implementation not to
accept any program that is not strictly conforming?", does your
comment above mean you would say Yes to that question?
As I said. and you quoted above, "No such requirement applies to any
program which is not strictly conforming". That means that "there is no
requirement to accept" or in other words "it is allowed to not accept",
or in other words "it is allowed to reject". How could I have reworded
that comment to render it unnecessary for you to ask the above question,
and why would such re-wording have been necessary?

The key point is that the code in question is strictly conforming, and a
implementation therefore IS required to accept it.

You earlier claimed "Every implementation is within its rights to reject
any program whose static text includes[*] a cast from a pointer to an
object type to a pointer to function type, regardless of whether the
cast has any chance of being executed." Since ensuring that such code
might not get executed is sufficient to make the program strictly
conforming (DR 109), and this code guarantees that it won't be executed,
how can an implementation be within it's rights to reject it?
Tim Rentsch
2023-11-10 01:12:26 UTC
Permalink
...
Post by Tim Rentsch
Post by Tim Rentsch
Post by Keith Thompson
Concretely, I believe that this program violates no syntax
error or constraint. It includes code whose behavior would be
undefined if it were executed, but the `if (0)` prevents that.
On what basis do you think a conforming implementation may
reject it?
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
The standard never talks about rejection. It requires that "A
conforming hosted implementation shall accept any strictly
conforming program.". No such requirement applies to any program
which is not strictly conforming. (4p6)
Perhaps I shouldn't have used the word "reject". If the question
were phrased "Does the C standard allow an implementation not to
accept any program that is not strictly conforming?", does your
comment above mean you would say Yes to that question?
As I said. and you quoted above, "No such requirement applies to
any program which is not strictly conforming". That means that
"there is no requirement to accept" or in other words "it is
allowed to not accept", or in other words "it is allowed to
reject". How could I have reworded that comment to render it
unnecessary for you to ask the above question,
Give a direct answer: "I believe the intent of the C standard
allows an implementation not to accept any program that is not
strictly conforming. (When you say reject I'm assuming you
mean the opposite of accept.)" Just those two sentences, and
nothing more.
and why would such re-wording have been necessary?
First, because I wasn't sure after reading your long answer
whether you might have been trying to make some subtle
distinction between the question I asked and the answer you were
giving.

Second, the purpose of posing the question I asked was to make
sure there is consensus on the threshold question. I think it's
important to focus on this issue while it is still being debated.
I expect you have observed that of the four people responding, two
have agreed (you and Kaz) and two have not agreed (Richard Damon
and Keith Thompson). I posted a followup question in response to
Richard's answer, but he has not yet replied. Keith's comments
have been the most emphatic in disagreeing, and I think it would
help to discuss the threshold question separately before we get to
the larger question, so the discussion doesn't get too confused.

Third, considering the two previous points, giving a short direct
answer as outlined above is important to eliminate the need for me
to respond and try to narrow the focus to just the threshold
question. I know the larger issue is important to you, and we
should get back to it, but I ask this question first because it's
important to what I have to say later. Trying to take two steps
at a time often leads to confusion, especially in the context of a
discussion in comp.lang.c.
The key point is that the code in question is strictly conforming,
and a implementation therefore IS required to accept it.
Before I explain why that isn't so I really think it would help to
make sure everyone is on the same page with respect to the threshold
question, because if someone doesn't agree that the standard allows
a conforming implementation to reject any program that isn't
strictly conforming there is no point trying to explain to them
further.
You earlier claimed "Every implementation is within its rights to
reject any program whose static text includes[*] a cast from a
pointer to an object type to a pointer to function type,
regardless of whether the cast has any chance of being executed."
Since ensuring that such code might not get executed is sufficient
to make the program strictly conforming (DR 109), and this code
guarantees that it won't be executed, how can an implementation be
within it's rights to reject it?
Your reasoning is fine as far as it goes, but it's incomplete. I
still would like to reach a shared understanding about the threshold
question. I just posted a reply earlier today to two of Keith's
postings on this, let's give him a chance to respond before going
further.
James Kuyper
2023-11-10 17:08:10 UTC
Permalink
...
Post by James Kuyper
Post by James Kuyper
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
The standard never talks about rejection. It requires that "A
conforming hosted implementation shall accept any strictly
conforming program.". No such requirement applies to any program
which is not strictly conforming. (4p6)
Perhaps I shouldn't have used the word "reject". If the question
were phrased "Does the C standard allow an implementation not to
accept any program that is not strictly conforming?", does your
comment above mean you would say Yes to that question?
As I said. and you quoted above, "No such requirement applies to
any program which is not strictly conforming". That means that
"there is no requirement to accept" or in other words "it is
allowed to not accept", or in other words "it is allowed to
reject". How could I have reworded that comment to render it
unnecessary for you to ask the above question,
Give a direct answer: "I believe the intent of the C standard
allows an implementation not to accept any program that is not
strictly conforming. (When you say reject I'm assuming you
mean the opposite of accept.)" Just those two sentences, and
nothing more.
There's several problems with that formulation, which is why I didn't
use it:
1. "I believe" implies either uncertainty, or that I consider it to be a
matter of opinion, not a matter of fact. Neither of those apply in this
context.

2. "the intent of the C standard" - the C standard is a document; it is
the C committee which had an intent, not the standard itself - but
that's a nitpick.
I'm primarily interested in what the standard actually says. I've an
interest in what the intent was only to the extent that I believe that
the standard fails to correctly reflect the committee's intent. In that
case the intent is valuable for determining how the standard needs to be
corrected to express that intent. I don't think that's applicable in
this context.

My statement was that, as a matter of fact, not an opinion, the C
standard allows an implementation to not accept a program that is not
strictly conforming, by the simple expedient of making the only
requirement that a program be accepted dependent on it being strictly
conforming.

There is uncertainty and room for differences of opinion about the
meaning of "accept"- but I don't see that uncertainty as being relevant
to Keith's original question. Unless the definition of "accept" is so
weird that it includes rejecting the program, Keith's point stands. As
far as I know, no one has proposed a definition for "accept" that is
that weird.
Post by James Kuyper
and why would such re-wording have been necessary?
First, because I wasn't sure after reading your long answer
whether you might have been trying to make some subtle
distinction between the question I asked and the answer you were
giving.
I was, and quite explicitly so. The key distinction is that the standard
only talks about acceptance, while Keith's question and yours were about
rejection. However, unless your definition of "accept" is so weird that
and implementation could simultaneously be said to have accepted and
rejected a program, that clause prohibits rejecting this strictly
conforming code.
Second, the purpose of posing the question I asked was to make
sure there is consensus on the threshold question. ...
I don't see why - what evidence is there that anyone fails to have
consensus on that point? Everyone who's claimed that such code cannot be
rejected has explicitly said that it was because they believe the code
is strictly conforming. No one has suggested that it fails to be
strictly conforming, but still cannot be rejected.
... I think it's
important to focus on this issue while it is still being debated.
I expect you have observed that of the four people responding, two
have agreed (you and Kaz) and two have not agreed (Richard Damon
and Keith Thompson).
The situation is not as simple as agreement or disagreement. Kaz appears
to agree that strictly conforming code must be accepted, but expresses
extreme agnosticism about what "accept" means, which is pretty much in
agreement with me. He has, however, claimed (on 2023-10-03) that the
if(0) guaranteeing that the code does not get executed does not protect
the code from having undefined behavior, whereas I read the resolution
of DR 109 as indicating that it does protect it - the code mentioned in
DR 109 was far less well protected against execution than this code, and
was still strictly conforming.

Richard Damon claimed that rejecting a program requires a constraint
violation, which is incomplete. A program may also be rejected for a
syntax error or for having code that would have undefined behavior if
executed, if that it was guaranteed that the code would be executed if
the program were executed. DR 109 made it clear that unless the code is
guaranteed to be executed, the fact that it would have undefined
behavior if executed does not, in itself, allow rejection of the
program. Since his contribution was rather brief, and no one mentioned
those points in response to that message, I'm not sure whether leaving
those exceptions out was accidental.

Keith has some subtle disagreements with me about the meaning of the
word "accept" - I say it's unspecified, so the requirement could be met
just by issuing a message "Program Accepted" - he thinks the meaning is
unclear, but is intended to be much stronger than that. However, he
agrees with me that this code is strictly conforming, and that, as a
result, an implementation is not permitted to reject it.

I posted a followup question in response to
Richard's answer, but he has not yet replied. Keith's comments
have been the most emphatic in disagreeing, and I think it would
help to discuss the threshold question separately before we get to
the larger question, so the discussion doesn't get too confused.
Keith is the one who has, most emphatically, asserted that the reason it
cannot be rejected is because it is strictly conforming - how could your
"threshold question" come into play?
Post by James Kuyper
The key point is that the code in question is strictly conforming,
and a implementation therefore IS required to accept it.
Before I explain why that isn't so I really think it would help to
make sure everyone is on the same page with respect to the threshold
question, because if someone doesn't agree that the standard allows
a conforming implementation to reject any program that isn't
strictly conforming there is no point trying to explain to them
further.
But no one has said anything to justify having any doubts about whether
they would agree. It is the strict conformance of the code that they are
asserting, not a requirement to accept code that isn't strictly conforming.
Post by James Kuyper
You earlier claimed "Every implementation is within its rights to
reject any program whose static text includes[*] a cast from a
pointer to an object type to a pointer to function type,
regardless of whether the cast has any chance of being executed."
Since ensuring that such code might not get executed is sufficient
to make the program strictly conforming (DR 109), and this code
guarantees that it won't be executed, how can an implementation be
within it's rights to reject it?
Your reasoning is fine as far as it goes, but it's incomplete. I
still would like to reach a shared understanding about the threshold
question. I just posted a reply earlier today to two of Keith's
postings on this, let's give him a chance to respond before going
further.
His last message that I've seen indicates that he will not respond until
you answer his question: ""On what basis do you think a conforming
implementation may reject it? Do you see a syntax rule or constraint
that it violates? Do you see some other basis for rejecting it?"

If you also refuse to answer his question until he answers yours, the
two of you are at an impasse. I consider his insistence on an answer to
be more reasonable than yours, because it is the directly relevant
question. I consider your insistence to be unreasonable, because nobody
has said anything to suggest disagreement with your threshold question.
In fact, we've tried (and apparently, in your eyes, failed, for reasons
that are unclear) to express basic agreement with it (subject to minor
quibble about the relationship between "accept" and "reject"). We only
disagree about it's applicability to this strictly conforming code.
Tim Rentsch
2023-11-13 16:16:16 UTC
Permalink
Post by James Kuyper
...
Post by Tim Rentsch
Post by Tim Rentsch
Post by Tim Rentsch
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
[...]
The standard never talks about rejection. It requires that "A
conforming hosted implementation shall accept any strictly
conforming program.". No such requirement applies to any program
which is not strictly conforming. (4p6)
Perhaps I shouldn't have used the word "reject". If the question
were phrased "Does the C standard allow an implementation not to
accept any program that is not strictly conforming?", does your
comment above mean you would say Yes to that question?
As I said. and you quoted above, "No such requirement applies to
any program which is not strictly conforming". That means that
"there is no requirement to accept" or in other words "it is
allowed to not accept", or in other words "it is allowed to
reject". How could I have reworded that comment to render it
unnecessary for you to ask the above question,
Give a direct answer: "I believe the intent of the C standard
allows an implementation not to accept any program that is not
strictly conforming. (When you say reject I'm assuming you
mean the opposite of accept.)" Just those two sentences, and
nothing more.
There's several problems with that formulation, which is why I didn't
1. "I believe" implies either uncertainty, or that I consider it to be a
matter of opinion, not a matter of fact. Neither of those apply in this
context.
2. "the intent of the C standard" - the C standard is a document; it is
the C committee which had an intent, not the standard itself - but
that's a nitpick.
I'm primarily interested in what the standard actually says. I've an
interest in what the intent was only to the extent that I believe that
the standard fails to correctly reflect the committee's intent. In that
case the intent is valuable for determining how the standard needs to be
corrected to express that intent. I don't think that's applicable in
this context.
My statement was that, as a matter of fact, not an opinion, the C
standard allows an implementation to not accept a program that is not
strictly conforming, by the simple expedient of making the only
requirement that a program be accepted dependent on it being strictly
conforming.
There is uncertainty and room for differences of opinion about the
meaning of "accept"- but I don't see that uncertainty as being relevant
to Keith's original question. Unless the definition of "accept" is so
weird that it includes rejecting the program, Keith's point stands. As
far as I know, no one has proposed a definition for "accept" that is
that weird.
Post by Tim Rentsch
and why would such re-wording have been necessary?
First, because I wasn't sure after reading your long answer
whether you might have been trying to make some subtle
distinction between the question I asked and the answer you were
giving.
I was, and quite explicitly so. The key distinction is that the standard
only talks about acceptance, while Keith's question and yours were about
rejection. However, unless your definition of "accept" is so weird that
and implementation could simultaneously be said to have accepted and
rejected a program, that clause prohibits rejecting this strictly
conforming code.
Post by Tim Rentsch
Second, the purpose of posing the question I asked was to make
sure there is consensus on the threshold question. ...
I don't see why - what evidence is there that anyone fails to have
consensus on that point? Everyone who's claimed that such code cannot be
rejected has explicitly said that it was because they believe the code
is strictly conforming. No one has suggested that it fails to be
strictly conforming, but still cannot be rejected.
Post by Tim Rentsch
... I think it's
important to focus on this issue while it is still being debated.
I expect you have observed that of the four people responding, two
have agreed (you and Kaz) and two have not agreed (Richard Damon
and Keith Thompson).
The situation is not as simple as agreement or disagreement. Kaz appears
to agree that strictly conforming code must be accepted, but expresses
extreme agnosticism about what "accept" means, which is pretty much in
agreement with me. He has, however, claimed (on 2023-10-03) that the
if(0) guaranteeing that the code does not get executed does not protect
the code from having undefined behavior, whereas I read the resolution
of DR 109 as indicating that it does protect it - the code mentioned in
DR 109 was far less well protected against execution than this code, and
was still strictly conforming.
Richard Damon claimed that rejecting a program requires a constraint
violation, which is incomplete. A program may also be rejected for a
syntax error or for having code that would have undefined behavior if
executed, if that it was guaranteed that the code would be executed if
the program were executed. DR 109 made it clear that unless the code is
guaranteed to be executed, the fact that it would have undefined
behavior if executed does not, in itself, allow rejection of the
program. Since his contribution was rather brief, and no one mentioned
those points in response to that message, I'm not sure whether leaving
those exceptions out was accidental.
Keith has some subtle disagreements with me about the meaning of the
word "accept" - I say it's unspecified, so the requirement could be met
just by issuing a message "Program Accepted" - he thinks the meaning is
unclear, but is intended to be much stronger than that. However, he
agrees with me that this code is strictly conforming, and that, as a
result, an implementation is not permitted to reject it.
I posted a followup question in response to
Post by Tim Rentsch
Richard's answer, but he has not yet replied. Keith's comments
have been the most emphatic in disagreeing, and I think it would
help to discuss the threshold question separately before we get to
the larger question, so the discussion doesn't get too confused.
Keith is the one who has, most emphatically, asserted that the reason it
cannot be rejected is because it is strictly conforming - how could your
"threshold question" come into play?
Post by Tim Rentsch
The key point is that the code in question is strictly conforming,
and a implementation therefore IS required to accept it.
Before I explain why that isn't so I really think it would help to
make sure everyone is on the same page with respect to the threshold
question, because if someone doesn't agree that the standard allows
a conforming implementation to reject any program that isn't
strictly conforming there is no point trying to explain to them
further.
But no one has said anything to justify having any doubts about whether
they would agree. It is the strict conformance of the code that they are
asserting, not a requirement to accept code that isn't strictly conforming.
Post by Tim Rentsch
You earlier claimed "Every implementation is within its rights to
reject any program whose static text includes[*] a cast from a
pointer to an object type to a pointer to function type,
regardless of whether the cast has any chance of being executed."
Since ensuring that such code might not get executed is sufficient
to make the program strictly conforming (DR 109), and this code
guarantees that it won't be executed, how can an implementation be
within it's rights to reject it?
Your reasoning is fine as far as it goes, but it's incomplete. I
still would like to reach a shared understanding about the threshold
question. I just posted a reply earlier today to two of Keith's
postings on this, let's give him a chance to respond before going
further.
His last message that I've seen indicates that he will not respond until
you answer his question: ""On what basis do you think a conforming
implementation may reject it? Do you see a syntax rule or constraint
that it violates? Do you see some other basis for rejecting it?"
If you also refuse to answer his question until he answers yours, the
two of you are at an impasse. I consider his insistence on an answer to
be more reasonable than yours, because it is the directly relevant
question. I consider your insistence to be unreasonable, because nobody
has said anything to suggest disagreement with your threshold question.
In fact, we've tried (and apparently, in your eyes, failed, for reasons
that are unclear) to express basic agreement with it (subject to minor
quibble about the relationship between "accept" and "reject"). We only
disagree about it's applicability to this strictly conforming code.
I don't see what it is you are hoping to accomplish with the
above comments. Can you shed some light on that?
James Kuyper
2023-11-13 19:15:14 UTC
Permalink
On 2023-11-13 at 11:16, Tim Rentsch wrote:
...
Post by Tim Rentsch
I don't see what it is you are hoping to accomplish with the
above comments. Can you shed some light on that?
You quoted a lot of different comments that I made, for a variety of
different purposes. Since you quoted them all, am I correct in thinking
that you want reasons for all of them? That's what I'm going to assume.,
I would have thought that was obvious, but things involving discussions
with you seldom are. In the following explanation, I will terminate any
statement of fact with a parenthesized number. One of the things I'd
like to accomplish is to identify which of those statements you agree
with, which ones you disagree with, and the reasons for those
disagreements. Would you please respond by identifying by number the
ones you disagree with, and explain why?

Most fundamentally, what I an hoping to accomplish is to convince you to
answer Keith's question.

Since you have said you won't answer his question until he has answered
your "threshold question", and he won't answer that till you answer his,
I'm trying to convince you to abandon that requirement, on three grounds:

1. The question cannot be answered purely by reference to the standard
(1). Firstly because the standard never uses the word "reject" (2). The
closest the standard ever comes to addressing your question is 4p6 (3).
That clause relies upon the word "accept" (4). The standard never
provides a definition of "accept" (5). Therefore, until specific
meanings for both "accept" and "reject" are agreed upon, your question
cannot be answered (6).

2. However, unless those terms are defined in such a way that an
implementation can simultaneously accept and reject a program, 4p6 means
that the answer to your question is "yes" (7). I do not interpret those
words in a way that allows the answer to be "no". This is as close as I
or anyone else can reasonably come to agreeing with your assertion, but
you don't seem willing to accept it as an agreement. Keith has already
said much the same thing, with subtle differences in how he addresses
the ambiguities in the way those terms are defined. If you're waiting
for a fuller agreement from me, you won't get it - the words of the
standard don't allow me to do so. If you're waiting for Keith to agree
more fully, he's made it clear he's not willing to discuss this further
until you answer his question. If you're unwilling to bend on that
issue, there's no point in continuing this discussion.

3. You have no justification for thinking that disagreement on
"threshold question" is relevant to the disagreement. Everyone who has
said that the code cannot be rejected has said, or at least implied,
that it cannot be rejected because the code is strictly conforming(7).
If the code is strictly conforming, 4p6 guarantees that it must be
accepted (8). If you think that the code is not strictly conforming,
then that is the point that's in need of discussion, and we've been
waiting an unconscionably long time for you to start that discussion.

As a secondary matter, when I first cited 4p6 as the closest thing the
standard has to an answer to your question, you responded in a way that
indicated that you found my explanation unclear, for reasons that I find
unclear. I want to be able to make similar statements in the future in a
way that is clear enough from the beginning to not provoke such
questions. I asked you for suggestions for how I could have worded my
statement to achieve that goal, and you proposed alternative wording.
However, your proposal was unsuitable, saying and implying things that I
did not intend to say and imply. Your proposal does not clear up my own
uncertainty about why you found my statement unclear. One of the things
I'm hoping to accomplish is to enable clear communication with you. I
would appreciate getting a second suggestion, one that avoids the
problems I had with your first suggestion.

Let me put it this way: would it have been possible for me to invoke 4p6
as the closest thing that the standard has to an answer to your
question, without suggesting that I have any uncertainty about it's
relevance, without suggesting that it's relevance is a matter of
opinion, while referring only to what the standard actually says,
without invoking the intent of the committee when they wrote it?
Tim Rentsch
2023-11-14 17:02:48 UTC
Permalink
...
Post by Tim Rentsch
I don't see what it is you are hoping to accomplish with the
above comments. Can you shed some light on that?
[long response]
I want to address one part of what you said in your two last
postings, while I think about how to respond to other parts.

In an earlier posting, I wrote:

Give a direct answer: "I believe the intent of the C standard
allows an implementation not to accept any program that is not
strictly conforming. (When you say reject I'm assuming you
mean the opposite of accept.)" Just those two sentences, and
nothing more.

When I say "the intent of the C standard", that is a stand-in for
a longer phrasing along the lines of "the intended meaning of the
C standard" or "the intent of the C standard's authors".

When I say "I believe ..." I mean it in the same way that I might
say "I believe the Riemann Hypothesis is true." That is, I am
making a statement about a question of fact, expressing a belief
about the answer to the question, without having proof or otherwise
convincing evidence.

To give a contrast, I do not mean "I believe ..." as addressing a
question of faith or a question of opinion. For example, "I believe
God exists" is a statement about a question of faith; answers to
questions of faith are unknowable, more or less by definition,
except by having faith.

Do you understand what I'm saying in those last three paragraphs?
Or is there something you're unsure about or find confusing?

When reading a statement or a question from someone, it's imporant
to understand what the writer thinks about what the statement or
question means. Similarly, when writing a statement or a question
for another person or persons, it's important to express what one
hopes to communicate in a way that will be understood by the
expected or intended audience as having the same meaning as what
the writer hopes to convey.

Does this last paragraph make sense? Would you say you agree with
it?
James Kuyper
2023-11-14 18:10:22 UTC
Permalink
On 2023-11-14 at 12:03, Tim Rentsch wrote:
...
Post by Tim Rentsch
I want to address one part of what you said in your two last
postings, while I think about how to respond to other parts.
Give a direct answer: "I believe the intent of the C standard
allows an implementation not to accept any program that is not
strictly conforming. (When you say reject I'm assuming you
mean the opposite of accept.)" Just those two sentences, and
nothing more.
When I say "the intent of the C standard", that is a stand-in for
a longer phrasing along the lines of "the intended meaning of the
C standard" or "the intent of the C standard's authors".
You could simply use "Committee", which is actually 1 character shorter
than "C standard". That removes length as a justification for using a
less accurate formulation.
Post by Tim Rentsch
When I say "I believe ..." I mean it in the same way that I might
say "I believe the Riemann Hypothesis is true." That is, I am
making a statement about a question of fact, expressing a belief
about the answer to the question, without having proof or otherwise
convincing evidence.
If you lack proof or convincing evidence, it seems to me that you should
have uncertainty about the truth of the statement. If you do have such
uncertainty, that fits in with my own use of "I believe" to express
uncertainty.

I have what I consider adequate proof and convincing evidence for the
statement that you suggested I should prepend with "I believe". The
relevant evidence is the clause I cited from the standard. If you
consider my argument insufficiently convincing, you should be explaining
what flaws you see in my argument, rather than suggesting that I cover
those flaws by prefixing the statement with "I believe".
Post by Tim Rentsch
To give a contrast, I do not mean "I believe ..." as addressing a
question of faith or a question of opinion. For example, "I believe
God exists" is a statement about a question of faith; answers to
questions of faith are unknowable, more or less by definition,
except by having faith.
Do you understand what I'm saying in those last three paragraphs?
Or is there something you're unsure about or find confusing?
I have adopted a different way of expressing myself precisely because I
consider the way you are expressing those things to be flawed. In
particular, I find it odd to prefix a perfectly ordinary statement of
fact with "I believe", if the intent is not to focus attention on the
fact that you believe it,rather than on the truth of the statement.
However, saying things that way is not sufficiently uncommon as to
seriously confuse me.
Post by Tim Rentsch
When reading a statement or a question from someone, it's imporant
to understand what the writer thinks about what the statement or
question means. Similarly, when writing a statement or a question
for another person or persons, it's important to express what one
hopes to communicate in a way that will be understood by the
expected or intended audience as having the same meaning as what
the writer hopes to convey.
Does this last paragraph make sense? Would you say you agree with
it?
Yes, it does. And I frequently despair of ever figuring out a way to say
what I mean that will be understood as intended by you. Also you are far
too prone to making objections without bothering to explain them, as if
filling in those details is an exercise by the student. On those rare
occasions when I've discovered what the student was supposed to figure
out for himself, it's often been something I would never have figured
out - usually because of differences in the way you think about things.
Tim Rentsch
2023-11-19 07:27:56 UTC
Permalink
Post by James Kuyper
Most fundamentally, what I an hoping to accomplish is to convince
you to answer Keith's question.
Consider a C compiler for a machine where function pointers are
much bigger than any object pointer type. Functions might even
live in a completely different address space than data (a
so-called Harvard architecture). On such a machine there is no
sensible way to change an object pointer into a function pointer,
or vice versa. Naturally the compiler would prefer to choose not
to translate any source file that contains such a conversion.
The C standard doesn't say function pointers may be converted to
object pointers, or the other way around. The obvious thing to
do is simply give an error message and forego the translation
effort. It doesn't make sense that the standard would insist
that an implementation translate a construct that is nonsensical,
not just in a particular case but in every possible case.
Converting between function pointers and object pointers isn't
like dividing by zero.

An impediment to giving Keith an answer to his question is that
what he's looking for is not just a response but an argument.
That is, it's important that what I say not only be something he
understands but also something that convinces him. That isn't
easy because his ideas of what the C standard requires are so
weird. Keith's view of the standard is heavily influenced by
what he wants it to require, and that want often prevails over
the plain language of text in the standard. His reaction to the
idea that any non-strictly-conforming program may be rejected,
for example, or the idea that "accept" does or should mean a
program must be successfully translated and executed, are clearly
at odds with the plain language says about what programs must be
accepted. In a footnote the C standard says "Strictly conforming
programs are intended to be maximally portable among conforming
implementations." Clearly this statement is about delimiting a
class of programs, and not about the quality of implementation of
a compiler or whole implementation. But Keith doesn't get that.

Try compiling Keith's program with gcc -pedantic-errors. I expect
you will find, as I did, that gcc flags the conversion with an error
and doesn't produce an output file. The C standard allows such
behavior only if the source file being compiled is not strictly
conforming. I have more confidence in gcc's understanding of the C
standard than I do in yours.
Post by James Kuyper
...
Post by Tim Rentsch
When reading a statement or a question from someone, it's imporant
to understand what the writer thinks about what the statement or
question means. Similarly, when writing a statement or a question
for another person or persons, it's important to express what one
hopes to communicate in a way that will be understood by the
expected or intended audience as having the same meaning as what
the writer hopes to convey.
Does this last paragraph make sense? Would you say you agree with
it?
Yes, it does. And I frequently despair of ever figuring out a way
to say what I mean that will be understood as intended by you. [...]
The problem is not me understanding you, but you not understanding
me. Your mental model for how to use and understand natural
language is peculiar. Also rigid and inflexible. Probably the most
troublesome property is that you are focused on argument more than
on understanding. You read the words but don't understand the
meaning. Instead you insist on playing idiosyncratic word games,
apparently believing that how you interpret the words is universal
and absolute. It isn't. Assigning meaning to natural language is
inherently a subjective process, not an objective one. You're a lot
better at arguing than you are at listening. You might want to
think about that.
James Kuyper
2023-11-19 17:22:58 UTC
Permalink
Post by Tim Rentsch
Post by James Kuyper
Most fundamentally, what I an hoping to accomplish is to convince
you to answer Keith's question.
Consider a C compiler for a machine where function pointers are
much bigger than any object pointer type. Functions might even
live in a completely different address space than data (a
so-called Harvard architecture). On such a machine there is no
sensible way to change an object pointer into a function pointer,
or vice versa. Naturally the compiler would prefer to choose not
to translate any source file that contains such a conversion.
But the standard does not allow such behavior - if they wish to claim
conformance to the C standard, they'll have to do something other than
what they'd prefer. Since the code with undefined behavior is protected
by an if(0), an implementation is not required to generate the
impossible conversion code, and is required to accept the program.
Post by Tim Rentsch
The C standard doesn't say function pointers may be converted to
object pointers, or the other way around. The obvious thing to
do is simply give an error message and forego the translation
effort. It doesn't make sense that the standard would insist
that an implementation translate a construct that is nonsensical,
not just in a particular case but in every possible case.
I appreciate that you feel that way - but the committee resolved DR 109
in conflict with your feeling. The code referred to by DR 109 divided an
integer by an integer constant of 0, which wasn't even protected by an
if(0). It was the only line in the only function defined in that
translation unit, which was absolutely guaranteed, if function were
called, to execute that line. The only reason the committee gave for it
being strictly conforming was that there was no guarantee that the rest
of the program ever actually called the function. But the committee
decided that that fact was sufficient to make the code, in itself,
strictly conforming, and the implementation was therefore required to
accept it. If the program as a whole did call that function, that fact
would be sufficient to justify rejecting the whole program, but it was
not sufficient to justify rejecting that particular translation unit.

Basically, code with undefined behavior prevents a program from being
strictly conforming only if execution of such code is an inevitable
consequence of starting the program. If executing that code can be
avoided, then only a failure to avoid it renders the the behavior
undefined. If that failure is not realized at compile time, then the
implementation must still accept it.

If the committee ruled that way on that code, how could you possibly
expect it to support rejection of this code?
Post by Tim Rentsch
Converting between function pointers and object pointers isn't
like dividing by zero.
True - there's implementations which can meaningfully do such a
conversion - there's no meaningful way to divide an integer by 0 (a
floating point calculation could reasonably produce infinity, if
infinity can be represented on that hardware). Which makes the
committee's decision on DR 109 even stronger.
Post by Tim Rentsch
An impediment to giving Keith an answer to his question is that
what he's looking for is not just a response but an argument.
That is, it's important that what I say not only be something he
understands but also something that convinces him.
That seems reasonable. When you disagreed with Keith's assertion that
the code has no syntax errors or constraint violations, we were
interested partly because of the possibility that you were correct - if
so, there's either a rule we're unaware of or one that we didn't realize
was applicable. Of course, I would never write code like that - putting
code inside an if(0) block is almost never a reasonable thing to do,
except as an ad-hoc version control method. However, if there is such a
rule, I might end up breaking it in some other, more reasonable context,
so I want to know about it.
However, if you're unable to provide a convincing argument that your
point of view is correct, then it's a point of view that is, quite
frankly, of no interest.

...
Post by Tim Rentsch
for example, or the idea that "accept" does or should mean a
program must be successfully translated and executed, are clearly
at odds with the plain language says about what programs must be
accepted.
There's nothing at all clear about it. The standard leaves "accept"
undefined. It would be odd for the standard to use both "accept" and
"successfully translate and execute", if those two phrases had exactly
the same meaning. However, the "one program" clause is already
sufficiently odd that I'm not willing to that reason to say that they
must have different meanings.

...
Post by Tim Rentsch
Try compiling Keith's program with gcc -pedantic-errors. I expect
you will find, as I did, that gcc flags the conversion with an error
and doesn't produce an output file. The C standard allows such
behavior only if the source file being compiled is not strictly
conforming. I have more confidence in gcc's understanding of the C
standard than I do in yours.
I have considerable trust in gcc, but more trust in the actual words of
the standard. Where there's a disagreement, I favor the actual words of
the standard. And, having seen how you interpret the standard, I
certainly trust my interpretation more than yours.

...
Post by Tim Rentsch
Post by James Kuyper
Yes, it does. And I frequently despair of ever figuring out a way
to say what I mean that will be understood as intended by you. [...]
The problem is not me understanding you, but you not understanding
me. Your mental model for how to use and understand natural
language is peculiar. Also rigid and inflexible. Probably the most
troublesome property is that you are focused on argument more than
on understanding. You read the words but don't understand the
meaning. Instead you insist on playing idiosyncratic word games,
apparently believing that how you interpret the words is universal
and absolute. It isn't. Assigning meaning to natural language is
inherently a subjective process, not an objective one. You're a lot
better at arguing than you are at listening. You might want to
think about that.
Subjectivity is fine for poetry - but we're talking about a standard. To
the extent that the meaning of words is subjective, they are unsuitable
for use in a standard. The whole point of the standard is to standardize
the requirements - that is, to make them exactly the same for everyone.
If you have to use subjective interpretations to determine what the
requirements are, the standard has failed the purpose for which it was
intended. An implementor could use it's subjective opinion to justify
implementing C in a way that would be inconsistent with the subjective
opinion of a user.
Keith Thompson
2023-11-19 21:31:36 UTC
Permalink
James Kuyper <***@alumni.caltech.edu> writes:
[...]
Post by James Kuyper
I appreciate that you feel that way - but the committee resolved DR 109
in conflict with your feeling. The code referred to by DR 109 divided an
integer by an integer constant of 0, which wasn't even protected by an
if(0). It was the only line in the only function defined in that
translation unit, which was absolutely guaranteed, if function were
called, to execute that line. The only reason the committee gave for it
being strictly conforming was that there was no guarantee that the rest
of the program ever actually called the function. But the committee
decided that that fact was sufficient to make the code, in itself,
strictly conforming, and the implementation was therefore required to
accept it. If the program as a whole did call that function, that fact
would be sufficient to justify rejecting the whole program, but it was
not sufficient to justify rejecting that particular translation unit.
Basically, code with undefined behavior prevents a program from being
strictly conforming only if execution of such code is an inevitable
consequence of starting the program. If executing that code can be
avoided, then only a failure to avoid it renders the the behavior
undefined. If that failure is not realized at compile time, then the
implementation must still accept it.
If the committee ruled that way on that code, how could you possibly
expect it to support rejection of this code?
[...]

For context, DR 109 can be seen at:
https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_109.html

The code the author (Ron Guilmette) asked about was (indentation added):

int array1[5];
int array2[5];
int *p1 = &array1[0];
int *p2 = &array2[0];

int foo()
{
int i;
i = (p1 > p2); /* Must this be "successfully translated"? */
1/0; /* Must this be "successfully translated"? */
return 0;
}

The committee response was:

The C Standard uses the term ``indeterminately valued'' not
``undefined value.'' Use of an indeterminate valued object
results in undefined behavior. The footnote to subclause 5.1.1.3
points out that an implementation is free to produce any number
of diagnostics as long as a valid program is still correctly
translated. If an expression whose evaulation would result
in undefined behavior appears in a context where a constant
expression is required, the containing program is not strictly
conforming. Furthermore, if every possible execution of a given
program would result in undefined behavior, the given program
is not strictly conforming. A conforming implementation must
not fail to translate a strictly conforming program simply
because *some* possible execution of that program would result
in undefined behavior. Because foo might never be called, the
example given must be successfully translated by a conforming
implementation.

The DR was submitted in 1993, and referred to the C90 standard.
I'm not aware of anything in later editions of the standard that
would invalidate the response.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */
Tim Rentsch
2024-01-23 02:15:03 UTC
Permalink
I have just posted a response to this posting in combination
with some remarks on another posting.
Kaz Kylheku
2023-10-25 06:37:17 UTC
Permalink
Post by Tim Rentsch
First let me ask a question. Does the C standard allow an
implementation to reject any program that is not strictly
conforming?
I think, yes.

"This document does not specify
[...]
— the size or complexity of a program and its data that will exceed the
capacity of any specific data-processing system or the capacity of a
particular processor;"

"The implementation shall be able to translate and execute a program
that uses but does not exceed the following limitations for these
constructs and entities ..."

There can be programs which stay within the limits, and are even
strictly conforming, which an implementation might not handle.

An implementation has to demonstrate translating and executing only one
("a") program which strains all the limits. It is not spelled out who
gets to choose the test case. Either the implementors choose the test
case, or else if their users choose the test cases, the implementors can
fix whatever it takes to make just one of them pass, and then the
conformance requirement is satisfied.

That leaves margin for the existence of strictly conforming programs
that are not handled.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.
Tim Rentsch
2023-09-23 16:13:51 UTC
Permalink
Post by Stefan Ram
When "1" is cast to a function type and then this is called,
one would expect this call to have undefined behavior. But
Of course what you mean is there is a cast to a pointer to
function type. The C standard requires the casted-to type
to be a scalar type, and functions are not scalar types,
so a pointer-to-function type is needed.
Post by Stefan Ram
|If a converted pointer is used to call a function whose type
|is not compatible with the referenced type, the behavior is
|undefined.
. At the address "1" there is not "a function whose type is not
compatible", but no function at all.
An implementation could choose to convert an integer 1 to
a pointer to an actual function, because the mapping is
implementation defined. Of course it's very likely that it
won't be, but it could be.
Post by Stefan Ram
Is "no function at all" also
"a function whose type is not compatible with the referenced type"
or is there some other part of the specification by which a call
to an address where there is no function at all has UB?
As James Kuyper rightly points out, trying to call through a
pointer that doesn't point to an actual function is undefined
behavior by virtue of the C standard not defining what happens
in such cases. Not having a definition has exactly the same
consequences as the standard giving an explicit statement that
the behavior is undefined (because the C standard says so).
Ben Bacarisse
2023-09-23 21:54:15 UTC
Permalink
Post by Tim Rentsch
Post by Stefan Ram
When "1" is cast to a function type and then this is called,
one would expect this call to have undefined behavior. But
Of course what you mean is there is a cast to a pointer to
function type. The C standard requires the casted-to type
to be a scalar type, and functions are not scalar types,
so a pointer-to-function type is needed.
Post by Stefan Ram
|If a converted pointer is used to call a function whose type
|is not compatible with the referenced type, the behavior is
|undefined.
. At the address "1" there is not "a function whose type is not
compatible", but no function at all.
An implementation could choose to convert an integer 1 to
a pointer to an actual function, because the mapping is
implementation defined. Of course it's very likely that it
won't be, but it could be.
I worked on a C compiler that did just this. The machine put every
function into it's own memory segment, and the code segments (entirely
separate from data) were simply numbered, so there was a function 1, a
function 2 and so on. If the C library did not change much, you got to
know that a call to, say, function 34 was printf.

Programmers of a certain age, and from this side of the Atlatic, might
know the company (though maybe not the machine) from this description
alone since the memory architecture (for a personal workstation) was
borrowed from it big brothers at the same firm.
--
Ben.
Tim Rentsch
2023-09-25 13:26:43 UTC
Permalink
[converting integers to pointer-to-function]
Post by Ben Bacarisse
Post by Tim Rentsch
An implementation could choose to convert an integer 1 to
a pointer to an actual function, because the mapping is
implementation defined. Of course it's very likely that it
won't be, but it could be.
I worked on a C compiler that did just this. The machine put every
function into it's own memory segment, and the code segments (entirely
separate from data) were simply numbered, so there was a function 1, a
function 2 and so on. If the C library did not change much, you got to
know that a call to, say, function 34 was printf.
Great info! thanks.
Post by Ben Bacarisse
Programmers of a certain age, and from this side of the Atlatic, might
know the company (though maybe not the machine) from this description
alone since the memory architecture (for a personal workstation) was
borrowed from it big brothers at the same firm.
I hope you realize that there is no way I could read this
and not be curious about the particulars.
Ben Bacarisse
2023-09-25 22:41:13 UTC
Permalink
Post by Tim Rentsch
[converting integers to pointer-to-function]
Post by Ben Bacarisse
Post by Tim Rentsch
An implementation could choose to convert an integer 1 to
a pointer to an actual function, because the mapping is
implementation defined. Of course it's very likely that it
won't be, but it could be.
I worked on a C compiler that did just this. The machine put every
function into it's own memory segment, and the code segments (entirely
separate from data) were simply numbered, so there was a function 1, a
function 2 and so on. If the C library did not change much, you got to
know that a call to, say, function 34 was printf.
Great info! thanks.
Post by Ben Bacarisse
Programmers of a certain age, and from this side of the Atlatic, might
know the company (though maybe not the machine) from this description
alone since the memory architecture (for a personal workstation) was
borrowed from it big brothers at the same firm.
I hope you realize that there is no way I could read this
and not be curious about the particulars.
I only held off in case anyone wanted to play...

It was ICL. The workstation was the Perq from Three Rivers Corp. which
ICL licenced (or bought out right -- I don't recall) and, after some
internal wrangling, decided to make run Unix. It was a microcoded CPU
that could load and save instruction sets, so the instruction set to run
C could be designed as the compiler was being written. The design team,
being all existing ICL engineers, chose to copy some aspects of the
companies big machines. I had never worked on them, but I think the
"every function in it's own numbered segment" came from the 1900 and
2900 series mainframes.

The Perq was a wonderful piece of kit and very innovative, but that's a
poor predictor of success in the computer field. After I'd been working
on the project for a while, I was sent (almost literally undercover) to
go see what the workstation from a company no one had ever heard of was
like. In my naivety (I was a graduate hire), I reported back that that
it was not particularly fast and the graphics (and screen) were not up
to much, but that it did already have a full Unix implementation. It
was the Sun-1 workstation.
--
Ben.
Vir Campestris
2023-09-26 19:42:56 UTC
Permalink
Post by Ben Bacarisse
I only held off in case anyone wanted to play...
It was ICL. The workstation was the Perq from Three Rivers Corp. which
ICL licenced (or bought out right -- I don't recall) and, after some
internal wrangling, decided to make run Unix. It was a microcoded CPU
that could load and save instruction sets, so the instruction set to run
C could be designed as the compiler was being written. The design team,
being all existing ICL engineers, chose to copy some aspects of the
companies big machines. I had never worked on them, but I think the
"every function in it's own numbered segment" came from the 1900 and
2900 series mainframes.
The Perq was a wonderful piece of kit and very innovative, but that's a
poor predictor of success in the computer field. After I'd been working
on the project for a while, I was sent (almost literally undercover) to
go see what the workstation from a company no one had ever heard of was
like. In my naivety (I was a graduate hire), I reported back that that
it was not particularly fast and the graphics (and screen) were not up
to much, but that it did already have a full Unix implementation. It
was the Sun-1 workstation.
Perq. OMG I haven't thought about that in _decades_!

I have this vague memory that there was an internal dispute about the
instruction set, and that part implementations were done on two.

IIRC (and it's very vague) it originally had a Pascal-oriented
instruction set, and it was recoded for C. But it could easily have been
the other way around! I wasn't involved, but I was near some of the guys
in BRA01 who were.

Andy
Ben Bacarisse
2023-09-27 02:56:40 UTC
Permalink
Post by Vir Campestris
Post by Ben Bacarisse
I only held off in case anyone wanted to play...
It was ICL. The workstation was the Perq from Three Rivers Corp. which
ICL licenced (or bought out right -- I don't recall) and, after some
internal wrangling, decided to make run Unix. It was a microcoded CPU
that could load and save instruction sets, so the instruction set to run
C could be designed as the compiler was being written. The design team,
being all existing ICL engineers, chose to copy some aspects of the
companies big machines. I had never worked on them, but I think the
"every function in it's own numbered segment" came from the 1900 and
2900 series mainframes.
The Perq was a wonderful piece of kit and very innovative, but that's a
poor predictor of success in the computer field. After I'd been working
on the project for a while, I was sent (almost literally undercover) to
go see what the workstation from a company no one had ever heard of was
like. In my naivety (I was a graduate hire), I reported back that that
it was not particularly fast and the graphics (and screen) were not up
to much, but that it did already have a full Unix implementation. It
was the Sun-1 workstation.
Perq. OMG I haven't thought about that in _decades_!
I have this vague memory that there was an internal dispute about the
instruction set, and that part implementations were done on two.
IIRC (and it's very vague) it originally had a Pascal-oriented instruction
set, and it was recoded for C. But it could easily have been the other way
around! I wasn't involved, but I was near some of the guys in BRA01 who
were.
It came to ICL with a Pascal-oriented instruction set done by the people
at Three Rivers. The C one was largely the work of a few people at
Dalkeith. What's BRA01? Bracknell? IIRC the work was moved to Stoke
when Dalkeith was closed but I had decided to move on by then.

There was a very tentative Lisp instruction set (the OS was designed to
be able to swap instruction sets with context switches between
processes) but I don't think that was in-house. It might even have been
the work of an early customer.
--
Ben.
Vir Campestris
2023-09-28 20:30:36 UTC
Permalink
Post by Ben Bacarisse
Post by Vir Campestris
Post by Ben Bacarisse
I only held off in case anyone wanted to play...
It was ICL. The workstation was the Perq from Three Rivers Corp. which
ICL licenced (or bought out right -- I don't recall) and, after some
internal wrangling, decided to make run Unix. It was a microcoded CPU
that could load and save instruction sets, so the instruction set to run
C could be designed as the compiler was being written. The design team,
being all existing ICL engineers, chose to copy some aspects of the
companies big machines. I had never worked on them, but I think the
"every function in it's own numbered segment" came from the 1900 and
2900 series mainframes.
The Perq was a wonderful piece of kit and very innovative, but that's a
poor predictor of success in the computer field. After I'd been working
on the project for a while, I was sent (almost literally undercover) to
go see what the workstation from a company no one had ever heard of was
like. In my naivety (I was a graduate hire), I reported back that that
it was not particularly fast and the graphics (and screen) were not up
to much, but that it did already have a full Unix implementation. It
was the Sun-1 workstation.
Perq. OMG I haven't thought about that in _decades_!
I have this vague memory that there was an internal dispute about the
instruction set, and that part implementations were done on two.
IIRC (and it's very vague) it originally had a Pascal-oriented instruction
set, and it was recoded for C. But it could easily have been the other way
around! I wasn't involved, but I was near some of the guys in BRA01 who
were.
It came to ICL with a Pascal-oriented instruction set done by the people
at Three Rivers. The C one was largely the work of a few people at
Dalkeith. What's BRA01? Bracknell? IIRC the work was moved to Stoke
when Dalkeith was closed but I had decided to move on by then.
There was a very tentative Lisp instruction set (the OS was designed to
be able to swap instruction sets with context switches between
processes) but I don't think that was in-house. It might even have been
the work of an early customer.
All ICL sites had a three letter town ID, plus a two digit sequence
number. A few towns had more than 9 sites. BRA01 was the main site at
Bracknell. I assumed from what you said that you worked for ICL.

Andy
Ben Bacarisse
2023-09-28 20:54:07 UTC
Permalink
...
Post by Vir Campestris
Post by Ben Bacarisse
Post by Vir Campestris
Perq. OMG I haven't thought about that in _decades_!
I have this vague memory that there was an internal dispute about the
instruction set, and that part implementations were done on two.
IIRC (and it's very vague) it originally had a Pascal-oriented instruction
set, and it was recoded for C. But it could easily have been the other way
around! I wasn't involved, but I was near some of the guys in BRA01 who
were.
It came to ICL with a Pascal-oriented instruction set done by the people
at Three Rivers. The C one was largely the work of a few people at
Dalkeith. What's BRA01? Bracknell?
...
Post by Vir Campestris
All ICL sites had a three letter town ID, plus a two digit sequence
number. A few towns had more than 9 sites. BRA01 was the main site at
Bracknell. I assumed from what you said that you worked for ICL.
Yes, I did. And, from what you tell me, I probably worked at DAL01
without ever knowing what it was called!
--
Ben.
Vir Campestris
2023-09-29 20:58:48 UTC
Permalink
Post by Ben Bacarisse
Yes, I did. And, from what you tell me, I probably worked at DAL01
without ever knowing what it was called!
Ah, Dalkeith. Other end of the country, I never got past Manchester.

And while I was looking I found this...

<http://www.chilton-computing.org.uk/acd/sus/perq_history/overview.htm>

<https://en.wikipedia.org/wiki/International_Computers_Limited#Locations>

Andy
Tim Rentsch
2023-09-27 05:48:10 UTC
Permalink
Post by Ben Bacarisse
Post by Tim Rentsch
Post by Ben Bacarisse
Programmers of a certain age, and from this side of the Atlatic,
might know the company (though maybe not the machine) from this
description alone since the memory architecture (for a personal
workstation) was borrowed from it big brothers at the same firm.
I hope you realize that there is no way I could read this
and not be curious about the particulars.
I only held off in case anyone wanted to play...
A laudable policy. Thank you for the followup info.
Loading...