aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaChecking.cpp
diff options
context:
space:
mode:
authorFraser Cormack <fraser@codeplay.com>2025-01-29 09:40:04 +0000
committerGitHub <noreply@github.com>2025-01-29 09:40:04 +0000
commit1ac3665e66c7ddb20ef26bc275ad005186ab09fb (patch)
tree8614a57d31b5e5763c9f28e0c3387c85b9fa9274 /clang/lib/Sema/SemaChecking.cpp
parente902cf2df1718b46f83ff132bf4ec5f84b163209 (diff)
downloadllvm-1ac3665e66c7ddb20ef26bc275ad005186ab09fb.zip
llvm-1ac3665e66c7ddb20ef26bc275ad005186ab09fb.tar.gz
llvm-1ac3665e66c7ddb20ef26bc275ad005186ab09fb.tar.bz2
[clang] Restrict the use of scalar types in vector builtins (#119423)
This commit restricts the use of scalar types in vector math builtins, particularly the `__builtin_elementwise_*` builtins. Previously, small scalar integer types would be promoted to `int`, as per the usual conversions. This would silently do the wrong thing for certain operations, such as `add_sat`, `popcount`, `bitreverse`, and others. Similarly, since unsigned integer types were promoted to `int`, something like `add_sat(unsigned char, unsigned char)` would perform a *signed* operation. With this patch, promotable scalar integer types are not promoted to int, and are kept intact. If any of the types differ in the binary and ternary builtins, an error is issued. Similarly an error is issued if builtins are supplied integer types of different signs. Mixing enums of different types in binary/ternary builtins now consistently raises an error in all language modes. This brings the behaviour surrounding scalar types more in line with that of vector types. No change is made to vector types, which are both not promoted and whose element types must match. Fixes #84047. RFC: https://discourse.llvm.org/t/rfc-change-behaviour-of-elementwise-builtins-on-scalar-integer-types/83725
Diffstat (limited to 'clang/lib/Sema/SemaChecking.cpp')
-rw-r--r--clang/lib/Sema/SemaChecking.cpp106
1 files changed, 73 insertions, 33 deletions
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index e440f60..61b2c8c 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -14649,11 +14649,23 @@ void Sema::CheckAddressOfPackedMember(Expr *rhs) {
_2, _3, _4));
}
+// Performs a similar job to Sema::UsualUnaryConversions, but without any
+// implicit promotion of integral/enumeration types.
+static ExprResult BuiltinVectorMathConversions(Sema &S, Expr *E) {
+ // First, convert to an r-value.
+ ExprResult Res = S.DefaultFunctionArrayLvalueConversion(E);
+ if (Res.isInvalid())
+ return ExprError();
+
+ // Promote floating-point types.
+ return S.UsualUnaryFPConversions(Res.get());
+}
+
bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) {
if (checkArgCount(TheCall, 1))
return true;
- ExprResult A = UsualUnaryConversions(TheCall->getArg(0));
+ ExprResult A = BuiltinVectorMathConversions(*this, TheCall->getArg(0));
if (A.isInvalid())
return true;
@@ -14668,57 +14680,77 @@ bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) {
}
bool Sema::BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly) {
- QualType Res;
- if (BuiltinVectorMath(TheCall, Res, FPOnly))
- return true;
- TheCall->setType(Res);
- return false;
+ if (auto Res = BuiltinVectorMath(TheCall, FPOnly); Res.has_value()) {
+ TheCall->setType(*Res);
+ return false;
+ }
+ return true;
}
bool Sema::BuiltinVectorToScalarMath(CallExpr *TheCall) {
- QualType Res;
- if (BuiltinVectorMath(TheCall, Res))
+ std::optional<QualType> Res = BuiltinVectorMath(TheCall);
+ if (!Res)
return true;
- if (auto *VecTy0 = Res->getAs<VectorType>())
+ if (auto *VecTy0 = (*Res)->getAs<VectorType>())
TheCall->setType(VecTy0->getElementType());
else
- TheCall->setType(Res);
+ TheCall->setType(*Res);
return false;
}
-bool Sema::BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly) {
+static bool checkBuiltinVectorMathMixedEnums(Sema &S, Expr *LHS, Expr *RHS,
+ SourceLocation Loc) {
+ QualType L = LHS->getEnumCoercedType(S.Context),
+ R = RHS->getEnumCoercedType(S.Context);
+ if (L->isUnscopedEnumerationType() && R->isUnscopedEnumerationType() &&
+ !S.Context.hasSameUnqualifiedType(L, R)) {
+ return S.Diag(Loc, diag::err_conv_mixed_enum_types_cxx26)
+ << LHS->getSourceRange() << RHS->getSourceRange()
+ << /*Arithmetic Between*/ 0 << L << R;
+ }
+ return false;
+}
+
+std::optional<QualType> Sema::BuiltinVectorMath(CallExpr *TheCall,
+ bool FPOnly) {
if (checkArgCount(TheCall, 2))
- return true;
+ return std::nullopt;
- ExprResult A = TheCall->getArg(0);
- ExprResult B = TheCall->getArg(1);
- // Do standard promotions between the two arguments, returning their common
- // type.
- Res = UsualArithmeticConversions(A, B, TheCall->getExprLoc(), ACK_Comparison);
- if (A.isInvalid() || B.isInvalid())
- return true;
+ if (checkBuiltinVectorMathMixedEnums(
+ *this, TheCall->getArg(0), TheCall->getArg(1), TheCall->getExprLoc()))
+ return std::nullopt;
- QualType TyA = A.get()->getType();
- QualType TyB = B.get()->getType();
+ Expr *Args[2];
+ for (int I = 0; I < 2; ++I) {
+ ExprResult Converted =
+ BuiltinVectorMathConversions(*this, TheCall->getArg(I));
+ if (Converted.isInvalid())
+ return std::nullopt;
+ Args[I] = Converted.get();
+ }
- if (Res.isNull() || TyA.getCanonicalType() != TyB.getCanonicalType())
- return Diag(A.get()->getBeginLoc(),
- diag::err_typecheck_call_different_arg_types)
- << TyA << TyB;
+ SourceLocation LocA = Args[0]->getBeginLoc();
+ QualType TyA = Args[0]->getType();
+ QualType TyB = Args[1]->getType();
+
+ if (TyA.getCanonicalType() != TyB.getCanonicalType()) {
+ Diag(LocA, diag::err_typecheck_call_different_arg_types) << TyA << TyB;
+ return std::nullopt;
+ }
if (FPOnly) {
- if (checkFPMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1))
- return true;
+ if (checkFPMathBuiltinElementType(*this, LocA, TyA, 1))
+ return std::nullopt;
} else {
- if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA, 1))
- return true;
+ if (checkMathBuiltinElementType(*this, LocA, TyA, 1))
+ return std::nullopt;
}
- TheCall->setArg(0, A.get());
- TheCall->setArg(1, B.get());
- return false;
+ TheCall->setArg(0, Args[0]);
+ TheCall->setArg(1, Args[1]);
+ return TyA;
}
bool Sema::BuiltinElementwiseTernaryMath(CallExpr *TheCall,
@@ -14726,9 +14758,17 @@ bool Sema::BuiltinElementwiseTernaryMath(CallExpr *TheCall,
if (checkArgCount(TheCall, 3))
return true;
+ SourceLocation Loc = TheCall->getExprLoc();
+ if (checkBuiltinVectorMathMixedEnums(*this, TheCall->getArg(0),
+ TheCall->getArg(1), Loc) ||
+ checkBuiltinVectorMathMixedEnums(*this, TheCall->getArg(1),
+ TheCall->getArg(2), Loc))
+ return true;
+
Expr *Args[3];
for (int I = 0; I < 3; ++I) {
- ExprResult Converted = UsualUnaryConversions(TheCall->getArg(I));
+ ExprResult Converted =
+ BuiltinVectorMathConversions(*this, TheCall->getArg(I));
if (Converted.isInvalid())
return true;
Args[I] = Converted.get();