Skip to content

Commit 350a476

Browse files
committed
Add diagnostics for use of 'this' in a potentially evaluated expression.
1 parent 919cd88 commit 350a476

File tree

3 files changed

+202
-3
lines changed

3 files changed

+202
-3
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13070,6 +13070,9 @@ def err_sycl_entry_point_invalid : Error<
1307013070
def err_sycl_entry_point_invalid_redeclaration : Error<
1307113071
"the %0 kernel name argument does not match prior"
1307213072
" declaration%diff{: $ vs $|}1,2">;
13073+
def err_sycl_entry_point_invalid_this : Error<
13074+
"'this' cannot be%select{| implicitly}0 used in a potentially evaluated"
13075+
" expression in the body of a function declared with the %1 attribute">;
1307313076
def err_sycl_kernel_name_conflict : Error<
1307413077
"the %0 kernel name argument conflicts with a previous declaration">;
1307513078
def warn_sycl_kernel_name_not_a_class_type : Warning<

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,9 +463,10 @@ class OutlinedFunctionDeclBodyInstantiator
463463
public:
464464
using ParmDeclMap = llvm::DenseMap<ParmVarDecl *, VarDecl *>;
465465

466-
OutlinedFunctionDeclBodyInstantiator(Sema &S, ParmDeclMap &M)
466+
OutlinedFunctionDeclBodyInstantiator(Sema &S, ParmDeclMap &M,
467+
FunctionDecl *FD)
467468
: TreeTransform<OutlinedFunctionDeclBodyInstantiator>(S), SemaRef(S),
468-
MapRef(M) {}
469+
MapRef(M), FD(FD) {}
469470

470471
// A new set of AST nodes is always required.
471472
bool AlwaysRebuild() { return true; }
@@ -491,9 +492,20 @@ class OutlinedFunctionDeclBodyInstantiator
491492
return DRE;
492493
}
493494

495+
// Diagnose CXXThisExpr in a potentially evaluated expression.
496+
ExprResult TransformCXXThisExpr(CXXThisExpr *CTE) {
497+
if (SemaRef.currentEvaluationContext().isPotentiallyEvaluated()) {
498+
SemaRef.Diag(CTE->getExprLoc(), diag::err_sycl_entry_point_invalid_this)
499+
<< (CTE->isImplicitCXXThis() ? /* implicit */ 1 : /* empty */ 0)
500+
<< FD->getAttr<SYCLKernelEntryPointAttr>();
501+
}
502+
return CTE;
503+
}
504+
494505
private:
495506
Sema &SemaRef;
496507
ParmDeclMap &MapRef;
508+
FunctionDecl *FD;
497509
};
498510

499511
OutlinedFunctionDecl *BuildSYCLKernelEntryPointOutline(Sema &SemaRef,
@@ -518,7 +530,8 @@ OutlinedFunctionDecl *BuildSYCLKernelEntryPointOutline(Sema &SemaRef,
518530
// evaluated contexts in the function body. This is not necessarily the
519531
// right place to add such a diagnostic.
520532

521-
OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap);
533+
OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap,
534+
FD);
522535
Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(Body).get();
523536
OFD->setBody(OFDBody);
524537
OFD->setNothrow();
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++17 -fsycl-is-host -verify %s
2+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++17 -fsycl-is-device -verify %s
3+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++20 -fsycl-is-host -verify -DCXX20 %s
4+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++20 -fsycl-is-device -verify -DCXX20 %s
5+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++23 -fsycl-is-host -verify -DCXX23 %s
6+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -std=c++23 -fsycl-is-device -verify -DCXX23 %s
7+
8+
// These tests validate diagnostics for invalid use of 'this' in the body of
9+
// a function declared with the sycl_kernel_entry_point attribute.
10+
11+
12+
template<typename T> struct remove_reference_t {
13+
using type = T;
14+
};
15+
template<typename T> struct remove_reference_t<T&> {
16+
using type = T;
17+
};
18+
19+
namespace std {
20+
struct type_info {
21+
virtual ~type_info();
22+
};
23+
} // namespace std
24+
25+
////////////////////////////////////////////////////////////////////////////////
26+
// Valid declarations.
27+
////////////////////////////////////////////////////////////////////////////////
28+
template<int, int=0> struct KN;
29+
30+
struct S1 {
31+
[[clang::sycl_kernel_entry_point(KN<1>)]] void ok1() {
32+
(void)sizeof(this);
33+
}
34+
};
35+
36+
struct S2 {
37+
[[clang::sycl_kernel_entry_point(KN<2>)]] void ok2() {
38+
(void)noexcept(this);
39+
}
40+
};
41+
42+
struct S3 {
43+
[[clang::sycl_kernel_entry_point(KN<3>)]] void ok3() {
44+
decltype(this) x = nullptr;
45+
}
46+
};
47+
48+
struct S4 {
49+
static void smf();
50+
[[clang::sycl_kernel_entry_point(KN<4>)]] void ok4() {
51+
remove_reference_t<decltype(*this)>::type::smf();
52+
}
53+
};
54+
55+
struct S5 {
56+
int dm;
57+
void mf();
58+
[[clang::sycl_kernel_entry_point(KN<5>)]] void ok5() {
59+
(void)typeid(*this); // S5 is not abstract, so 'this' is not evaluated.
60+
(void)typeid(dm); // 'int' is not an abstract class type; implicit 'this' is not evaluated.
61+
(void)typeid(mf()); // 'void' is not an abstract class type; implicit 'this' is not evaluated.
62+
}
63+
};
64+
65+
template<typename KN, bool B>
66+
struct S6 {
67+
void mf() noexcept(B);
68+
[[clang::sycl_kernel_entry_point(KN)]] void ok6() noexcept(noexcept(mf())) {}
69+
};
70+
template void S6<KN<6,0>, false>::ok6();
71+
template void S6<KN<6,1>, true>::ok6();
72+
73+
template<typename KN, bool B>
74+
struct S7 {
75+
void mf() noexcept(B);
76+
[[clang::sycl_kernel_entry_point(KN)]] void ok7() noexcept(noexcept(this->mf())) {}
77+
};
78+
template void S7<KN<7,0>, false>::ok7();
79+
template void S7<KN<7,1>, true>::ok7();
80+
81+
#if defined(CXX20)
82+
template<typename KN, typename T>
83+
struct S8 {
84+
void mf(T);
85+
[[clang::sycl_kernel_entry_point(KN)]] void ok8() requires(requires { mf(1); }) {}
86+
};
87+
template void S8<KN<8>, int>::ok8();
88+
89+
template<typename KN, typename T>
90+
struct S9 {
91+
void mf(T);
92+
[[clang::sycl_kernel_entry_point(KN)]] void ok9() requires(requires { this->mf(1); }) {}
93+
};
94+
template void S9<KN<9>, int>::ok9();
95+
#endif
96+
97+
#if defined(CXX23)
98+
struct S10 {
99+
[[clang::sycl_kernel_entry_point(KN<10>)]] void ok10(this S10 self) {
100+
(void)self;
101+
}
102+
};
103+
#endif
104+
105+
106+
////////////////////////////////////////////////////////////////////////////////
107+
// Invalid declarations.
108+
////////////////////////////////////////////////////////////////////////////////
109+
110+
template<int, int=0> struct BADKN;
111+
112+
// expected-error@+3 {{'this' cannot be used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
113+
struct B1 {
114+
[[clang::sycl_kernel_entry_point(BADKN<1>)]] void bad1() {
115+
(void)this;
116+
}
117+
};
118+
119+
// expected-error@+4 {{'this' cannot be implicitly used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
120+
struct B2 {
121+
int dm;
122+
[[clang::sycl_kernel_entry_point(BADKN<2>)]] void bad2() {
123+
(void)dm;
124+
}
125+
};
126+
127+
// expected-error@+4 {{'this' cannot be implicitly used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
128+
struct B3 {
129+
void mf();
130+
[[clang::sycl_kernel_entry_point(BADKN<3>)]] void bad3() {
131+
(void)mf();
132+
}
133+
};
134+
135+
// expected-error@+4 {{'this' cannot be used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
136+
struct B4 {
137+
virtual void vmf() = 0;
138+
[[clang::sycl_kernel_entry_point(BADKN<4>)]] void bad4() {
139+
(void)typeid(*this); // B4 is abstract, so 'this' is evaluated.
140+
}
141+
};
142+
143+
// A diagnostic is not currently issued for uninstantiated definitions. In this
144+
// case, a declaration is instantiated, but a definition isn't. A diagnostic
145+
// will be issued if a definition is instantiated (as the next test exercises).
146+
struct B5 {
147+
template<typename KN>
148+
[[clang::sycl_kernel_entry_point(KN)]] void bad5() {
149+
(void)this;
150+
}
151+
};
152+
extern template void B5::bad5<BADKN<5>>();
153+
154+
// expected-error@+4 {{'this' cannot be used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
155+
struct B6 {
156+
template<typename KN>
157+
[[clang::sycl_kernel_entry_point(KN)]] void bad6() {
158+
(void)this;
159+
}
160+
};
161+
// expected-note@+1 {{in instantiation of function template specialization 'B6::bad6<BADKN<6>>' requested here}}
162+
template void B6::bad6<BADKN<6>>();
163+
164+
// A diagnostic is not currently issued for uninstantiated definitions. In this
165+
// case, a declaration is instantiated, but a definition isn't. A diagnostic
166+
// will be issued if a definition is instantiated (as the next test exercises).
167+
template<typename KN>
168+
struct B7 {
169+
[[clang::sycl_kernel_entry_point(KN)]] void bad7() {
170+
(void)this;
171+
}
172+
};
173+
extern template void B7<BADKN<7>>::bad7();
174+
175+
// expected-error@+4 {{'this' cannot be used in a potentially evaluated expression in the body of a function declared with the 'clang::sycl_kernel_entry_point' attribute}}
176+
template<typename KN>
177+
struct B8 {
178+
[[clang::sycl_kernel_entry_point(KN)]] void bad8() {
179+
(void)this;
180+
}
181+
};
182+
// expected-note@+1 {{in instantiation of member function 'B8<BADKN<8>>::bad8' requested here}}
183+
template void B8<BADKN<8>>::bad8();

0 commit comments

Comments
 (0)