Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src lint: prepare to make strict bool mode even stricter



details:   https://anonhg.NetBSD.org/src/rev/ce1728376f41
branches:  trunk
changeset: 949765:ce1728376f41
user:      rillig <rillig%NetBSD.org@localhost>
date:      Sat Jan 16 15:02:11 2021 +0000

description:
lint: prepare to make strict bool mode even stricter

Currently, strict bool mode still allows integer constant expressions to
be converted implicitly to bool.  This is something that other languages
such as Go, Java, C#, Pascal don't allow.

By providing a custom implementation of <stdbool.h> that defines false
and true to custom bool constant identifiers, lint will cover these
cases as well.

To prepare for this, reword the rules and restructure the tests in
d_c99_bool_strict.c.

diffstat:

 tests/usr.bin/xlint/check-expect.lua            |     6 +-
 tests/usr.bin/xlint/lint1/d_c99_bool_strict.c   |  1003 +++++++++++++---------
 tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp |   221 ++--
 usr.bin/xlint/lint1/tree.c                      |    19 +-
 4 files changed, 720 insertions(+), 529 deletions(-)

diffs (truncated from 1373 to 300 lines):

diff -r 59eac3a30141 -r ce1728376f41 tests/usr.bin/xlint/check-expect.lua
--- a/tests/usr.bin/xlint/check-expect.lua      Sat Jan 16 12:57:37 2021 +0000
+++ b/tests/usr.bin/xlint/check-expect.lua      Sat Jan 16 15:02:11 2021 +0000
@@ -1,5 +1,5 @@
 #!  /usr/bin/lua
--- $NetBSD: check-expect.lua,v 1.1 2021/01/12 20:42:01 rillig Exp $
+-- $NetBSD: check-expect.lua,v 1.2 2021/01/16 15:02:11 rillig Exp $
 
 --[[
 
@@ -99,8 +99,8 @@
     end
 
     if not found then
-      errors:add("error: %s:%d: message \"%s\" is not declared in %s:%d",
-        exp_fname, act.exp_lineno, act.msg, c_fname, act.c_lineno)
+      errors:add("error: %s:%d: must expect \"%s\"",
+        c_fname, act.c_lineno, act.msg)
     end
   end
 
diff -r 59eac3a30141 -r ce1728376f41 tests/usr.bin/xlint/lint1/d_c99_bool_strict.c
--- a/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c     Sat Jan 16 12:57:37 2021 +0000
+++ b/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c     Sat Jan 16 15:02:11 2021 +0000
@@ -1,45 +1,106 @@
-/*     $NetBSD: d_c99_bool_strict.c,v 1.7 2021/01/15 23:15:28 rillig Exp $     */
+/*     $NetBSD: d_c99_bool_strict.c,v 1.8 2021/01/16 15:02:11 rillig Exp $     */
 # 3 "d_c99_bool_strict.c"
 
 /*
  * The option -T treats _Bool as incompatible with all other scalar types.
- * This means:
+ * This is implemented by the following rules:
+ *
+ * strict-bool-typedef:
+ *     The type _Bool is compatible with any typedef of _Bool.
  *
- * SB001: Controlling expressions in 'if', 'while', 'for', '?:' must be of
- * type _Bool instead of scalar.
+ *     Note: Since <stdbool.h> defines bool as textual alias of _Bool,
+ *     having another typedef for bool is unusual.
+ *
+ * strict-bool-constant:
+ *     There are 2 bool constants named false and true.
+ *     No other constants are compatible with type _Bool.
+ *
+ *     Note: Internally these constants are named __lint_false and
+ *     __lint_true.
  *
- * SB002: The operators '!', '==', '!=', '<', '<=', '>=', '>', '&&', '||'
- * return _Bool instead of int.
+ * strict-bool-bit-field:
+ *     A struct or union member that is a bit field with underlying type
+ *     bool is compatible with plain bool.
+ *
+ * strict-bool-conversion:
+ *     There is no implicit conversion between _Bool and any other type.
  *
- * SB003: The operators '!', '&&', '||' take _Bool instead of scalar.
+ * strict-bool-controlling-expression:
+ *     Controlling expressions in 'if', 'while', 'for', '?:' must be of
+ *     type bool.
  *
- * SB004: The only operators that take _Bool are '!', '==', '!=',
- * '&', '^', '|', '&&', '||', '?', ':', '=', '&=', '^=', '|='.
+ * strict-bool-operand-unary:
+ *     Operator        bool?   scalar?
+ *     !               yes     no
+ *     The other binary operators do not accept bool operands.
  *
- * SB005: There is no implicit conversion from _Bool to any other type.
+ * strict-bool-operand-binary:
+ *     Operator        left:   bool?   other?  right:  bool?   other?
+ *     .                       -       yes             yes     yes
+ *     ->                      -       yes             yes     yes
+ *     <=, <, >=, >            yes     yes             yes     yes
+ *     ==, !=                  yes     yes             yes     yes
+ *     &                       yes     yes             yes     yes
+ *     ^                       yes     yes             yes     yes
+ *     |                       yes     yes             yes     yes
+ *     &&                      yes     -               yes     -
+ *     ||                      yes     -               yes     -
+ *     ?                       yes     -               yes     yes
+ *     :                       yes     yes             yes     yes
+ *     =                       yes     yes             yes     yes
+ *     &=, ^=, |=              yes     yes             yes     yes
+ *     ,                       yes     yes             yes     yes
+ *     The other binary operators do not accept bool operands.
  *
- * SB006: An expression is compatible with type _Bool if its main operator
- * returns type _Bool, or if the expression is an integer constant expression
- * with value 0 or 1.
+ * strict-bool-operator-result:
+ *     The result type of the operators '!', '<', '<=', '>', '>=',
+ *     '==', '!=', '&&', '||' is _Bool instead of int.
+ *
+ * strict-bool-bitwise-and:
+ *     Expressions of the form "flags & FLAG" are compatible with _Bool if
+ *     the left operand has enum type, the right operand is an integer
+ *     constant and the resulting value is used in a context where it is
+ *     implicitly and immediately compared to zero.
  *
- * SB007: Expressions like "flags & FLAG" are compatible with _Bool if
- * they appear in a context where they are immediately compared to zero.
- * Assigning to a _Bool variable does not count as such a context, to
- * allow programs to be compiled without silent changes on a compiler that
- * is lacking the special _Bool type.
+ *     Note: An efficient implementation technique for a collection of bool
+ *     flags is an enum.  The enum declaration groups the available
+ *     constants, and as of 2020, compilers such as GCC and Clang have basic
+ *     support for detecting type mismatches on enums.
+ *
+ *     Note: Examples for such contexts are controlling expressions or the
+ *     operands of the operators '!', '&&', '||'.
+ *
+ *     Note: Counterexamples for contexts are assignments to a bool variable.
+ *
+ *     Note: These rules ensure that conforming code can be compiled without
+ *     change in behavior using old compilers that implement bool as an
+ *     ordinary integer type, without the special rule C99 6.3.1.2.
  *
- * SB008: Bit fields in struct may be based on _Bool.  These bit fields
- * typically have type _Bool:1 and can be converted to _Bool and back.
+ *     Note: There is a crucial difference between a _Bool variable and an
+ *     ordinary integer variable.  C99 6.3.1.2 defines a conversion from an
+ *     arbitrary scalar value to _Bool as equivalent to (value != 0 ? 1 : 0).
+ *     This means that even if _Bool is implemented as an 8-bit unsigned
+ *     integer, assigning 256 to it would still result in the value 1 being
+ *     stored.  Storing 256 in an ordinary 8-bit unsigned integer would
+ *     result in the value 0 being stored.  See the test d_c99_bool.c for
+ *     more details.
+ */
+
+/*
+ * The header <stdbool.h> defines the macros bool = _Bool, false = 0 and
+ * true = 1.  Without further hacks, this would mean that constant expressions
+ * of integer type have to be regarded as possible boolean constants if their
+ * value is either 0 or 1.
+ *
+ * This would not help in migrating old code to use bool consistently.
+ * Therefore lint provides its own <stdbool.h> header that expands false to
+ * __lint_false and true to __lint_true, two predefined constant expressions.
  */
 
 /* lint1-extra-flags: -T */
 
 /*
- * The header <stdbool.h> defines the macros bool = _Bool, false = 0 and
- * true = 1.  Therefore, constant expressions of integer type have to be
- * regarded as possible boolean constants if their value is either 0 or 1.
- * At this point of the translation, the preprocessor has already removed
- * the words "false" and "true" from the source code.
+ * strict-bool-typedef
  */
 
 /*
@@ -48,49 +109,490 @@
  */
 typedef _Bool bool;
 
+extern void accept_bool(bool);
+extern void println(const char *);
+extern void take_arguments(bool, int, const char *, ...);
+extern void do_nothing(void);
+
+/*
+ * strict-bool-constant
+ */
+
 void
-SB001_controlling_expression(bool b, int i, double d, const void *p)
+strict_bool_constant(void)
 {
+       accept_bool(__lint_false);
+       accept_bool(__lint_true);
+       accept_bool(0);         /* TODO: expect: 334 */
+       accept_bool(1);         /* TODO: expect: 334 */
+       accept_bool(2);         /* expect: 334 */
+}
+
+enum strict_bool_constant_expressions {
+       /* Ok: __lint_false is a boolean constant expression. */
+       FALSE = __lint_false ? 100 : 101,
+
+       /* Ok: __lint_true is a boolean constant expression. */
+       TRUE = __lint_true ? 100 : 101,
+
+       /* Not ok: an integer is not a boolean constant expression. */
+       INT0 = 0 ? 100 : 101,   /* TODO: expect: 331 */
+
+       /* Not ok: an integer is not a boolean constant expression. */
+       INT1 = 1 ? 100 : 101,   /* TODO: expect: 331 */
+
+       /* Not ok: 2 is not a boolean constant. */
+       INT2 = 2 ? 100 : 101,   /* expect: 331 */
+
+       /* Not ok: compound integer expressions are not bool. */
+       ARITH = (2 - 2) ? 100 : 101,    /* TODO: expect: 331 */
+
+       /*
+        * Without strict bool mode, these two variants of an expression can
+        * occur when a preprocessor macro is either defined to 1 or left
+        * empty, as in lint1/ops.def.
+        *
+        * TODO: figure out an elegant way to achieve the same effect in
+        *  strict bool mode.
+        */
+       BINARY_PLUS = (1 + 0) ? 100 : 101, /* TODO: expect: 331 */
+       UNARY_PLUS = (+0) ? 100 : 101,  /* TODO: expect: 331 */
+
+       /* The main operator '>' has return type bool. */
+       Q1 = (13 > 12) ? 100 : 101,
+
+       /*
+        * The parenthesized expression has type int and thus cannot be
+        * used as the controlling expression in the '?:' operator.
+        */
+       Q2 = (13 > 12 ? 1 : 7) ? 100 : 101,
+
+       BINAND_BOOL = __lint_false & __lint_true,
+       BINAND_INT = 0 & 1,
+
+       BINXOR_BOOL = __lint_false ^ __lint_true,
+       BINXOR_INT = 0 ^ 1,
 
-       /* Fine due to SB006. */
-       if (/*CONSTCOND*/0)
-               return;
+       BINOR_BOOL = __lint_false | __lint_true,
+       BINOR_INT = 0 | 1,
+
+       LOGOR_BOOL = __lint_false || __lint_true,
+       LOGOR_INT = 0 || 1,     /* TODO: expect: 331, 332 */
+
+       LOGAND_BOOL = __lint_false && __lint_true,
+       LOGAND_INT = 0 && 1,    /* TODO: expect: 331, 332 */
+};
+
+/*
+ * strict-bool-bit-fields
+ */
+
+void
+strict_bool_bit_fields(void)
+{
+       struct flags {
+               bool bool_flag: 1;
+               unsigned uint_flag: 1;
+       };
+
+       struct flags flags = { __lint_false, 0 };
+       struct flags *flags_ptr = &flags;
+       bool b;
+
+       b = flags.bool_flag;
+       b = flags.uint_flag;            /* expect: 107 */
+       flags.bool_flag = b;
+       flags.uint_flag = b;            /* expect: 107 */
+
+       b = flags_ptr->bool_flag;
+       b = flags_ptr->uint_flag;       /* expect: 107 */
+       flags_ptr->bool_flag = b;
+       flags_ptr->uint_flag = b;       /* expect: 107 */
+}
+
+void
+strict_bool_bit_fields_operand_conversion(void)
+{
+       struct s {
+               bool ordinary;
+               bool bit_field: 1;
+       };
+
+       struct s s = { 0 };
+
+       s.ordinary = s.ordinary | s.ordinary;
+       s.bit_field = s.bit_field | s.bit_field; /* FIXME *//* expect: 107 */
+}
+
+/*
+ * strict-bool-conversion
+ */
+
+bool
+strict_bool_conversion_return_false(void)
+{
+       return __lint_false;
+}
+
+bool
+strict_bool_conversion_return_true(void)
+{
+       return __lint_true;
+}
 
-       /* Fine due to SB006. */
-       if (/*CONSTCOND*/1)
-               return;
+bool
+strict_bool_conversion_return_bool(bool b)



Home | Main Index | Thread Index | Old Index