Skip to content

Commit ce98062

Browse files
committed
Create isForwardDeclarable
1 parent 0b88367 commit ce98062

File tree

2 files changed

+109
-99
lines changed

2 files changed

+109
-99
lines changed

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 108 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,84 @@ void SemaSYCL::checkSYCLDeviceVarDecl(VarDecl *Var) {
489489
checkSYCLType(*this, Ty, Loc, Visited);
490490
}
491491

492+
enum NotForwardDeclarableReason {
493+
UnscopedEnum,
494+
StdNamespace,
495+
UnnamedTag,
496+
NotAtNamespaceScope,
497+
None
498+
};
499+
500+
// This is a helper function which is used to check if a class declaration is:
501+
// * declared within namespace 'std' (at any level)
502+
// e.g., namespace std { namespace literals { class Whatever; } }
503+
// h.single_task<std::literals::Whatever>([]() {});
504+
// * declared within a function
505+
// e.g., void foo() { struct S { int i; };
506+
// h.single_task<S>([]() {}); }
507+
// * declared within another tag
508+
// e.g., struct S { struct T { int i } t; };
509+
// h.single_task<S::T>([]() {});
510+
// User for kernel name types and class/struct types used in free function
511+
// kernel arguments.
512+
static NotForwardDeclarableReason
513+
isForwardDeclarable(const NamedDecl *DeclToCheck, SemaSYCL &S,
514+
bool DiagForFreeFunction = false) {
515+
if (const auto *ED = dyn_cast<EnumDecl>(DeclToCheck);
516+
ED && !ED->isScoped() && !ED->isFixed())
517+
return NotForwardDeclarableReason::UnscopedEnum;
518+
519+
const DeclContext *DeclCtx = DeclToCheck->getDeclContext();
520+
if (DeclCtx) {
521+
while (!DeclCtx->isTranslationUnit() &&
522+
(isa<NamespaceDecl>(DeclCtx) || isa<LinkageSpecDecl>(DeclCtx))) {
523+
const auto *NSDecl = dyn_cast<NamespaceDecl>(DeclCtx);
524+
// We don't report free function kernel parameter case because the
525+
// restriction for the type used there to be forward declarable comes from
526+
// the need to forward declare it in the integration header. We're safe
527+
// to do so because the integration header is an implemention detail and
528+
// is generated by the compiler.
529+
// We do diagnose case with kernel name type since the spec requires us to
530+
// do so.
531+
if (!DiagForFreeFunction && NSDecl && NSDecl->isStdNamespace())
532+
return NotForwardDeclarableReason::StdNamespace;
533+
DeclCtx = DeclCtx->getParent();
534+
}
535+
}
536+
537+
// Check if the we've met a Tag declaration local to a non-namespace scope
538+
// (i.e. Inside a function or within another Tag etc).
539+
if (const auto *Tag = dyn_cast<TagDecl>(DeclToCheck)) {
540+
if (Tag->getIdentifier() == nullptr)
541+
return NotForwardDeclarableReason::UnnamedTag;
542+
if (!DeclCtx->isTranslationUnit()) {
543+
// Diagnose used types without complete definition i.e.
544+
// int main() {
545+
// class KernelName1;
546+
// parallel_for<class KernelName1>(..);
547+
// }
548+
// For kernel name type This case can only be diagnosed during host
549+
// compilation because the integration header is required to distinguish
550+
// between the invalid code (above) and the following valid code:
551+
// int main() {
552+
// parallel_for<class KernelName2>(..);
553+
// }
554+
// The device compiler forward declares both KernelName1 and
555+
// KernelName2 in the integration header as ::KernelName1 and
556+
// ::KernelName2. The problem with the former case is the additional
557+
// declaration 'class KernelName1' in non-global scope. Lookup in this
558+
// case will resolve to ::main::KernelName1 (instead of
559+
// ::KernelName1). Since this is not visible to runtime code that
560+
// submits kernels, this is invalid.
561+
if (Tag->isCompleteDefinition() ||
562+
S.getLangOpts().SYCLEnableIntHeaderDiags || DiagForFreeFunction)
563+
return NotForwardDeclarableReason::NotAtNamespaceScope;
564+
}
565+
}
566+
567+
return NotForwardDeclarableReason::None;
568+
}
569+
492570
// Tests whether given function is a lambda function or '()' operator used as
493571
// SYCL kernel body function (e.g. in parallel_for).
494572
// NOTE: This is incomplete implemenation. See TODO in the FE TODO list for the
@@ -1964,25 +2042,9 @@ class SyclKernelFieldChecker : public SyclKernelFieldHandler {
19642042
// For free functions all struct/class kernel arguments are forward declared
19652043
// in integration header, that adds additional restrictions for kernel
19662044
// arguments.
1967-
// Lambdas are not forward declarable. So, diagnose them properly.
1968-
if (RD->isLambda()) {
1969-
Diag.Report(PD->getLocation(),
1970-
diag::err_bad_free_function_kernel_param_type)
1971-
<< ParamTy;
1972-
Diag.Report(PD->getLocation(),
1973-
diag::note_free_function_kernel_param_type_not_fwd_declarable)
1974-
<< ParamTy;
1975-
IsInvalid = true;
1976-
return isValid();
1977-
}
1978-
1979-
// Check that the type is defined at namespace scope.
1980-
const DeclContext *DeclCtx = RD->getDeclContext();
1981-
while (!DeclCtx->isTranslationUnit() &&
1982-
(isa<NamespaceDecl>(DeclCtx) || isa<LinkageSpecDecl>(DeclCtx)))
1983-
DeclCtx = DeclCtx->getParent();
1984-
1985-
if (!DeclCtx->isTranslationUnit()) {
2045+
NotForwardDeclarableReason NFDR =
2046+
isForwardDeclarable(RD, SemaSYCLRef, /*DiagForFreeFunction=*/true);
2047+
if (NFDR != NotForwardDeclarableReason::None) {
19862048
Diag.Report(PD->getLocation(),
19872049
diag::err_bad_free_function_kernel_param_type)
19882050
<< ParamTy;
@@ -4875,89 +4937,37 @@ class SYCLKernelNameTypeVisitor
48754937
}
48764938

48774939
void DiagnoseKernelNameType(const NamedDecl *DeclNamed) {
4878-
/*
4879-
This is a helper function which throws an error if the kernel name
4880-
declaration is:
4881-
* declared within namespace 'std' (at any level)
4882-
e.g., namespace std { namespace literals { class Whatever; } }
4883-
h.single_task<std::literals::Whatever>([]() {});
4884-
* declared within a function
4885-
e.g., void foo() { struct S { int i; };
4886-
h.single_task<S>([]() {}); }
4887-
* declared within another tag
4888-
e.g., struct S { struct T { int i } t; };
4889-
h.single_task<S::T>([]() {});
4890-
*/
4891-
4892-
if (const auto *ED = dyn_cast<EnumDecl>(DeclNamed)) {
4893-
if (!ED->isScoped() && !ED->isFixed()) {
4940+
if (!IsUnnamedKernel) {
4941+
NotForwardDeclarableReason NFDR = isForwardDeclarable(DeclNamed, S);
4942+
switch (NFDR) {
4943+
case NotForwardDeclarableReason::UnscopedEnum:
48944944
S.Diag(KernelInvocationFuncLoc, diag::err_sycl_kernel_incorrectly_named)
48954945
<< /* unscoped enum requires fixed underlying type */ 1
48964946
<< DeclNamed;
48974947
IsInvalid = true;
4898-
}
4899-
}
4900-
4901-
const DeclContext *DeclCtx = DeclNamed->getDeclContext();
4902-
if (DeclCtx && !IsUnnamedKernel) {
4903-
4904-
// Check if the kernel name declaration is declared within namespace
4905-
// "std" (at any level).
4906-
while (!DeclCtx->isTranslationUnit() && isa<NamespaceDecl>(DeclCtx)) {
4907-
const auto *NSDecl = cast<NamespaceDecl>(DeclCtx);
4908-
if (NSDecl->isStdNamespace()) {
4909-
S.Diag(KernelInvocationFuncLoc,
4910-
diag::err_invalid_std_type_in_sycl_kernel)
4911-
<< KernelNameType << DeclNamed;
4912-
IsInvalid = true;
4913-
return;
4914-
}
4915-
DeclCtx = DeclCtx->getParent();
4916-
}
4917-
4918-
// Check if the kernel name is a Tag declaration
4919-
// local to a non-namespace scope (i.e. Inside a function or within
4920-
// another Tag etc).
4921-
if (!DeclCtx->isTranslationUnit() && !isa<NamespaceDecl>(DeclCtx)) {
4922-
if (const auto *Tag = dyn_cast<TagDecl>(DeclNamed)) {
4923-
bool UnnamedLambdaUsed = Tag->getIdentifier() == nullptr;
4924-
4925-
if (UnnamedLambdaUsed) {
4926-
S.Diag(KernelInvocationFuncLoc,
4927-
diag::err_sycl_kernel_incorrectly_named)
4928-
<< /* unnamed type is invalid */ 2 << KernelNameType;
4929-
IsInvalid = true;
4930-
return;
4931-
}
4932-
4933-
// Diagnose used types without complete definition i.e.
4934-
// int main() {
4935-
// class KernelName1;
4936-
// parallel_for<class KernelName1>(..);
4937-
// }
4938-
// This case can only be diagnosed during host compilation because the
4939-
// integration header is required to distinguish between the invalid
4940-
// code (above) and the following valid code:
4941-
// int main() {
4942-
// parallel_for<class KernelName2>(..);
4943-
// }
4944-
// The device compiler forward declares both KernelName1 and
4945-
// KernelName2 in the integration header as ::KernelName1 and
4946-
// ::KernelName2. The problem with the former case is the additional
4947-
// declaration 'class KernelName1' in non-global scope. Lookup in this
4948-
// case will resolve to ::main::KernelName1 (instead of
4949-
// ::KernelName1). Since this is not visible to runtime code that
4950-
// submits kernels, this is invalid.
4951-
if (Tag->isCompleteDefinition() ||
4952-
S.getLangOpts().SYCLEnableIntHeaderDiags) {
4953-
S.Diag(KernelInvocationFuncLoc,
4954-
diag::err_sycl_kernel_incorrectly_named)
4955-
<< /* kernel name should be forward declarable at namespace
4956-
scope */
4957-
0 << KernelNameType;
4958-
IsInvalid = true;
4959-
}
4960-
}
4948+
return;
4949+
case NotForwardDeclarableReason::StdNamespace:
4950+
S.Diag(KernelInvocationFuncLoc,
4951+
diag::err_invalid_std_type_in_sycl_kernel)
4952+
<< KernelNameType << DeclNamed;
4953+
IsInvalid = true;
4954+
return;
4955+
case NotForwardDeclarableReason::UnnamedTag:
4956+
S.Diag(KernelInvocationFuncLoc, diag::err_sycl_kernel_incorrectly_named)
4957+
<< /* unnamed type is invalid */ 2 << KernelNameType;
4958+
IsInvalid = true;
4959+
return;
4960+
case NotForwardDeclarableReason::NotAtNamespaceScope:
4961+
S.Diag(KernelInvocationFuncLoc, diag::err_sycl_kernel_incorrectly_named)
4962+
<< /* kernel name should be forward declarable at namespace
4963+
scope */
4964+
0 << KernelNameType;
4965+
IsInvalid = true;
4966+
return;
4967+
case NotForwardDeclarableReason::None:
4968+
default:
4969+
// Do nothing, we're fine.
4970+
break;
49614971
}
49624972
}
49634973
}

clang/test/SemaSYCL/kernelname-enum.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -fsyntax-only -sycl-std=2020 -verify %s
1+
// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -fsyntax-only -sycl-std=2020 -fno-sycl-unnamed-lambda -verify %s
22

33
// This test verifies that kernel names containing unscoped enums are diagnosed correctly.
44

0 commit comments

Comments
 (0)