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