@@ -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 }
0 commit comments