[clang] [Clang][Sema] Fix -Whigher-precision-for-complex-division (PR #131477)
Mészáros Gergely via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 11 01:29:21 PDT 2025
https://github.com/Maetveis updated https://github.com/llvm/llvm-project/pull/131477
>From d3da72bf61ae8268a25e5331acefe895c783905a 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] [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`)
Fixes: #131127
---
clang/docs/ReleaseNotes.rst | 11 +++
clang/lib/Sema/SemaExpr.cpp | 78 ++++++++--------
.../complex-div-warn-higher-precision.cpp | 93 +++++++++++++++++++
3 files changed, 144 insertions(+), 38 deletions(-)
create mode 100644 clang/test/Sema/complex-div-warn-higher-precision.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 46d2debec3621..ae04ff52cdfdc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -349,6 +349,17 @@ 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:
+
+ - 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 (``/=``)
+
+ Fixes #GH131127
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c25daaa022f49..1ac84d7e95323 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) {
+ // 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)
+ 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);
}
@@ -15336,39 +15376,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) {
@@ -15379,11 +15386,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.cpp b/clang/test/Sema/complex-div-warn-higher-precision.cpp
new file mode 100644
index 0000000000000..a7cc2fbc7ed21
--- /dev/null
+++ b/clang/test/Sema/complex-div-warn-higher-precision.cpp
@@ -0,0 +1,93 @@
+// 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_cr_mixed1(_Complex double a, float b) {
+ return a / b;
+}
+
+_Complex double div_cr_mixed2(_Complex float 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}}
More information about the cfe-commits
mailing list