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