Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ Improvements to Clang's diagnostics
an override of a virtual method.
- Fixed fix-it hint for fold expressions. Clang now correctly places the suggested right
parenthesis when diagnosing malformed fold expressions. (#GH151787)
- Added fix-it hint for when scoped enumerations require explicit conversions for binary operations. (#GH24265)

Improvements to Clang's time-trace
----------------------------------
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -4401,6 +4401,14 @@ def warn_impcast_different_enum_types : Warning<
def warn_impcast_int_to_enum : Warning<
"implicit conversion from %0 to enumeration type %1 is invalid in C++">,
InGroup<ImplicitIntToEnumCast>, DefaultIgnore;

def note_no_implicit_conversion_for_scoped_enum
: Note<"no implicit conversion for scoped enum; consider casting to "
"underlying type">;
def note_no_implicit_conversion_for_scoped_enum_cxx23
: Note<"no implicit conversion for scoped enum; consider using "
"std::to_underlying">;

def warn_impcast_bool_to_null_pointer : Warning<
"initialization of pointer of type %0 to null from a constant boolean "
"expression">, InGroup<BoolConversion>;
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8055,8 +8055,8 @@ class Sema final : public SemaBase {
ExprResult &RHS);

QualType CheckMultiplyDivideOperands( // C99 6.5.5
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign,
bool IsDivide);
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc);
QualType CheckRemainderOperands( // C99 6.5.5
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
bool IsCompAssign = false);
Expand All @@ -8065,7 +8065,7 @@ class Sema final : public SemaBase {
BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr);
QualType CheckSubtractionOperands( // C99 6.5.6
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
QualType *CompLHSTy = nullptr);
BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr);
QualType CheckShiftOperands( // C99 6.5.7
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
BinaryOperatorKind Opc, bool IsCompAssign = false);
Expand Down
124 changes: 97 additions & 27 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10742,9 +10742,53 @@ static void DiagnoseBadDivideOrRemainderValues(Sema& S, ExprResult &LHS,
<< IsDiv << RHS.get()->getSourceRange());
}

static void diagnoseScopedEnums(Sema &S, const SourceLocation Loc,
const ExprResult &LHS, const ExprResult &RHS,
BinaryOperatorKind Opc) {
const Expr *LHSExpr = LHS.get();
const Expr *RHSExpr = RHS.get();
if (!LHSExpr || !RHSExpr)
return;
const QualType LHSType = LHSExpr->getType();
const QualType RHSType = RHSExpr->getType();
const bool LHSIsScoped = LHSType->isScopedEnumeralType();
const bool RHSIsScoped = RHSType->isScopedEnumeralType();
if (!LHSIsScoped && !RHSIsScoped)
return;
if (!LHSIsScoped && !LHSType->isIntegralOrUnscopedEnumerationType())
return;
if (!RHSIsScoped && !RHSType->isIntegralOrUnscopedEnumerationType())
return;
if (BinaryOperator::isAssignmentOp(Opc) && LHSIsScoped)
return;
bool isCxx23 = S.getLangOpts().CPlusPlus23;
unsigned diagID =
isCxx23 ? diag::note_no_implicit_conversion_for_scoped_enum_cxx23
: diag::note_no_implicit_conversion_for_scoped_enum;
auto diagnosticHelper = [&S, isCxx23, diagID](const Expr *expr, const QualType type) {
SourceLocation beginLoc = expr->getBeginLoc();
QualType intType =
type->castAs<EnumType>()->getDecl()->getIntegerType();
std::string insertionString =
isCxx23 ? "std::to_underlying("
: "static_cast<" + intType.getAsString() + ">(";
S.Diag(beginLoc, diagID)
<< FixItHint::CreateInsertion(beginLoc, insertionString)
<< FixItHint::CreateInsertion(expr->getEndLoc(), ")");
};
if (LHSIsScoped) {
diagnosticHelper(LHSExpr, LHSType);
}
if (RHSIsScoped) {
diagnosticHelper(RHSExpr, RHSType);
}
}

QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
SourceLocation Loc,
bool IsCompAssign, bool IsDiv) {
SourceLocation Loc, BinaryOperatorKind Opc) {
bool IsCompAssign = Opc == BO_MulAssign || Opc == BO_DivAssign;
bool IsDiv = Opc == BO_Div || Opc == BO_DivAssign;

checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false);

QualType LHSTy = LHS.get()->getType();
Expand Down Expand Up @@ -10772,9 +10816,11 @@ QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS,
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();


if (compType.isNull() || !compType->isArithmeticType())
return InvalidOperands(Loc, LHS, RHS);
if (compType.isNull() || !compType->isArithmeticType()) {
InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
return QualType();
}
if (IsDiv) {
DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc);
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv);
Expand Down Expand Up @@ -10837,8 +10883,12 @@ QualType Sema::CheckRemainderOperands(

if (compType.isNull() ||
(!compType->isIntegerType() &&
!(getLangOpts().HLSL && compType->isFloatingType())))
return InvalidOperands(Loc, LHS, RHS);
!(getLangOpts().HLSL && compType->isFloatingType()))) {
InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(*this, Loc, LHS, RHS,
IsCompAssign ? BO_RemAssign : BO_Rem);
return QualType();
}
DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */);
return compType;
}
Expand Down Expand Up @@ -11194,7 +11244,9 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
} else if (PExp->getType()->isObjCObjectPointerType()) {
isObjCPointer = true;
} else {
return InvalidOperands(Loc, LHS, RHS);
InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
return QualType();
}
}
assert(PExp->getType()->isAnyPointerType());
Expand Down Expand Up @@ -11251,7 +11303,8 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS,
// C99 6.5.6
QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
SourceLocation Loc,
QualType* CompLHSTy) {
BinaryOperatorKind Opc,
QualType *CompLHSTy) {
checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false);

if (LHS.get()->getType()->isVectorType() ||
Expand Down Expand Up @@ -11396,7 +11449,9 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS,
}
}

return InvalidOperands(Loc, LHS, RHS);
InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
return QualType();
}

static bool isScopedEnumerationType(QualType T) {
Expand Down Expand Up @@ -11744,8 +11799,11 @@ QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS,
// Embedded-C 4.1.6.2.2: The LHS may also be fixed-point.
if ((!LHSType->isFixedPointOrIntegerType() &&
!LHSType->hasIntegerRepresentation()) ||
!RHSType->hasIntegerRepresentation())
return InvalidOperands(Loc, LHS, RHS);
!RHSType->hasIntegerRepresentation()) {
InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
return QualType();
}

// C++0x: Don't allow scoped enums. FIXME: Use something better than
// hasIntegerRepresentation() above instead of this.
Expand Down Expand Up @@ -12311,8 +12369,11 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
S.UsualArithmeticConversions(LHS, RHS, Loc, ArithConvKind::Comparison);
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
if (Type.isNull())
return S.InvalidOperands(Loc, LHS, RHS);
if (Type.isNull()) {
S.InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(S, Loc, LHS, RHS, BO_Cmp);
return QualType();
}

std::optional<ComparisonCategoryType> CCT =
getComparisonCategoryForBuiltinCmp(Type);
Expand Down Expand Up @@ -12344,8 +12405,11 @@ static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
S.UsualArithmeticConversions(LHS, RHS, Loc, ArithConvKind::Comparison);
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
if (Type.isNull())
return S.InvalidOperands(Loc, LHS, RHS);
if (Type.isNull()) {
S.InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(S, Loc, LHS, RHS, Opc);
return QualType();
}
assert(Type->isArithmeticType() || Type->isEnumeralType());

if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc))
Expand Down Expand Up @@ -13355,7 +13419,9 @@ inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS,

if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType())
return compType;
return InvalidOperands(Loc, LHS, RHS);
InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
return QualType();
}

// C99 6.5.[13,14]
Expand Down Expand Up @@ -13457,13 +13523,19 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS,
// C++ [expr.log.or]p1
// The operands are both contextually converted to type bool.
ExprResult LHSRes = PerformContextuallyConvertToBool(LHS.get());
if (LHSRes.isInvalid())
return InvalidOperands(Loc, LHS, RHS);
if (LHSRes.isInvalid()) {
InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
return QualType();
}
LHS = LHSRes;

ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get());
if (RHSRes.isInvalid())
return InvalidOperands(Loc, LHS, RHS);
if (RHSRes.isInvalid()) {
InvalidOperands(Loc, LHS, RHS);
diagnoseScopedEnums(*this, Loc, LHS, RHS, Opc);
return QualType();
}
RHS = RHSRes;

// C++ [expr.log.and]p2
Expand Down Expand Up @@ -15057,8 +15129,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
case BO_Mul:
case BO_Div:
ConvertHalfVec = true;
ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false,
Opc == BO_Div);
ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, Opc);
break;
case BO_Rem:
ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc);
Expand All @@ -15069,7 +15140,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
break;
case BO_Sub:
ConvertHalfVec = true;
ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc);
ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc);
break;
case BO_Shl:
case BO_Shr:
Expand Down Expand Up @@ -15113,8 +15184,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
case BO_MulAssign:
case BO_DivAssign:
ConvertHalfVec = true;
CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true,
Opc == BO_DivAssign);
CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, Opc);
CompLHSTy = CompResultTy;
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
ResultTy =
Expand All @@ -15136,7 +15206,7 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
break;
case BO_SubAssign:
ConvertHalfVec = true;
CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy);
CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy);
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
ResultTy =
CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum class E { e };

template<typename T> int f(T t) { return ~t; } // expected-error {{invalid argument type}}
template<typename T, typename U> int f(T t, U u) { return t % u; } // expected-error {{invalid operands to}}
// expected-note@-1 {{no implicit conversion for scoped enum}}

int b1 = ~E::e; // expected-error {{invalid argument type}}
int b2 = f(E::e); // expected-note {{in instantiation of}}
Expand Down
Loading
Loading