Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 7 Mar 2012 14:29:28 +1100 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Dimitry Andric <dim@freebsd.org>
Cc:        svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org
Subject:   Re: svn commit: r232620 - in head/include: . xlocale
Message-ID:  <20120307120126.K969@besplex.bde.org>
In-Reply-To: <201203062015.q26KFNJA055743@svn.freebsd.org>
References:  <201203062015.q26KFNJA055743@svn.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, 6 Mar 2012, Dimitry Andric wrote:

> Log:
>  After r232498, programs built with -ansi or -std=c89 including <ctype.h>
>  would not compile anymore, due to plain 'inline' keywords.  Fix this by
>  using __inline instead.
>
>  Reported by:	Jia-Shiun Li <jiashiun@gmail.com>
>  Discussed with:	theraven

Any chance of also fixing the style bugs?  Old ctype was one of the
most carefully written headers, so it was almost free of style bugs.

> Modified: head/include/runetype.h
> ==============================================================================
> --- head/include/runetype.h	Tue Mar  6 20:01:25 2012	(r232619)
> +++ head/include/runetype.h	Tue Mar  6 20:15:23 2012	(r232620)
> @@ -90,7 +90,7 @@ extern const _RuneLocale *_CurrentRuneLo
> extern const _RuneLocale *__getCurrentRuneLocale(void);

Old ctype had no style bugs like declaring prototypes as extern.

Old ctype carefully indented prototypes.

> #else
> extern _Thread_local const _RuneLocale *_ThreadRuneLocale;
> -static inline const _RuneLocale *__getCurrentRuneLocale(void)
> +static __inline const _RuneLocale *__getCurrentRuneLocale(void)
> {
>
> 	if (_ThreadRuneLocale)
>
> Modified: head/include/xlocale/_ctype.h
> ==============================================================================
> --- head/include/xlocale/_ctype.h	Tue Mar  6 20:01:25 2012	(r232619)
> +++ head/include/xlocale/_ctype.h	Tue Mar  6 20:15:23 2012	(r232620)
> @@ -55,11 +55,11 @@ _RuneLocale	*__runes_for_locale(locale_t
> #ifndef _XLOCALE_INLINE
> #if __GNUC__ && !__GNUC_STDC_INLINE__
> /* GNU89 inline has nonstandard semantics. */
> -#define _XLOCALE_INLINE extern inline
> +#define _XLOCALE_INLINE extern __inline

Old ctype always used a tab after #define.

Old ctype even used a tab after macro names.

> #else
> /* Hack to work around people who define inline away */

Old ctype mostly terminated its comments.

This comment now doesn't match the code.  The code is now supposed to
use __inline, so it isn't affected by defining away inline.

> #ifdef inline

This part of the code was nonsense and hasn't really changed.  The
code should be using __inline, so it shouldn't be affected by any
definition of inline.  <sys/cdefs.h> defines both inline and __inline
to support old compilers.  FreeBSD headers should not have their own
ifdefs for this.

> -#define _XLOCALE_INLINE __inline static
> +#define _XLOCALE_INLINE static __inline

This change is just a partial style fix.  __inline was already used here,
and still is, but in a different order.

2 style bugs remain in this line (same 2 as above).

> #else
> /* Define with C++ / C99 compatible semantics */
> #define _XLOCALE_INLINE inline

This still doesn't use __inline, so it is still broken in most cases where
it is reached at all, if any.  `inline' not being defined has very little
to do with whether it actually works here.

There are some complications for old gcc extern inline vs C99 extern
inline, but otherwise this code should just use __inline as defined
in <sys/cdefs.h> and not add its own ifdef tangle to the one there.
Or better yet, use only static __inline like old ctype does.  I once
hoped to replace the static inlines in ctype.h by extern inline ones,
but that was before changing the semantics of extern inline made it
evern harder to use.  Using it in libm mainly gives unportability to
C90.

Repeating the above, with the ifdefs untangled a bit:

> Modified: head/include/xlocale/_ctype.h
> ==============================================================================
> --- head/include/xlocale/_ctype.h	Tue Mar  6 20:01:25 2012	(r232619)
> +++ head/include/xlocale/_ctype.h	Tue Mar  6 20:15:23 2012	(r232620)
> @@ -55,11 +55,11 @@ _RuneLocale	*__runes_for_locale(locale_t
> #ifndef _XLOCALE_INLINE
> #if __GNUC__ && !__GNUC_STDC_INLINE__

The first part of this should be written as defined(__GNUC__), to support
broken compilers that warn about an undefined identifier being used in an
ifdef.

Unfortunately, the simpler test 'ifdef __GNUC_GNU_INLINE__' seems to be
unusable, since old versions of gcc that only have gnu inline also don't
have a feature test for it.

> /* GNU89 inline has nonstandard semantics. */
> -#define _XLOCALE_INLINE extern inline
> +#define _XLOCALE_INLINE extern __inline

I'm not sure if this actually works.  Old gnu extern inline is quite
different from C99 plain inline.

Anyway, the above seems to fix the main bug.  Old version of gcc have
both inline and __inline, but -ansi or -std=c89 kills both.  Except,
very old versions of gcc (gcc-1?) have neither.  These are partially
handled by <sys/cdefs.h> killing __inline.  <sys/cdefs.h> normally
doesn't kill inline; it takes the namespace pollution NO_ANSI_KEYWORDS
to do that; the semantics of this are even subtler.  The fix seems
to be only partial, since "extern inline" in any spelling simply
doesn't work for compilers that don't support inline in any form.
Here we have slightly wrong ifdefs that lead gcc-1 being misclassified
and then mishandled in this clause of the ifdef tangle.  The non-working
spelling is now "extern" instead of "extern inline".  The "extern" has
been reduced to a style bug, since without a non-null "inline", it has
no effect, but it needs to have an effect to work.

> #else

Now we have classified the compiler as either non-gcc, or gcc with
STDC extern inline.

> /* Hack to work around people who define inline away */
> #ifdef inline
> -#define _XLOCALE_INLINE __inline static
> +#define _XLOCALE_INLINE static __inline
> #else
> /* Define with C++ / C99 compatible semantics */
> #define _XLOCALE_INLINE inline

It is now clearer that the inline ifdef has almost no effect.  `inline'
should never be defined.  <sys/cdefs.h> only defines it under weird
obsolete conditions, and the application shouldn't define it.  But if
it might be defined, then we get the following behaviour from its ifdef:
- if it is defined, then we use static __inline.  If this actually works,
   then it is what we should use, without ifdefs, in all cases.
- if it is not defined, then we assume C99 extern inline semantics (with
   implicit extern (IIRC, the extern is still needed, but is placed
   weirdly, not in the header file but in the C file; thus it is not
   part of the public interface and needs slightly different ifdefs).

   This assumption is invalid, since we haven't done a full classification
   of the compiler.  We only know that the compiler supports C99 extern
   semantics if it is gcc.  This leaves the following broken cases:
   - any C90 compiler that is not gcc
   - any K&R compiler
   In both of the cases, the compiler won't support inline in any form, so
   the only chance for working is to use "static __inline" where
   <sys/cdefs.h> kills __inline, or to not have any inline functions in
   headers.  Old <ctype.h> did the latter, and still works with sources
   installed on 19 Feb 2012.  Now it declares bazillions of inline
   functions.  Most kernel headers do the former.

Differences between the results of cc -E and cc -E
-I<up-to-date-src/include> on the file

#undef __GNUC__
#include <ctype.h>

% diff -c2 a~ a
% *** a~	Wed Mar  7 02:30:02 2012
% --- a	Wed Mar  7 02:30:11 2012
% ***************
% *** 4,11 ****
%   # 1 "a.c"
% 
% ! # 1 "/usr/include/ctype.h" 1 3 4
% ! # 44 "/usr/include/ctype.h" 3 4
%   # 1 "/usr/include/sys/cdefs.h" 1 3 4
% ! # 45 "/usr/include/ctype.h" 2 3 4
%   # 1 "/usr/include/sys/_types.h" 1 3 4
%   # 33 "/usr/include/sys/_types.h" 3 4
% --- 4,11 ----
%   # 1 "a.c"
% 
% ! # 1 "include/ctype.h" 1
% ! # 44 "include/ctype.h"
%   # 1 "/usr/include/sys/cdefs.h" 1 3 4
% ! # 45 "include/ctype.h" 2
%   # 1 "/usr/include/sys/_types.h" 1 3 4
%   # 33 "/usr/include/sys/_types.h" 3 4
% ***************
% *** 136,142 ****
%    __int64_t _mbstateL;
%   } __mbstate_t;
% ! # 46 "/usr/include/ctype.h" 2 3 4
% ! # 1 "/usr/include/_ctype.h" 1 3 4
% ! # 70 "/usr/include/_ctype.h" 3 4
% 
%   unsigned long ___runetype(__ct_rune_t) ;
% --- 136,142 ----
%    __int64_t _mbstateL;
%   } __mbstate_t;
% ! # 46 "include/ctype.h" 2
% ! # 1 "include/_ctype.h" 1
% ! # 70 "include/_ctype.h"
% 
%   unsigned long ___runetype(__ct_rune_t) ;
% ***************
% *** 144,150 ****
%   __ct_rune_t ___toupper(__ct_rune_t) ;
% 
% ! # 86 "/usr/include/_ctype.h" 3 4
%   extern int __mb_sb_limit;
% ! # 172 "/usr/include/_ctype.h" 3 4
% 
%   int __maskrune(__ct_rune_t, unsigned long);
% --- 144,150 ----
%   __ct_rune_t ___toupper(__ct_rune_t) ;
% 
% ! # 86 "include/_ctype.h"
%   extern int __mb_sb_limit;
% ! # 172 "include/_ctype.h"
% 
%   int __maskrune(__ct_rune_t, unsigned long);
% ***************
% *** 159,163 ****
%   int __wcwidth(__ct_rune_t);
% 
% ! # 47 "/usr/include/ctype.h" 2 3 4
% 
% 
% --- 159,163 ----
%   int __wcwidth(__ct_rune_t);
% 
% ! # 47 "include/ctype.h" 2
% 
% 
% ***************
% *** 195,197 ****
% --- 195,274 ----
% 
% 
% + 
% + # 1 "include/xlocale/_ctype.h" 1
% + # 44 "include/xlocale/_ctype.h"

This include is about the only change in ctype.h.

% + typedef struct _xlocale *locale_t;

xlocale also gives massive namespace pollution.  locale_t isn't in
C90.  Compiling with -std=c89 -pedantic doesn't change the output at all.

% + 
% + 
% + 
% + 
% + unsigned long ___runetype_l(__ct_rune_t, locale_t) ;

The strange formatting (space before semicolon) is due to a __pure being
there.  __pure is correctly ifdefed, so my #undef of __GNUC__ kills it

% + __ct_rune_t ___tolower_l(__ct_rune_t, locale_t) ;
% + __ct_rune_t ___toupper_l(__ct_rune_t, locale_t) ;
% + _RuneLocale *__runes_for_locale(locale_t, int*);

The misformatting of "int*" is in the source code.

The ifdefs are broken in other ways.  _RuneLocale is not declared here.
Without the #undef of __GNUC__, it is declared, and the preprocessed
output is 132 lines longer and all compile, and the extra lines seem
to be mostly correct too, since they are careful about namespaces and
don't declare any new APIs.

% + # 91 "include/xlocale/_ctype.h"
% + static int

__inline was corrctly killed here.

% + __sbmaskrune_l(__ct_rune_t _c, unsigned long _f, locale_t locale)

Namespace pollution (parameter name `locale' in the application namesspace.
The other arg names are from old ctype code which is missing such bugs.

% + {
% +  int mb_sb_limit;

More namespace pollution.


% +  _RuneLocale *runes = __runes_for_locale(locale, &mb_sb_limit);

More namespace pollution.

Style bugs: initialization in declaration; no blank line after declarations).

% +  return (_c < 0 || _c >= mb_sb_limit) ? 0 :
% +         runes->__runetype[_c] & _f;

Style bugs: unnecessary line splitting; weird continuation indent of 7
spaces (4 would be KNF and 1 tab would be gnu).

% + }
% + 
% + static int
% + __sbistype_l(__ct_rune_t _c, unsigned long _f, locale_t locale)
% + {
% +  return (!!__sbmaskrune_l(_c, _f, locale));
% + }

Similarly.

% + 
% + 
% + 
% + 
% + 
% + 
% + 
% + inline int isalnum_l(int c, locale_t l); inline int isalnum_l(int c, locale_t l) { return __sbistype_l(c, 0x00000100L|0x00000400L, l); }

Now since we misclassified the compiler and don't use __inline, we have
syntax errors from `inline'.

We also have namespace pollution in almost every indentifier:
- isalnum_l, locale_t: not in C89
- c, l: in application namespace

We also have a redundant forward declaration.  This seems to be technically
necessary in some cases, to avoid warnings from -Wmissing-prototypes.  But
surely any such warnings are bugs for inline functions, and the compilers
that don't support inline functions also don't support such warnings?
In the source code, the 'inline' part is spelled _XLOCALE_INLINE, and
it should reduce to the missing-prototype case, except I faked the compiler
not being gcc and with a real non-gcc there would be no warning.

The style bugs of hard explicit long constants and no space around the
binary operator '|' seem to be in old ctype code.

% + inline int isalpha_l(int c, locale_t l); inline int isalpha_l(int c, locale_t l) { return __sbistype_l(c, 0x00000100L, l); }
% + inline int isblank_l(int c, locale_t l); inline int isblank_l(int c, locale_t l) { return __sbistype_l(c, 0x00020000L, l); }
% + inline int iscntrl_l(int c, locale_t l); inline int iscntrl_l(int c, locale_t l) { return __sbistype_l(c, 0x00000200L, l); }
% + inline int isdigit_l(int c, locale_t l); inline int isdigit_l(int c, locale_t l) { return __sbistype_l(c, 0x00000400L, l); }
% + inline int isgraph_l(int c, locale_t l); inline int isgraph_l(int c, locale_t l) { return __sbistype_l(c, 0x00000800L, l); }
% + inline int ishexnumber_l(int c, locale_t l); inline int ishexnumber_l(int c, locale_t l) { return __sbistype_l(c, 0x00010000L, l); }
% + inline int isideogram_l(int c, locale_t l); inline int isideogram_l(int c, locale_t l) { return __sbistype_l(c, 0x00080000L, l); }
% + inline int islower_l(int c, locale_t l); inline int islower_l(int c, locale_t l) { return __sbistype_l(c, 0x00001000L, l); }
% + inline int isnumber_l(int c, locale_t l); inline int isnumber_l(int c, locale_t l) { return __sbistype_l(c, 0x00000400L, l); }
% + inline int isphonogram_l(int c, locale_t l); inline int isphonogram_l(int c, locale_t l) { return __sbistype_l(c, 0x00200000L, l); }
% + inline int isprint_l(int c, locale_t l); inline int isprint_l(int c, locale_t l) { return __sbistype_l(c, 0x00040000L, l); }
% + inline int ispunct_l(int c, locale_t l); inline int ispunct_l(int c, locale_t l) { return __sbistype_l(c, 0x00002000L, l); }
% + inline int isrune_l(int c, locale_t l); inline int isrune_l(int c, locale_t l) { return __sbistype_l(c, 0xFFFFFF00L, l); }
% + inline int isspace_l(int c, locale_t l); inline int isspace_l(int c, locale_t l) { return __sbistype_l(c, 0x00004000L, l); }
% + inline int isspecial_l(int c, locale_t l); inline int isspecial_l(int c, locale_t l) { return __sbistype_l(c, 0x00100000L, l); }
% + inline int isupper_l(int c, locale_t l); inline int isupper_l(int c, locale_t l) { return __sbistype_l(c, 0x00008000L, l); }
% + inline int isxdigit_l(int c, locale_t l); inline int isxdigit_l(int c, locale_t l) { return __sbistype_l(c, 0x00010000L, l); }

These bugs are easy to fix since they are all generated by 1 macro.

% + # 170 "include/xlocale/_ctype.h"
% + inline int digittoint_l(int, locale_t);
% + inline int tolower_l(int, locale_t);
% + inline int toupper_l(int, locale_t);
% + 
% + inline int digittoint_l(int __c, locale_t __l)
% + { return __sbmaskrune_l((__c), 0xFF, __l); }

Mounds of style bugs here.

% + 
% + inline int tolower_l(int __c, locale_t __l)
% + {
% +  int __limit;
% +  _RuneLocale *__runes = __runes_for_locale(__l, &__limit);
% +  return (__c < 0 || __c >= __limit) ? __c :
% +         __runes->__maplower[__c];
% + }
% + inline int toupper_l(int __c, locale_t __l)
% + {
% +  int __limit;
% +  _RuneLocale *__runes = __runes_for_locale(__l, &__limit);
% +  return (__c < 0 || __c >= __limit) ? __c :
% +         __runes->__mapupper[__c];
% + }

As above, but is more careful about namespaces.  This uses ugly
unecessary double underscores instead of the necessary single
underscores and the broken no underscores.  Now the only namespace
pollution is the non-C90 interfaces.

% + # 84 "include/ctype.h" 2
% + 
% + 
%   # 2 "a.c" 2

Next I tried defining inline as 'glorp'.  This worked as correctly as
possible -- it changed all the plain inlines to `static' (according
to the ifdefs that avoid using inline if someone is hacking on it) and
didn't cause any syntax errors.  Of course the glorp would cause syntax
errors in anything that uses plain inline.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120307120126.K969>