Skip to content

[clang] Report erroneous floating point results in _Complex math #90588

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

Merged
merged 1 commit into from
Jun 8, 2024

Conversation

tbaederr
Copy link
Contributor

Use handleFloatFloatBinOp to properly diagnose NaN results and divisions by zero.

Fixes #84871

@tbaederr tbaederr changed the title [clang][Interp] Report erroneous floating point results in _Complex math [clang]Report erroneous floating point results in _Complex math Apr 30, 2024
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Apr 30, 2024
@llvmbot
Copy link
Member

llvmbot commented Apr 30, 2024

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

Use handleFloatFloatBinOp to properly diagnose NaN results and divisions by zero.

Fixes #84871


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

2 Files Affected:

  • (modified) clang/lib/AST/ExprConstant.cpp (+21-6)
  • (modified) clang/test/SemaCXX/complex-folding.cpp (+44-29)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f1aa19e4409e15..86fb396fabe2d9 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15209,11 +15209,21 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
       APFloat &ResI = Result.getComplexFloatImag();
       if (LHSReal) {
         assert(!RHSReal && "Cannot have two real operands for a complex op!");
-        ResR = A * C;
-        ResI = A * D;
+        ResR = A;
+        ResI = A;
+        // ResR = A * C;
+        // ResI = A * D;
+        if (!handleFloatFloatBinOp(Info, E, ResR, BO_Mul, C) ||
+            !handleFloatFloatBinOp(Info, E, ResI, BO_Mul, D))
+          return false;
       } else if (RHSReal) {
-        ResR = C * A;
-        ResI = C * B;
+        // ResR = C * A;
+        // ResI = C * B;
+        ResR = C;
+        ResI = C;
+        if (!handleFloatFloatBinOp(Info, E, ResR, BO_Mul, A) ||
+            !handleFloatFloatBinOp(Info, E, ResI, BO_Mul, B))
+          return false;
       } else {
         // In the fully general case, we need to handle NaNs and infinities
         // robustly.
@@ -15289,8 +15299,13 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
       APFloat &ResR = Result.getComplexFloatReal();
       APFloat &ResI = Result.getComplexFloatImag();
       if (RHSReal) {
-        ResR = A / C;
-        ResI = B / C;
+        ResR = A;
+        ResI = B;
+        // ResR = A / C;
+        // ResI = B / C;
+        if (!handleFloatFloatBinOp(Info, E, ResR, BO_Div, C) ||
+            !handleFloatFloatBinOp(Info, E, ResI, BO_Div, C))
+          return false;
       } else {
         if (LHSReal) {
           // No real optimizations we can do here, stub out with zero.
diff --git a/clang/test/SemaCXX/complex-folding.cpp b/clang/test/SemaCXX/complex-folding.cpp
index 054f159e9ce0dd..d3e428f2b75cc2 100644
--- a/clang/test/SemaCXX/complex-folding.cpp
+++ b/clang/test/SemaCXX/complex-folding.cpp
@@ -57,43 +57,58 @@ static_assert(((1.25 + 0.5j) / (0.25 - 0.75j)) == (-0.1 + 1.7j));
 static_assert(((1.25 + 0.5j) / 0.25) == (5.0 + 2.0j));
 static_assert((1.25 / (0.25 - 0.75j)) == (0.5 + 1.5j));
 
-// Test that infinities are preserved, don't turn into NaNs, and do form zeros
-// when the divisor.
+
 static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) * 1.0)) == 1);
-static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) * 1.0)) == 1);
+static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) * 1.0)) == 1); // expected-error {{static assertion}} \
+                                                                                          // expected-note {{produces a NaN}}
 static_assert(__builtin_isinf_sign(__real__(1.0 * (__builtin_inf() + 1.0j))) == 1);
-static_assert(__builtin_isinf_sign(__imag__(1.0 * (1.0 + __builtin_inf() * 1.0j))) == 1);
-
+static_assert(__builtin_isinf_sign(__imag__(1.0 * (1.0 + __builtin_inf() * 1.0j))) == 1); // expected-error {{static assertion}} \
+                                                                                          // expected-note {{produces a NaN}}
 static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) * (1.0 + 1.0j))) == 1);
 static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) * (__builtin_inf() + 1.0j))) == 1);
 static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) * (__builtin_inf() + 1.0j))) == 1);
-
-static_assert(__builtin_isinf_sign(__real__((1.0 + __builtin_inf() * 1.0j) * (1.0 + 1.0j))) == -1);
-static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) * (1.0 + 1.0j))) == 1);
-static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == -1);
-static_assert(__builtin_isinf_sign(__imag__((1.0 + 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == 1);
-
-static_assert(__builtin_isinf_sign(__real__((1.0 + __builtin_inf() * 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == -1);
-static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + __builtin_inf() * 1.0j) * (__builtin_inf() + __builtin_inf() * 1.0j))) == -1);
-
+static_assert(__builtin_isinf_sign(__real__((1.0 + __builtin_inf() * 1.0j) * (1.0 + 1.0j))) == -1);  // expected-error {{static assertion}} \
+                                                                                                     // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) * (1.0 + 1.0j))) == 1); // expected-error {{static assertion}} \
+                                                                                                   // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == -1); // expected-error {{static assertion}} \
+                                                                                                    // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__imag__((1.0 + 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == 1); // expected-error {{static assertion}} \
+                                                                                                   // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__real__((1.0 + __builtin_inf() * 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == -1); // expected-error {{static assertion}} \
+                                                                                                                      // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + __builtin_inf() * 1.0j) * (__builtin_inf() + __builtin_inf() * 1.0j))) == -1); // expected-error {{static assertion}} \
+                                                                                                                                              // expected-note {{produces a NaN}}
 static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / (1.0 + 1.0j))) == 1);
-static_assert(__builtin_isinf_sign(__imag__(1.0 + (__builtin_inf() * 1.0j) / (1.0 + 1.0j))) == 1);
-static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / (1.0 + 1.0j))) == 1);
+static_assert(__builtin_isinf_sign(__imag__(1.0 + (__builtin_inf() * 1.0j) / (1.0 + 1.0j))) == 1); // expected-error {{static assertion}} \
+                                                                                                   // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / (1.0 + 1.0j))) == 1); // expected-error {{static assertion}} \
+                                                                                                               // expected-note {{produces a NaN}}
 static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / 1.0)) == 1);
-static_assert(__builtin_isinf_sign(__imag__(1.0 + (__builtin_inf() * 1.0j) / 1.0)) == 1);
-static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / 1.0)) == 1);
-
+static_assert(__builtin_isinf_sign(__imag__(1.0 + (__builtin_inf() * 1.0j) / 1.0)) == 1); // expected-error {{static assertion}} \
+                                                                                          // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / 1.0)) == 1); // expected-error {{static assertion}} \
+                                                                                                      // expected-note {{produces a NaN}}
 static_assert(((1.0 + 1.0j) / (__builtin_inf() + 1.0j)) == (0.0 + 0.0j));
-static_assert(((1.0 + 1.0j) / (1.0 + __builtin_inf() * 1.0j)) == (0.0 + 0.0j));
-static_assert(((1.0 + 1.0j) / (__builtin_inf() + __builtin_inf() * 1.0j)) == (0.0 + 0.0j));
+static_assert(((1.0 + 1.0j) / (1.0 + __builtin_inf() * 1.0j)) == (0.0 + 0.0j)); // expected-error {{static assertion}} \
+                                                                                // expected-note {{produces a NaN}}
+static_assert(((1.0 + 1.0j) / (__builtin_inf() + __builtin_inf() * 1.0j)) == (0.0 + 0.0j)); // expected-error {{static assertion}} \
+                                                                                            // expected-note {{produces a NaN}}
 static_assert(((1.0 + 1.0j) / __builtin_inf()) == (0.0 + 0.0j));
-
+static_assert(1.0j / 0.0 == 1); // expected-error {{static assertion}} \
+                                // expected-note {{division by zero}}
 static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) / (0.0 + 0.0j))) == 1);
-static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) / 0.0)) == 1);
-
+static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) / 0.0)) == 1); // expected-error {{static assertion}} \
+                                                                        // expected-note {{division by zero}}
 static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / (0.0 + 0.0j))) == 1);
-static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) / (0.0 + 0.0j))) == 1);
-static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / (0.0 + 0.0j))) == 1);
-static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / 0.0)) == 1);
-static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) / 0.0)) == 1);
-static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / 0.0)) == 1);
+static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) / (0.0 + 0.0j))) == 1); // expected-error {{static assertion}} \
+                                                                                                   // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / (0.0 + 0.0j))) == 1); // expected-error {{static assertion}} \
+                                                                                                               // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / 0.0)) == 1); // expected-error {{static assertion}} \
+                                                                                    // expected-note {{division by zero}}
+static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) / 0.0)) == 1); // expected-error {{static assertion}} \
+                                                                                          // expected-note {{produces a NaN}}
+static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / 0.0)) == 1); // expected-error {{static assertion}} \
+                                                                                                      // expected-note {{produces a NaN}}
+

@tbaederr tbaederr changed the title [clang]Report erroneous floating point results in _Complex math [clang] Report erroneous floating point results in _Complex math May 2, 2024
@tbaederr
Copy link
Contributor Author

tbaederr commented May 7, 2024

Ping

1 similar comment
@tbaederr
Copy link
Contributor Author

Ping

@AaronBallman
Copy link
Collaborator

The changes seem reasonable to me, but I'd like to hear from @jcranmer-intel and/or @hubert-reinterpretcast in terms of whether they agree with this direction.

@tbaederr
Copy link
Contributor Author

Copy link
Contributor

@jcranmer-intel jcranmer-intel left a comment

Choose a reason for hiding this comment

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

After banging my head on the desk trying to figure out why the tests were giving such strange results, I realized the issue...

Could you please use __builtin_complex in the test to construct the complex infinities instead of __builtin_infinity() * 1.0j?

Use handleFloatFloatBinOp to properly diagnose NaN results and divisions
by zero.
@tbaederr
Copy link
Contributor Author

tbaederr commented Jun 6, 2024

Ping

Copy link
Collaborator

After banging my head on the desk trying to figure out why the tests were giving such strange results, I realized the issue...

Could you please use __builtin_complex in the test to construct the complex infinities instead of __builtin_infinity() * 1.0j?

It seems this review feedback as not been addressed?

@tbaederr
Copy link
Contributor Author

tbaederr commented Jun 6, 2024

After banging my head on the desk trying to figure out why the tests were giving such strange results, I realized the issue...
Could you please use __builtin_complex in the test to construct the complex infinities instead of __builtin_infinity() * 1.0j?

It seems this review feedback as not been addressed?

The tests aren't using __builtin_complex, but they're now creating the complex values in a different way that creates inf/nan in those complex values instead of multiplying with infinity. See the first hunk in the test file

Copy link
Collaborator

After banging my head on the desk trying to figure out why the tests were giving such strange results, I realized the issue...
Could you please use __builtin_complex in the test to construct the complex infinities instead of __builtin_infinity() * 1.0j?

It seems this review feedback as not been addressed?

The tests aren't using __builtin_complex, but they're now creating the complex values in a different way that creates inf/nan in those complex values instead of multiplying with infinity. See the first hunk in the test file

Out of curiosity, why are they not using __builtin_complex as requested?

@jcranmer-intel are you okay with the new approach?

@tbaederr
Copy link
Contributor Author

tbaederr commented Jun 6, 2024

Out of curiosity, why are they not using __builtin_complex as requested?

I started out replacing a lot of those expressions with __builtin_complex but ended up just creating a variable for it so the lines don't get too unwieldy. But then I naturally just used an initlist instead of __builtin_complex.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

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

Changes LGTM unless @jcranmer-intel still has concerns.

@tbaederr tbaederr merged commit 9ddc014 into llvm:main Jun 8, 2024
8 checks passed
nekoshirro pushed a commit to nekoshirro/Alchemist-LLVM that referenced this pull request Jun 9, 2024
…m#90588)

Use handleFloatFloatBinOp to properly diagnose NaN results and divisions
by zero.

Fixes llvm#84871

Signed-off-by: Hafidz Muzakky <[email protected]>
@HerrCai0907 HerrCai0907 mentioned this pull request Jun 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clang does not report invalid arithmetic operations at compile time when doing _Complex math.
4 participants