Home | History | Annotate | Line # | Download | only in lint1
d_c99_bool_strict_syshdr.c revision 1.7
      1 /*	$NetBSD: d_c99_bool_strict_syshdr.c,v 1.7 2021/01/24 07:58:48 rillig Exp $	*/
      2 # 3 "d_c99_bool_strict_syshdr.c"
      3 
      4 /*
      5  * Macros from system headers may use int expressions where bool expressions
      6  * are expected.  These headers are not allowed to include <stdbool.h>
      7  * themselves, and even if they could, lint must accept other scalar types
      8  * as well, since system headers are not changed lightheartedly.
      9  */
     10 
     11 /* lint1-extra-flags: -T */
     12 
     13 extern const unsigned short *ctype_table;
     14 
     15 extern void println(const char *);
     16 
     17 /*
     18  * On NetBSD 8, <sys/select.h> defines FD_ISSET by enclosing the statements
     19  * in the well-known 'do { ... } while (CONSTCOND 0)' loop.  The 0 in the
     20  * controlling expression has type INT but should be allowed nevertheless
     21  * since that header does not have a way to distinguish between bool and int.
     22  * It just follows the C99 standard, unlike the lint-provided stdbool.h, which
     23  * redefines 'false' to '__lint_false'.  Plus, <sys/select.h> must not include
     24  * <stdbool.h> itself.
     25  */
     26 void
     27 strict_bool_system_header_statement_macro(void)
     28 {
     29 
     30 	do {
     31 		println("nothing");
     32 	} while (/*CONSTCOND*/0);	/* expect: 333 */
     33 
     34 # 35 "d_c99_bool_strict_syshdr.c" 3 4
     35 	do {
     36 		println("nothing");
     37 	} while (/*CONSTCOND*/0);	/* ok */
     38 
     39 # 40 "d_c99_bool_strict_syshdr.c"
     40 	do {
     41 		println("nothing");
     42 	} while (/*CONSTCOND*/0);	/* expect: 333 */
     43 }
     44 
     45 
     46 /*
     47  * The macros from <ctype.h> can be implemented in different ways.  The C
     48  * standard defines them as returning 'int'.  In strict bool mode, the actual
     49  * return type can be INT or BOOL, depending on whether the macros do the
     50  * comparison against 0 themselves.
     51  *
     52  * Since that comparison is more code to write and in exceptional situations
     53  * more code to execute, they will probably leave out the extra comparison,
     54  * but both ways are possible.
     55  *
     56  * In strict bool mode, there must be a way to call these function-like macros
     57  * portably, without triggering type errors, no matter whether they return
     58  * BOOL or INT.
     59  *
     60  * The expressions from this example cross the boundary between system header
     61  * and application code.  They need to carry the information that they are
     62  * half-BOOL, half-INT across to the enclosing expressions.
     63  */
     64 void
     65 strict_bool_system_header_ctype(int c)
     66 {
     67 	/*
     68 	 * The macro returns INT, which may be outside the range of a
     69 	 * uint8_t variable, therefore it must not be assigned directly.
     70 	 * All other combinations of type are safe from truncation.
     71 	 */
     72 	_Bool system_int_assigned_to_bool =
     73 # 74 "d_c99_bool_strict_syshdr.c" 3 4
     74 	    (int)((ctype_table + 1)[c] & 0x0040)	/* INT */
     75 # 76 "d_c99_bool_strict_syshdr.c"
     76 	;			/* expect: 107 */
     77 
     78 	int system_bool_assigned_to_int =
     79 # 80 "d_c99_bool_strict_syshdr.c" 3 4
     80 	    (int)((ctype_table + 1)[c] & 0x0040) != 0	/* BOOL */
     81 # 82 "d_c99_bool_strict_syshdr.c"
     82 	;
     83 
     84 	if (
     85 # 86 "d_c99_bool_strict_syshdr.c" 3 4
     86 	    (int)((ctype_table + 1)[c] & 0x0040)	/* INT */
     87 # 88 "d_c99_bool_strict_syshdr.c"
     88 	)
     89 		println("system macro returning INT");
     90 
     91 	if (
     92 # 93 "d_c99_bool_strict_syshdr.c" 3 4
     93 	    ((ctype_table + 1)[c] & 0x0040) != 0	/* BOOL */
     94 # 95 "d_c99_bool_strict_syshdr.c"
     95 	)
     96 		println("system macro returning BOOL");
     97 }
     98 
     99 static inline _Bool
    100 ch_isspace_sys_int(char c)
    101 {
    102 	return
    103 # 104 "d_c99_bool_strict_syshdr.c" 3 4
    104 	    ((ctype_table + 1)[c] & 0x0040)
    105 # 106 "d_c99_bool_strict_syshdr.c"
    106 	    != 0;
    107 }
    108 
    109 /*
    110  * isspace is defined to return an int.  Comparing this int with 0 is the
    111  * safe way to convert it to _Bool.  This must be allowed even if isspace
    112  * does the comparison itself.
    113  */
    114 static inline _Bool
    115 ch_isspace_sys_bool(char c)
    116 {
    117 	return
    118 # 119 "d_c99_bool_strict_syshdr.c" 3 4
    119 	    ((ctype_table + 1)[(unsigned char)c] & 0x0040) != 0
    120 # 121 "d_c99_bool_strict_syshdr.c"
    121 	    != 0;
    122 }
    123 
    124 /*
    125  * There are several functions from system headers that have return type
    126  * int.  For this return type there are many API conventions:
    127  *
    128  *	* isspace: 0 means no, non-zero means yes
    129  *	* open: 0 means success, -1 means failure
    130  *	* main: 0 means success, non-zero means failure
    131  *	* strcmp: 0 means equal, < 0 means less than, > 0 means greater than
    132  *
    133  * Without a detailed list of individual functions, it's not possible to
    134  * guess what the return value means.  Therefore in strict bool mode, the
    135  * return value of these functions cannot be implicitly converted to bool,
    136  * not even in a context where the result is compared to 0.  Allowing that
    137  * would allow expressions like !strcmp(s1, s2), which is not correct since
    138  * strcmp returns an "ordered comparison result", not a bool.
    139  */
    140 
    141 # 1 "math.h" 3 4
    142 extern int finite(double);
    143 # 1 "string.h" 3 4
    144 extern int strcmp(const char *, const char *);
    145 # 146 "d_c99_bool_strict_syshdr.c"
    146 
    147 /*ARGSUSED*/
    148 _Bool
    149 call_finite_bad(double d)
    150 {
    151 	return finite(d);	/* expect: 211 */
    152 }
    153 
    154 _Bool
    155 call_finite_good(double d)
    156 {
    157 	return finite(d) != 0;
    158 }
    159 
    160 /*ARGSUSED*/
    161 _Bool
    162 str_equal_bad(const char *s1, const char *s2)
    163 {
    164 	return !strcmp(s1, s2);	/* expect: 330, 214 */
    165 }
    166 
    167 _Bool
    168 str_equal_good(const char *s1, const char *s2)
    169 {
    170 	return strcmp(s1, s2) == 0;
    171 }
    172