Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/tests/usr.bin/xlint/lint1 lint: extend test for strict _Bool...
details: https://anonhg.NetBSD.org/src/rev/4bba42b0fdae
branches: trunk
changeset: 958574:4bba42b0fdae
user: rillig <rillig%NetBSD.org@localhost>
date: Mon Jan 11 00:28:28 2021 +0000
description:
lint: extend test for strict _Bool handling
There is no danger in allowing (flags & FLAG) as a controlling
expression, provided that it is immediately compared to zero, such as in
an if statement or as the operand of a logical operator.
diffstat:
tests/usr.bin/xlint/lint1/d_c99_bool_strict.c | 181 ++++++++++++++++++++---
tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp | 8 +-
2 files changed, 163 insertions(+), 26 deletions(-)
diffs (218 lines):
diff -r 8b97d66dbe11 -r 4bba42b0fdae tests/usr.bin/xlint/lint1/d_c99_bool_strict.c
--- a/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c Sun Jan 10 23:59:53 2021 +0000
+++ b/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c Mon Jan 11 00:28:28 2021 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: d_c99_bool_strict.c,v 1.2 2021/01/10 21:45:50 rillig Exp $ */
+/* $NetBSD: d_c99_bool_strict.c,v 1.3 2021/01/11 00:28:28 rillig Exp $ */
# 3 "d_c99_bool_strict.c"
/*
@@ -21,6 +21,15 @@
* SB006: A constant integer expression is compatible with type _Bool if
* it is an integer constant with value 0 or 1, or if the result type of
* its main operator is _Bool.
+ *
+ * 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.
+ *
+ * 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.
*/
// Not yet implemented: /* lint1-extra-flags: -T */
@@ -251,33 +260,157 @@
LOGAND = 0 && 1, /* ok */
};
-enum BitSet {
- ONE = 1 << 0,
- TWO = 1 << 1,
- FOUR = 1 << 2
+/*
+ * An efficient implementation technique for a collection of boolean 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.
+ */
+
+enum Flags {
+ FLAG0 = 1 << 0,
+ FLAG1 = 1 << 1,
+ FLAG28 = 1 << 28
};
/*
- * It is debatable whether it is a good idea to allow expressions like these
- * for _Bool. The strict rules above ensure that the code works in the same
- * way whether or not the special rule C99 6.3.1.2 is active or not.
+ * The usual way to query one of the flags is demonstrated below.
+ */
+
+extern void println(const char *);
+
+void
+query_flag_from_enum_bit_set(enum Flags flags)
+{
+
+ if (flags & FLAG0)
+ println("FLAG0 is set");
+
+ if ((flags & FLAG1) != 0)
+ println("FLAG1 is set");
+
+ if ((flags & (FLAG0 | FLAG1)) == (FLAG0 | FLAG1))
+ println("FLAG0 and FLAG1 are both set");
+ if (flags & FLAG0 && flags & FLAG1)
+ println("FLAG0 and FLAG1 are both set");
+
+ if ((flags & (FLAG0 | FLAG1)) != 0)
+ println("At least one of FLAG0 and FLAG1 is set");
+
+ if (flags & FLAG28)
+ println("FLAG28 is set");
+}
+
+/*
+ * In all the above conditions (or controlling expressions, as the C standard
+ * calls them), the result of the operator '&' is compared against 0. This
+ * makes this pattern work, no matter whether the bits are in the low-value
+ * range or in the high-value range (such as FLAG28, which has the value
+ * 1073741824, which is more than what fits into an unsigned char). Even
+ * if an enum could be extended to larger types than int, this pattern
+ * would work.
+ */
+
+/*
+ * There is a crucial difference between a _Bool variable and an ordinary
+ * integer variable though. C99 6.3.1.2 defines a conversion from an
+ * arbitrary scalar type to _Bool as (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.
*
- * If the code were to switch away from the C99 bool type to an ordinary
- * unsigned integer type, the behavior might silently change. Because the
- * rule C99 6.3.1.2 is no longer active in that case, high bits of the enum
- * constant may get lost, thus evaluating to false even though a bit is set.
- *
- * It's probably better to not allow this kind of expressions, even though
- * it may be popular, especially in usr.bin/make.
+ * Because of this, expressions like (flags & FLAG28) are only allowed in
+ * bool context if they are guaranteed not to be truncated, even if the
+ * result were to be stored in a plain unsigned integer.
*/
-int
-S007_allow_flag_test_on_bit_set_enums(enum BitSet bs)
+
+void
+SB007_allow_flag_test_on_bit_set_enums(enum Flags flags)
+{
+ bool b;
+
+ /*
+ * FLAG0 has the value 1 and can therefore be stored in a bool
+ * variable without truncation. Nevertheless this special case
+ * is not allowed because it would be too confusing if FLAG0 would
+ * work and all the other flags wouldn't.
+ */
+ b = flags & FLAG0;
+
+ /*
+ * Assuming that FLAG1 is set in flags, a _Bool variable stores this
+ * as 1, as defined by C99 6.3.1.2. An unsigned char variable would
+ * store it as 2, as that is the integer value of FLAG1. Since FLAG1
+ * fits in an unsigned char, no truncation takes place.
+ */
+ b = flags & FLAG1;
+
+ /*
+ * In a _Bool variable, FLAG28 is stored as 1, as above. In an
+ * unsigned char, the stored value would be 0 since bit 28 is out of
+ * range for an unsigned char, which usually has 8 significant bits.
+ */
+ b = flags & FLAG28;
+}
+
+/* A bool bit field is compatible with bool. Other bit fields are not. */
+
+struct flags {
+ bool bool_flag: 1;
+ unsigned uint_flag: 1;
+};
+
+void
+SB008_flags_from_bit_fields(const struct flags *flags)
{
- if (bs & ONE)
- return 1;
- if (!(bs & TWO))
- return 2;
- if (bs & FOUR)
- return 2;
- return 4;
+ bool b;
+
+ b = flags->bool_flag; /* ok */
+ b = flags->uint_flag; /* not ok */
+}
+
+/* Test implicit conversion when returning a value from a function. */
+
+bool
+returning_bool(bool b, int i, const char *p)
+{
+ if (i > 0)
+ return b; /* ok */
+ if (i < 0)
+ return i; /* not ok */
+ return p; /* not ok */
}
+
+char
+returning_char(bool b, int i, const char *p)
+{
+ if (i > 0)
+ return b; /* not ok */
+ if (i < 0)
+ return i; /* not ok, but not related to bool */
+ return p; /* not ok */
+}
+
+/* Test passing arguments to a function. */
+
+extern void taking_arguments(bool, int, const char *, ...);
+
+void
+passing_arguments(bool b, int i, const char *p)
+{
+ /* No conversion necessary. */
+ taking_arguments(b, i, p);
+
+ /* Implicitly converting bool to other scalar types. */
+ taking_arguments(b, b, b);
+
+ /* Implicitly converting int to bool (arg #1). */
+ taking_arguments(i, i, i);
+
+ /* Implicitly converting pointer to bool (arg #1). */
+ taking_arguments(p, p, p);
+
+ /* Passing bool as vararg. */
+ taking_arguments(b, i, p, b, i, p);
+}
diff -r 8b97d66dbe11 -r 4bba42b0fdae tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp
--- a/tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp Sun Jan 10 23:59:53 2021 +0000
+++ b/tests/usr.bin/xlint/lint1/d_c99_bool_strict.exp Mon Jan 11 00:28:28 2021 +0000
@@ -1,2 +1,6 @@
-d_c99_bool_strict.c(88): warning: illegal combination of pointer (pointer to void) and integer (_Bool), op = [123]
-d_c99_bool_strict.c(208): warning: illegal combination of pointer (pointer to void) and integer (_Bool), op = [123]
+d_c99_bool_strict.c(97): warning: illegal combination of pointer (pointer to void) and integer (_Bool), op = [123]
+d_c99_bool_strict.c(217): warning: illegal combination of pointer (pointer to void) and integer (_Bool), op = [123]
+d_c99_bool_strict.c(392): warning: illegal combination of integer (char) and pointer (pointer to const char) [183]
+d_c99_bool_strict.c(406): warning: illegal combination of pointer (pointer to const char) and integer (_Bool), arg #3 [154]
+d_c99_bool_strict.c(409): warning: illegal combination of pointer (pointer to const char) and integer (int), arg #3 [154]
+d_c99_bool_strict.c(412): warning: illegal combination of integer (int) and pointer (pointer to const char), arg #2 [154]
Home |
Main Index |
Thread Index |
Old Index