Module Name: src Committed By: rillig Date: Tue Apr 6 13:17:04 UTC 2021
Modified Files: src/usr.bin/xlint/lint1: Makefile externs1.h tree.c Added Files: src/usr.bin/xlint/lint1: ckbool.c Log Message: lint: move check for strict bool mode into separate file No functional change. To generate a diff of this commit: cvs rdiff -u -r1.65 -r1.66 src/usr.bin/xlint/lint1/Makefile cvs rdiff -u -r0 -r1.1 src/usr.bin/xlint/lint1/ckbool.c cvs rdiff -u -r1.100 -r1.101 src/usr.bin/xlint/lint1/externs1.h cvs rdiff -u -r1.266 -r1.267 src/usr.bin/xlint/lint1/tree.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/xlint/lint1/Makefile diff -u src/usr.bin/xlint/lint1/Makefile:1.65 src/usr.bin/xlint/lint1/Makefile:1.66 --- src/usr.bin/xlint/lint1/Makefile:1.65 Mon Apr 5 02:05:47 2021 +++ src/usr.bin/xlint/lint1/Makefile Tue Apr 6 13:17:04 2021 @@ -1,10 +1,11 @@ -# $NetBSD: Makefile,v 1.65 2021/04/05 02:05:47 rillig Exp $ +# $NetBSD: Makefile,v 1.66 2021/04/06 13:17:04 rillig Exp $ .include <bsd.own.mk> PROG= lint1 -SRCS= cgram.y ckctype.c ckgetopt.c decl.c emit.c emit1.c err.c \ - func.c init.c inittyp.c lex.c \ +SRCS= cgram.y \ + ckbool.c ckctype.c ckgetopt.c \ + decl.c emit.c emit1.c err.c func.c init.c inittyp.c lex.c \ main1.c mem.c mem1.c oper.c print.c scan.l tree.c tyname.c MAN= lint.7 Index: src/usr.bin/xlint/lint1/externs1.h diff -u src/usr.bin/xlint/lint1/externs1.h:1.100 src/usr.bin/xlint/lint1/externs1.h:1.101 --- src/usr.bin/xlint/lint1/externs1.h:1.100 Mon Apr 5 02:05:47 2021 +++ src/usr.bin/xlint/lint1/externs1.h Tue Apr 6 13:17:04 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: externs1.h,v 1.100 2021/04/05 02:05:47 rillig Exp $ */ +/* $NetBSD: externs1.h,v 1.101 2021/04/06 13:17:04 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl @@ -336,6 +336,13 @@ extern int lex_input(void); extern char *print_tnode(char *, size_t, const tnode_t *); /* + * ckbool.c + */ +extern bool typeok_scalar_strict_bool(op_t, const mod_t *, int, + const tnode_t *, const tnode_t *); +extern bool fallback_symbol_strict_bool(sym_t *); + +/* * ckctype.c */ extern void check_ctype_function_call(const tnode_t *, const tnode_t *); Index: src/usr.bin/xlint/lint1/tree.c diff -u src/usr.bin/xlint/lint1/tree.c:1.266 src/usr.bin/xlint/lint1/tree.c:1.267 --- src/usr.bin/xlint/lint1/tree.c:1.266 Mon Apr 5 02:05:47 2021 +++ src/usr.bin/xlint/lint1/tree.c Tue Apr 6 13:17:04 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: tree.c,v 1.266 2021/04/05 02:05:47 rillig Exp $ */ +/* $NetBSD: tree.c,v 1.267 2021/04/06 13:17:04 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl @@ -37,7 +37,7 @@ #include <sys/cdefs.h> #if defined(__RCSID) && !defined(lint) -__RCSID("$NetBSD: tree.c,v 1.266 2021/04/05 02:05:47 rillig Exp $"); +__RCSID("$NetBSD: tree.c,v 1.267 2021/04/06 13:17:04 rillig Exp $"); #endif #include <float.h> @@ -207,23 +207,8 @@ static void fallback_symbol(sym_t *sym) { - if (Tflag && strcmp(sym->s_name, "__lint_false") == 0) { - sym->s_scl = CTCONST; /* close enough */ - sym->s_type = gettyp(BOOL); - sym->s_value.v_tspec = BOOL; - sym->s_value.v_ansiu = false; - sym->s_value.v_quad = 0; + if (fallback_symbol_strict_bool(sym)) return; - } - - if (Tflag && strcmp(sym->s_name, "__lint_true") == 0) { - sym->s_scl = CTCONST; /* close enough */ - sym->s_type = gettyp(BOOL); - sym->s_value.v_tspec = BOOL; - sym->s_value.v_ansiu = false; - sym->s_value.v_quad = 1; - return; - } if (block_level > 0 && (strcmp(sym->s_name, "__FUNCTION__") == 0 || strcmp(sym->s_name, "__PRETTY_FUNCTION__") == 0)) { @@ -736,42 +721,6 @@ is_null_pointer(const tnode_t *tn) && (tn->tn_op == CON && tn->tn_val->v_quad == 0); } -/* - * See if the node is valid as operand of an operator that compares its - * argument with 0. - */ -bool -is_typeok_bool_operand(const tnode_t *tn) -{ - tspec_t t; - - lint_assert(Tflag); - - tn = before_conversion(tn); - t = tn->tn_type->t_tspec; - - if (t == BOOL) - return true; - - if (tn->tn_from_system_header && is_scalar(t)) - return true; - - /* For enums that are used as bit sets, allow "flags & FLAG". */ - if (tn->tn_op == BITAND && - tn->tn_left->tn_op == CVT && - tn->tn_left->tn_type->t_tspec == INT && !tn->tn_left->tn_cast && - tn->tn_left->tn_left->tn_type->t_tspec == ENUM && - /* - * XXX: Somehow the type information got lost here. The type - * of the enum constant on the right-hand side should still be - * ENUM, but is INT. - */ - tn->tn_right->tn_type->t_tspec == INT) - return true; - - return false; -} - static bool typeok_incdec(op_t op, const tnode_t *tn, const type_t *tp) { @@ -1109,159 +1058,7 @@ typeok_assign(const mod_t *mp, const tno return true; } -/* - * See if in strict bool mode, the operator takes either two bool operands - * or two arbitrary other operands. - */ -static bool -is_assignment_bool_or_other(op_t op) -{ - return op == ASSIGN || - op == ANDASS || op == XORASS || op == ORASS || - op == RETURN || op == INIT || op == FARG; -} - -static bool -is_symmetric_bool_or_other(op_t op) -{ - return op == EQ || op == NE || - op == BITAND || op == BITXOR || op == BITOR || - op == COLON; -} - -static bool -is_int_constant_zero(const tnode_t *tn, tspec_t t) -{ - return t == INT && tn->tn_op == CON && tn->tn_val->v_quad == 0; -} - -static bool -is_typeok_strict_bool(op_t op, - const tnode_t *ln, tspec_t lt, - const tnode_t *rn, tspec_t rt) -{ - if (rn == NULL) - return true; /* TODO: check unary operators as well. */ - - if ((lt == BOOL) == (rt == BOOL)) - return true; - - if ((ln->tn_from_system_header || rn->tn_from_system_header) && - (is_int_constant_zero(ln, lt) || is_int_constant_zero(rn, rt))) - return true; - - if (is_assignment_bool_or_other(op)) { - return lt != BOOL && - (ln->tn_from_system_header || rn->tn_from_system_header); - } - - return !is_symmetric_bool_or_other(op); -} - -/* - * Some operators require that either both operands are bool or both are - * scalar. - * - * Code that passes this check can be compiled in a pre-C99 environment that - * doesn't implement the special rule C99 6.3.1.2, without silent change in - * behavior. - */ -static bool -typeok_strict_bool_compatible(op_t op, int arg, - const tnode_t *ln, tspec_t lt, - const tnode_t *rn, tspec_t rt) -{ - - if (is_typeok_strict_bool(op, ln, lt, rn, rt)) - return true; - - if (op == FARG) { - /* argument #%d expects '%s', gets passed '%s' */ - error(334, arg, tspec_name(lt), tspec_name(rt)); - } else if (op == RETURN) { - /* return value type mismatch (%s) and (%s) */ - error(211, tspec_name(lt), tspec_name(rt)); - } else { - /* operands of '%s' have incompatible types (%s != %s) */ - error(107, op_name(op), tspec_name(lt), tspec_name(rt)); - } - - return false; -} - -/* - * In strict bool mode, check whether the types of the operands match the - * operator. - */ -static bool -typeok_scalar_strict_bool(op_t op, const mod_t *mp, int arg, - const tnode_t *ln, - const tnode_t *rn) - -{ - tspec_t lt, rt; - - ln = before_conversion(ln); - lt = ln->tn_type->t_tspec; - - if (rn != NULL) { - rn = before_conversion(rn); - rt = rn->tn_type->t_tspec; - } else { - rt = NOTSPEC; - } - - if (!typeok_strict_bool_compatible(op, arg, ln, lt, rn, rt)) - return false; - if (mp->m_requires_bool || op == QUEST) { - bool binary = mp->m_binary; - bool lbool = is_typeok_bool_operand(ln); - bool ok = true; - - if (!binary && !lbool) { - /* operand of '%s' must be bool, not '%s' */ - error(330, op_name(op), tspec_name(lt)); - ok = false; - } - if (binary && !lbool) { - /* left operand of '%s' must be bool, not '%s' */ - error(331, op_name(op), tspec_name(lt)); - ok = false; - } - if (binary && op != QUEST && !is_typeok_bool_operand(rn)) { - /* right operand of '%s' must be bool, not '%s' */ - error(332, op_name(op), tspec_name(rt)); - ok = false; - } - return ok; - } - - if (!mp->m_takes_bool) { - bool binary = mp->m_binary; - bool lbool = ln->tn_type->t_tspec == BOOL; - bool ok = true; - - if (!binary && lbool) { - /* operand of '%s' must not be bool */ - error(335, op_name(op)); - ok = false; - } - if (binary && lbool) { - /* left operand of '%s' must not be bool */ - error(336, op_name(op)); - ok = false; - } - if (binary && rn->tn_type->t_tspec == BOOL) { - /* right operand of '%s' must not be bool */ - error(337, op_name(op)); - ok = false; - } - return ok; - } - - return true; -} /* Check the types using the information from modtab[]. */ static bool Added files: Index: src/usr.bin/xlint/lint1/ckbool.c diff -u /dev/null src/usr.bin/xlint/lint1/ckbool.c:1.1 --- /dev/null Tue Apr 6 13:17:04 2021 +++ src/usr.bin/xlint/lint1/ckbool.c Tue Apr 6 13:17:04 2021 @@ -0,0 +1,270 @@ +/* $NetBSD: ckbool.c,v 1.1 2021/04/06 13:17:04 rillig Exp $ */ + +/*- + * Copyright (c) 2021 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roland Illig <ril...@netbsd.org>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> + +#if defined(__RCSID) && !defined(lint) +__RCSID("$NetBSD: ckbool.c,v 1.1 2021/04/06 13:17:04 rillig Exp $"); +#endif + +#include <string.h> + +#include "lint1.h" + + +/* + * The option -T treats _Bool as incompatible with all other scalar types. + * See d_c99_bool_strict.c for the exact rules and for examples. + */ + +static const char * +op_name(op_t op) +{ + return modtab[op].m_name; +} + +/* + * See if in strict bool mode, the operator takes either two bool operands + * or two arbitrary other operands. + */ +static bool +is_assignment_bool_or_other(op_t op) +{ + return op == ASSIGN || + op == ANDASS || op == XORASS || op == ORASS || + op == RETURN || op == INIT || op == FARG; +} + +static bool +is_symmetric_bool_or_other(op_t op) +{ + return op == EQ || op == NE || + op == BITAND || op == BITXOR || op == BITOR || + op == COLON; +} + +static bool +is_int_constant_zero(const tnode_t *tn, tspec_t t) +{ + return t == INT && tn->tn_op == CON && tn->tn_val->v_quad == 0; +} + +static bool +is_typeok_strict_bool(op_t op, + const tnode_t *ln, tspec_t lt, + const tnode_t *rn, tspec_t rt) +{ + if (rn == NULL) + return true; /* TODO: check unary operators as well. */ + + if ((lt == BOOL) == (rt == BOOL)) + return true; + + if ((ln->tn_from_system_header || rn->tn_from_system_header) && + (is_int_constant_zero(ln, lt) || is_int_constant_zero(rn, rt))) + return true; + + if (is_assignment_bool_or_other(op)) { + return lt != BOOL && + (ln->tn_from_system_header || rn->tn_from_system_header); + } + + return !is_symmetric_bool_or_other(op); +} + +/* + * Some operators require that either both operands are bool or both are + * scalar. + * + * Code that passes this check can be compiled in a pre-C99 environment that + * doesn't implement the special rule C99 6.3.1.2, without silent change in + * behavior. + */ +static bool +typeok_strict_bool_compatible(op_t op, int arg, + const tnode_t *ln, tspec_t lt, + const tnode_t *rn, tspec_t rt) +{ + + if (is_typeok_strict_bool(op, ln, lt, rn, rt)) + return true; + + if (op == FARG) { + /* argument #%d expects '%s', gets passed '%s' */ + error(334, arg, tspec_name(lt), tspec_name(rt)); + } else if (op == RETURN) { + /* return value type mismatch (%s) and (%s) */ + error(211, tspec_name(lt), tspec_name(rt)); + } else { + /* operands of '%s' have incompatible types (%s != %s) */ + error(107, op_name(op), tspec_name(lt), tspec_name(rt)); + } + + return false; +} + +/* + * In strict bool mode, check whether the types of the operands match the + * operator. + */ +bool +typeok_scalar_strict_bool(op_t op, const mod_t *mp, int arg, + const tnode_t *ln, + const tnode_t *rn) + +{ + tspec_t lt, rt; + + ln = before_conversion(ln); + lt = ln->tn_type->t_tspec; + + if (rn != NULL) { + rn = before_conversion(rn); + rt = rn->tn_type->t_tspec; + } else { + rt = NOTSPEC; + } + + if (!typeok_strict_bool_compatible(op, arg, ln, lt, rn, rt)) + return false; + + if (mp->m_requires_bool || op == QUEST) { + bool binary = mp->m_binary; + bool lbool = is_typeok_bool_operand(ln); + bool ok = true; + + if (!binary && !lbool) { + /* operand of '%s' must be bool, not '%s' */ + error(330, op_name(op), tspec_name(lt)); + ok = false; + } + if (binary && !lbool) { + /* left operand of '%s' must be bool, not '%s' */ + error(331, op_name(op), tspec_name(lt)); + ok = false; + } + if (binary && op != QUEST && !is_typeok_bool_operand(rn)) { + /* right operand of '%s' must be bool, not '%s' */ + error(332, op_name(op), tspec_name(rt)); + ok = false; + } + return ok; + } + + if (!mp->m_takes_bool) { + bool binary = mp->m_binary; + bool lbool = ln->tn_type->t_tspec == BOOL; + bool ok = true; + + if (!binary && lbool) { + /* operand of '%s' must not be bool */ + error(335, op_name(op)); + ok = false; + } + if (binary && lbool) { + /* left operand of '%s' must not be bool */ + error(336, op_name(op)); + ok = false; + } + if (binary && rn->tn_type->t_tspec == BOOL) { + /* right operand of '%s' must not be bool */ + error(337, op_name(op)); + ok = false; + } + return ok; + } + + return true; +} + +/* + * See if the node is valid as operand of an operator that compares its + * argument with 0. + */ +bool +is_typeok_bool_operand(const tnode_t *tn) +{ + tspec_t t; + + lint_assert(Tflag); + + tn = before_conversion(tn); + t = tn->tn_type->t_tspec; + + if (t == BOOL) + return true; + + if (tn->tn_from_system_header && is_scalar(t)) + return true; + + /* For enums that are used as bit sets, allow "flags & FLAG". */ + if (tn->tn_op == BITAND && + tn->tn_left->tn_op == CVT && + tn->tn_left->tn_type->t_tspec == INT && !tn->tn_left->tn_cast && + tn->tn_left->tn_left->tn_type->t_tspec == ENUM && + /* + * XXX: Somehow the type information got lost here. The type + * of the enum constant on the right-hand side should still be + * ENUM, but is INT. + */ + tn->tn_right->tn_type->t_tspec == INT) + return true; + + return false; +} + +bool +fallback_symbol_strict_bool(sym_t *sym) +{ + if (Tflag && strcmp(sym->s_name, "__lint_false") == 0) { + sym->s_scl = CTCONST; /* close enough */ + sym->s_type = gettyp(BOOL); + sym->s_value.v_tspec = BOOL; + sym->s_value.v_ansiu = false; + sym->s_value.v_quad = 0; + return true; + } + + if (Tflag && strcmp(sym->s_name, "__lint_true") == 0) { + sym->s_scl = CTCONST; /* close enough */ + sym->s_type = gettyp(BOOL); + sym->s_value.v_tspec = BOOL; + sym->s_value.v_ansiu = false; + sym->s_value.v_quad = 1; + return true; + } + + return false; +}