Skip to content

Commit 1595988

Browse files
authored
Reapply "[Clang][Sema] Earlier type checking for builtin unary operators (llvm#90500)" (llvm#92283)
This patch reapplies llvm#90500, addressing a bug which caused binary operators with dependent operands to be incorrectly rebuilt by `TreeTransform`.
1 parent f60c699 commit 1595988

File tree

17 files changed

+586
-263
lines changed

17 files changed

+586
-263
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ C++ Specific Potentially Breaking Changes
5656

5757
- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906).
5858

59+
- Clang now performs semantic analysis for unary operators with dependent operands
60+
that are known to be of non-class non-enumeration type prior to instantiation.
61+
5962
ABI Changes in This Version
6063
---------------------------
6164
- Fixed Microsoft name mangling of implicitly defined variables used for thread

clang/include/clang/AST/Type.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8044,7 +8044,10 @@ inline bool Type::isUndeducedType() const {
80448044
/// Determines whether this is a type for which one can define
80458045
/// an overloaded operator.
80468046
inline bool Type::isOverloadableType() const {
8047-
return isDependentType() || isRecordType() || isEnumeralType();
8047+
if (!CanonicalType->isDependentType())
8048+
return isRecordType() || isEnumeralType();
8049+
return !isArrayType() && !isFunctionType() && !isAnyPointerType() &&
8050+
!isMemberPointerType();
80488051
}
80498052

80508053
/// Determines whether this type is written as a typedef-name.

clang/lib/Sema/SemaExpr.cpp

Lines changed: 177 additions & 186 deletions
Large diffs are not rendered by default.

clang/lib/Sema/TreeTransform.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16236,10 +16236,11 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
1623616236
return getSema().CreateBuiltinUnaryOp(OpLoc, Opc, First);
1623716237
}
1623816238
} else {
16239-
if (!First->getType()->isOverloadableType() &&
16239+
if (!First->isTypeDependent() && !Second->isTypeDependent() &&
16240+
!First->getType()->isOverloadableType() &&
1624016241
!Second->getType()->isOverloadableType()) {
16241-
// Neither of the arguments is an overloadable type, so try to
16242-
// create a built-in binary operation.
16242+
// Neither of the arguments is type-dependent or has an overloadable
16243+
// type, so try to create a built-in binary operation.
1624316244
BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op);
1624416245
ExprResult Result
1624516246
= SemaRef.CreateBuiltinBinOp(OpLoc, Opc, First, Second);
@@ -16250,12 +16251,8 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
1625016251
}
1625116252
}
1625216253

16253-
// Add any functions found via argument-dependent lookup.
16254-
Expr *Args[2] = { First, Second };
16255-
unsigned NumArgs = 1 + (Second != nullptr);
16256-
1625716254
// Create the overloaded operator invocation for unary operators.
16258-
if (NumArgs == 1 || isPostIncDec) {
16255+
if (!Second || isPostIncDec) {
1625916256
UnaryOperatorKind Opc
1626016257
= UnaryOperator::getOverloadedOpcode(Op, isPostIncDec);
1626116258
return SemaRef.CreateOverloadedUnaryOp(OpLoc, Opc, Functions, First,
@@ -16264,8 +16261,8 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
1626416261

1626516262
// Create the overloaded operator invocation for binary operators.
1626616263
BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op);
16267-
ExprResult Result = SemaRef.CreateOverloadedBinOp(
16268-
OpLoc, Opc, Functions, Args[0], Args[1], RequiresADL);
16264+
ExprResult Result = SemaRef.CreateOverloadedBinOp(OpLoc, Opc, Functions,
16265+
First, Second, RequiresADL);
1626916266
if (Result.isInvalid())
1627016267
return ExprError();
1627116268

clang/test/AST/ast-dump-expr-json.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4261,9 +4261,9 @@ void TestNonADLCall3() {
42614261
// CHECK-NEXT: }
42624262
// CHECK-NEXT: },
42634263
// CHECK-NEXT: "type": {
4264-
// CHECK-NEXT: "qualType": "<dependent type>"
4264+
// CHECK-NEXT: "qualType": "V"
42654265
// CHECK-NEXT: },
4266-
// CHECK-NEXT: "valueCategory": "prvalue",
4266+
// CHECK-NEXT: "valueCategory": "lvalue",
42674267
// CHECK-NEXT: "isPostfix": false,
42684268
// CHECK-NEXT: "opcode": "*",
42694269
// CHECK-NEXT: "canOverflow": false,

clang/test/AST/ast-dump-expr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ void PrimaryExpressions(Ts... a) {
282282
// CHECK-NEXT: CompoundStmt
283283
// CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <col:8> col:8 implicit 'V'
284284
// CHECK-NEXT: ParenListExpr 0x{{[^ ]*}} <col:8> 'NULL TYPE'
285-
// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <col:8> '<dependent type>' prefix '*' cannot overflow
285+
// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <col:8> 'V' lvalue prefix '*' cannot overflow
286286
// CHECK-NEXT: CXXThisExpr 0x{{[^ ]*}} <col:8> 'V *' this
287287
}
288288
};

clang/test/AST/ast-dump-lambda.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ template <typename... Ts> void test(Ts... a) {
8181
// CHECK-NEXT: | | | `-CompoundStmt {{.*}} <col:15, col:16>
8282
// CHECK-NEXT: | | `-FieldDecl {{.*}} <col:8> col:8{{( imported)?}} implicit 'V'
8383
// CHECK-NEXT: | |-ParenListExpr {{.*}} <col:8> 'NULL TYPE'
84-
// CHECK-NEXT: | | `-UnaryOperator {{.*}} <col:8> '<dependent type>' prefix '*' cannot overflow
84+
// CHECK-NEXT: | | `-UnaryOperator {{.*}} <col:8> 'V' lvalue prefix '*' cannot overflow
8585
// CHECK-NEXT: | | `-CXXThisExpr {{.*}} <col:8> 'V *' this
8686
// CHECK-NEXT: | `-CompoundStmt {{.*}} <col:15, col:16>
8787
// CHECK-NEXT: |-DeclStmt {{.*}} <line:22:3, col:11>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %clang_cc1 -Wno-unused -fsyntax-only %s -verify
2+
3+
struct A {
4+
void operator*();
5+
void operator+();
6+
void operator-();
7+
void operator!();
8+
void operator~();
9+
void operator&();
10+
void operator++();
11+
void operator--();
12+
};
13+
14+
struct B { };
15+
16+
template<typename T, typename U>
17+
void dependent(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) {
18+
*t;
19+
+t;
20+
-t;
21+
!t;
22+
~t;
23+
&t;
24+
++t;
25+
--t;
26+
27+
*pt;
28+
+pt;
29+
-pt; // expected-error {{invalid argument type 'T *' to unary expression}}
30+
!pt;
31+
~pt; // expected-error {{invalid argument type 'T *' to unary expression}}
32+
&pt;
33+
++pt;
34+
--pt;
35+
36+
*mpt; // expected-error {{indirection requires pointer operand ('T U::*' invalid)}}
37+
+mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}}
38+
-mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}}
39+
!mpt;
40+
~mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}}
41+
&mpt;
42+
++mpt; // expected-error {{cannot increment value of type 'T U::*'}}
43+
--mpt; // expected-error {{cannot decrement value of type 'T U::*'}}
44+
45+
*ft;
46+
+ft;
47+
-ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}}
48+
!ft;
49+
~ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}}
50+
&ft;
51+
++ft; // expected-error {{cannot increment value of type 'T ()'}}
52+
--ft; // expected-error {{cannot decrement value of type 'T ()'}}
53+
54+
*at;
55+
+at;
56+
-at; // expected-error {{invalid argument type 'T *' to unary expression}}
57+
!at;
58+
~at; // expected-error {{invalid argument type 'T *' to unary expression}}
59+
&at;
60+
++at; // expected-error {{cannot increment value of type 'T[4]'}}
61+
--at; // expected-error {{cannot decrement value of type 'T[4]'}}
62+
}
63+
64+
// Make sure we only emit diagnostics once.
65+
template void dependent(A t, A* pt, A B::* mpt, A(&ft)(), A(&at)[4]);
Lines changed: 128 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,139 @@
1-
// RUN: %clang_cc1 -std=c++17 -ast-dump %s -ast-dump-filter Test | FileCheck %s
1+
// RUN: %clang_cc1 -std=c++17 -Wno-unused -ast-dump %s -ast-dump-filter Test | FileCheck %s
22

3-
struct A{};
3+
namespace Test {
4+
template<typename T, typename U>
5+
void Unary(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) {
6+
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '*' cannot overflow
7+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
8+
*t;
49

5-
template <typename T, typename U>
6-
auto Test(T* pt, U* pu) {
7-
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '*'
8-
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
9-
(void)*pt;
10+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '+' cannot overflow
11+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
12+
+t;
1013

11-
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '++'
12-
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
13-
(void)(++pt);
14+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '-' cannot overflow
15+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
16+
-t;
1417

15-
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '+'
16-
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
17-
(void)(+pt);
18+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '!' cannot overflow
19+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
20+
!t;
1821

19-
// CHECK: BinaryOperator {{.*}} '<dependent type>' '+'
20-
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
21-
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
22-
(void)(pt + 3);
22+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '~' cannot overflow
23+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
24+
~t;
2325

24-
// CHECK: BinaryOperator {{.*}} '<dependent type>' '-'
25-
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
26-
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
27-
(void)(pt - pt);
26+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
27+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
28+
&t;
2829

29-
// CHECK: BinaryOperator {{.*}} '<dependent type>' '-'
30-
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
31-
// CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *'
32-
(void)(pt - pu);
30+
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '++' cannot overflow
31+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
32+
++t;
3333

34-
// CHECK: BinaryOperator {{.*}} '<dependent type>' '=='
35-
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
36-
// CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *'
37-
(void)(pt == pu);
34+
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '--' cannot overflow
35+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
36+
--t;
3837

39-
}
38+
// CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' cannot overflow
39+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue>
40+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
41+
*pt;
4042

43+
// CHECK: UnaryOperator {{.*}} 'T *' prefix '+' cannot overflow
44+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue>
45+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
46+
+pt;
4147

48+
// CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow
49+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean>
50+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue>
51+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
52+
!pt;
53+
54+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
55+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
56+
&pt;
57+
58+
// CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '++' cannot overflow
59+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
60+
++pt;
61+
62+
// CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '--' cannot overflow
63+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
64+
--pt;
65+
66+
// CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow
67+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <MemberPointerToBoolean>
68+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T U::*' <LValueToRValue>
69+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T U::*' lvalue ParmVar {{.*}} 'mpt' 'T U::*'
70+
!mpt;
71+
72+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
73+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T U::*' lvalue ParmVar {{.*}} 'mpt' 'T U::*'
74+
&mpt;
75+
76+
// CHECK: UnaryOperator {{.*}} 'T ()' lvalue prefix '*' cannot overflow
77+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay>
78+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()'
79+
*ft;
80+
81+
// CHECK: UnaryOperator {{.*}} 'T (*)()' prefix '+' cannot overflow
82+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay>
83+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()'
84+
+ft;
85+
86+
// CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow
87+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean>
88+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay>
89+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()'
90+
!ft;
91+
92+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
93+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()'
94+
&ft;
95+
96+
// CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' cannot overflow
97+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay>
98+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]'
99+
*at;
100+
101+
// CHECK: UnaryOperator {{.*}} 'T *' prefix '+' cannot overflow
102+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay>
103+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]'
104+
+at;
105+
106+
// CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow
107+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean>
108+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay>
109+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]'
110+
!at;
111+
112+
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
113+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]'
114+
&at;
115+
}
116+
117+
template<typename T, typename U>
118+
void Binary(T* pt, U* pu) {
119+
// CHECK: BinaryOperator {{.*}} '<dependent type>' '+'
120+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
121+
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
122+
pt + 3;
123+
124+
// CHECK: BinaryOperator {{.*}} '<dependent type>' '-'
125+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
126+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
127+
pt - pt;
128+
129+
// CHECK: BinaryOperator {{.*}} '<dependent type>' '-'
130+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
131+
// CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *'
132+
pt - pu;
133+
134+
// CHECK: BinaryOperator {{.*}} '<dependent type>' '=='
135+
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
136+
// CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *'
137+
pt == pu;
138+
}
139+
} // namespace Test

clang/test/CXX/over/over.built/p10.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ void f(int i, float f, bool b, char c, int* pi, A* pa, T* pt) {
1515

1616
(void)-pi; // expected-error {{invalid argument type}}
1717
(void)-pa; // expected-error {{invalid argument type}}
18-
(void)-pt; // FIXME: we should be able to give an error here.
18+
(void)-pt; // expected-error {{invalid argument type}}
1919
}
2020

clang/test/CXX/over/over.built/p11.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ void f(int i, float f, bool b, char c, int* pi, T* pt) {
77
(void)~b;
88
(void)~c;
99
(void)~pi; // expected-error {{invalid argument type}}
10-
(void)~pt; // FIXME: we should be able to give an error here.
10+
(void)~pt; // expected-error {{invalid argument type}}
1111
}
1212

0 commit comments

Comments
 (0)