1/* $NetBSD: ckbool.c,v 1.32 2024/05/12 12:32:39 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@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)
39__RCSID("$NetBSD: ckbool.c,v 1.32 2024/05/12 12:32:39 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 detailed rules and for examples.
50 */
51
52
53static bool
54is_int_constant_zero(const tnode_t *tn, tspec_t t)
55{
56	return t == INT && tn->tn_op == CON && tn->u.value.u.integer == 0;
57}
58
59static bool
60is_typeok_strict_bool_binary(op_t op,
61			     const tnode_t *ln, tspec_t lt,
62			     const tnode_t *rn, tspec_t rt)
63{
64	if ((lt == BOOL) == (rt == BOOL))
65		return true;
66
67	if (op == FARG && rn->tn_sys)
68		return false;
69
70	if ((ln->tn_sys || rn->tn_sys) &&
71	    (is_int_constant_zero(ln, lt) || is_int_constant_zero(rn, rt)))
72		return true;
73
74	if (op == ASSIGN || op == ANDASS || op == XORASS || op == ORASS ||
75	    op == RETURN || op == INIT || op == FARG)
76		return lt != BOOL && (ln->tn_sys || rn->tn_sys);
77
78	if (op == EQ || op == NE ||
79	    op == BITAND || op == BITXOR || op == BITOR ||
80	    op == COLON)
81		return false;
82
83	return true;
84}
85
86/*
87 * Some operators require that either both operands are bool or both are
88 * scalar.
89 *
90 * Code that passes this check can be compiled in a pre-C99 environment that
91 * doesn't implement the special rule C99 6.3.1.2, without silent change in
92 * behavior.
93 */
94static bool
95typeok_strict_bool_binary_compatible(op_t op, int arg,
96				     const tnode_t *ln, tspec_t lt,
97				     const tnode_t *rn, tspec_t rt)
98{
99	if (is_typeok_strict_bool_binary(op, ln, lt, rn, rt))
100		return true;
101
102	if (op == FARG)
103		/* parameter %d expects '%s', gets passed '%s' */
104		error(334, arg, tspec_name(lt), tspec_name(rt));
105	else if (op == RETURN)
106		/* function has return type '%s' but returns '%s' */
107		error(211, tspec_name(lt), tspec_name(rt));
108	else
109		/* operands of '%s' have incompatible types '%s' and '%s' */
110		error(107, op_name(op), tspec_name(lt), tspec_name(rt));
111
112	return false;
113}
114
115/*
116 * In strict bool mode, check whether the types of the operands match the
117 * operator.
118 */
119bool
120typeok_scalar_strict_bool(op_t op, const mod_t *mp, int arg,
121			  const tnode_t *ln,
122			  const tnode_t *rn)
123{
124	ln = before_conversion(ln);
125	tspec_t lt = ln->tn_type->t_tspec;
126	tspec_t rt = NO_TSPEC;
127	if (rn != NULL) {
128		rn = before_conversion(rn);
129		rt = rn->tn_type->t_tspec;
130	}
131
132	if (rn != NULL &&
133	    !typeok_strict_bool_binary_compatible(op, arg, ln, lt, rn, rt))
134		return false;
135
136	if (mp->m_compares_with_zero) {
137		bool binary = mp->m_binary;
138		bool lbool = is_typeok_bool_compares_with_zero(ln, false);
139		bool ok = true;
140
141		if (!binary && !lbool) {
142			/* operand of '%s' must be bool, not '%s' */
143			error(330, op_name(op), tspec_name(lt));
144			ok = false;
145		}
146		if (binary && !lbool) {
147			/* left operand of '%s' must be bool, not '%s' */
148			error(331, op_name(op), tspec_name(lt));
149			ok = false;
150		}
151		if (binary && op != QUEST &&
152		    !is_typeok_bool_compares_with_zero(rn, false)) {
153			/* right operand of '%s' must be bool, not '%s' */
154			error(332, op_name(op), tspec_name(rt));
155			ok = false;
156		}
157		return ok;
158	}
159
160	if (!mp->m_takes_bool) {
161		bool binary = mp->m_binary;
162		bool lbool = lt == BOOL;
163		bool ok = true;
164
165		if (!binary && lbool) {
166			/* operand of '%s' must not be bool */
167			error(335, op_name(op));
168			ok = false;
169		}
170		if (binary && lbool) {
171			/* left operand of '%s' must not be bool */
172			error(336, op_name(op));
173			ok = false;
174		}
175		if (binary && rt == BOOL) {
176			/* right operand of '%s' must not be bool */
177			error(337, op_name(op));
178			ok = false;
179		}
180		return ok;
181	}
182
183	return true;
184}
185
186bool
187is_typeok_bool_compares_with_zero(const tnode_t *tn, bool is_do_while)
188{
189	while (tn->tn_op == COMMA)
190		tn = tn->u.ops.right;
191	tn = before_conversion(tn);
192
193	return tn->tn_type->t_tspec == BOOL
194	    || tn->tn_op == BITAND
195	    || (is_do_while && is_int_constant_zero(tn, tn->tn_type->t_tspec))
196	    || (tn->tn_sys && is_scalar(tn->tn_type->t_tspec));
197}
198
199bool
200fallback_symbol_strict_bool(sym_t *sym)
201{
202	if (strcmp(sym->s_name, "__lint_false") == 0) {
203		sym->s_scl = BOOL_CONST;
204		sym->s_type = gettyp(BOOL);
205		sym->u.s_bool_constant = false;
206		return true;
207	}
208
209	if (strcmp(sym->s_name, "__lint_true") == 0) {
210		sym->s_scl = BOOL_CONST;
211		sym->s_type = gettyp(BOOL);
212		sym->u.s_bool_constant = true;
213		return true;
214	}
215
216	return false;
217}
218