[clang] [Clang][Sema] Fix -Whigher-precision-for-complex-division (PR #131477)

Mészáros Gergely via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 9 01:28:18 PDT 2025


https://github.com/Maetveis updated https://github.com/llvm/llvm-project/pull/131477

>From 92101ddf4a5f40b12434fb0e2b6e67c176750f9d Mon Sep 17 00:00:00 2001
From: Gergely Meszaros <meszaros.gergely97 at gmail.com>
Date: Sat, 15 Mar 2025 22:37:07 +0100
Subject: [PATCH 1/4] [Clang][Sema] Fix -Whigher-precision-for-complex-division

- Fix false positive when divisor is a real number
- Fix false negative when divident is real, but divisor is complex
- Fix false negative when due to promotion the division is performed
  in higher precision than the divident.
- Fix false negative in divide and assign (`a /= b`)
---
 clang/lib/Sema/SemaExpr.cpp                   | 78 ++++++++++---------
 .../Sema/complex-div-warn-higher-precision.c  | 58 ++++++++++++++
 2 files changed, 98 insertions(+), 38 deletions(-)
 create mode 100644 clang/test/Sema/complex-div-warn-higher-precision.c

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e7f418ae6802e..5001df20afe03 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10591,6 +10591,45 @@ static void checkArithmeticNull(Sema &S, ExprResult &LHS, ExprResult &RHS,
       << LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
 }
 
+static void DetectPrecisionLossInComplexDivision(Sema &S, QualType DivisorTy,
+                                                 SourceLocation OpLoc) {
+  // Either real/real or complex/real division.
+  // Either way there can be no precision loss.
+  auto *CT = DivisorTy->getAs<ComplexType>();
+  if (!CT)
+    return;
+
+  QualType ElementType = CT->getElementType();
+  bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
+                                LangOptions::ComplexRangeKind::CX_Promoted;
+  if (!ElementType->isFloatingType() || !IsComplexRangePromoted)
+    return;
+
+  ASTContext &Ctx = S.getASTContext();
+  QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
+  const llvm::fltSemantics &ElementTypeSemantics =
+      Ctx.getFloatTypeSemantics(ElementType);
+  const llvm::fltSemantics &HigherElementTypeSemantics =
+      Ctx.getFloatTypeSemantics(HigherElementType);
+
+  if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
+       llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) ||
+      (HigherElementType == Ctx.LongDoubleTy &&
+       !Ctx.getTargetInfo().hasLongDoubleType())) {
+    // Retain the location of the first use of higher precision type.
+    if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
+      S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
+    for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
+      if (Type == HigherElementType) {
+        Num++;
+        return;
+      }
+    }
+    S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
+        HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
+  }
+}
+
 static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS,
                                           SourceLocation Loc) {
   const auto *LUE = dyn_cast<UnaryExprOrTypeTraitExpr>(LHS);
@@ -10685,6 +10724,7 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
   if (compType.isNull() || !compType->isArithmeticType())
     return InvalidOperands(Loc, LHS, RHS);
   if (IsDiv) {
+    DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc);
     DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv);
     DiagnoseDivisionSizeofPointerOrArray(*this, LHS.get(), RHS.get(), Loc);
   }
@@ -15314,39 +15354,6 @@ static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc,
     DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr);
 }
 
-static void DetectPrecisionLossInComplexDivision(Sema &S, SourceLocation OpLoc,
-                                                 Expr *Operand) {
-  if (auto *CT = Operand->getType()->getAs<ComplexType>()) {
-    QualType ElementType = CT->getElementType();
-    bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
-                                  LangOptions::ComplexRangeKind::CX_Promoted;
-    if (ElementType->isFloatingType() && IsComplexRangePromoted) {
-      ASTContext &Ctx = S.getASTContext();
-      QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
-      const llvm::fltSemantics &ElementTypeSemantics =
-          Ctx.getFloatTypeSemantics(ElementType);
-      const llvm::fltSemantics &HigherElementTypeSemantics =
-          Ctx.getFloatTypeSemantics(HigherElementType);
-      if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
-           llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) ||
-          (HigherElementType == Ctx.LongDoubleTy &&
-           !Ctx.getTargetInfo().hasLongDoubleType())) {
-        // Retain the location of the first use of higher precision type.
-        if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
-          S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
-        for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
-          if (Type == HigherElementType) {
-            Num++;
-            return;
-          }
-        }
-        S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
-            HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
-      }
-    }
-  }
-}
-
 ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
                             tok::TokenKind Kind,
                             Expr *LHSExpr, Expr *RHSExpr) {
@@ -15357,11 +15364,6 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
   // Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0"
   DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr);
 
-  // Emit warnings if the requested higher precision type equal to the current
-  // type precision.
-  if (Kind == tok::TokenKind::slash)
-    DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);
-
   BuiltinCountedByRefKind K =
       BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind;
 
diff --git a/clang/test/Sema/complex-div-warn-higher-precision.c b/clang/test/Sema/complex-div-warn-higher-precision.c
new file mode 100644
index 0000000000000..73a6e37b82e2c
--- /dev/null
+++ b/clang/test/Sema/complex-div-warn-higher-precision.c
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-linux -verify=no-diag \
+// RUN: -DDIV_CC -DDIV_RC -DDIVASSIGN -DDIVMIXEDFD -DDIVMIXEDID
+
+// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify=no-diag
+// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIV_CC
+// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIV_RC
+// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVASSIGN
+// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVMIXEDFD
+// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVMIXEDID
+
+_Complex double div_ccf(_Complex float a, _Complex float b) {
+    return a / b;
+}
+
+_Complex double div_cr(_Complex double a, double b) {
+    return a / b;
+}
+
+_Complex double div_rr(double a, double b) {
+    return a / b;
+}
+
+_Complex int div_ii(_Complex int a, _Complex int b) {
+    return a / b;
+}
+
+#ifdef DIV_CC
+_Complex double div_cc(_Complex double a, const _Complex double b) {
+    return a / b; // #1
+}
+#endif // DIV_CC
+
+#ifdef DIV_RC
+_Complex double div_rc(double a, _Complex float b) {
+    return a / b; // #1
+}
+#endif // DIV_RC
+
+#ifdef DIVASSIGN
+_Complex double divassign(_Complex double a, _Complex double b) {
+    return a /= b; // #1
+}
+#endif // DIVASSIGN
+
+#ifdef DIVMIXEDFD
+_Complex double divmixedfd(_Complex float a, _Complex double b) {
+    return a / b; // #1
+}
+#endif // DIVMIXEDFD
+
+#ifdef DIVMIXEDID
+_Complex double divmixedid(_Complex int a, _Complex double b) {
+    return a / b; // #1
+}
+#endif // DIVMIXEDID
+
+// no-diag-no-diagnostics
+// expected-warning@#1 {{excess precision is requested but the target does not support excess precision which may result in observable differences in complex division behavior}}

>From 0556094e56c7540342883abb8a00ba849fdf16e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A9sz=C3=A1ros=20Gergely?= <maetveis at gmail.com>
Date: Fri, 21 Mar 2025 07:23:48 +0100
Subject: [PATCH 2/4] Reword comment to make it clearer

---
 clang/lib/Sema/SemaExpr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 5001df20afe03..36068d1a4b94e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10593,7 +10593,7 @@ static void checkArithmeticNull(Sema &S, ExprResult &LHS, ExprResult &RHS,
 
 static void DetectPrecisionLossInComplexDivision(Sema &S, QualType DivisorTy,
                                                  SourceLocation OpLoc) {
-  // Either real/real or complex/real division.
+  // If the divisor is real, then this is real/real or complex/real division.
   // Either way there can be no precision loss.
   auto *CT = DivisorTy->getAs<ComplexType>();
   if (!CT)

>From 10b5c5aebb4b50326c4b87826e84afacf1d926ae Mon Sep 17 00:00:00 2001
From: Gergely Meszaros <gergely.meszaros at intel.com>
Date: Wed, 9 Apr 2025 00:52:54 -0700
Subject: [PATCH 3/4] Improve testing

- Add user-defined overloaded operator test
- Add /= test with different types
- Add reversed mixed type test
- Use -fsyntax-only to avoid unnecessary codegen
---
 .../Sema/complex-div-warn-higher-precision.c  | 58 -------------
 .../complex-div-warn-higher-precision.cpp     | 85 +++++++++++++++++++
 2 files changed, 85 insertions(+), 58 deletions(-)
 delete mode 100644 clang/test/Sema/complex-div-warn-higher-precision.c
 create mode 100644 clang/test/Sema/complex-div-warn-higher-precision.cpp

diff --git a/clang/test/Sema/complex-div-warn-higher-precision.c b/clang/test/Sema/complex-div-warn-higher-precision.c
deleted file mode 100644
index 73a6e37b82e2c..0000000000000
--- a/clang/test/Sema/complex-div-warn-higher-precision.c
+++ /dev/null
@@ -1,58 +0,0 @@
-// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-linux -verify=no-diag \
-// RUN: -DDIV_CC -DDIV_RC -DDIVASSIGN -DDIVMIXEDFD -DDIVMIXEDID
-
-// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify=no-diag
-// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIV_CC
-// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIV_RC
-// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVASSIGN
-// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVMIXEDFD
-// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVMIXEDID
-
-_Complex double div_ccf(_Complex float a, _Complex float b) {
-    return a / b;
-}
-
-_Complex double div_cr(_Complex double a, double b) {
-    return a / b;
-}
-
-_Complex double div_rr(double a, double b) {
-    return a / b;
-}
-
-_Complex int div_ii(_Complex int a, _Complex int b) {
-    return a / b;
-}
-
-#ifdef DIV_CC
-_Complex double div_cc(_Complex double a, const _Complex double b) {
-    return a / b; // #1
-}
-#endif // DIV_CC
-
-#ifdef DIV_RC
-_Complex double div_rc(double a, _Complex float b) {
-    return a / b; // #1
-}
-#endif // DIV_RC
-
-#ifdef DIVASSIGN
-_Complex double divassign(_Complex double a, _Complex double b) {
-    return a /= b; // #1
-}
-#endif // DIVASSIGN
-
-#ifdef DIVMIXEDFD
-_Complex double divmixedfd(_Complex float a, _Complex double b) {
-    return a / b; // #1
-}
-#endif // DIVMIXEDFD
-
-#ifdef DIVMIXEDID
-_Complex double divmixedid(_Complex int a, _Complex double b) {
-    return a / b; // #1
-}
-#endif // DIVMIXEDID
-
-// no-diag-no-diagnostics
-// expected-warning@#1 {{excess precision is requested but the target does not support excess precision which may result in observable differences in complex division behavior}}
diff --git a/clang/test/Sema/complex-div-warn-higher-precision.cpp b/clang/test/Sema/complex-div-warn-higher-precision.cpp
new file mode 100644
index 0000000000000..ada3736855427
--- /dev/null
+++ b/clang/test/Sema/complex-div-warn-higher-precision.cpp
@@ -0,0 +1,85 @@
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-linux -verify=no-diag \
+// RUN: -DDIV_CC -DDIV_RC -DDIVASSIGN -DDIVMIXEDFD -DDIVMIXEDFD2 -DDIVMIXEDID -DDIVASSIGN_MIXEDFD
+
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify=no-diag
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIV_CC
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIV_RC
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVASSIGN
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVMIXEDFD
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVMIXEDFD2
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVMIXEDID
+// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVASSIGN_MIXEDFD
+
+_Complex double div_ccf(_Complex float a, _Complex float b) {
+    return a / b;
+}
+
+_Complex double div_cr(_Complex double a, double b) {
+    return a / b;
+}
+
+_Complex double div_rr(double a, double b) {
+    return a / b;
+}
+
+_Complex int div_ii(_Complex int a, _Complex int b) {
+    return a / b;
+}
+
+struct UserT {
+    friend UserT operator/(UserT, _Complex double);
+    friend UserT operator/(_Complex double, UserT);
+};
+
+UserT div_uc(UserT a, _Complex double b) {
+    return a / b;
+}
+
+UserT div_cu(_Complex double a, UserT b) {
+    return a / b;
+}
+
+#ifdef DIV_CC
+_Complex double div_cc(_Complex double a, const _Complex double b) {
+    return a / b; // #1
+}
+#endif // DIV_CC
+
+#ifdef DIV_RC
+_Complex double div_rc(double a, _Complex float b) {
+    return a / b; // #1
+}
+#endif // DIV_RC
+
+#ifdef DIVASSIGN
+_Complex double divassign(_Complex double a, _Complex double b) {
+    return a /= b; // #1
+}
+#endif // DIVASSIGN
+
+#ifdef DIVMIXEDFD
+_Complex double divmixedfd(_Complex float a, _Complex double b) {
+    return a / b; // #1
+}
+#endif // DIVMIXEDFD
+
+#ifdef DIVMIXEDFD2
+_Complex double divmixedfd2(_Complex double a, _Complex float b) {
+    return a / b; // #1
+}
+#endif // DIVMIXEDFD2
+
+#ifdef DIVMIXEDID
+_Complex double divmixedid(_Complex int a, _Complex double b) {
+    return a / b; // #1
+}
+#endif // DIVMIXEDID
+
+#ifdef DIVASSIGN_MIXEDFD
+_Complex double divassign_mixedfd(_Complex float a, _Complex double b) {
+    return a /= b; // #1
+}
+#endif // DIVMIXEDFD
+
+// no-diag-no-diagnostics
+// expected-warning@#1 {{excess precision is requested but the target does not support excess precision which may result in observable differences in complex division behavior}}

>From 53aff6542e355ded9b933a0dca12700286f1fff1 Mon Sep 17 00:00:00 2001
From: Gergely Meszaros <gergely.meszaros at intel.com>
Date: Wed, 9 Apr 2025 01:26:12 -0700
Subject: [PATCH 4/4] Add Release Notes

---
 clang/docs/ReleaseNotes.rst | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5b702b56038f7..d2ec4e78b80ff 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -326,6 +326,16 @@ Improvements to Clang's diagnostics
 - Now correctly diagnose a tentative definition of an array with static
   storage duration in pedantic mode in C. (#GH50661)
 
+- ``-Whigher-precisision-for-complex-divison`` no longer incorrectly warns when the divisor is real
+  in complex division. (#GH131127)
+
+- ``-Whigher-precisision-for-complex-divison`` now correctly warns when (#GH131127):
+
+  - The dividend is real, but the divisor is complex.
+  - When the complex division happens in a higher precision type than the dividend due to arithmetic promotion.
+  - When using the divide and assign operator (``/=``)
+  
+
 Improvements to Clang's time-trace
 ----------------------------------
 



More information about the cfe-commits mailing list