[clang] [Clang][Sema] Earlier type checking for builtin unary operators (PR #90500)
Krystian Stasiowski via cfe-commits
cfe-commits at lists.llvm.org
Mon May 6 06:10:39 PDT 2024
https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/90500
>From 68ae8a9321b96da8cde1a1813d5e2b0c352649b7 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at 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 b1322f30fa6b6a..ad670165242187 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
@@ -11117,7 +11117,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,
@@ -14288,7 +14288,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();
@@ -14742,7 +14744,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 at -1 {{cannot assign to a variable captured by copy in a non-mutable lambda}}
- // expected-note at -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 6cf946e846de44e9410b8e98ecf9ecc06bcde79d Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at 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 46dd52f8c4c133..28af33e33c328b 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 01241d9ae7cf00bc1f488a62e1c302934faf37e6 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at 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 b146a9b56884ad..67e33b5926b408 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
>From 196359f6bd9b9129990be3e33110d9529fd1135d Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at 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 ad670165242187..bf2035cf8fc32e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -14286,13 +14286,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
@@ -14374,7 +14369,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
@@ -14744,11 +14738,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();
@@ -15802,188 +15791,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}}
}
More information about the cfe-commits
mailing list