Skip to content

Commit 99b4539

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`)
1 parent 3b5413c commit 99b4539

File tree

2 files changed

+96
-36
lines changed

2 files changed

+96
-36
lines changed

clang/lib/Sema/SemaExpr.cpp

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10555,6 +10555,43 @@ static void checkArithmeticNull(Sema &S, ExprResult &LHS, ExprResult &RHS,
1055510555
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
1055610556
}
1055710557

10558+
static void DetectPrecisionLossInComplexDivision(Sema &S, QualType DivisorTy,
10559+
SourceLocation OpLoc) {
10560+
// Either real/real or complex/real division.
10561+
// Either way there can be no precision loss.
10562+
auto *CT = DivisorTy->getAs<ComplexType>();
10563+
if (!CT)
10564+
return;
10565+
10566+
QualType ElementType = CT->getElementType();
10567+
bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
10568+
LangOptions::ComplexRangeKind::CX_Promoted;
10569+
if (!ElementType->isFloatingType() || !IsComplexRangePromoted)
10570+
return;
10571+
10572+
ASTContext &Ctx = S.getASTContext();
10573+
QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
10574+
const llvm::fltSemantics &ElementTypeSemantics =
10575+
Ctx.getFloatTypeSemantics(ElementType);
10576+
const llvm::fltSemantics &HigherElementTypeSemantics =
10577+
Ctx.getFloatTypeSemantics(HigherElementType);
10578+
10579+
if (llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
10580+
llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) {
10581+
// Retain the location of the first use of higher precision type.
10582+
if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
10583+
S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
10584+
for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
10585+
if (Type == HigherElementType) {
10586+
Num++;
10587+
return;
10588+
}
10589+
}
10590+
S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
10591+
HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
10592+
}
10593+
}
10594+
1055810595
static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS,
1055910596
SourceLocation Loc) {
1056010597
const auto *LUE = dyn_cast<UnaryExprOrTypeTraitExpr>(LHS);
@@ -10649,6 +10686,7 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
1064910686
if (compType.isNull() || !compType->isArithmeticType())
1065010687
return InvalidOperands(Loc, LHS, RHS);
1065110688
if (IsDiv) {
10689+
DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc);
1065210690
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv);
1065310691
DiagnoseDivisionSizeofPointerOrArray(*this, LHS.get(), RHS.get(), Loc);
1065410692
}
@@ -15280,37 +15318,6 @@ static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc,
1528015318
DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr);
1528115319
}
1528215320

15283-
static void DetectPrecisionLossInComplexDivision(Sema &S, SourceLocation OpLoc,
15284-
Expr *Operand) {
15285-
if (auto *CT = Operand->getType()->getAs<ComplexType>()) {
15286-
QualType ElementType = CT->getElementType();
15287-
bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
15288-
LangOptions::ComplexRangeKind::CX_Promoted;
15289-
if (ElementType->isFloatingType() && IsComplexRangePromoted) {
15290-
ASTContext &Ctx = S.getASTContext();
15291-
QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
15292-
const llvm::fltSemantics &ElementTypeSemantics =
15293-
Ctx.getFloatTypeSemantics(ElementType);
15294-
const llvm::fltSemantics &HigherElementTypeSemantics =
15295-
Ctx.getFloatTypeSemantics(HigherElementType);
15296-
if (llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
15297-
llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) {
15298-
// Retain the location of the first use of higher precision type.
15299-
if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
15300-
S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
15301-
for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
15302-
if (Type == HigherElementType) {
15303-
Num++;
15304-
return;
15305-
}
15306-
}
15307-
S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
15308-
HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
15309-
}
15310-
}
15311-
}
15312-
}
15313-
1531415321
ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1531515322
tok::TokenKind Kind,
1531615323
Expr *LHSExpr, Expr *RHSExpr) {
@@ -15321,11 +15328,6 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1532115328
// Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0"
1532215329
DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr);
1532315330

15324-
// Emit warnings if the requested higher precision type equal to the current
15325-
// type precision.
15326-
if (Kind == tok::TokenKind::slash)
15327-
DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);
15328-
1532915331
BuiltinCountedByRefKind K =
1533015332
BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind;
1533115333

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-linux -verify=no-diag \
2+
// RUN: -DDIV_CC -DDIV_RC -DDIVASSIGN -DDIVMIXEDFD -DDIVMIXEDID
3+
4+
// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify=no-diag
5+
// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIV_CC
6+
// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIV_RC
7+
// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVASSIGN
8+
// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVMIXEDFD
9+
// RUN: %clang_cc1 %s -complex-range=promoted -triple x86_64-unknown-windows -verify -DDIVMIXEDID
10+
11+
_Complex double div_ccf(_Complex float a, _Complex float b) {
12+
return a / b;
13+
}
14+
15+
_Complex double div_cr(_Complex double a, double b) {
16+
return a / b;
17+
}
18+
19+
_Complex double div_rr(double a, double b) {
20+
return a / b;
21+
}
22+
23+
_Complex int div_ii(_Complex int a, _Complex int b) {
24+
return a / b;
25+
}
26+
27+
#ifdef DIV_CC
28+
_Complex double div_cc(_Complex double a, const _Complex double b) {
29+
return a / b; // #1
30+
}
31+
#endif // DIV_CC
32+
33+
#ifdef DIV_RC
34+
_Complex double div_rc(double a, _Complex float b) {
35+
return a / b; // #1
36+
}
37+
#endif // DIV_RC
38+
39+
#ifdef DIVASSIGN
40+
_Complex double divassign(_Complex double a, _Complex double b) {
41+
return a /= b; // #1
42+
}
43+
#endif // DIVASSIGN
44+
45+
#ifdef DIVMIXEDFD
46+
_Complex double divmixedfd(_Complex float a, _Complex double b) {
47+
return a / b; // #1
48+
}
49+
#endif // DIVMIXEDFD
50+
51+
#ifdef DIVMIXEDID
52+
_Complex double divmixedid(_Complex int a, _Complex double b) {
53+
return a / b; // #1
54+
}
55+
#endif // DIVMIXEDID
56+
57+
// no-diag-no-diagnostics
58+
// 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)