Skip to content

Commit 479f384

Browse files
committed
wip: prevent any type mixing
1 parent 4d39f93 commit 479f384

File tree

7 files changed

+103
-183
lines changed

7 files changed

+103
-183
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2323,7 +2323,8 @@ class Sema final : public SemaBase {
23232323
const FunctionProtoType *Proto);
23242324

23252325
/// \param FPOnly restricts the arguments to floating-point types.
2326-
bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly = false);
2326+
std::optional<QualType> BuiltinVectorMath(CallExpr *TheCall,
2327+
bool FPOnly = false);
23272328
bool BuiltinVectorToScalarMath(CallExpr *TheCall);
23282329

23292330
void checkLifetimeCaptureBy(FunctionDecl *FDecl, bool IsMemberFunction,
@@ -7557,6 +7558,11 @@ class Sema final : public SemaBase {
75577558
ExprResult DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
75587559
FunctionDecl *FDecl);
75597560

7561+
// Check that the usual arithmetic conversions can be performed on this pair
7562+
// of expressions that might be of enumeration type.
7563+
void checkEnumArithmeticConversions(Expr *LHS, Expr *RHS, SourceLocation Loc,
7564+
Sema::ArithConvKind ACK);
7565+
75607566
// UsualArithmeticConversions - performs the UsualUnaryConversions on it's
75617567
// operands and then handles various conversions that are common to binary
75627568
// operators (C99 6.3.1.8). If both operands aren't arithmetic, this

clang/lib/Sema/SemaChecking.cpp

Lines changed: 49 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2765,44 +2765,6 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
27652765
// types only.
27662766
case Builtin::BI__builtin_elementwise_add_sat:
27672767
case Builtin::BI__builtin_elementwise_sub_sat: {
2768-
if (checkArgCount(TheCall, 2))
2769-
return ExprError();
2770-
ExprResult LHS = TheCall->getArg(0);
2771-
ExprResult RHS = TheCall->getArg(1);
2772-
QualType LHSType = LHS.get()->getType().getUnqualifiedType();
2773-
QualType RHSType = RHS.get()->getType().getUnqualifiedType();
2774-
// If both LHS/RHS are promotable integer types, do not perform the usual
2775-
// conversions - we must keep the saturating operation at the correct
2776-
// bitwidth.
2777-
if (Context.isPromotableIntegerType(LHSType) &&
2778-
Context.isPromotableIntegerType(RHSType)) {
2779-
// First, convert each argument to an r-value.
2780-
ExprResult ResLHS = DefaultFunctionArrayLvalueConversion(LHS.get());
2781-
if (ResLHS.isInvalid())
2782-
return ExprError();
2783-
LHS = ResLHS.get();
2784-
2785-
ExprResult ResRHS = DefaultFunctionArrayLvalueConversion(RHS.get());
2786-
if (ResRHS.isInvalid())
2787-
return ExprError();
2788-
RHS = ResRHS.get();
2789-
2790-
LHSType = LHS.get()->getType().getUnqualifiedType();
2791-
RHSType = RHS.get()->getType().getUnqualifiedType();
2792-
2793-
// If the two integer types are not of equal order, cast the smaller
2794-
// integer one to the larger one
2795-
if (int Order = Context.getIntegerTypeOrder(LHSType, RHSType); Order == 1)
2796-
RHS = ImpCastExprToType(RHS.get(), LHSType, CK_IntegralCast);
2797-
else if (Order == -1)
2798-
LHS = ImpCastExprToType(LHS.get(), RHSType, CK_IntegralCast);
2799-
2800-
TheCall->setArg(0, LHS.get());
2801-
TheCall->setArg(1, RHS.get());
2802-
TheCall->setType(LHS.get()->getType().getUnqualifiedType());
2803-
break;
2804-
}
2805-
28062768
if (BuiltinElementwiseMath(TheCall))
28072769
return ExprError();
28082770

@@ -2828,28 +2790,11 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
28282790
break;
28292791
case Builtin::BI__builtin_elementwise_popcount:
28302792
case Builtin::BI__builtin_elementwise_bitreverse: {
2831-
if (checkArgCount(TheCall, 1))
2832-
return ExprError();
2833-
2834-
Expr *Arg = TheCall->getArg(0);
2835-
QualType ArgTy = Arg->getType();
2836-
2837-
// If the argument is a promotable integer type, do not perform the usual
2838-
// conversions - we must keep the operation at the correct bitwidth.
2839-
if (Context.isPromotableIntegerType(ArgTy)) {
2840-
// Convert the argument to an r-value - avoid the usual conversions.
2841-
ExprResult ResLHS = DefaultFunctionArrayLvalueConversion(Arg);
2842-
if (ResLHS.isInvalid())
2843-
return ExprError();
2844-
Arg = ResLHS.get();
2845-
TheCall->setArg(0, Arg);
2846-
TheCall->setType(Arg->getType());
2847-
break;
2848-
}
2849-
28502793
if (PrepareBuiltinElementwiseMathOneArgCall(TheCall))
28512794
return ExprError();
28522795

2796+
const Expr *Arg = TheCall->getArg(0);
2797+
QualType ArgTy = Arg->getType();
28532798
QualType EltTy = ArgTy;
28542799

28552800
if (auto *VecTy = EltTy->getAs<VectorType>())
@@ -14649,11 +14594,18 @@ void Sema::CheckAddressOfPackedMember(Expr *rhs) {
1464914594
_2, _3, _4));
1465014595
}
1465114596

14597+
static ExprResult UsualUnaryConversionsNoPromoteInt(Sema &S, Expr *E) {
14598+
// Don't promote integer types
14599+
if (QualType Ty = E->getType(); S.getASTContext().isPromotableIntegerType(Ty))
14600+
return S.DefaultFunctionArrayLvalueConversion(E);
14601+
return S.UsualUnaryConversions(E);
14602+
}
14603+
1465214604
bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) {
1465314605
if (checkArgCount(TheCall, 1))
1465414606
return true;
1465514607

14656-
ExprResult A = UsualUnaryConversions(TheCall->getArg(0));
14608+
ExprResult A = UsualUnaryConversionsNoPromoteInt(*this, TheCall->getArg(0));
1465714609
if (A.isInvalid())
1465814610
return true;
1465914611

@@ -14668,57 +14620,63 @@ bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) {
1466814620
}
1466914621

1467014622
bool Sema::BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly) {
14671-
QualType Res;
14672-
if (BuiltinVectorMath(TheCall, Res, FPOnly))
14673-
return true;
14674-
TheCall->setType(Res);
14675-
return false;
14623+
if (auto Res = BuiltinVectorMath(TheCall, FPOnly); Res.has_value()) {
14624+
TheCall->setType(*Res);
14625+
return false;
14626+
}
14627+
return true;
1467614628
}
1467714629

1467814630
bool Sema::BuiltinVectorToScalarMath(CallExpr *TheCall) {
14679-
QualType Res;
14680-
if (BuiltinVectorMath(TheCall, Res))
14631+
std::optional<QualType> Res = BuiltinVectorMath(TheCall);
14632+
if (!Res)
1468114633
return true;
1468214634

14683-
if (auto *VecTy0 = Res->getAs<VectorType>())
14635+
if (auto *VecTy0 = (*Res)->getAs<VectorType>())
1468414636
TheCall->setType(VecTy0->getElementType());
1468514637
else
14686-
TheCall->setType(Res);
14638+
TheCall->setType(*Res);
1468714639

1468814640
return false;
1468914641
}
1469014642

14691-
bool Sema::BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly) {
14643+
std::optional<QualType> Sema::BuiltinVectorMath(CallExpr *TheCall,
14644+
bool FPOnly) {
1469214645
if (checkArgCount(TheCall, 2))
14693-
return true;
14646+
return std::nullopt;
1469414647

14695-
ExprResult A = TheCall->getArg(0);
14696-
ExprResult B = TheCall->getArg(1);
14697-
// Do standard promotions between the two arguments, returning their common
14698-
// type.
14699-
Res = UsualArithmeticConversions(A, B, TheCall->getExprLoc(), ACK_Comparison);
14700-
if (A.isInvalid() || B.isInvalid())
14701-
return true;
14648+
checkEnumArithmeticConversions(TheCall->getArg(0), TheCall->getArg(1),
14649+
TheCall->getExprLoc(), ACK_Comparison);
1470214650

14703-
QualType TyA = A.get()->getType();
14704-
QualType TyB = B.get()->getType();
14651+
Expr *Args[2];
14652+
for (int I = 0; I < 2; ++I) {
14653+
ExprResult Converted =
14654+
UsualUnaryConversionsNoPromoteInt(*this, TheCall->getArg(I));
14655+
if (Converted.isInvalid())
14656+
return std::nullopt;
14657+
Args[I] = Converted.get();
14658+
}
1470514659

14706-
if (Res.isNull() || TyA.getCanonicalType() != TyB.getCanonicalType())
14707-
return Diag(A.get()->getBeginLoc(),
14708-
diag::err_typecheck_call_different_arg_types)
14709-
<< TyA << TyB;
14660+
SourceLocation LocA = Args[0]->getBeginLoc();
14661+
QualType TyA = Args[0]->getType();
14662+
QualType TyB = Args[1]->getType();
14663+
14664+
if (TyA.getCanonicalType() != TyB.getCanonicalType()) {
14665+
Diag(LocA, diag::err_typecheck_call_different_arg_types) << TyA << TyB;
14666+
return std::nullopt;
14667+
}
1471014668

1471114669
if (FPOnly) {
14712-
if (checkFPMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1))
14713-
return true;
14670+
if (checkFPMathBuiltinElementType(*this, LocA, TyA, 1))
14671+
return std::nullopt;
1471414672
} else {
14715-
if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1))
14716-
return true;
14673+
if (checkMathBuiltinElementType(*this, LocA, TyA, 1))
14674+
return std::nullopt;
1471714675
}
1471814676

14719-
TheCall->setArg(0, A.get());
14720-
TheCall->setArg(1, B.get());
14721-
return false;
14677+
TheCall->setArg(0, Args[0]);
14678+
TheCall->setArg(1, Args[1]);
14679+
return TyA;
1472214680
}
1472314681

1472414682
bool Sema::BuiltinElementwiseTernaryMath(CallExpr *TheCall,
@@ -14728,7 +14686,8 @@ bool Sema::BuiltinElementwiseTernaryMath(CallExpr *TheCall,
1472814686

1472914687
Expr *Args[3];
1473014688
for (int I = 0; I < 3; ++I) {
14731-
ExprResult Converted = UsualUnaryConversions(TheCall->getArg(I));
14689+
ExprResult Converted =
14690+
UsualUnaryConversionsNoPromoteInt(*this, TheCall->getArg(I));
1473214691
if (Converted.isInvalid())
1473314692
return true;
1473414693
Args[I] = Converted.get();

clang/lib/Sema/SemaExpr.cpp

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,64 +1489,63 @@ static QualType handleFixedPointConversion(Sema &S, QualType LHSTy,
14891489

14901490
/// Check that the usual arithmetic conversions can be performed on this pair of
14911491
/// expressions that might be of enumeration type.
1492-
static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS,
1493-
SourceLocation Loc,
1494-
Sema::ArithConvKind ACK) {
1492+
void Sema::checkEnumArithmeticConversions(Expr *LHS, Expr *RHS,
1493+
SourceLocation Loc,
1494+
Sema::ArithConvKind ACK) {
14951495
// C++2a [expr.arith.conv]p1:
14961496
// If one operand is of enumeration type and the other operand is of a
14971497
// different enumeration type or a floating-point type, this behavior is
14981498
// deprecated ([depr.arith.conv.enum]).
14991499
//
15001500
// Warn on this in all language modes. Produce a deprecation warning in C++20.
15011501
// Eventually we will presumably reject these cases (in C++23 onwards?).
1502-
QualType L = LHS->getEnumCoercedType(S.Context),
1503-
R = RHS->getEnumCoercedType(S.Context);
1502+
QualType L = LHS->getEnumCoercedType(Context),
1503+
R = RHS->getEnumCoercedType(Context);
15041504
bool LEnum = L->isUnscopedEnumerationType(),
15051505
REnum = R->isUnscopedEnumerationType();
15061506
bool IsCompAssign = ACK == Sema::ACK_CompAssign;
15071507
if ((!IsCompAssign && LEnum && R->isFloatingType()) ||
15081508
(REnum && L->isFloatingType())) {
1509-
S.Diag(Loc, S.getLangOpts().CPlusPlus26
1510-
? diag::err_arith_conv_enum_float_cxx26
1511-
: S.getLangOpts().CPlusPlus20
1512-
? diag::warn_arith_conv_enum_float_cxx20
1513-
: diag::warn_arith_conv_enum_float)
1509+
Diag(Loc, getLangOpts().CPlusPlus26 ? diag::err_arith_conv_enum_float_cxx26
1510+
: getLangOpts().CPlusPlus20
1511+
? diag::warn_arith_conv_enum_float_cxx20
1512+
: diag::warn_arith_conv_enum_float)
15141513
<< LHS->getSourceRange() << RHS->getSourceRange() << (int)ACK << LEnum
15151514
<< L << R;
15161515
} else if (!IsCompAssign && LEnum && REnum &&
1517-
!S.Context.hasSameUnqualifiedType(L, R)) {
1516+
!Context.hasSameUnqualifiedType(L, R)) {
15181517
unsigned DiagID;
15191518
// In C++ 26, usual arithmetic conversions between 2 different enum types
15201519
// are ill-formed.
1521-
if (S.getLangOpts().CPlusPlus26)
1520+
if (getLangOpts().CPlusPlus26)
15221521
DiagID = diag::err_conv_mixed_enum_types_cxx26;
15231522
else if (!L->castAs<EnumType>()->getDecl()->hasNameForLinkage() ||
15241523
!R->castAs<EnumType>()->getDecl()->hasNameForLinkage()) {
15251524
// If either enumeration type is unnamed, it's less likely that the
15261525
// user cares about this, but this situation is still deprecated in
15271526
// C++2a. Use a different warning group.
1528-
DiagID = S.getLangOpts().CPlusPlus20
1529-
? diag::warn_arith_conv_mixed_anon_enum_types_cxx20
1530-
: diag::warn_arith_conv_mixed_anon_enum_types;
1527+
DiagID = getLangOpts().CPlusPlus20
1528+
? diag::warn_arith_conv_mixed_anon_enum_types_cxx20
1529+
: diag::warn_arith_conv_mixed_anon_enum_types;
15311530
} else if (ACK == Sema::ACK_Conditional) {
15321531
// Conditional expressions are separated out because they have
15331532
// historically had a different warning flag.
1534-
DiagID = S.getLangOpts().CPlusPlus20
1533+
DiagID = getLangOpts().CPlusPlus20
15351534
? diag::warn_conditional_mixed_enum_types_cxx20
15361535
: diag::warn_conditional_mixed_enum_types;
15371536
} else if (ACK == Sema::ACK_Comparison) {
15381537
// Comparison expressions are separated out because they have
15391538
// historically had a different warning flag.
1540-
DiagID = S.getLangOpts().CPlusPlus20
1539+
DiagID = getLangOpts().CPlusPlus20
15411540
? diag::warn_comparison_mixed_enum_types_cxx20
15421541
: diag::warn_comparison_mixed_enum_types;
15431542
} else {
1544-
DiagID = S.getLangOpts().CPlusPlus20
1543+
DiagID = getLangOpts().CPlusPlus20
15451544
? diag::warn_arith_conv_mixed_enum_types_cxx20
15461545
: diag::warn_arith_conv_mixed_enum_types;
15471546
}
1548-
S.Diag(Loc, DiagID) << LHS->getSourceRange() << RHS->getSourceRange()
1549-
<< (int)ACK << L << R;
1547+
Diag(Loc, DiagID) << LHS->getSourceRange() << RHS->getSourceRange()
1548+
<< (int)ACK << L << R;
15501549
}
15511550
}
15521551

@@ -1557,7 +1556,7 @@ static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS,
15571556
QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS,
15581557
SourceLocation Loc,
15591558
ArithConvKind ACK) {
1560-
checkEnumArithmeticConversions(*this, LHS.get(), RHS.get(), Loc, ACK);
1559+
checkEnumArithmeticConversions(LHS.get(), RHS.get(), Loc, ACK);
15611560

15621561
if (ACK != ACK_CompAssign) {
15631562
LHS = UsualUnaryConversions(LHS.get());

0 commit comments

Comments
 (0)