Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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