ckbool.c revision 1.1 1 /* $NetBSD: ckbool.c,v 1.1 2021/04/06 13:17:04 rillig Exp $ */
2
3 /*-
4 * Copyright (c) 2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Roland Illig <rillig (at) NetBSD.org>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
35
36 #include <sys/cdefs.h>
37
38 #if defined(__RCSID) && !defined(lint)
39 __RCSID("$NetBSD: ckbool.c,v 1.1 2021/04/06 13:17:04 rillig Exp $");
40 #endif
41
42 #include <string.h>
43
44 #include "lint1.h"
45
46
47 /*
48 * The option -T treats _Bool as incompatible with all other scalar types.
49 * See d_c99_bool_strict.c for the exact rules and for examples.
50 */
51
52 static const char *
53 op_name(op_t op)
54 {
55 return modtab[op].m_name;
56 }
57
58 /*
59 * See if in strict bool mode, the operator takes either two bool operands
60 * or two arbitrary other operands.
61 */
62 static bool
63 is_assignment_bool_or_other(op_t op)
64 {
65 return op == ASSIGN ||
66 op == ANDASS || op == XORASS || op == ORASS ||
67 op == RETURN || op == INIT || op == FARG;
68 }
69
70 static bool
71 is_symmetric_bool_or_other(op_t op)
72 {
73 return op == EQ || op == NE ||
74 op == BITAND || op == BITXOR || op == BITOR ||
75 op == COLON;
76 }
77
78 static bool
79 is_int_constant_zero(const tnode_t *tn, tspec_t t)
80 {
81 return t == INT && tn->tn_op == CON && tn->tn_val->v_quad == 0;
82 }
83
84 static bool
85 is_typeok_strict_bool(op_t op,
86 const tnode_t *ln, tspec_t lt,
87 const tnode_t *rn, tspec_t rt)
88 {
89 if (rn == NULL)
90 return true; /* TODO: check unary operators as well. */
91
92 if ((lt == BOOL) == (rt == BOOL))
93 return true;
94
95 if ((ln->tn_from_system_header || rn->tn_from_system_header) &&
96 (is_int_constant_zero(ln, lt) || is_int_constant_zero(rn, rt)))
97 return true;
98
99 if (is_assignment_bool_or_other(op)) {
100 return lt != BOOL &&
101 (ln->tn_from_system_header || rn->tn_from_system_header);
102 }
103
104 return !is_symmetric_bool_or_other(op);
105 }
106
107 /*
108 * Some operators require that either both operands are bool or both are
109 * scalar.
110 *
111 * Code that passes this check can be compiled in a pre-C99 environment that
112 * doesn't implement the special rule C99 6.3.1.2, without silent change in
113 * behavior.
114 */
115 static bool
116 typeok_strict_bool_compatible(op_t op, int arg,
117 const tnode_t *ln, tspec_t lt,
118 const tnode_t *rn, tspec_t rt)
119 {
120
121 if (is_typeok_strict_bool(op, ln, lt, rn, rt))
122 return true;
123
124 if (op == FARG) {
125 /* argument #%d expects '%s', gets passed '%s' */
126 error(334, arg, tspec_name(lt), tspec_name(rt));
127 } else if (op == RETURN) {
128 /* return value type mismatch (%s) and (%s) */
129 error(211, tspec_name(lt), tspec_name(rt));
130 } else {
131 /* operands of '%s' have incompatible types (%s != %s) */
132 error(107, op_name(op), tspec_name(lt), tspec_name(rt));
133 }
134
135 return false;
136 }
137
138 /*
139 * In strict bool mode, check whether the types of the operands match the
140 * operator.
141 */
142 bool
143 typeok_scalar_strict_bool(op_t op, const mod_t *mp, int arg,
144 const tnode_t *ln,
145 const tnode_t *rn)
146
147 {
148 tspec_t lt, rt;
149
150 ln = before_conversion(ln);
151 lt = ln->tn_type->t_tspec;
152
153 if (rn != NULL) {
154 rn = before_conversion(rn);
155 rt = rn->tn_type->t_tspec;
156 } else {
157 rt = NOTSPEC;
158 }
159
160 if (!typeok_strict_bool_compatible(op, arg, ln, lt, rn, rt))
161 return false;
162
163 if (mp->m_requires_bool || op == QUEST) {
164 bool binary = mp->m_binary;
165 bool lbool = is_typeok_bool_operand(ln);
166 bool ok = true;
167
168 if (!binary && !lbool) {
169 /* operand of '%s' must be bool, not '%s' */
170 error(330, op_name(op), tspec_name(lt));
171 ok = false;
172 }
173 if (binary && !lbool) {
174 /* left operand of '%s' must be bool, not '%s' */
175 error(331, op_name(op), tspec_name(lt));
176 ok = false;
177 }
178 if (binary && op != QUEST && !is_typeok_bool_operand(rn)) {
179 /* right operand of '%s' must be bool, not '%s' */
180 error(332, op_name(op), tspec_name(rt));
181 ok = false;
182 }
183 return ok;
184 }
185
186 if (!mp->m_takes_bool) {
187 bool binary = mp->m_binary;
188 bool lbool = ln->tn_type->t_tspec == BOOL;
189 bool ok = true;
190
191 if (!binary && lbool) {
192 /* operand of '%s' must not be bool */
193 error(335, op_name(op));
194 ok = false;
195 }
196 if (binary && lbool) {
197 /* left operand of '%s' must not be bool */
198 error(336, op_name(op));
199 ok = false;
200 }
201 if (binary && rn->tn_type->t_tspec == BOOL) {
202 /* right operand of '%s' must not be bool */
203 error(337, op_name(op));
204 ok = false;
205 }
206 return ok;
207 }
208
209 return true;
210 }
211
212 /*
213 * See if the node is valid as operand of an operator that compares its
214 * argument with 0.
215 */
216 bool
217 is_typeok_bool_operand(const tnode_t *tn)
218 {
219 tspec_t t;
220
221 lint_assert(Tflag);
222
223 tn = before_conversion(tn);
224 t = tn->tn_type->t_tspec;
225
226 if (t == BOOL)
227 return true;
228
229 if (tn->tn_from_system_header && is_scalar(t))
230 return true;
231
232 /* For enums that are used as bit sets, allow "flags & FLAG". */
233 if (tn->tn_op == BITAND &&
234 tn->tn_left->tn_op == CVT &&
235 tn->tn_left->tn_type->t_tspec == INT && !tn->tn_left->tn_cast &&
236 tn->tn_left->tn_left->tn_type->t_tspec == ENUM &&
237 /*
238 * XXX: Somehow the type information got lost here. The type
239 * of the enum constant on the right-hand side should still be
240 * ENUM, but is INT.
241 */
242 tn->tn_right->tn_type->t_tspec == INT)
243 return true;
244
245 return false;
246 }
247
248 bool
249 fallback_symbol_strict_bool(sym_t *sym)
250 {
251 if (Tflag && strcmp(sym->s_name, "__lint_false") == 0) {
252 sym->s_scl = CTCONST; /* close enough */
253 sym->s_type = gettyp(BOOL);
254 sym->s_value.v_tspec = BOOL;
255 sym->s_value.v_ansiu = false;
256 sym->s_value.v_quad = 0;
257 return true;
258 }
259
260 if (Tflag && strcmp(sym->s_name, "__lint_true") == 0) {
261 sym->s_scl = CTCONST; /* close enough */
262 sym->s_type = gettyp(BOOL);
263 sym->s_value.v_tspec = BOOL;
264 sym->s_value.v_ansiu = false;
265 sym->s_value.v_quad = 1;
266 return true;
267 }
268
269 return false;
270 }
271