Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,14 @@ Improvements to Clang's diagnostics

- An error is now emitted when a ``musttail`` call is made to a function marked with the ``not_tail_called`` attribute. (#GH133509).

- ``-Whigher-precisision-for-complex-divison`` warns when:

- The divisor is complex
- When the complex division happens in a higher precision type due to arithmetic promotion
- When using the divide and assign operator (``/=``)

Fixes #GH131127

Improvements to Clang's time-trace
----------------------------------

Expand Down
78 changes: 40 additions & 38 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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;

Expand Down
93 changes: 93 additions & 0 deletions clang/test/Sema/complex-div-warn-higher-precision.cpp
Original file line number Diff line number Diff line change
@@ -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}}
Loading