Skip to content

Commit d3da72b

Browse files
committed
[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
1 parent 0276915 commit d3da72b

File tree

3 files changed

+144
-38
lines changed

3 files changed

+144
-38
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,17 @@ Improvements to Clang's diagnostics
349349
- Now correctly diagnose a tentative definition of an array with static
350350
storage duration in pedantic mode in C. (#GH50661)
351351

352+
- ``-Whigher-precisision-for-complex-divison`` no longer incorrectly warns when the divisor is real
353+
in complex division. (#GH131127)
354+
355+
- ``-Whigher-precisision-for-complex-divison`` now correctly warns when:
356+
357+
- The dividend is real, but the divisor is complex.
358+
- When the complex division happens in a higher precision type than the dividend due to arithmetic promotion.
359+
- When using the divide and assign operator (``/=``)
360+
361+
Fixes #GH131127
362+
352363
Improvements to Clang's time-trace
353364
----------------------------------
354365

clang/lib/Sema/SemaExpr.cpp

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10591,6 +10591,45 @@ static void checkArithmeticNull(Sema &S, ExprResult &LHS, ExprResult &RHS,
1059110591
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
1059210592
}
1059310593

10594+
static void DetectPrecisionLossInComplexDivision(Sema &S, QualType DivisorTy,
10595+
SourceLocation OpLoc) {
10596+
// If the divisor is real, then this is real/real or complex/real division.
10597+
// Either way there can be no precision loss.
10598+
auto *CT = DivisorTy->getAs<ComplexType>();
10599+
if (!CT)
10600+
return;
10601+
10602+
QualType ElementType = CT->getElementType();
10603+
bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
10604+
LangOptions::ComplexRangeKind::CX_Promoted;
10605+
if (!ElementType->isFloatingType() || !IsComplexRangePromoted)
10606+
return;
10607+
10608+
ASTContext &Ctx = S.getASTContext();
10609+
QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
10610+
const llvm::fltSemantics &ElementTypeSemantics =
10611+
Ctx.getFloatTypeSemantics(ElementType);
10612+
const llvm::fltSemantics &HigherElementTypeSemantics =
10613+
Ctx.getFloatTypeSemantics(HigherElementType);
10614+
10615+
if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
10616+
llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) ||
10617+
(HigherElementType == Ctx.LongDoubleTy &&
10618+
!Ctx.getTargetInfo().hasLongDoubleType())) {
10619+
// Retain the location of the first use of higher precision type.
10620+
if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
10621+
S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
10622+
for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
10623+
if (Type == HigherElementType) {
10624+
Num++;
10625+
return;
10626+
}
10627+
}
10628+
S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
10629+
HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
10630+
}
10631+
}
10632+
1059410633
static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS,
1059510634
SourceLocation Loc) {
1059610635
const auto *LUE = dyn_cast<UnaryExprOrTypeTraitExpr>(LHS);
@@ -10685,6 +10724,7 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
1068510724
if (compType.isNull() || !compType->isArithmeticType())
1068610725
return InvalidOperands(Loc, LHS, RHS);
1068710726
if (IsDiv) {
10727+
DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc);
1068810728
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv);
1068910729
DiagnoseDivisionSizeofPointerOrArray(*this, LHS.get(), RHS.get(), Loc);
1069010730
}
@@ -15336,39 +15376,6 @@ static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc,
1533615376
DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr);
1533715377
}
1533815378

15339-
static void DetectPrecisionLossInComplexDivision(Sema &S, SourceLocation OpLoc,
15340-
Expr *Operand) {
15341-
if (auto *CT = Operand->getType()->getAs<ComplexType>()) {
15342-
QualType ElementType = CT->getElementType();
15343-
bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
15344-
LangOptions::ComplexRangeKind::CX_Promoted;
15345-
if (ElementType->isFloatingType() && IsComplexRangePromoted) {
15346-
ASTContext &Ctx = S.getASTContext();
15347-
QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
15348-
const llvm::fltSemantics &ElementTypeSemantics =
15349-
Ctx.getFloatTypeSemantics(ElementType);
15350-
const llvm::fltSemantics &HigherElementTypeSemantics =
15351-
Ctx.getFloatTypeSemantics(HigherElementType);
15352-
if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
15353-
llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) ||
15354-
(HigherElementType == Ctx.LongDoubleTy &&
15355-
!Ctx.getTargetInfo().hasLongDoubleType())) {
15356-
// Retain the location of the first use of higher precision type.
15357-
if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
15358-
S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
15359-
for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
15360-
if (Type == HigherElementType) {
15361-
Num++;
15362-
return;
15363-
}
15364-
}
15365-
S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
15366-
HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
15367-
}
15368-
}
15369-
}
15370-
}
15371-
1537215379
ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1537315380
tok::TokenKind Kind,
1537415381
Expr *LHSExpr, Expr *RHSExpr) {
@@ -15379,11 +15386,6 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1537915386
// Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0"
1538015387
DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr);
1538115388

15382-
// Emit warnings if the requested higher precision type equal to the current
15383-
// type precision.
15384-
if (Kind == tok::TokenKind::slash)
15385-
DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);
15386-
1538715389
BuiltinCountedByRefKind K =
1538815390
BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind;
1538915391

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-linux -verify=no-diag \
2+
// RUN: -DDIV_CC -DDIV_RC -DDIVASSIGN -DDIVMIXEDFD -DDIVMIXEDFD2 -DDIVMIXEDID -DDIVASSIGN_MIXEDFD
3+
4+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify=no-diag
5+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIV_CC
6+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIV_RC
7+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVASSIGN
8+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVMIXEDFD
9+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVMIXEDFD2
10+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVMIXEDID
11+
// RUN: %clang_cc1 %s -complex-range=promoted -fsyntax-only -triple x86_64-unknown-windows -verify -DDIVASSIGN_MIXEDFD
12+
13+
_Complex double div_ccf(_Complex float a, _Complex float b) {
14+
return a / b;
15+
}
16+
17+
_Complex double div_cr(_Complex double a, double b) {
18+
return a / b;
19+
}
20+
21+
_Complex double div_cr_mixed1(_Complex double a, float b) {
22+
return a / b;
23+
}
24+
25+
_Complex double div_cr_mixed2(_Complex float a, double b) {
26+
return a / b;
27+
}
28+
29+
_Complex double div_rr(double a, double b) {
30+
return a / b;
31+
}
32+
33+
_Complex int div_ii(_Complex int a, _Complex int b) {
34+
return a / b;
35+
}
36+
37+
struct UserT {
38+
friend UserT operator/(UserT, _Complex double);
39+
friend UserT operator/(_Complex double, UserT);
40+
};
41+
42+
UserT div_uc(UserT a, _Complex double b) {
43+
return a / b;
44+
}
45+
46+
UserT div_cu(_Complex double a, UserT b) {
47+
return a / b;
48+
}
49+
50+
#ifdef DIV_CC
51+
_Complex double div_cc(_Complex double a, const _Complex double b) {
52+
return a / b; // #1
53+
}
54+
#endif // DIV_CC
55+
56+
#ifdef DIV_RC
57+
_Complex double div_rc(double a, _Complex float b) {
58+
return a / b; // #1
59+
}
60+
#endif // DIV_RC
61+
62+
#ifdef DIVASSIGN
63+
_Complex double divassign(_Complex double a, _Complex double b) {
64+
return a /= b; // #1
65+
}
66+
#endif // DIVASSIGN
67+
68+
#ifdef DIVMIXEDFD
69+
_Complex double divmixedfd(_Complex float a, _Complex double b) {
70+
return a / b; // #1
71+
}
72+
#endif // DIVMIXEDFD
73+
74+
#ifdef DIVMIXEDFD2
75+
_Complex double divmixedfd2(_Complex double a, _Complex float b) {
76+
return a / b; // #1
77+
}
78+
#endif // DIVMIXEDFD2
79+
80+
#ifdef DIVMIXEDID
81+
_Complex double divmixedid(_Complex int a, _Complex double b) {
82+
return a / b; // #1
83+
}
84+
#endif // DIVMIXEDID
85+
86+
#ifdef DIVASSIGN_MIXEDFD
87+
_Complex double divassign_mixedfd(_Complex float a, _Complex double b) {
88+
return a /= b; // #1
89+
}
90+
#endif // DIVMIXEDFD
91+
92+
// no-diag-no-diagnostics
93+
// 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}}

0 commit comments

Comments
 (0)