[clang] Reapply "[Clang][Sema] Earlier type checking for builtin unary operators (#90500)" (PR #92283)
via cfe-commits
cfe-commits at lists.llvm.org
Wed May 15 08:37:49 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Krystian Stasiowski (sdkrystian)
<details>
<summary>Changes</summary>
This patch reapplies #<!-- -->90500, addressing a bug which caused binary operators with dependent operands to be incorrectly rebuilt by `TreeTransform`.
---
Patch is 46.60 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/92283.diff
17 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (+3)
- (modified) clang/include/clang/AST/Type.h (+4-1)
- (modified) clang/lib/Sema/SemaExpr.cpp (+177-186)
- (modified) clang/lib/Sema/TreeTransform.h (+7-10)
- (modified) clang/test/AST/ast-dump-expr-json.cpp (+2-2)
- (modified) clang/test/AST/ast-dump-expr.cpp (+1-1)
- (modified) clang/test/AST/ast-dump-lambda.cpp (+1-1)
- (added) clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp (+65)
- (modified) clang/test/CXX/over/over.built/ast.cpp (+128-30)
- (modified) clang/test/CXX/over/over.built/p10.cpp (+1-1)
- (modified) clang/test/CXX/over/over.built/p11.cpp (+1-1)
- (added) clang/test/CXX/over/over.oper/over.oper.general/p1.cpp (+173)
- (modified) clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp (+10-15)
- (modified) clang/test/Frontend/noderef_templates.cpp (+2-2)
- (modified) clang/test/SemaCXX/cxx2b-deducing-this.cpp (+2-4)
- (modified) clang/test/SemaTemplate/class-template-spec.cpp (+6-6)
- (modified) clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp (+3-3)
``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 49ab222bec405..a2e44efe41347 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -55,6 +55,9 @@ C++ Specific Potentially Breaking Changes
- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906).
+- Clang now performs semantic analysis for unary operators with dependent operands
+ that are known to be of non-class non-enumeration type prior to instantiation.
+
ABI Changes in This Version
---------------------------
- Fixed Microsoft name mangling of implicitly defined variables used for thread
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index e6643469e0b33..da3834f19ca04 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -8044,7 +8044,10 @@ inline bool Type::isUndeducedType() const {
/// Determines whether this is a type for which one can define
/// an overloaded operator.
inline bool Type::isOverloadableType() const {
- return isDependentType() || isRecordType() || isEnumeralType();
+ if (!CanonicalType->isDependentType())
+ return isRecordType() || isEnumeralType();
+ return !isArrayType() && !isFunctionType() && !isAnyPointerType() &&
+ !isMemberPointerType();
}
/// Determines whether this type is written as a typedef-name.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ec84798e4ce60..50569c1cd5367 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -672,12 +672,12 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
// We don't want to throw lvalue-to-rvalue casts on top of
// expressions of certain types in C++.
- if (getLangOpts().CPlusPlus &&
- (E->getType() == Context.OverloadTy ||
- // FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied
- // to pointer types even if the pointee type is dependent.
- (T->isDependentType() && !T->isPointerType()) || T->isRecordType()))
- return E;
+ if (getLangOpts().CPlusPlus) {
+ if (T == Context.OverloadTy || T->isRecordType() ||
+ (T->isDependentType() && !T->isAnyPointerType() &&
+ !T->isMemberPointerType()))
+ return E;
+ }
// The C standard is actually really unclear on this point, and
// DR106 tells us what the result should be but not why. It's
@@ -10827,7 +10827,7 @@ static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc,
if (const AtomicType *ResAtomicType = ResType->getAs<AtomicType>())
ResType = ResAtomicType->getValueType();
- assert(ResType->isAnyPointerType() && !ResType->isDependentType());
+ assert(ResType->isAnyPointerType());
QualType PointeeTy = ResType->getPointeeType();
return S.RequireCompleteSizedType(
Loc, PointeeTy,
@@ -13957,9 +13957,6 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op,
ExprObjectKind &OK,
SourceLocation OpLoc, bool IsInc,
bool IsPrefix) {
- if (Op->isTypeDependent())
- return S.Context.DependentTy;
-
QualType ResType = Op->getType();
// Atomic types can be used for increment / decrement where the non-atomic
// versions can, so ignore the _Atomic() specifier for the purpose of
@@ -14410,9 +14407,6 @@ static void RecordModifiableNonNullParam(Sema &S, const Expr *Exp) {
static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK,
SourceLocation OpLoc,
bool IsAfterAmp = false) {
- if (Op->isTypeDependent())
- return S.Context.DependentTy;
-
ExprResult ConvResult = S.UsualUnaryConversions(Op);
if (ConvResult.isInvalid())
return QualType();
@@ -15368,14 +15362,10 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc,
}
if (getLangOpts().CPlusPlus) {
- // If either expression is type-dependent, always build an
- // overloaded op.
- if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent())
- return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr);
-
- // Otherwise, build an overloaded op if either expression has an
- // overloadable type.
- if (LHSExpr->getType()->isOverloadableType() ||
+ // Otherwise, build an overloaded op if either expression is type-dependent
+ // or has an overloadable type.
+ if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() ||
+ LHSExpr->getType()->isOverloadableType() ||
RHSExpr->getType()->isOverloadableType())
return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr);
}
@@ -15466,190 +15456,191 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 1);
}
- switch (Opc) {
- case UO_PreInc:
- case UO_PreDec:
- case UO_PostInc:
- case UO_PostDec:
- resultType =
- CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc,
- Opc == UO_PreInc || Opc == UO_PostInc,
- Opc == UO_PreInc || Opc == UO_PreDec);
- CanOverflow = isOverflowingIntegerType(Context, resultType);
- break;
- case UO_AddrOf:
- resultType = CheckAddressOfOperand(Input, OpLoc);
- CheckAddressOfNoDeref(InputExpr);
- RecordModifiableNonNullParam(*this, InputExpr);
- break;
- case UO_Deref: {
- Input = DefaultFunctionArrayLvalueConversion(Input.get());
- if (Input.isInvalid())
- return ExprError();
- resultType =
- CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp);
- break;
- }
- case UO_Plus:
- case UO_Minus:
- CanOverflow = Opc == UO_Minus &&
- isOverflowingIntegerType(Context, Input.get()->getType());
- Input = UsualUnaryConversions(Input.get());
- if (Input.isInvalid())
- return ExprError();
- // Unary plus and minus require promoting an operand of half vector to a
- // float vector and truncating the result back to a half vector. For now, we
- // do this only when HalfArgsAndReturns is set (that is, when the target is
- // arm or arm64).
- ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get());
-
- // If the operand is a half vector, promote it to a float vector.
- if (ConvertHalfVec)
- Input = convertVector(Input.get(), Context.FloatTy, *this);
- resultType = Input.get()->getType();
- if (resultType->isDependentType())
- break;
- if (resultType->isArithmeticType()) // C99 6.5.3.3p1
- break;
- else if (resultType->isVectorType() &&
- // The z vector extensions don't allow + or - with bool vectors.
- (!Context.getLangOpts().ZVector ||
- resultType->castAs<VectorType>()->getVectorKind() !=
- VectorKind::AltiVecBool))
- break;
- else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and -
- break;
- else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6
- Opc == UO_Plus && resultType->isPointerType())
+ if (InputExpr->isTypeDependent() &&
+ InputExpr->getType()->isSpecificBuiltinType(BuiltinType::Dependent)) {
+ resultType = Context.DependentTy;
+ } else {
+ switch (Opc) {
+ case UO_PreInc:
+ case UO_PreDec:
+ case UO_PostInc:
+ case UO_PostDec:
+ resultType =
+ CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc,
+ Opc == UO_PreInc || Opc == UO_PostInc,
+ Opc == UO_PreInc || Opc == UO_PreDec);
+ CanOverflow = isOverflowingIntegerType(Context, resultType);
break;
-
- return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
- << resultType << Input.get()->getSourceRange());
-
- case UO_Not: // bitwise complement
- Input = UsualUnaryConversions(Input.get());
- if (Input.isInvalid())
- return ExprError();
- resultType = Input.get()->getType();
- if (resultType->isDependentType())
+ case UO_AddrOf:
+ resultType = CheckAddressOfOperand(Input, OpLoc);
+ CheckAddressOfNoDeref(InputExpr);
+ RecordModifiableNonNullParam(*this, InputExpr);
break;
- // C99 6.5.3.3p1. We allow complex int and float as a GCC extension.
- if (resultType->isComplexType() || resultType->isComplexIntegerType())
- // C99 does not support '~' for complex conjugation.
- Diag(OpLoc, diag::ext_integer_complement_complex)
- << resultType << Input.get()->getSourceRange();
- else if (resultType->hasIntegerRepresentation())
+ case UO_Deref: {
+ Input = DefaultFunctionArrayLvalueConversion(Input.get());
+ if (Input.isInvalid())
+ return ExprError();
+ resultType =
+ CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp);
break;
- else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) {
- // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate
- // on vector float types.
- QualType T = resultType->castAs<ExtVectorType>()->getElementType();
- if (!T->isIntegerType())
- return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
- << resultType << Input.get()->getSourceRange());
- } else {
- return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
- << resultType << Input.get()->getSourceRange());
- }
- break;
-
- case UO_LNot: // logical negation
- // Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5).
- Input = DefaultFunctionArrayLvalueConversion(Input.get());
- if (Input.isInvalid())
- return ExprError();
- resultType = Input.get()->getType();
-
- // Though we still have to promote half FP to float...
- if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) {
- Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast)
- .get();
- resultType = Context.FloatTy;
}
+ case UO_Plus:
+ case UO_Minus:
+ CanOverflow = Opc == UO_Minus &&
+ isOverflowingIntegerType(Context, Input.get()->getType());
+ Input = UsualUnaryConversions(Input.get());
+ if (Input.isInvalid())
+ return ExprError();
+ // Unary plus and minus require promoting an operand of half vector to a
+ // float vector and truncating the result back to a half vector. For now,
+ // we do this only when HalfArgsAndReturns is set (that is, when the
+ // target is arm or arm64).
+ ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get());
+
+ // If the operand is a half vector, promote it to a float vector.
+ if (ConvertHalfVec)
+ Input = convertVector(Input.get(), Context.FloatTy, *this);
+ resultType = Input.get()->getType();
+ if (resultType->isArithmeticType()) // C99 6.5.3.3p1
+ break;
+ else if (resultType->isVectorType() &&
+ // The z vector extensions don't allow + or - with bool vectors.
+ (!Context.getLangOpts().ZVector ||
+ resultType->castAs<VectorType>()->getVectorKind() !=
+ VectorKind::AltiVecBool))
+ break;
+ else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and -
+ break;
+ else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6
+ Opc == UO_Plus && resultType->isPointerType())
+ break;
- // WebAsembly tables can't be used in unary expressions.
- if (resultType->isPointerType() &&
- resultType->getPointeeType().isWebAssemblyReferenceType()) {
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
<< resultType << Input.get()->getSourceRange());
- }
- if (resultType->isDependentType())
- break;
- if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) {
- // C99 6.5.3.3p1: ok, fallthrough;
- if (Context.getLangOpts().CPlusPlus) {
- // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9:
- // operand contextually converted to bool.
- Input = ImpCastExprToType(Input.get(), Context.BoolTy,
- ScalarTypeToBooleanCastKind(resultType));
- } else if (Context.getLangOpts().OpenCL &&
- Context.getLangOpts().OpenCLVersion < 120) {
- // OpenCL v1.1 6.3.h: The logical operator not (!) does not
- // operate on scalar float types.
- if (!resultType->isIntegerType() && !resultType->isPointerType())
- return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
- << resultType << Input.get()->getSourceRange());
- }
- } else if (resultType->isExtVectorType()) {
- if (Context.getLangOpts().OpenCL &&
- Context.getLangOpts().getOpenCLCompatibleVersion() < 120) {
- // OpenCL v1.1 6.3.h: The logical operator not (!) does not
- // operate on vector float types.
+ case UO_Not: // bitwise complement
+ Input = UsualUnaryConversions(Input.get());
+ if (Input.isInvalid())
+ return ExprError();
+ resultType = Input.get()->getType();
+ // C99 6.5.3.3p1. We allow complex int and float as a GCC extension.
+ if (resultType->isComplexType() || resultType->isComplexIntegerType())
+ // C99 does not support '~' for complex conjugation.
+ Diag(OpLoc, diag::ext_integer_complement_complex)
+ << resultType << Input.get()->getSourceRange();
+ else if (resultType->hasIntegerRepresentation())
+ break;
+ else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) {
+ // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate
+ // on vector float types.
QualType T = resultType->castAs<ExtVectorType>()->getElementType();
if (!T->isIntegerType())
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
<< resultType << Input.get()->getSourceRange());
+ } else {
+ return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
+ << resultType << Input.get()->getSourceRange());
}
- // Vector logical not returns the signed variant of the operand type.
- resultType = GetSignedVectorType(resultType);
break;
- } else if (Context.getLangOpts().CPlusPlus && resultType->isVectorType()) {
- const VectorType *VTy = resultType->castAs<VectorType>();
- if (VTy->getVectorKind() != VectorKind::Generic)
+
+ case UO_LNot: // logical negation
+ // Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5).
+ Input = DefaultFunctionArrayLvalueConversion(Input.get());
+ if (Input.isInvalid())
+ return ExprError();
+ resultType = Input.get()->getType();
+
+ // Though we still have to promote half FP to float...
+ if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) {
+ Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast)
+ .get();
+ resultType = Context.FloatTy;
+ }
+
+ // WebAsembly tables can't be used in unary expressions.
+ if (resultType->isPointerType() &&
+ resultType->getPointeeType().isWebAssemblyReferenceType()) {
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
<< resultType << Input.get()->getSourceRange());
+ }
- // Vector logical not returns the signed variant of the operand type.
- resultType = GetSignedVectorType(resultType);
- break;
- } else {
- return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
- << resultType << Input.get()->getSourceRange());
- }
+ if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) {
+ // C99 6.5.3.3p1: ok, fallthrough;
+ if (Context.getLangOpts().CPlusPlus) {
+ // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9:
+ // operand contextually converted to bool.
+ Input = ImpCastExprToType(Input.get(), Context.BoolTy,
+ ScalarTypeToBooleanCastKind(resultType));
+ } else if (Context.getLangOpts().OpenCL &&
+ Context.getLangOpts().OpenCLVersion < 120) {
+ // OpenCL v1.1 6.3.h: The logical operator not (!) does not
+ // operate on scalar float types.
+ if (!resultType->isIntegerType() && !resultType->isPointerType())
+ return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
+ << resultType << Input.get()->getSourceRange());
+ }
+ } else if (resultType->isExtVectorType()) {
+ if (Context.getLangOpts().OpenCL &&
+ Context.getLangOpts().getOpenCLCompatibleVersion() < 120) {
+ // OpenCL v1.1 6.3.h: The logical operator not (!) does not
+ // operate on vector float types.
+ QualType T = resultType->castAs<ExtVectorType>()->getElementType();
+ if (!T->isIntegerType())
+ return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
+ << resultType << Input.get()->getSourceRange());
+ }
+ // Vector logical not returns the signed variant of the operand type.
+ resultType = GetSignedVectorType(resultType);
+ break;
+ } else if (Context.getLangOpts().CPlusPlus &&
+ resultType->isVectorType()) {
+ const VectorType *VTy = resultType->castAs<VectorType>();
+ if (VTy->getVectorKind() != VectorKind::Generic)
+ return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
+ << resultType << Input.get()->getSourceRange());
- // LNot always has type int. C99 6.5.3.3p5.
- // In C++, it's bool. C++ 5.3.1p8
- resultType = Context.getLogicalOperationType();
- break;
- case UO_Real:
- case UO_Imag:
- resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real);
- // _Real maps ordinary l-values into ordinary l-values. _Imag maps ordinary
- // complex l-values to ordinary l-values and all other values to r-values.
- if (Input.isInvalid())
- return ExprError();
- if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) {
- if (Input.get()->isGLValue() &&
- Input.get()->getObjectKind() == OK_Ordinary)
- VK = Input.get()->getValueKind();
- } else if (!getLangOpts().CPlusPlus) {
- // In C, a volatile scalar is read by __imag. In C++, it is not.
- Input = DefaultLvalueConversion(Input.get());
+ // Vector logical not returns the signed variant of the operand type.
+ resultType = GetSignedVectorType(resultType);
+ break;
+ } else {
+ return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
+ << resultType << Input.get()->getSourceRange());
+ }
+
+ // LNot always has type int. C99 6.5.3.3p5.
+ // In C++, it's bool. C++ 5.3.1p8
+ resultType = Context.getLogicalOperationType();
+ break;
+ case UO_Real:
+ case UO_Imag:
+ resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real);
+ // _Real maps ordinary l-values into ordinary l-values. _Imag maps
+ // ordinary complex l-values to ordinary l-values and all other values to
+ // r-values.
+ if (Input.isInvalid())
+ return ExprError();
+ if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) {
+ if (Input.get()->isGLValue() &&
+ Input.get()->getObjectKind() == OK_Ordinary)
+ VK = Input.get()->getValueKind();
+ } else if (!getLangOpts().CPlusPlus) {
+ // In C, a volatile scalar is read by __imag. In C++, it is not.
+ Input = DefaultLvalueConversion(Input.get());
+ }
+ break;
+ case UO_Extension:
+ resultType = Input.get()->getType();
+ VK = Input.get()->getValueKind();
+ OK = Input.get()->getObjectKind();
+ break;
+ case UO_Coawait:
+ // It's unnecessary to represent the pass-through operator co_await in the
+ // AST; j...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/92283
More information about the cfe-commits
mailing list