Module Name: src
Committed By: rillig
Date: Sun May 12 11:46:14 UTC 2024
Modified Files:
src/tests/usr.bin/xlint/lint1: d_c99_bool_strict.c
d_c99_bool_strict_syshdr.c
Log Message:
tests/lint: clean up tests for strict bool mode
To generate a diff of this commit:
cvs rdiff -u -r1.48 -r1.49 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c
cvs rdiff -u -r1.22 -r1.23 \
src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c
diff -u src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c:1.48 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c:1.49
--- src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c:1.48 Sat Dec 30 17:09:42 2023
+++ src/tests/usr.bin/xlint/lint1/d_c99_bool_strict.c Sun May 12 11:46:14 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: d_c99_bool_strict.c,v 1.48 2023/12/30 17:09:42 rillig Exp $ */
+/* $NetBSD: d_c99_bool_strict.c,v 1.49 2024/05/12 11:46:14 rillig Exp $ */
# 3 "d_c99_bool_strict.c"
/*
@@ -27,7 +27,7 @@
*
* strict-bool-controlling-expression:
* Controlling expressions in 'if', 'while', 'for', '?:' must be of
- * type bool.
+ * type bool, except for a literal 0 in a do-while loop.
*
* strict-bool-operand-unary:
* Operator bool? scalar?
@@ -62,29 +62,17 @@
* the resulting value is used in a context where it is implicitly and
* immediately compared to zero.
*
- * 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. Another implementation
- * technique for bit sets is a plain integer.
- *
* 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.
- *
- * 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.
+ * Note: Counterexamples for contexts are assignments to a bool variable,
+ * as without the conversion from C99 6.3.1.2, converting an integer to a
+ * "bool-like" integer type truncated the value instead of comparing it
+ * to 0.
+ *
+ * Note: These rules ensure that conforming code behaves the same in both
+ * C99 and in environments that emulate a boolean type using a small
+ * integer type.
*/
/*
@@ -413,9 +401,7 @@ strict_bool_conversion_from_bool_to_scal
}
/*
- * strict-bool-controlling-expression:
- * Controlling expressions in 'if', 'while', 'for', '?:' must be of
- * type bool.
+ * strict-bool-controlling-expression
*/
void
@@ -466,14 +452,36 @@ strict_bool_controlling_expression(bool
do_nothing();
if (p != (void *)0)
do_nothing();
+
+ // An endless loop. The preferred form is 'for (;;)' instead.
+ do {
+ /* expect+1: warning: constant in conditional context [161] */
+ } while (__lint_true);
+
+ // A do-once "loop", often used in statement macros.
+ /* expect+1: warning: loop not entered at top [207] */
+ do {
+ } while (__lint_false);
+
+ // This form is too unusual to be allowed in strict bool mode.
+ do {
+ /* expect+2: error: controlling expression must be bool, not 'int' [333] */
+ /* expect+1: warning: constant in conditional context [161] */
+ } while (1);
+
+ // Even though 0 is an integer instead of a bool, this idiom is so
+ // common that it is frequently used in system headers. Since the
+ // Clang preprocessor does not mark each token as coming from a system
+ // header or from user code, this idiom can only be allowed everywhere
+ // or nowhere.
+ /* expect+1: warning: loop not entered at top [207] */
+ do {
+ /* expect+1: error: controlling expression must be bool, not 'int' [333] */
+ } while (0);
}
/*
- * strict-bool-operand-unary:
- * Operator bool? scalar?
- * ! yes -
- * & yes yes
- * The other unary operators do not accept bool operands.
+ * strict-bool-operand-unary
*/
void
@@ -517,23 +525,7 @@ strict_bool_operand_unary_address(void)
/* see strict_bool_operand_unary_all below for the other unary operators. */
/*
- * 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
- * The other binary operators do not accept bool operands.
+ * strict-bool-operand-binary
*/
/*
@@ -739,9 +731,7 @@ strict_bool_operand_binary_comma(bool b,
}
/*
- * strict-bool-operator-result:
- * The result type of the operators '!', '<', '<=', '>', '>=',
- * '==', '!=', '&&', '||' is _Bool instead of int.
+ * strict-bool-operator-result
*/
void
@@ -793,20 +783,7 @@ strict_bool_operator_result(bool b)
/*
- * strict-bool-bitwise-and:
- * Expressions of the form "flags & FLAG" are compatible with _Bool if
- * the resulting value is used in a context where it is implicitly and
- * immediately compared to zero.
- *
- * Note: Examples for such contexts are controlling expressions or the
- * operands of the operators '!', '&&', '||'.
- *
- * Note: Counterexamples for contexts are assignments to a bool variable,
- * as before C99, the conversion was defined differently.
- *
- * 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.
+ * strict-bool-bitwise-and
*/
enum Flags {
@@ -955,23 +932,6 @@ bool_as_array_index(bool cond)
}
void
-do_while_false(void)
-{
- do {
-
- } while (__lint_false);
-}
-
-void
-do_while_true(void)
-{
- do {
-
- } while (__lint_true);
- /* expect-1: warning: constant in conditional context [161] */
-}
-
-void
initialization(void)
{
struct {
@@ -985,127 +945,3 @@ initialization(void)
{ 1 },
};
}
-
-/*
- * For expressions that originate from a system header, the strict type rules
- * are relaxed a bit, to allow for expressions like 'flags & FLAG', even
- * though they are not strictly boolean.
- *
- * This shouldn't apply to function call expressions though since one of the
- * goals of strict bool mode is to normalize all expressions calling 'strcmp'
- * to be of the form 'strcmp(a, b) == 0' instead of '!strcmp(a, b)'.
- */
-# 1 "stdio.h" 1 3 4
-typedef struct stdio_file {
- int fd;
-} FILE;
-int ferror(FILE *);
-FILE stdio_files[3];
-FILE *stdio_stdout;
-# 1006 "d_c99_bool_strict.c" 2
-# 1 "string.h" 1 3 4
-int strcmp(const char *, const char *);
-# 1009 "d_c99_bool_strict.c" 2
-
-void
-controlling_expression(FILE *f, const char *a, const char *b)
-{
- /* expect+1: error: controlling expression must be bool, not 'int' [333] */
- if (ferror(f))
- return;
- /* expect+1: error: controlling expression must be bool, not 'int' [333] */
- if (strcmp(a, b))
- return;
- /* expect+1: error: operand of '!' must be bool, not 'int' [330] */
- if (!ferror(f))
- return;
- /* expect+1: error: operand of '!' must be bool, not 'int' [330] */
- if (!strcmp(a, b))
- return;
-
- /*
- * Before tree.c 1.395 from 2021-11-16, the expression below didn't
- * produce a warning since the expression 'stdio_files' came from a
- * system header (via a macro), and this property was passed up to
- * the expression 'ferror(stdio_files[1])'.
- *
- * That was wrong though since the type of a function call expression
- * only depends on the function itself but not its arguments types.
- * The old rule had allowed a raw condition 'strcmp(a, b)' without
- * the comparison '!= 0', as long as one of its arguments came from a
- * system header.
- *
- * Seen in bin/echo/echo.c, function main, call to ferror.
- */
- /* expect+5: error: controlling expression must be bool, not 'int' [333] */
- if (ferror(
-# 1043 "d_c99_bool_strict.c" 3 4
- &stdio_files[1]
-# 1045 "d_c99_bool_strict.c"
- ))
- return;
-
- /*
- * Before cgram.y 1.369 from 2021-11-16, at the end of parsing the
- * name 'stdio_stdout', the parser already looked ahead to the next
- * token, to see whether it was the '(' of a function call.
- *
- * At that point, the parser was no longer in a system header,
- * therefore 'stdio_stdout' had tn_sys == false, and this information
- * was pushed down to the whole function call expression (which was
- * another bug that got fixed in tree.c 1.395 from 2021-11-16).
- */
- /* expect+5: error: controlling expression must be bool, not 'int' [333] */
- if (ferror(
-# 1061 "d_c99_bool_strict.c" 3 4
- stdio_stdout
-# 1063 "d_c99_bool_strict.c"
- ))
- return;
-
- /*
- * In this variant of the pattern, there is a token ')' after the
- * name 'stdio_stdout', which even before tree.c 1.395 from
- * 2021-11-16 had the effect that at the end of parsing the name, the
- * parser was still in the system header, thus setting tn_sys (or
- * rather tn_relaxed at that time) to true.
- */
- /* expect+5: error: controlling expression must be bool, not 'int' [333] */
- if (ferror(
-# 1076 "d_c99_bool_strict.c" 3 4
- (stdio_stdout)
-# 1078 "d_c99_bool_strict.c"
- ))
- return;
-
- /*
- * Before cgram.y 1.369 from 2021-11-16, the comment following
- * 'stdio_stdout' did not prevent the search for '('. At the point
- * where build_name called expr_alloc_tnode, the parser was already
- * in the main file again, thus treating 'stdio_stdout' as not coming
- * from a system header.
- *
- * This has been fixed in tree.c 1.395 from 2021-11-16. Before that,
- * an expression had come from a system header if its operands came
- * from a system header, but that was only close to the truth. In a
- * case where both operands come from a system header but the
- * operator comes from the main translation unit, the main
- * translation unit still has control over the whole expression. So
- * the correct approach is to focus on the operator, not the
- * operands. There are a few corner cases where the operator is
- * invisible (for implicit conversions) or synthetic (for translating
- * 'arr[index]' to '*(arr + index)', but these are handled as well.
- */
- /* expect+5: error: controlling expression must be bool, not 'int' [333] */
- if (ferror(
-# 1102 "d_c99_bool_strict.c" 3 4
- stdio_stdout /* comment */
-# 1104 "d_c99_bool_strict.c"
- ))
- return;
-}
-
-// In strict bool mode, the identifiers '__lint_false' and '__lint_true' are
-// predefined, but not any others.
-/* expect+1: error: '__lint_unknown' undefined [99] */
-int unknown = sizeof __lint_unknown;
Index: src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c
diff -u src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c:1.22 src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c:1.23
--- src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c:1.22 Sun Aug 6 19:44:50 2023
+++ src/tests/usr.bin/xlint/lint1/d_c99_bool_strict_syshdr.c Sun May 12 11:46:14 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: d_c99_bool_strict_syshdr.c,v 1.22 2023/08/06 19:44:50 rillig Exp $ */
+/* $NetBSD: d_c99_bool_strict_syshdr.c,v 1.23 2024/05/12 11:46:14 rillig Exp $ */
# 3 "d_c99_bool_strict_syshdr.c"
/*
@@ -286,3 +286,123 @@ if_pointer_or_int(void)
)
return;
}
+
+
+/*
+ * For expressions that originate from a system header, the strict type rules
+ * are relaxed a bit, to allow for expressions like 'flags & FLAG', even
+ * though they are not strictly boolean.
+ *
+ * This shouldn't apply to function call expressions though since one of the
+ * goals of strict bool mode is to normalize all expressions calling 'strcmp'
+ * to be of the form 'strcmp(a, b) == 0' instead of '!strcmp(a, b)'.
+ */
+# 1 "stdio.h" 1 3 4
+typedef struct stdio_file {
+ int fd;
+} FILE;
+int ferror(FILE *);
+FILE stdio_files[3];
+FILE *stdio_stdout;
+# 308 "d_c99_bool_strict_syshdr.c" 2
+# 1 "string.h" 1 3 4
+int strcmp(const char *, const char *);
+# 311 "d_c99_bool_strict_syshdr.c" 2
+
+void
+controlling_expression(FILE *f, const char *a, const char *b)
+{
+ /* expect+1: error: controlling expression must be bool, not 'int' [333] */
+ if (ferror(f))
+ return;
+ /* expect+1: error: controlling expression must be bool, not 'int' [333] */
+ if (strcmp(a, b))
+ return;
+ /* expect+1: error: operand of '!' must be bool, not 'int' [330] */
+ if (!ferror(f))
+ return;
+ /* expect+1: error: operand of '!' must be bool, not 'int' [330] */
+ if (!strcmp(a, b))
+ return;
+
+ /*
+ * Before tree.c 1.395 from 2021-11-16, the expression below didn't
+ * produce a warning since the expression 'stdio_files' came from a
+ * system header (via a macro), and this property was passed up to
+ * the expression 'ferror(stdio_files[1])'.
+ *
+ * That was wrong though since the type of a function call expression
+ * only depends on the function itself but not its arguments types.
+ * The old rule had allowed a raw condition 'strcmp(a, b)' without
+ * the comparison '!= 0', as long as one of its arguments came from a
+ * system header.
+ *
+ * Seen in bin/echo/echo.c, function main, call to ferror.
+ */
+ /* expect+5: error: controlling expression must be bool, not 'int' [333] */
+ if (ferror(
+# 345 "d_c99_bool_strict_syshdr.c" 3 4
+ &stdio_files[1]
+# 347 "d_c99_bool_strict_syshdr.c"
+ ))
+ return;
+
+ /*
+ * Before cgram.y 1.369 from 2021-11-16, at the end of parsing the
+ * name 'stdio_stdout', the parser already looked ahead to the next
+ * token, to see whether it was the '(' of a function call.
+ *
+ * At that point, the parser was no longer in a system header,
+ * therefore 'stdio_stdout' had tn_sys == false, and this information
+ * was pushed down to the whole function call expression (which was
+ * another bug that got fixed in tree.c 1.395 from 2021-11-16).
+ */
+ /* expect+5: error: controlling expression must be bool, not 'int' [333] */
+ if (ferror(
+# 363 "d_c99_bool_strict_syshdr.c" 3 4
+ stdio_stdout
+# 365 "d_c99_bool_strict_syshdr.c"
+ ))
+ return;
+
+ /*
+ * In this variant of the pattern, there is a token ')' after the
+ * name 'stdio_stdout', which even before tree.c 1.395 from
+ * 2021-11-16 had the effect that at the end of parsing the name, the
+ * parser was still in the system header, thus setting tn_sys (or
+ * rather tn_relaxed at that time) to true.
+ */
+ /* expect+5: error: controlling expression must be bool, not 'int' [333] */
+ if (ferror(
+# 378 "d_c99_bool_strict_syshdr.c" 3 4
+ (stdio_stdout)
+# 380 "d_c99_bool_strict_syshdr.c"
+ ))
+ return;
+
+ /*
+ * Before cgram.y 1.369 from 2021-11-16, the comment following
+ * 'stdio_stdout' did not prevent the search for '('. At the point
+ * where build_name called expr_alloc_tnode, the parser was already
+ * in the main file again, thus treating 'stdio_stdout' as not coming
+ * from a system header.
+ *
+ * This has been fixed in tree.c 1.395 from 2021-11-16. Before that,
+ * an expression had come from a system header if its operands came
+ * from a system header, but that was only close to the truth. In a
+ * case where both operands come from a system header but the
+ * operator comes from the main translation unit, the main
+ * translation unit still has control over the whole expression. So
+ * the correct approach is to focus on the operator, not the
+ * operands. There are a few corner cases where the operator is
+ * invisible (for implicit conversions) or synthetic (for translating
+ * 'arr[index]' to '*(arr + index)', but these are handled as well.
+ */
+ /* expect+5: error: controlling expression must be bool, not 'int' [333] */
+ if (ferror(
+# 404 "d_c99_bool_strict_syshdr.c" 3 4
+ stdio_stdout /* comment */
+# 406 "d_c99_bool_strict_syshdr.c"
+ ))
+ return;
+}