[comp.lang.c] is

randy@gtx.com (Randy D. Miller) (11/13/90)

Can NULL always be cast to a function pointer, even in implementations
that legally define NULL as (void *)0?  For example, are the following
lines legal?

   /* example 1 */     int (*f1)(void) = NULL;
   /* example 2 */     int (*f2)(void) = (int (*)(void))NULL;

According to ANSI X3.159-1989 3.2.2.3 (pointer conversions) and
3.3.16.1 (assignment operator), example #1 is clearly legal even when
NULL is (void *)0, because the null pointer constant is a special case
(but no other pointer to void may be converted to a pointer to a
function).  But is the cast in example #2 legal?  It just does
explicitly what is done implicitly in example #1.  I tried it on a few
compilers and lints that define NULL as (void *)0; only Microsoft
version 6.0 rejected it with an "illegal cast" error message (using 
cl -W4 -Za).

Chris Torek, in <15047@mimsy.UUCP> said,
>(remember that a cast is equivalent to assignment to an unnamed
>temporary variable---I like to consider `assignment' to include all
>casts).

If that is true, then example #2 must also be legal.  However, I can't
see anything in the Standard that specifies that all the constraints of
section 3.3.16.1 (assignment operator) apply to section 3.3.4 (cast
operator).  3.3.16.1 specifically allows that a null pointer constant
may be assigned to "a pointer," which includes function pointers.
3.3.4 doesn't specifically treat casts of the null pointer constant.
But maybe this wording in 3.3.4 associates the two sections:

   "Conversions that involve pointers (other than as permitted by the
   constraints of 3.3.16.1) shall be specified by means of an explicit
   cast;"

But surely that can't mean that if a conversion is "permitted" by
3.3.16.1, it *cannot* have an explicit cast, can it?  If so, then the
cast operator is unlike an assignment in this one special case where
the null pointer constant is converted to a function pointer, and you
must only make the assignment *without* an explicit cast.  Are there
any other cases where an implicit conversion is legal, but the
identical explicit cast is not?  Or is Chris Torek right and MSC 6.0
wrong?

-- 
Randy D. Miller   sun!sunburn!gtx!randy  or  hrc!gtx!randy  or  randy@gtx.UUCP
GTX Corp., 8836 N. 23rd Avenue, Phoenix, Arizona  85021   (602) 870-1696

gwyn@smoke.brl.mil (Doug Gwyn) (11/14/90)

In article <1391@gtx.com> randy@gtx.UUCP (Randy D. Miller) writes:
-Can NULL always be cast to a function pointer, even in implementations
-that legally define NULL as (void *)0?  For example, are the following
-lines legal?
-   /* example 1 */     int (*f1)(void) = NULL;
-   /* example 2 */     int (*f2)(void) = (int (*)(void))NULL;

No, the second is not required to be supported by the implementation,
but the first is (3.2.2.3).

volpe@camelback.crd.ge.com (Christopher R Volpe) (11/15/90)

In article <14457@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
|>In article <1391@gtx.com> randy@gtx.UUCP (Randy D. Miller) writes:
|>-Can NULL always be cast to a function pointer, even in implementations
|>-that legally define NULL as (void *)0?  For example, are the following
|>-lines legal?
|>-   /* example 1 */     int (*f1)(void) = NULL;
|>-   /* example 2 */     int (*f2)(void) = (int (*)(void))NULL;
|>
|>No, the second is not required to be supported by the implementation,
|>but the first is (3.2.2.3).

What's wrong with the second example? A null pointer constant 
represented by "(void *)0" is first cast to a function pointer (perfectly
legal), and the resulting expression is assigned to the variable
f2, of identical type, which should also be legal. Can you explain the
problem?
         
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

gwyn@smoke.brl.mil (Doug Gwyn) (11/16/90)

In article <13799@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes:
-In article <14457@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
-|>In article <1391@gtx.com> randy@gtx.UUCP (Randy D. Miller) writes:
-|>-Can NULL always be cast to a function pointer, even in implementations
-|>-that legally define NULL as (void *)0?  For example, are the following
-|>-lines legal?
-|>-   /* example 1 */     int (*f1)(void) = NULL;
-|>-   /* example 2 */     int (*f2)(void) = (int (*)(void))NULL;
-|>No, the second is not required to be supported by the implementation,
-|>but the first is (3.2.2.3).
-What's wrong with the second example? A null pointer constant 
-represented by "(void *)0" is first cast to a function pointer (perfectly
-legal), and the resulting expression is assigned to the variable
-f2, of identical type, which should also be legal. Can you explain the
-problem?

The problem is that you can't point to any requirement in the standard
that the implementation support casting (void*)0 to a pointer to function.

A null pointer constant can be safely compared to a pointer to function,
and it can be safely assigned to a pointer to function modifiable lvalue,
but that's it.  Casting a null pointer constant to pointer to function is
not permitted in a strictly conforming program.

barmar@think.com (Barry Margolin) (11/16/90)

In article <14463@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>A null pointer constant can be safely compared to a pointer to function,
>and it can be safely assigned to a pointer to function modifiable lvalue,
>but that's it.  Casting a null pointer constant to pointer to function is
>not permitted in a strictly conforming program.

I thought that the semantics of casting were equivalent to that of
assignment to an object of the given type.  So how can an assignment be
valid but the corresponding cast be invalid?  Or is this equivalence an old
rule of thumb that isn't precisely correct?  Are there other examples of
assignments that don't have corresponding casts or vice versa?


--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

volpe@camelback.crd.ge.com (Christopher R Volpe) (11/16/90)

In article <14463@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
|>In article <13799@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com
(Christopher R Volpe) writes:
|>-In article <14457@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
|>-|>In article <1391@gtx.com> randy@gtx.UUCP (Randy D. Miller) writes:

[Deleted example]

|>-|>No, the second is not required to be supported by the implementation,
|>-|>but the first is (3.2.2.3).
|>-What's wrong with the second example? A null pointer constant 
|>-represented by "(void *)0" is first cast to a function pointer (perfectly
|>-legal), and the resulting expression is assigned to the variable
|>-f2, of identical type, which should also be legal. Can you explain the
|>-problem?
|>
|>The problem is that you can't point to any requirement in the standard
|>that the implementation support casting (void*)0 to a pointer to function.

It seems to me that the "spirit" of the cast operator is to make
explicit a conversion where otherwise there would be no conversion
or the conversion would be implicit rather than explicit. If the
implicit conversion in example 1 is allowed, shouldn't the explicit
conversion to the same thing be allowed? 

If the above is not true, then where in the standard does it say that
an implementation must support casting an int into a float? I
don't see it in 3.3.4? Doesn't this fall into the same category?
Neither violates the constraint in 3.3.4, but then neither is explicitly
supported. Both *conversions* are supported, if done via assignment
operators.
                       
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

gwyn@smoke.brl.mil (Doug Gwyn) (11/16/90)

In article <1990Nov15.204858.20696@Think.COM> barmar@think.com (Barry Margolin) writes:
>I thought that the semantics of casting were equivalent to that of
>assignment to an object of the given type.

Nope, conversion and assignment have different rules.

gwyn@smoke.brl.mil (Doug Gwyn) (11/16/90)

In article <13870@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes:
>If the above is not true, then where in the standard does it say that
>an implementation must support casting an int into a float?

Conversions involving pointers have an explicit set of requirements
in 3.3.4 beyond the basic semantics.  The basic semantics suffice for
interconversion of arithmetic values; 3.2.1.3 gives details.

>Both *conversions* are supported, if done via assignment operators.

I can't parse that.

Certain license beyond 3.3.4 is granted in 3.3.16.1 for assignment
involving certain pointers that meet the constraints of 3.3.16.1.

If you have further questions about this, I suggest you send X3 a request
for an interpretation ruling.  Possibilities for misunderstanding are
limitless, and I really cannot spend much more time on this than I
already have.  I think the standard is clear on this.

volpe@camelback.crd.ge.com (Christopher R Volpe) (11/16/90)

Sorry for wasting the bandwidth and your time, but...

In article <14484@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
|>In article <13870@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com
(Christopher R Volpe) writes:
|>>If the above is not true, then where in the standard does it say that
|>>an implementation must support casting an int into a float?
|>
|>Conversions involving pointers have an explicit set of requirements
|>in 3.3.4 beyond the basic semantics.  The basic semantics suffice for
|>interconversion of arithmetic values; 3.2.1.3 gives details.

3.3.4 doesn't clearly define "basic semantics" and certainly doesn't
say that it supports interconversion of arithmetic values. 3.2.1.3
describes what happens WHEN this type of conversion takes place, but 3.3.4
doesn't say that it supports the conversions in 3.2.1.3. If this is
to be inferred from the phrase "the type name shall specify ... scalar type",
in 3.3.4 Constraints, then the pointer conversion in question falls
under the same category. 

The int->float conversion is no more explicitly sanctioned in 3.3.4 than
the pointer conversion. The use of the word "converts" in 3.3.4 Semantics
"""""implies""""" (note quotes) that conversions that are legal
"in general" are legal in a cast. Finally, K&R2 (yes, I know it ain't
gospel) says in 2.7:
      The precise meaning of the cast is AS IF [emphasis mine] the
      expression were assigned to a variable of the specified type...
So, is K&R2 wrong here? K&R1 says the same thing, and if this is
legal and well defined in K&R1, and not legal and well defined in
the Standard, and no diagnostic is required (since no constraint is
violated) then this qualifies as a QUIET CHANGE, which the rationale
fails to mention.

BTW, Doug, since you claim that "(int (*)())NULL" is illegal, I assume
you mean that it is illegal whether NULL is defined as "0" or "(void *)0",
right? Your argument about null pointer constants doesn't (I assume)
depend on the form in which that constant is specified. So the above
subject line is a little misleading. 

|>
|>>Both *conversions* are supported, if done via assignment operators.
|>
|>I can't parse that.

Ok, I guess I wasn't clear. I simply meant that each type of conversion
(int->float, null_pointer_constant->any_pointer_type) is
sanctioned in 3.3.16.1. No cast is REQUIRED for either. So an
implemention must be able to perform this kind of conversion, even if
the Standard doesn't explicitly say that this kind of conversion
can be done via a cast operator.

|>
|>Certain license beyond 3.3.4 is granted in 3.3.16.1 for assignment
|>involving certain pointers that meet the constraints of 3.3.16.1.
|>
|>If you have further questions about this, I suggest you send X3 a request
|>for an interpretation ruling.  Possibilities for misunderstanding are
|>limitless, and I really cannot spend much more time on this than I
|>already have.  I think the standard is clear on this.

Ok, I would like to send X3 a request for an interpretation ruling.
Can someone kindly tell me how to go about doing this?  At the very
least, even if X3 doesn't agree with me about the legality issue, I
think it would be interested to know about the discrepancy with
the rationale, which states that "the only integer that can safely
be converted to a pointer is the constant 0 [3.2.2.3 rat.]" and
that "The conversion of the integer constant 0 to a pointer is
defined similarly to the Base Document [3.3.4 rat.(cast operator)]".    
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

gwyn@smoke.brl.mil (Doug Gwyn) (11/17/90)

In article <13920@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes:
>In article <14484@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
>|>In article <13870@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com
>(Christopher R Volpe) writes:
>|>>If the above is not true, then where in the standard does it say that
>|>>an implementation must support casting an int into a float?
>|>Conversions involving pointers have an explicit set of requirements
>|>in 3.3.4 beyond the basic semantics.  The basic semantics suffice for
>|>interconversion of arithmetic values; 3.2.1.3 gives details.
>3.3.4 doesn't clearly define "basic semantics" and certainly doesn't
>say that it supports interconversion of arithmetic values.

The first paragraph of 3.3.4 Semantics is what I referred to as the basic
semantics.  The rest of that section are further conditions that apply
solely to conversions involving pointers.  It is clear what arithmetic
values are, but not clear what pointer values are, thus the need for
additional rules explaining what is required to be supported for pointers.

>3.3.4 doesn't say that it supports the conversions in 3.2.1.3.

For conversion by casts, 3.3.4 Semantics first paragraph covers it.
For conversions upon assignment or an operation specified to be as if
by simple assignment, 3.3.16.1 covers it.  3.3.16.1 Constraints limits
what is permitted for pointers (in a strictly conforming program), and
3.3.4 Semantics past the first paragraph spells out all other ways that
pointers may be converted (in a strictly conforming program).

If you think that these portions should not have been buried in the
sections where they are in fact found, many on X3J11 would tend to
agree with you.

>So, is K&R2 wrong here?

Yes, assignment and conversion by cast have slightly different semantics.
(I won't attempt to guess whether or not this was intentional, but it
should be presumed to be so unless X3J11 later declares otherwise.)

>BTW, Doug, since you claim that "(int (*)())NULL" is illegal, I assume
>you mean that it is illegal whether NULL is defined as "0" or "(void *)0",
>right? Your argument about null pointer constants doesn't (I assume)
>depend on the form in which that constant is specified. So the above
>subject line is a little misleading. 

The subject line was inherited from the original posting.
Indeed, I don't think an implementation is required to accept
	(int (*)())0
but it also is not required to diagnose it (because it is not a syntactic
or constraint violation), and thus it may elect to support it.  (Many old
implementations did.  They need not stop doing so, but portable coders
should become aware of this and avoid using such constructs.)

>Ok, I would like to send X3 a request for an interpretation ruling.
>Can someone kindly tell me how to go about doing this?

Send a letter to X3 Secretariat: Computer and Business Equipment
Manufacturers Association, 311 First Street, N.W., Suite 500,
Washington DC 20001-2178.  Briefly state the issue, including
references to the relevant parts of the C standard, give a good
example, state the possible alternative interpretations, give an
argument for the interpretation you would prefer, and request
that the matter be brought before the X3J11 technical committee
for a decision on how to interpret the standard in such a context.

P.S.  Reminder: These opinions are mine, not necessarily X3J11's,
and X3J11 may decide upon a different interpretation.

chris@mimsy.umd.edu (Chris Torek) (11/18/90)

In article <14498@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>Yes, assignment and conversion by cast have slightly different semantics.

Aside from casts being permitted where assignments are not, I think this
is a Bad Thing.  Having a cast `mean' the same thing as an assignment,
with the addition that (being explicit) it is a little more `powerful'
and can override objections like `the pointers have different types',
significantly reduces what I might call the `cognitive size' of the
language.  It is much easier to think of

	i = (short)j;

as `get the value of j, put it in a short, and pull it back out' than
as `apply a short cast, with separate semantics from assignment to a
short, to get a result'.  The latter requires one to carry the separate
semantics around in one's head in order to be able to decide what the
code above means.  (Obviously one must always carry assignment semantics
around, unless one never uses assignment.  This, however, doubles the
load.)

>(I won't attempt to guess whether or not this was intentional, but it
>should be presumed to be so unless X3J11 later declares otherwise.)

I hope they do so declare.

>Indeed, I don't think an implementation is required to accept
>	(int (*)())0
[or	(int (*)(void))0	?]
>but it also is not required to diagnose it (because it is not a syntactic
>or constraint violation), and thus it may elect to support it.  (Many old
>implementations did.  They need not stop doing so, but portable coders
>should become aware of this and avoid using such constructs.)

This would make it difficult to pass a `nil pointer to function of no
arguments returning int' to a function for which no prototype can be
provided.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.brl.mil (Doug Gwyn) (11/18/90)

In article <27780@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
>This would make it difficult to pass a `nil pointer to function of no
>arguments returning int' to a function for which no prototype can be
>provided.

But not impossible, as a suitable pointer variable initialized to a null
pointer (trivially accomplished) could be used.  You could even define
it to be const-qualified and hope the compiler is good at optimizations.

steve@groucho.ucar.edu (Steve Emmerson) (11/18/90)

In <14517@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:

>In article <27780@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
>>This would make it difficult to pass a `nil pointer to function of no
>>arguments returning int' to a function for which no prototype can be
>>provided.

>But not impossible, as a suitable pointer variable initialized to a null
>pointer (trivially accomplished) could be used.  You could even define
>it to be const-qualified and hope the compiler is good at optimizations.

Whoa!  So the following is non-conforming:

    foo((int((*)(void)))NULL)

where the prototype for foo() isn't in scope and it takes the indicated
argument type.

This has implications for those of us who, at least, *try* to write 
portable code (though, as Doug indicated, the problem is avoidable).

Are you sure of this, Doug?  Or do we need an Official Ruling?  

I know.  When in doubt, request a ruling ;-).

Steve Emmerson        steve@unidata.ucar.edu        ...!ncar!unidata!steve