Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #176196 > unrolled thread

Re: Call to a function

Started byKaz Kylheku <864-117-4973@kylheku.com>
First post2023-09-22 15:21 +0000
Last post2023-10-03 20:41 -0700
Articles 11 — 5 participants

Back to article view | Back to comp.lang.c

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: Call to a function Kaz Kylheku <864-117-4973@kylheku.com> - 2023-09-22 15:21 +0000
    Re: Call to a function James Kuyper <jameskuyper@alumni.caltech.edu> - 2023-09-22 11:39 -0400
      Re: Call to a function Kaz Kylheku <864-117-4973@kylheku.com> - 2023-09-22 16:47 +0000
        Re: Call to a function James Kuyper <jameskuyper@alumni.caltech.edu> - 2023-09-23 01:26 -0400
        Re: Call to a function Tim Rentsch <tr.17687@z991.linuxsc.com> - 2023-09-23 07:38 -0700
    Re: Call to a function Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2023-09-22 11:54 -0700
      Re: Call to a function Tim Rentsch <tr.17687@z991.linuxsc.com> - 2023-10-03 06:34 -0700
        Re: Call to a function Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2023-10-03 15:13 -0700
          Re: Call to a function Kaz Kylheku <864-117-4973@kylheku.com> - 2023-10-04 01:52 +0000
            Re: Call to a function Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2023-10-03 19:13 -0700
          Re: Call to a function "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2023-10-03 20:41 -0700

#176196 — Re: Call to a function

FromKaz Kylheku <864-117-4973@kylheku.com>
Date2023-09-22 15:21 +0000
SubjectRe: Call to a function
Message-ID<20230922081706.858@kylheku.com>
On 2023-09-22, Stefan Ram <ram@zedat.fu-berlin.de> wrote:
>   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.

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.

>   . 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: @Kazinator@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

[toc] | [next] | [standalone]


#176197

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2023-09-22 11:39 -0400
Message-ID<uekcfs$a1at$1@dont-email.me>
In reply to#176196
On 9/22/23 11:21, Kaz Kylheku wrote:
> On 2023-09-22, Stefan Ram <ram@zedat.fu-berlin.de> wrote:
>>   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.
> 
> 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.
> 
>>   . 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;
"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.

[toc] | [prev] | [next] | [standalone]


#176199

FromKaz Kylheku <864-117-4973@kylheku.com>
Date2023-09-22 16:47 +0000
Message-ID<20230922091405.491@kylheku.com>
In reply to#176197
On 2023-09-22, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
> On 9/22/23 11:21, Kaz Kylheku wrote:
>> The conversion is not supported by ISO C, and so itself has
>> undefined behavior:
>> 
>>   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: @Kazinator@mstdn.ca
NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

[toc] | [prev] | [next] | [standalone]


#176235

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2023-09-23 01:26 -0400
Message-ID<uelsth$lofc$1@dont-email.me>
In reply to#176199
On 9/22/23 12:47, Kaz Kylheku wrote:
> On 2023-09-22, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
>> On 9/22/23 11:21, Kaz Kylheku wrote:
>>> The conversion is not supported by ISO C, and so itself has
>>> undefined behavior:
>>>
>>>   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.

That's 6.5.2.2p9 in the latest version of the standard that I have
access to.

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

[toc] | [prev] | [next] | [standalone]


#176248

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2023-09-23 07:38 -0700
Message-ID<86v8c1j6op.fsf@linuxsc.com>
In reply to#176199
Kaz Kylheku <864-117-4973@kylheku.com> writes:

> On 2023-09-22, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
>
>> On 9/22/23 11:21, Kaz Kylheku wrote:
>>
>>> The conversion is not supported by ISO C, and so itself has
>>> undefined behavior:
>>>
>>>   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.

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.

[toc] | [prev] | [next] | [standalone]


#176208

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2023-09-22 11:54 -0700
Message-ID<87zg1et4wv.fsf@nosuchdomain.example.com>
In reply to#176196
Kaz Kylheku <864-117-4973@kylheku.com> writes:
> On 2023-09-22, Stefan Ram <ram@zedat.fu-berlin.de> wrote:
>>   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.
>
> 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.
>
>>   . 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;

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.

> 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+u@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#177212

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2023-10-03 06:34 -0700
Message-ID<86jzs3de3h.fsf@linuxsc.com>
In reply to#176208
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

> Kaz Kylheku <864-117-4973@kylheku.com> writes:
>
>> On 2023-09-22, Stefan Ram <ram@zedat.fu-berlin.de> wrote:
>>
>>>   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.
>>
>> 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.
>>
>>>   . 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;
>
> 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.

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

[toc] | [prev] | [next] | [standalone]


#177360

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2023-10-03 15:13 -0700
Message-ID<87h6n7tkv4.fsf@nosuchdomain.example.com>
In reply to#177212
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
[...]
> 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+u@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#177428

FromKaz Kylheku <864-117-4973@kylheku.com>
Date2023-10-04 01:52 +0000
Message-ID<20231003163801.879@kylheku.com>
In reply to#177360
On 2023-10-03, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
> [...]
>> 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.

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

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

[toc] | [prev] | [next] | [standalone]


#177443

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2023-10-03 19:13 -0700
Message-ID<87zg0z3zj0.fsf@nosuchdomain.example.com>
In reply to#177428
Kaz Kylheku <864-117-4973@kylheku.com> writes:
> On 2023-10-03, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
>> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
>> [...]
>>> 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.

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

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

>> 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?

> 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+u@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#177490

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2023-10-03 20:41 -0700
Message-ID<ufimtk$3g6$2@dont-email.me>
In reply to#177360
On 10/3/2023 3:13 PM, Keith Thompson wrote:
> Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
> [...]
>> 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

https://youtu.be/JOiZP8FS5Ww

;^)


[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web