Skip to content

[clang][CFG] Fix assertion failure in checkIncorrectLogicOperator #142897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

ziqingluo-90
Copy link
Contributor

checkIncorrectLogicOperator checks if an expression, for example x != 0 || x != 1.0, is always true or false by comparing the two literals 0 and 1.0. But in case x is a 16-bit float, the two literals have distinct types---16-bit float and double, respectively. Directly comparing APValues extracted from the two literals results in an assertion failure because of their distinct types.

This commit fixes the issue by doing a conversion from the "smaller" one to the "bigger" one. The two literals must be compatible because both of them are comparing with x.

rdar://152456316

`checkIncorrectLogicOperator` checks if an expression, for example
`x != 0 || x != 1.0`, is always true or false by comparing the two
literals `0` and `1.0`.  But in case `x` is a 16-bit float, the two
literals have distinct types---16-bit float and double, respectively.
Directly comparing `APValue`s extracted from the two literals results
in an assertion failure because of their distinct types.

This commit fixes the issue by doing a conversion from the "smaller"
one to the "bigger" one.  The two literals must be compatible because
both of them are comparing with `x`.

rdar://152456316
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:analysis labels Jun 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 5, 2025

@llvm/pr-subscribers-clang-analysis

Author: Ziqing Luo (ziqingluo-90)

Changes

checkIncorrectLogicOperator checks if an expression, for example x != 0 || x != 1.0, is always true or false by comparing the two literals 0 and 1.0. But in case x is a 16-bit float, the two literals have distinct types---16-bit float and double, respectively. Directly comparing APValues extracted from the two literals results in an assertion failure because of their distinct types.

This commit fixes the issue by doing a conversion from the "smaller" one to the "bigger" one. The two literals must be compatible because both of them are comparing with x.

rdar://152456316


Full diff: https://github.com/llvm/llvm-project/pull/142897.diff

2 Files Affected:

  • (modified) clang/lib/Analysis/CFG.cpp (+22)
  • (added) clang/test/Sema/warn-unreachable_crash.cpp (+8)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 7f37a8b18d46e..c9610cc2888ad 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -1261,6 +1261,28 @@ class CFGBuilder {
         L2Result.Val.getKind() == APValue::Float) {
       llvm::APFloat L1 = L1Result.Val.getFloat();
       llvm::APFloat L2 = L2Result.Val.getFloat();
+      // Note that L1 and L2 do not necessarily have the same type.  For example
+      // `x != 0 || x != 1.0`, if `x` is a float16, the two literals `0` and
+      // `1.0` are float16 and double respectively.  In this case, we should do
+      // a conversion before comparing L1 and L2.  Their types must be
+      // compatible since they are comparing with the same DRE.
+      int8_t Order = Context->getFloatingTypeOrder(NumExpr1->getType(),
+                                                   NumExpr2->getType());
+      bool convertLoseInfo = false;
+
+      if (Order > 0) {
+        // type rank L1 > L2:
+        if (L2.convert(L1.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
+                       &convertLoseInfo))
+          return {};
+      } else if (Order < 0)
+        // type rank L1 < L2:
+        if (L1.convert(L2.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
+                       &convertLoseInfo))
+          return {};
+      if (convertLoseInfo)
+        return {}; // If the conversion loses info, bail
+
       llvm::APFloat MidValue = L1;
       MidValue.add(L2, llvm::APFloat::rmNearestTiesToEven);
       MidValue.divide(llvm::APFloat(MidValue.getSemantics(), "2.0"),
diff --git a/clang/test/Sema/warn-unreachable_crash.cpp b/clang/test/Sema/warn-unreachable_crash.cpp
new file mode 100644
index 0000000000000..7b23f30c6a214
--- /dev/null
+++ b/clang/test/Sema/warn-unreachable_crash.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -verify -Wunreachable-code %s
+
+static void test(__fp16& x) {
+  if (x != 0 || x != 1.0) { // expected-note{{}}
+      x = 0.9;
+    } else
+      x = 0.8; // expected-warning{{code will never be executed}}
+}

@llvmbot
Copy link
Member

llvmbot commented Jun 5, 2025

@llvm/pr-subscribers-clang

Author: Ziqing Luo (ziqingluo-90)

Changes

checkIncorrectLogicOperator checks if an expression, for example x != 0 || x != 1.0, is always true or false by comparing the two literals 0 and 1.0. But in case x is a 16-bit float, the two literals have distinct types---16-bit float and double, respectively. Directly comparing APValues extracted from the two literals results in an assertion failure because of their distinct types.

This commit fixes the issue by doing a conversion from the "smaller" one to the "bigger" one. The two literals must be compatible because both of them are comparing with x.

rdar://152456316


Full diff: https://github.com/llvm/llvm-project/pull/142897.diff

2 Files Affected:

  • (modified) clang/lib/Analysis/CFG.cpp (+22)
  • (added) clang/test/Sema/warn-unreachable_crash.cpp (+8)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 7f37a8b18d46e..c9610cc2888ad 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -1261,6 +1261,28 @@ class CFGBuilder {
         L2Result.Val.getKind() == APValue::Float) {
       llvm::APFloat L1 = L1Result.Val.getFloat();
       llvm::APFloat L2 = L2Result.Val.getFloat();
+      // Note that L1 and L2 do not necessarily have the same type.  For example
+      // `x != 0 || x != 1.0`, if `x` is a float16, the two literals `0` and
+      // `1.0` are float16 and double respectively.  In this case, we should do
+      // a conversion before comparing L1 and L2.  Their types must be
+      // compatible since they are comparing with the same DRE.
+      int8_t Order = Context->getFloatingTypeOrder(NumExpr1->getType(),
+                                                   NumExpr2->getType());
+      bool convertLoseInfo = false;
+
+      if (Order > 0) {
+        // type rank L1 > L2:
+        if (L2.convert(L1.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
+                       &convertLoseInfo))
+          return {};
+      } else if (Order < 0)
+        // type rank L1 < L2:
+        if (L1.convert(L2.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
+                       &convertLoseInfo))
+          return {};
+      if (convertLoseInfo)
+        return {}; // If the conversion loses info, bail
+
       llvm::APFloat MidValue = L1;
       MidValue.add(L2, llvm::APFloat::rmNearestTiesToEven);
       MidValue.divide(llvm::APFloat(MidValue.getSemantics(), "2.0"),
diff --git a/clang/test/Sema/warn-unreachable_crash.cpp b/clang/test/Sema/warn-unreachable_crash.cpp
new file mode 100644
index 0000000000000..7b23f30c6a214
--- /dev/null
+++ b/clang/test/Sema/warn-unreachable_crash.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -verify -Wunreachable-code %s
+
+static void test(__fp16& x) {
+  if (x != 0 || x != 1.0) { // expected-note{{}}
+      x = 0.9;
+    } else
+      x = 0.8; // expected-warning{{code will never be executed}}
+}

@ziqingluo-90
Copy link
Contributor Author

Godbolt example: https://godbolt.org/z/6dv3Kearx

CC @YutongZhuu as this issue was first introduced in YutongZhuu@9ae3368

@ziqingluo-90 ziqingluo-90 requested a review from ahatanak June 5, 2025 05:17
// `1.0` are float16 and double respectively. In this case, we should do
// a conversion before comparing L1 and L2. Their types must be
// compatible since they are comparing with the same DRE.
int8_t Order = Context->getFloatingTypeOrder(NumExpr1->getType(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int8_t Order = Context->getFloatingTypeOrder(NumExpr1->getType(),
int Order = Context->getFloatingTypeSemanticOrder(NumExpr1->getType(),

Why we use int8_t here? the return type of getFloatingTypeOrder is int.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, I initially thought it just returns -1,0, or 1 but it's not the case.

if (L1.convert(L2.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
&convertLoseInfo))
return {};
if (convertLoseInfo)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems we check APFloat::opStatus is enough. We already ignore loseInfo in constant evaluator

St = Result.convert(Info.Ctx.getFloatTypeSemantics(DestType), RM, &ignored);


if (Order > 0) {
// type rank L1 > L2:
if (L2.convert(L1.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think useif (L2.convert(L1.getSemantics(), llvm::APFloat::rmNearestTiesToEven, &convertLoseInfo) != APFloat::opOK) more clear, WDYT?

@yronglin
Copy link
Contributor

yronglin commented Jun 5, 2025

Thank you working on this! Overall LGTM! But I don't think I'm qualified to make the final sign. CC @steakhal.
Could you also add an item in ReleaseNotes?

@yronglin yronglin requested a review from steakhal June 5, 2025 16:20
Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I have much to say. I never really touched floating-point stuff.
I left the comments I had while reading though.

Copy link
Contributor

@yronglin yronglin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:analysis clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants