Skip to content

Commit f3c7744

Browse files
Maetveiszahiraam
andauthored
[Clang][Sema] Fix -Whigher-precision-for-complex-division (llvm#131477)
- 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: llvm#131127 --------- Co-authored-by: Zahira Ammarguellat <[email protected]>
1 parent 123b0e2 commit f3c7744

File tree

3 files changed

+141
-38
lines changed

3 files changed

+141
-38
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,14 @@ Improvements to Clang's diagnostics
371371
372372
- An error is now emitted when a ``musttail`` call is made to a function marked with the ``not_tail_called`` attribute. (#GH133509).
373373

374+
- ``-Whigher-precisision-for-complex-divison`` warns when:
375+
376+
- The divisor is complex.
377+
- When the complex division happens in a higher precision type due to arithmetic promotion.
378+
- When using the divide and assign operator (``/=``).
379+
380+
Fixes #GH131127
381+
374382
Improvements to Clang's time-trace
375383
----------------------------------
376384

clang/lib/Sema/SemaExpr.cpp

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

10605+
static void DetectPrecisionLossInComplexDivision(Sema &S, QualType DivisorTy,
10606+
SourceLocation OpLoc) {
10607+
// If the divisor is real, then this is real/real or complex/real division.
10608+
// Either way there can be no precision loss.
10609+
auto *CT = DivisorTy->getAs<ComplexType>();
10610+
if (!CT)
10611+
return;
10612+
10613+
QualType ElementType = CT->getElementType();
10614+
bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
10615+
LangOptions::ComplexRangeKind::CX_Promoted;
10616+
if (!ElementType->isFloatingType() || !IsComplexRangePromoted)
10617+
return;
10618+
10619+
ASTContext &Ctx = S.getASTContext();
10620+
QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
10621+
const llvm::fltSemantics &ElementTypeSemantics =
10622+
Ctx.getFloatTypeSemantics(ElementType);
10623+
const llvm::fltSemantics &HigherElementTypeSemantics =
10624+
Ctx.getFloatTypeSemantics(HigherElementType);
10625+
10626+
if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
10627+
llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) ||
10628+
(HigherElementType == Ctx.LongDoubleTy &&
10629+
!Ctx.getTargetInfo().hasLongDoubleType())) {
10630+
// Retain the location of the first use of higher precision type.
10631+
if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
10632+
S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
10633+
for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
10634+
if (Type == HigherElementType) {
10635+
Num++;
10636+
return;
10637+
}
10638+
}
10639+
S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
10640+
HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
10641+
}
10642+
}
10643+
1060510644
static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS,
1060610645
SourceLocation Loc) {
1060710646
const auto *LUE = dyn_cast<UnaryExprOrTypeTraitExpr>(LHS);
@@ -10696,6 +10735,7 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
1069610735
if (compType.isNull() || !compType->isArithmeticType())
1069710736
return InvalidOperands(Loc, LHS, RHS);
1069810737
if (IsDiv) {
10738+
DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc);
1069910739
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv);
1070010740
DiagnoseDivisionSizeofPointerOrArray(*this, LHS.get(), RHS.get(), Loc);
1070110741
}
@@ -15347,39 +15387,6 @@ static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc,
1534715387
DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr);
1534815388
}
1534915389

15350-
static void DetectPrecisionLossInComplexDivision(Sema &S, SourceLocation OpLoc,
15351-
Expr *Operand) {
15352-
if (auto *CT = Operand->getType()->getAs<ComplexType>()) {
15353-
QualType ElementType = CT->getElementType();
15354-
bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
15355-
LangOptions::ComplexRangeKind::CX_Promoted;
15356-
if (ElementType->isFloatingType() && IsComplexRangePromoted) {
15357-
ASTContext &Ctx = S.getASTContext();
15358-
QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
15359-
const llvm::fltSemantics &ElementTypeSemantics =
15360-
Ctx.getFloatTypeSemantics(ElementType);
15361-
const llvm::fltSemantics &HigherElementTypeSemantics =
15362-
Ctx.getFloatTypeSemantics(HigherElementType);
15363-
if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
15364-
llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) ||
15365-
(HigherElementType == Ctx.LongDoubleTy &&
15366-
!Ctx.getTargetInfo().hasLongDoubleType())) {
15367-
// Retain the location of the first use of higher precision type.
15368-
if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
15369-
S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
15370-
for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
15371-
if (Type == HigherElementType) {
15372-
Num++;
15373-
return;
15374-
}
15375-
}
15376-
S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
15377-
HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
15378-
}
15379-
}
15380-
}
15381-
}
15382-
1538315390
ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1538415391
tok::TokenKind Kind,
1538515392
Expr *LHSExpr, Expr *RHSExpr) {
@@ -15390,11 +15397,6 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1539015397
// Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0"
1539115398
DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr);
1539215399

15393-
// Emit warnings if the requested higher precision type equal to the current
15394-
// type precision.
15395-
if (Kind == tok::TokenKind::slash)
15396-
DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);
15397-
1539815400
BuiltinCountedByRefKind K =
1539915401
BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind;
1540015402

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)