Re: CURRENT: CLANG 3.3 and -stad=c++11 and -stdlib=libc++: isnan()/isninf() oddity

From: Bruce Evans <brde_at_optusnet.com.au>
Date: Thu, 11 Jul 2013 23:02:10 +1000 (EST)
On Thu, 11 Jul 2013, Tijl Coosemans wrote:

> On 2013-07-11 06:21, Bruce Evans wrote:
>> On Wed, 10 Jul 2013, Garrett Wollman wrote:
>>> <<On Wed, 10 Jul 2013 22:12:59 +0200, Tijl Coosemans <tijl_at_freebsd.org> said:
>>>> I think isnan(double) and isinf(double) in math.h should only be
>>>> visible if (_BSD_VISIBLE || _XSI_VISIBLE) && __ISO_C_VISIBLE < 1999.
>>>> For C99 and higher there should only be the isnan/isinf macros.
>>>
>>> I believe you are correct.  POSIX.1-2008 (which is aligned with C99)
>>> consistently calls isnan() a "macro", and gives a pseudo-prototype of
>>>
>>>     int isnan(real-floating x);
>>
>> Almost any macro may be implemented as a function, if no conforming
>> program can tell the difference.  It is impossible for technical reasons
>> to implement isnan() as a macro (except on weird implementations where
>> all real-floating types are physically the same).  In the FreeBSD
>> implementation, isnan() is a macro, but it is also a function, and
>> the macro expands to the function in double precision:
>>
>> % #define    isnan(x)                    \
>> %     ((sizeof (x) == sizeof (float)) ? __isnanf(x)    \
>> %     : (sizeof (x) == sizeof (double)) ? isnan(x)    \
>> %     : __isnanl(x))
>
> The C99 standard says isnan is a macro. I would say that only means
> defined(isnan) is true. Whether that macro then expands to function
> calls or not is not important.

I think it means only that defined(isnan) is true.  isnan() can still be
a function (declared or just in the compile-time namespace somewhere,
or in a library object).  It is reserved in the compile-time namespace,
and the standard doesn't cover library objects, so conforming applications
can't reference either except via the isnan() macro (if that has its
strange historical implementation).

>> I don't see how any conforming program can access the isnan() function
>> directly.  It is just as protected as __isnan() would be.  (isnan)()
>> gives the function (the function prototype uses this), but conforming
>> programs can't do that since the function might not exist.
>
> I don't think the standard allows a function to be declared with the same
> name as a standard macro (it does allow the reverse: define a macro with
> the same name as a standard function). I believe the following code is
> C99 conforming but it currently does not compile with our math.h:
>
> ------
> #include <math.h>
>
> int (isnan)(int a, int b, int c) {
>        return (a + b + c);
> }
> ------

I think isnan is just reserved, so you can't redefine it an any way.  I
think the reverse is even less allowed.  Almost any standard function may
be implemented as a macro, and then any macro definition of it would
conflict with the previous macro even more than with a previous prototype.
E.g.:

 	/* Header. */
 	void exit(int);
 	#define exit(x) __exit(x)

 	/* Application. */
 	#undef exit		/* non-conforming */
 	#define exit(x) my_exit(x)	/* conflicts without the #undef */

Now suppose the header doesn't define exit().

 	#define exit(x) my_exit(x)

This hides the protoype but doesn't automatically cause problems, especially
if exit() is not used after this point.  But this is still non-conforming,
since exit() is reserved.

Here are some relevant parts of C99 (n869.txt):

%%%
          -- Each  identifier  with  file scope listed in any of the
             following  subclauses  (including  the  future  library
             directions)  is  reserved  for  use  as macro and as an
             identifier with file scope in the same  name  space  if
             any of its associated headers is included.

        [#2]  No  other  identifiers  are  reserved.  If the program
        declares or defines an identifier in a context in  which  it
        is  reserved  (other than as allowed by 7.1.4), or defines a
        reserved  identifier  as  a  macro  name,  the  behavior  is
        undefined.

        [#3]   If  the  program  removes  (with  #undef)  any  macro
        definition of an identifier in the first group listed above,
        the behavior is undefined.
%%%

Without any include of a header that is specified to declare exit(),
file scope things are permitted for it, including defining it and
making it a static function, but not making it an extern function.

isnan is reserved for use as a macro and as an identifier with file
scope by the first clause above.  Thus (isnan) cannot even be defined
as a static function.  But (isnan) is not reserved in inner scopes.
I thought that declarations like "int (isnan);" are impossible since
they look like syntax errors, but this syntax seems to be allowed an
actually work with gcc-3.3.3 and TenDRA-5.0.0.  So you can have
variables with silly names like (isnan) and (getchar) :-).  However,
(NULL) for a variable name doesn't work, and (isnan) is a syntax error
for struct member names.  The compilers may be correct in allowing
(isnan) but not (NULL) for variables.  isnan happens to be function-like,
so the parentheses are special for (isnan), but the parentheses are not
special for (NULL).  cpp confirms this -- NULL inside of parentheses
still gets expanded.  The above quote of C99 can cover both since it
just says that isnan is reserved for use as a macro.  In (isnan), the
parentheses prevent isnan being interpreted as a macro so the reservation
doesn't apply.

Bruce
Received on Thu Jul 11 2013 - 11:02:25 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:39 UTC