https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/90500
>From 1b3476db3208ccb0b425ff604755349437d28863 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Thu, 25 Apr 2024 08:17:21 -0400 Subject: [PATCH 1/4] [Clang][Sema] Earlier type checking for builtin unary operators --- clang/include/clang/AST/Type.h | 5 ++++- clang/lib/Sema/SemaExpr.cpp | 22 +++++++++++-------- clang/test/AST/ast-dump-expr-json.cpp | 4 ++-- clang/test/AST/ast-dump-expr.cpp | 2 +- clang/test/AST/ast-dump-lambda.cpp | 2 +- clang/test/CXX/over/over.built/ast.cpp | 8 ++++--- clang/test/Frontend/noderef_templates.cpp | 4 ++-- clang/test/SemaCXX/cxx2b-deducing-this.cpp | 6 ++--- .../test/SemaTemplate/class-template-spec.cpp | 12 +++++----- 9 files changed, 36 insertions(+), 29 deletions(-) diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index e6643469e0b334..da3834f19ca044 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 0c37f43f75401b..cf61c191e5baef 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -671,12 +671,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 @@ -11116,7 +11116,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, @@ -14287,7 +14287,9 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, ExprObjectKind &OK, SourceLocation OpLoc, bool IsInc, bool IsPrefix) { - if (Op->isTypeDependent()) + if (Op->isTypeDependent() && + (Op->hasPlaceholderType() || + Op->getType()->isSpecificBuiltinType(BuiltinType::Dependent))) return S.Context.DependentTy; QualType ResType = Op->getType(); @@ -14725,7 +14727,9 @@ 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()) + if (Op->isTypeDependent() && + (Op->hasPlaceholderType() || + Op->getType()->isSpecificBuiltinType(BuiltinType::Dependent))) return S.Context.DependentTy; ExprResult ConvResult = S.UsualUnaryConversions(Op); diff --git a/clang/test/AST/ast-dump-expr-json.cpp b/clang/test/AST/ast-dump-expr-json.cpp index 0fb07b0b434cc3..4b7365e554cb7c 100644 --- a/clang/test/AST/ast-dump-expr-json.cpp +++ b/clang/test/AST/ast-dump-expr-json.cpp @@ -4261,9 +4261,9 @@ void TestNonADLCall3() { // CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "<dependent type>" +// CHECK-NEXT: "qualType": "V" // CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "valueCategory": "lvalue", // CHECK-NEXT: "isPostfix": false, // CHECK-NEXT: "opcode": "*", // CHECK-NEXT: "canOverflow": false, diff --git a/clang/test/AST/ast-dump-expr.cpp b/clang/test/AST/ast-dump-expr.cpp index 69e65e22d61d0d..4df5ba4276abd2 100644 --- a/clang/test/AST/ast-dump-expr.cpp +++ b/clang/test/AST/ast-dump-expr.cpp @@ -282,7 +282,7 @@ void PrimaryExpressions(Ts... a) { // CHECK-NEXT: CompoundStmt // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <col:8> col:8 implicit 'V' // CHECK-NEXT: ParenListExpr 0x{{[^ ]*}} <col:8> 'NULL TYPE' - // CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <col:8> '<dependent type>' prefix '*' cannot overflow + // CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <col:8> 'V' lvalue prefix '*' cannot overflow // CHECK-NEXT: CXXThisExpr 0x{{[^ ]*}} <col:8> 'V *' this } }; diff --git a/clang/test/AST/ast-dump-lambda.cpp b/clang/test/AST/ast-dump-lambda.cpp index ef8789cd97d3e7..a4d3fe4fbda57c 100644 --- a/clang/test/AST/ast-dump-lambda.cpp +++ b/clang/test/AST/ast-dump-lambda.cpp @@ -81,7 +81,7 @@ template <typename... Ts> void test(Ts... a) { // CHECK-NEXT: | | | `-CompoundStmt {{.*}} <col:15, col:16> // CHECK-NEXT: | | `-FieldDecl {{.*}} <col:8> col:8{{( imported)?}} implicit 'V' // CHECK-NEXT: | |-ParenListExpr {{.*}} <col:8> 'NULL TYPE' -// CHECK-NEXT: | | `-UnaryOperator {{.*}} <col:8> '<dependent type>' prefix '*' cannot overflow +// CHECK-NEXT: | | `-UnaryOperator {{.*}} <col:8> 'V' lvalue prefix '*' cannot overflow // CHECK-NEXT: | | `-CXXThisExpr {{.*}} <col:8> 'V *' this // CHECK-NEXT: | `-CompoundStmt {{.*}} <col:15, col:16> // CHECK-NEXT: |-DeclStmt {{.*}} <line:22:3, col:11> diff --git a/clang/test/CXX/over/over.built/ast.cpp b/clang/test/CXX/over/over.built/ast.cpp index 56a63431269f30..b95a453de90222 100644 --- a/clang/test/CXX/over/over.built/ast.cpp +++ b/clang/test/CXX/over/over.built/ast.cpp @@ -4,15 +4,17 @@ struct A{}; template <typename T, typename U> auto Test(T* pt, U* pu) { - // CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '*' + // CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue> // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' (void)*pt; - // CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '++' + // CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '++' // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' (void)(++pt); - // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '+' + // CHECK: UnaryOperator {{.*}} 'T *' prefix '+' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue> // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' (void)(+pt); diff --git a/clang/test/Frontend/noderef_templates.cpp b/clang/test/Frontend/noderef_templates.cpp index 5fde6efd87c7fb..9e54cd5d788992 100644 --- a/clang/test/Frontend/noderef_templates.cpp +++ b/clang/test/Frontend/noderef_templates.cpp @@ -3,8 +3,8 @@ #define NODEREF __attribute__((noderef)) template <typename T> -int func(T NODEREF *a) { // expected-note 2 {{a declared here}} - return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}} +int func(T NODEREF *a) { // expected-note 3 {{a declared here}} + return *a + 1; // expected-warning 3 {{dereferencing a; was declared with a 'noderef' type}} } void func() { diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp index 5f29a955e053c3..aa64530bd5be3d 100644 --- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp +++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp @@ -19,7 +19,7 @@ struct S { // new and delete are implicitly static void *operator new(this unsigned long); // expected-error{{an explicit object parameter cannot appear in a static function}} void operator delete(this void*); // expected-error{{an explicit object parameter cannot appear in a static function}} - + void g(this auto) const; // expected-error{{explicit object member function cannot have 'const' qualifier}} void h(this auto) &; // expected-error{{explicit object member function cannot have '&' qualifier}} void i(this auto) &&; // expected-error{{explicit object member function cannot have '&&' qualifier}} @@ -198,9 +198,7 @@ void func(int i) { void TestMutationInLambda() { [i = 0](this auto &&){ i++; }(); [i = 0](this auto){ i++; }(); - [i = 0](this const auto&){ i++; }(); - // expected-error@-1 {{cannot assign to a variable captured by copy in a non-mutable lambda}} - // expected-note@-2 {{in instantiation of}} + [i = 0](this const auto&){ i++; }(); // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} int x; const auto l1 = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp index 56b8207bd9a435..faa54c36753831 100644 --- a/clang/test/SemaTemplate/class-template-spec.cpp +++ b/clang/test/SemaTemplate/class-template-spec.cpp @@ -18,7 +18,7 @@ int test_specs(A<float, float> *a1, A<float, int> *a2) { return a1->x + a2->y; } -int test_incomplete_specs(A<double, double> *a1, +int test_incomplete_specs(A<double, double> *a1, A<double> *a2) { (void)a1->x; // expected-error{{member access into incomplete type}} @@ -39,7 +39,7 @@ template <> struct X<int, int> { int foo(); }; // #1 template <> struct X<float> { int bar(); }; // #2 typedef int int_type; -void testme(X<int_type> *x1, X<float, int> *x2) { +void testme(X<int_type> *x1, X<float, int> *x2) { (void)x1->foo(); // okay: refers to #1 (void)x2->bar(); // okay: refers to #2 } @@ -53,7 +53,7 @@ struct A<char> { A<char>::A() { } // Make sure we can see specializations defined before the primary template. -namespace N{ +namespace N{ template<typename T> struct A0; } @@ -97,7 +97,7 @@ namespace M { template<> struct ::A<long double>; // expected-error{{must occur at global scope}} } -template<> struct N::B<char> { +template<> struct N::B<char> { int testf(int x) { return f(x); } }; @@ -138,9 +138,9 @@ namespace PR18009 { template <typename T> struct C { template <int N, int M> struct S; - template <int N> struct S<N, N ? **(T(*)[N])0 : 0> {}; // expected-error {{depends on a template parameter of the partial specialization}} + template <int N> struct S<N, N ? **(T(*)[N])0 : 0> {}; // ok }; - C<int> c; // expected-note {{in instantiation of}} + C<int> c; template<int A> struct outer { template<int B, int C> struct inner {}; >From 5c37accf845486ed507d0bd7997ab8f2735db0b9 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 3 May 2024 09:51:52 -0400 Subject: [PATCH 2/4] [FOLD] update tests after #90152 --- .../temp.res/temp.dep/temp.dep.type/p4.cpp | 25 ++++++++----------- .../ASTMatchers/ASTMatchersNarrowingTest.cpp | 6 ++--- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp index 0f24d716a7b749..09585acfa61fad 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp @@ -357,17 +357,14 @@ namespace N0 { a->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} a->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - // FIXME: An overloaded unary 'operator*' is built for these - // even though the operand is a pointer (to a dependent type). - // Type::isOverloadableType should return false for such cases. - (*this).x4; - (*this).B::x4; - (*this).A::x4; - (*this).B::A::x4; - (*this).f4(); - (*this).B::f4(); - (*this).A::f4(); - (*this).B::A::f4(); + (*this).x4; // expected-error{{no member named 'x4' in 'B<T>'}} + (*this).B::x4; // expected-error{{no member named 'x4' in 'B<T>'}} + (*this).A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + (*this).B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + (*this).f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + (*this).B::f4(); // expected-error{{no member named 'f4' in 'B<T>'}} + (*this).A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + (*this).B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} b.x4; // expected-error{{no member named 'x4' in 'B<T>'}} b.B::x4; // expected-error{{no member named 'x4' in 'B<T>'}} @@ -399,15 +396,13 @@ namespace N1 { f<0>(); this->f<0>(); a->f<0>(); - // FIXME: This should not require 'template'! - (*this).f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + (*this).f<0>(); b.f<0>(); x.f<0>(); this->x.f<0>(); a->x.f<0>(); - // FIXME: This should not require 'template'! - (*this).x.f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + (*this).x.f<0>(); b.x.f<0>(); // FIXME: None of these should require 'template'! diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index c08deb903f129b..f26140675fd462 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -1572,9 +1572,9 @@ TEST_P(ASTMatchersTest, IsArrow_MatchesMemberVariablesViaArrow) { EXPECT_TRUE( matches("template <class T> class Y { void x() { this->m; } int m; };", memberExpr(isArrow()))); - EXPECT_TRUE( - notMatches("template <class T> class Y { void x() { (*this).m; } };", - cxxDependentScopeMemberExpr(isArrow()))); + EXPECT_TRUE(notMatches( + "template <class T> class Y { void x() { (*this).m; } int m; };", + memberExpr(isArrow()))); } TEST_P(ASTMatchersTest, IsArrow_MatchesStaticMemberVariablesViaArrow) { >From 67d535ae580286d528b27ba26b201605503ccdc2 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 3 May 2024 09:59:56 -0400 Subject: [PATCH 3/4] [FOLD] add release note --- clang/docs/ReleaseNotes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 85f7144a27caac..7b64ecee398d74 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -53,6 +53,9 @@ C++ Specific Potentially Breaking Changes it's negative spelling can be used to obtain compatibility with previous versions of clang. +- 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 >From 249658fd89e11ca6fa8366b823373b54e191d64a Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski <sdkryst...@gmail.com> Date: Fri, 3 May 2024 11:50:18 -0400 Subject: [PATCH 4/4] [FOLD] more tests & support for more operators --- clang/lib/Sema/SemaExpr.cpp | 344 +++++++++--------- .../expr/expr.unary/expr.unary.general/p1.cpp | 65 ++++ clang/test/CXX/over/over.built/ast.cpp | 160 ++++++-- clang/test/CXX/over/over.built/p10.cpp | 2 +- clang/test/CXX/over/over.built/p11.cpp | 2 +- 5 files changed, 363 insertions(+), 210 deletions(-) create mode 100644 clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index cf61c191e5baef..e03bf7ae6a2635 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14285,13 +14285,8 @@ static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS, static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, ExprValueKind &VK, ExprObjectKind &OK, - SourceLocation OpLoc, - bool IsInc, bool IsPrefix) { - if (Op->isTypeDependent() && - (Op->hasPlaceholderType() || - Op->getType()->isSpecificBuiltinType(BuiltinType::Dependent))) - return S.Context.DependentTy; - + SourceLocation OpLoc, bool IsInc, + bool IsPrefix) { 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 @@ -14373,7 +14368,6 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, } } - /// getPrimaryDecl - Helper function for CheckAddressOfOperand(). /// This routine allows us to typecheck complex/recursive expressions /// where the declaration is needed for type checking. We only need to @@ -14727,11 +14721,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() && - (Op->hasPlaceholderType() || - Op->getType()->isSpecificBuiltinType(BuiltinType::Dependent))) - return S.Context.DependentTy; - ExprResult ConvResult = S.UsualUnaryConversions(Op); if (ConvResult.isInvalid()) return QualType(); @@ -15785,188 +15774,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; just return the input expression instead. + assert(!Input.get()->getType()->isDependentType() && + "the co_await expression must be non-dependant before " + "building operator co_await"); + return Input; } - 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; just return the input expression instead. - assert(!Input.get()->getType()->isDependentType() && - "the co_await expression must be non-dependant before " - "building operator co_await"); - return Input; } if (resultType.isNull() || Input.isInvalid()) return ExprError(); diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp new file mode 100644 index 00000000000000..6744ce1cad174e --- /dev/null +++ b/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -Wno-unused -fsyntax-only %s -verify + +struct A { + void operator*(); + void operator+(); + void operator-(); + void operator!(); + void operator~(); + void operator&(); + void operator++(); + void operator--(); +}; + +struct B { }; + +template<typename T, typename U> +void dependent(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) { + *t; + +t; + -t; + !t; + ~t; + &t; + ++t; + --t; + + *pt; + +pt; + -pt; // expected-error {{invalid argument type 'T *' to unary expression}} + !pt; + ~pt; // expected-error {{invalid argument type 'T *' to unary expression}} + &pt; + ++pt; + --pt; + + *mpt; // expected-error {{indirection requires pointer operand ('T U::*' invalid)}} + +mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}} + -mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}} + !mpt; + ~mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}} + &mpt; + ++mpt; // expected-error {{cannot increment value of type 'T U::*'}} + --mpt; // expected-error {{cannot decrement value of type 'T U::*'}} + + *ft; + +ft; + -ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}} + !ft; + ~ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}} + &ft; + ++ft; // expected-error {{cannot increment value of type 'T ()'}} + --ft; // expected-error {{cannot decrement value of type 'T ()'}} + + *at; + +at; + -at; // expected-error {{invalid argument type 'T *' to unary expression}} + !at; + ~at; // expected-error {{invalid argument type 'T *' to unary expression}} + &at; + ++at; // expected-error {{cannot increment value of type 'T[4]'}} + --at; // expected-error {{cannot decrement value of type 'T[4]'}} +} + +// Make sure we only emit diagnostics once. +template void dependent(A t, A* pt, A B::* mpt, A(&ft)(), A(&at)[4]); diff --git a/clang/test/CXX/over/over.built/ast.cpp b/clang/test/CXX/over/over.built/ast.cpp index b95a453de90222..78f86edb1e9616 100644 --- a/clang/test/CXX/over/over.built/ast.cpp +++ b/clang/test/CXX/over/over.built/ast.cpp @@ -1,43 +1,139 @@ -// RUN: %clang_cc1 -std=c++17 -ast-dump %s -ast-dump-filter Test | FileCheck %s +// RUN: %clang_cc1 -std=c++17 -Wno-unused -ast-dump %s -ast-dump-filter Test | FileCheck %s -struct A{}; +namespace Test { + template<typename T, typename U> + void Unary(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) { + // CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '*' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + *t; -template <typename T, typename U> -auto Test(T* pt, U* pu) { - // CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue> - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - (void)*pt; + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '+' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + +t; - // CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '++' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - (void)(++pt); + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '-' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + -t; - // CHECK: UnaryOperator {{.*}} 'T *' prefix '+' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue> - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - (void)(+pt); + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '!' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + !t; - // CHECK: BinaryOperator {{.*}} '<dependent type>' '+' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 - (void)(pt + 3); + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '~' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + ~t; - // CHECK: BinaryOperator {{.*}} '<dependent type>' '-' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - (void)(pt - pt); + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + &t; - // CHECK: BinaryOperator {{.*}} '<dependent type>' '-' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - // CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *' - (void)(pt - pu); + // CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '++' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + ++t; - // CHECK: BinaryOperator {{.*}} '<dependent type>' '==' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - // CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *' - (void)(pt == pu); + // CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '--' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + --t; -} + // CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + *pt; + // CHECK: UnaryOperator {{.*}} 'T *' prefix '+' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + +pt; + // CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean> + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + !pt; + + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + &pt; + + // CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '++' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + ++pt; + + // CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '--' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + --pt; + + // CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <MemberPointerToBoolean> + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T U::*' <LValueToRValue> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T U::*' lvalue ParmVar {{.*}} 'mpt' 'T U::*' + !mpt; + + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T U::*' lvalue ParmVar {{.*}} 'mpt' 'T U::*' + &mpt; + + // CHECK: UnaryOperator {{.*}} 'T ()' lvalue prefix '*' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()' + *ft; + + // CHECK: UnaryOperator {{.*}} 'T (*)()' prefix '+' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()' + +ft; + + // CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean> + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()' + !ft; + + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()' + &ft; + + // CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]' + *at; + + // CHECK: UnaryOperator {{.*}} 'T *' prefix '+' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]' + +at; + + // CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean> + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay> + // CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]' + !at; + + // CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]' + &at; + } + + template<typename T, typename U> + void Binary(T* pt, U* pu) { + // CHECK: BinaryOperator {{.*}} '<dependent type>' '+' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 + pt + 3; + + // CHECK: BinaryOperator {{.*}} '<dependent type>' '-' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + pt - pt; + + // CHECK: BinaryOperator {{.*}} '<dependent type>' '-' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *' + pt - pu; + + // CHECK: BinaryOperator {{.*}} '<dependent type>' '==' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *' + pt == pu; + } +} // namespace Test diff --git a/clang/test/CXX/over/over.built/p10.cpp b/clang/test/CXX/over/over.built/p10.cpp index 678056da582057..8ff2396d0b6f95 100644 --- a/clang/test/CXX/over/over.built/p10.cpp +++ b/clang/test/CXX/over/over.built/p10.cpp @@ -15,6 +15,6 @@ void f(int i, float f, bool b, char c, int* pi, A* pa, T* pt) { (void)-pi; // expected-error {{invalid argument type}} (void)-pa; // expected-error {{invalid argument type}} - (void)-pt; // FIXME: we should be able to give an error here. + (void)-pt; // expected-error {{invalid argument type}} } diff --git a/clang/test/CXX/over/over.built/p11.cpp b/clang/test/CXX/over/over.built/p11.cpp index 7ebf16b95439f6..f7a741db726def 100644 --- a/clang/test/CXX/over/over.built/p11.cpp +++ b/clang/test/CXX/over/over.built/p11.cpp @@ -7,6 +7,6 @@ void f(int i, float f, bool b, char c, int* pi, T* pt) { (void)~b; (void)~c; (void)~pi; // expected-error {{invalid argument type}} - (void)~pt; // FIXME: we should be able to give an error here. + (void)~pt; // expected-error {{invalid argument type}} } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits