Skip to content

Commit 66d0105

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 be6ccc9 commit 66d0105

File tree

2 files changed

+98
-38
lines changed

2 files changed

+98
-38
lines changed

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+
// Either 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
}
@@ -15314,39 +15354,6 @@ static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc,
1531415354
DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr);
1531515355
}
1531615356

15317-
static void DetectPrecisionLossInComplexDivision(Sema &S, SourceLocation OpLoc,
15318-
Expr *Operand) {
15319-
if (auto *CT = Operand->getType()->getAs<ComplexType>()) {
15320-
QualType ElementType = CT->getElementType();
15321-
bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() ==
15322-
LangOptions::ComplexRangeKind::CX_Promoted;
15323-
if (ElementType->isFloatingType() && IsComplexRangePromoted) {
15324-
ASTContext &Ctx = S.getASTContext();
15325-
QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType);
15326-
const llvm::fltSemantics &ElementTypeSemantics =
15327-
Ctx.getFloatTypeSemantics(ElementType);
15328-
const llvm::fltSemantics &HigherElementTypeSemantics =
15329-
Ctx.getFloatTypeSemantics(HigherElementType);
15330-
if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 >
15331-
llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) ||
15332-
(HigherElementType == Ctx.LongDoubleTy &&
15333-
!Ctx.getTargetInfo().hasLongDoubleType())) {
15334-
// Retain the location of the first use of higher precision type.
15335-
if (!S.LocationOfExcessPrecisionNotSatisfied.isValid())
15336-
S.LocationOfExcessPrecisionNotSatisfied = OpLoc;
15337-
for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) {
15338-
if (Type == HigherElementType) {
15339-
Num++;
15340-
return;
15341-
}
15342-
}
15343-
S.ExcessPrecisionNotSatisfied.push_back(std::make_pair(
15344-
HigherElementType, S.ExcessPrecisionNotSatisfied.size()));
15345-
}
15346-
}
15347-
}
15348-
}
15349-
1535015357
ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1535115358
tok::TokenKind Kind,
1535215359
Expr *LHSExpr, Expr *RHSExpr) {
@@ -15357,11 +15364,6 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
1535715364
// Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0"
1535815365
DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr);
1535915366

15360-
// Emit warnings if the requested higher precision type equal to the current
15361-
// type precision.
15362-
if (Kind == tok::TokenKind::slash)
15363-
DetectPrecisionLossInComplexDivision(*this, TokLoc, LHSExpr);
15364-
1536515367
BuiltinCountedByRefKind K =
1536615368
BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind;
1536715369

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)