nathanchance wrote:

@nikic I noticed your comment on #118472 (the motivator for this change AFAICT):

> `-fwrapv` should already cover pointers

but it does not seem like this warning takes that into account (see the last 
example below)? I noticed a few instances of this warning in the Linux kernel 
but it sets `-fno-strict-overflow` for well defined overflow semantics it can 
depend on because it wants to avoid optimizations like the one that triggered 
this warning (`-fno-delete-null-pointer-checks` is a similarly used flag), so 
claiming the result of the checks are always false just does not seem to line 
up with reality. It seems like if the optimizer is going to respect this check 
and not remove it, this warning should not trigger either.

```c
int check(const int* foo, unsigned int idx)
{
        return foo + idx < foo;
}
```

```
$ clang-19 -O2 -c test.c

$ llvm-objdump -dr test.o
...
0000000000000000 <check>:
       0: 31 c0                         xorl    %eax, %eax
       2: c3                            retq

$ clang-19 -O2 -fno-strict-overflow -c test.o

$ llvm-objdump -dr test.o
...
0000000000000000 <check>:
       0: 89 f0                         movl    %esi, %eax
       2: 48 8d 0c 87                   leaq    (%rdi,%rax,4), %rcx
       6: 31 c0                         xorl    %eax, %eax
       8: 48 39 f9                      cmpq    %rdi, %rcx
       b: 0f 92 c0                      setb    %al
       e: c3                            retq
```
        
```
$ gcc --version | head -1
gcc (GCC) 14.2.1 20240910

$ gcc -O2 -c test.c

$ llvm-objdump -dr test.o
...
0000000000000000 <check>:
       0: 31 c0                         xorl    %eax, %eax
       2: c3                            retq

$ gcc -O2 -fno-strict-overflow -c test.c

$ llvm-objdump -dr test.o
...
0000000000000000 <check>:
       0: 89 f6                         movl    %esi, %esi
       2: 48 8d 04 b7                   leaq    (%rdi,%rsi,4), %rax
       6: 48 39 f8                      cmpq    %rdi, %rax
       9: 0f 92 c0                      setb    %al
       c: 0f b6 c0                      movzbl  %al, %eax
       f: c3                            retq
```

```
$ clang --version | head -1
ClangBuiltLinux clang version 20.0.0git 
(https://github.com/llvm/llvm-project.git 
99c2e3b78210a345afb1b5121f12b0e7bf923543)

$ clang -O2 -c test.c
test.c:3:19: warning: pointer comparison always evaluates to false 
[-Wtautological-compare]
    3 |         return foo + idx < foo;
      |                          ^
1 warning generated.

$ llvm-objdump -dr test.o
...
0000000000000000 <check>:
       0: 31 c0                         xorl    %eax, %eax
       2: c3                            retq

$ clang -O2 -c -fno-strict-overflow test.c
test.c:3:19: warning: pointer comparison always evaluates to false 
[-Wtautological-compare]
    3 |         return foo + idx < foo;
      |                          ^
1 warning generated.

$ llvm-objdump -dr test.o
...
0000000000000000 <check>:
       0: 89 f0                         movl    %esi, %eax
       2: 48 8d 0c 87                   leaq    (%rdi,%rax,4), %rcx
       6: 31 c0                         xorl    %eax, %eax
       8: 48 39 f9                      cmpq    %rdi, %rcx
       b: 0f 92 c0                      setb    %al
       e: c3                            retq
```

I tested something like:

```diff
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e06a092177ef..2dd7c5951d71 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -11789,10 +11789,11 @@ static bool checkForArray(const Expr *E) {
 /// Detect patterns ptr + size >= ptr and ptr + size < ptr, where ptr is a
 /// pointer and size is an unsigned integer. Return whether the result is
 /// always true/false.
-static std::optional<bool> isTautologicalBoundsCheck(const Expr *LHS,
+static std::optional<bool> isTautologicalBoundsCheck(Sema &S,
+                                                     const Expr *LHS,
                                                      const Expr *RHS,
                                                      BinaryOperatorKind Opc) {
-  if (!LHS->getType()->isPointerType())
+  if (!LHS->getType()->isPointerType() || 
S.getLangOpts().isSignedOverflowDefined())
     return std::nullopt;

   // Canonicalize to >= or < predicate.
@@ -11940,7 +11941,7 @@ static void diagnoseTautologicalComparison(Sema &S, 
SourceLocation Loc,
                                 << 1 /*array comparison*/
                                 << Result);
     } else if (std::optional<bool> Res =
-                   isTautologicalBoundsCheck(LHS, RHS, Opc)) {
+                   isTautologicalBoundsCheck(S, LHS, RHS, Opc)) {
       S.DiagRuntimeBehavior(Loc, nullptr,
                             S.PDiag(diag::warn_comparison_always)
                                 << 2 /*pointer comparison*/
```

which results in a behavior I would expect:

```
$ clang -fsyntax-only test.c
test.c:3:19: warning: pointer comparison always evaluates to false 
[-Wtautological-compare]
    3 |         return foo + idx < foo;
      |                          ^
1 warning generated.

$ clang -fsyntax-only -fwrapv test.c
```

If I am missing something, please let me know.

https://github.com/llvm/llvm-project/pull/120222
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to