Skip to content

Commit 8019cbb

Browse files
authored
[Clang][Sema] Earlier type checking for builtin unary operators (#90500)
Currently, clang postpones all semantic analysis of unary operators with operands of pointer/pointer to member/array/function type until instantiation whenever that type is dependent (e.g. `T*` where `T` is a type template parameter). Consequently, the uninstantiated AST nodes all have the type `ASTContext::DependentTy` (which, for the purposes of #90152, is undesirable as that type may be the current instantiation! (e.g. `*this`)) This patch moves the point at which we perform semantic analysis for such expression to be prior to instantiation.
1 parent 4c68de5 commit 8019cbb

File tree

15 files changed

+404
-246
lines changed

15 files changed

+404
-246
lines changed

clang/docs/ReleaseNotes.rst

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

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

58+
- Clang now performs semantic analysis for unary operators with dependent operands
59+
that are known to be of non-class non-enumeration type prior to instantiation.
60+
5861
ABI Changes in This Version
5962
---------------------------
6063
- 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: 175 additions & 179 deletions
Large diffs are not rendered by default.

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

clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -357,17 +357,14 @@ namespace N0 {
357357
a->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}}
358358
a->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}}
359359

360-
// FIXME: An overloaded unary 'operator*' is built for these
361-
// even though the operand is a pointer (to a dependent type).
362-
// Type::isOverloadableType should return false for such cases.
363-
(*this).x4;
364-
(*this).B::x4;
365-
(*this).A::x4;
366-
(*this).B::A::x4;
367-
(*this).f4();
368-
(*this).B::f4();
369-
(*this).A::f4();
370-
(*this).B::A::f4();
360+
(*this).x4; // expected-error{{no member named 'x4' in 'B<T>'}}
361+
(*this).B::x4; // expected-error{{no member named 'x4' in 'B<T>'}}
362+
(*this).A::x4; // expected-error{{no member named 'x4' in 'N0::A'}}
363+
(*this).B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}}
364+
(*this).f4(); // expected-error{{no member named 'f4' in 'B<T>'}}
365+
(*this).B::f4(); // expected-error{{no member named 'f4' in 'B<T>'}}
366+
(*this).A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}}
367+
(*this).B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}}
371368

372369
b.x4; // expected-error{{no member named 'x4' in 'B<T>'}}
373370
b.B::x4; // expected-error{{no member named 'x4' in 'B<T>'}}
@@ -399,15 +396,13 @@ namespace N1 {
399396
f<0>();
400397
this->f<0>();
401398
a->f<0>();
402-
// FIXME: This should not require 'template'!
403-
(*this).f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
399+
(*this).f<0>();
404400
b.f<0>();
405401

406402
x.f<0>();
407403
this->x.f<0>();
408404
a->x.f<0>();
409-
// FIXME: This should not require 'template'!
410-
(*this).x.f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
405+
(*this).x.f<0>();
411406
b.x.f<0>();
412407

413408
// FIXME: None of these should require 'template'!

clang/test/Frontend/noderef_templates.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
#define NODEREF __attribute__((noderef))
44

55
template <typename T>
6-
int func(T NODEREF *a) { // expected-note 2 {{a declared here}}
7-
return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}}
6+
int func(T NODEREF *a) { // expected-note 3 {{a declared here}}
7+
return *a + 1; // expected-warning 3 {{dereferencing a; was declared with a 'noderef' type}}
88
}
99

1010
void func() {

clang/test/SemaCXX/cxx2b-deducing-this.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ struct S {
1919
// new and delete are implicitly static
2020
void *operator new(this unsigned long); // expected-error{{an explicit object parameter cannot appear in a static function}}
2121
void operator delete(this void*); // expected-error{{an explicit object parameter cannot appear in a static function}}
22-
22+
2323
void g(this auto) const; // expected-error{{explicit object member function cannot have 'const' qualifier}}
2424
void h(this auto) &; // expected-error{{explicit object member function cannot have '&' qualifier}}
2525
void i(this auto) &&; // expected-error{{explicit object member function cannot have '&&' qualifier}}
@@ -198,9 +198,7 @@ void func(int i) {
198198
void TestMutationInLambda() {
199199
[i = 0](this auto &&){ i++; }();
200200
[i = 0](this auto){ i++; }();
201-
[i = 0](this const auto&){ i++; }();
202-
// expected-error@-1 {{cannot assign to a variable captured by copy in a non-mutable lambda}}
203-
// expected-note@-2 {{in instantiation of}}
201+
[i = 0](this const auto&){ i++; }(); // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
204202

205203
int x;
206204
const auto l1 = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}

0 commit comments

Comments
 (0)