diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 1e89a6805ce9c6..0e07c5d6ce8fba 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -3360,6 +3360,16 @@ class ASTContext : public RefCountedBase { /// this function. void registerSYCLEntryPointFunction(FunctionDecl *FD); + /// Given a type used as a SYCL kernel name, returns a reference to the + /// metadata generated from the corresponding SYCL kernel entry point. + /// Aborts if the provided type is not a registered SYCL kernel name. + const SYCLKernelInfo &getSYCLKernelInfo(QualType T) const; + + /// Returns a pointer to the metadata generated from the corresponding + /// SYCLkernel entry point if the provided type corresponds to a registered + /// SYCL kernel name. Returns a null pointer otherwise. + const SYCLKernelInfo *findSYCLKernelInfo(QualType T) const; + //===--------------------------------------------------------------------===// // Statistics //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index e51a74655dd5ec..5039c20d8b73be 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1516,11 +1516,22 @@ def SYCLKernel : InheritableAttr { def SYCLKernelEntryPoint : InheritableAttr { let Spellings = [Clang<"sycl_kernel_entry_point">]; - let Args = [TypeArgument<"KernelName">]; + let Args = [ + // KernelName is required and specifies the kernel name type. + TypeArgument<"KernelName">, + // InvalidAttr is a fake argument used to track whether the + // semantic requirements of the attribute have been satisified. + // A fake argument is used to enable serialization support. + DefaultBoolArgument<"Invalid", /*default=*/0, /*fake=*/1> + ]; let Subjects = SubjectList<[Function], ErrorDiag>; let TemplateDependent = 1; let LangOpts = [SYCLHost, SYCLDevice]; let Documentation = [SYCLKernelEntryPointDocs]; + let AdditionalMembers = [{ + void setInvalidAttr() { invalid = true; } + bool isInvalidAttr() const { return invalid; } + }]; } def SYCLSpecialClass: InheritableAttr { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b8d702e41aa0bb..506fe38eb882b2 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -475,7 +475,7 @@ not first appear on a declaration that follows a definition of the function. The attribute only appertains to functions and only those that meet the following requirements. -* Has a ``void`` return type. +* Has a non-deduced ``void`` return type. * Is not a non-static member function, constructor, or destructor. * Is not a C variadic function. * Is not a coroutine. diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 3ac490d30371b1..594e99a19b64d6 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -648,6 +648,7 @@ def PoundPragmaMessage : DiagGroup<"#pragma-messages">, def : DiagGroup<"redundant-decls">; def RedeclaredClassMember : DiagGroup<"redeclared-class-member">; def GNURedeclaredEnum : DiagGroup<"gnu-redeclared-enum">; +def RedundantAttribute : DiagGroup<"redundant-attribute">; def RedundantMove : DiagGroup<"redundant-move">; def Register : DiagGroup<"register", [DeprecatedRegister]>; def ReturnTypeCLinkage : DiagGroup<"return-type-c-linkage">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ab2d6237c1cab8..d4e897868f1a9a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12429,6 +12429,33 @@ def err_sycl_special_type_num_init_method : Error< "types with 'sycl_special_class' attribute must have one and only one '__init' " "method defined">; +// SYCL kernel entry point diagnostics +def err_sycl_entry_point_invalid : Error< + "'sycl_kernel_entry_point' attribute cannot be applied to a" + " %select{non-static member function|variadic function|deleted function|" + "defaulted function|constexpr function|consteval function|" + "function declared with the 'noreturn' attribute|coroutine}0">; +def err_sycl_entry_point_invalid_redeclaration : Error< + "'sycl_kernel_entry_point' kernel name argument does not match prior" + " declaration%diff{: $ vs $|}0,1">; +def err_sycl_kernel_name_conflict : Error< + "'sycl_kernel_entry_point' kernel name argument conflicts with a previous" + " declaration">; +def warn_sycl_kernel_name_not_a_class_type : Warning< + "%0 is not a valid SYCL kernel name type; a non-union class type is required">, + InGroup>, DefaultError; +def warn_sycl_entry_point_redundant_declaration : Warning< + "redundant 'sycl_kernel_entry_point' attribute">, InGroup; +def err_sycl_entry_point_after_definition : Error< + "'sycl_kernel_entry_point' attribute cannot be added to a function after the" + " function is defined">; +def err_sycl_entry_point_return_type : Error< + "'sycl_kernel_entry_point' attribute only applies to functions with a" + " 'void' return type">; +def err_sycl_entry_point_deduced_return_type : Error< + "'sycl_kernel_entry_point' attribute only applies to functions with a" + " non-deduced 'void' return type">; + def warn_cuda_maxclusterrank_sm_90 : Warning< "maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring " "%1 attribute">, InGroup; diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h index c9f3358124eda7..5bb0de40c886c7 100644 --- a/clang/include/clang/Sema/SemaSYCL.h +++ b/clang/include/clang/Sema/SemaSYCL.h @@ -63,6 +63,8 @@ class SemaSYCL : public SemaBase { void handleKernelAttr(Decl *D, const ParsedAttr &AL); void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL); + + void CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD); }; } // namespace clang diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index b10513f49a8d16..8f04b58841964a 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -14502,6 +14502,19 @@ void ASTContext::registerSYCLEntryPointFunction(FunctionDecl *FD) { std::make_pair(KernelNameType, BuildSYCLKernelInfo(KernelNameType, FD))); } +const SYCLKernelInfo &ASTContext::getSYCLKernelInfo(QualType T) const { + CanQualType KernelNameType = getCanonicalType(T); + return SYCLKernels.at(KernelNameType); +} + +const SYCLKernelInfo *ASTContext::findSYCLKernelInfo(QualType T) const { + CanQualType KernelNameType = getCanonicalType(T); + auto IT = SYCLKernels.find(KernelNameType); + if (IT != SYCLKernels.end()) + return &IT->second; + return nullptr; +} + OMPTraitInfo &ASTContext::getNewOMPTraitInfo() { OMPTraitInfoVector.emplace_back(new OMPTraitInfo()); return *OMPTraitInfoVector.back(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4001c4d263f1d2..75920052c4f0cc 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -52,6 +52,7 @@ #include "clang/Sema/SemaOpenMP.h" #include "clang/Sema/SemaPPC.h" #include "clang/Sema/SemaRISCV.h" +#include "clang/Sema/SemaSYCL.h" #include "clang/Sema/SemaSwift.h" #include "clang/Sema/SemaWasm.h" #include "clang/Sema/Template.h" @@ -2923,7 +2924,7 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) { AttrVec &NewAttributes = New->getAttrs(); for (unsigned I = 0, E = NewAttributes.size(); I != E;) { - const Attr *NewAttribute = NewAttributes[I]; + Attr *NewAttribute = NewAttributes[I]; if (isa(NewAttribute) || isa(NewAttribute)) { if (FunctionDecl *FD = dyn_cast(New)) { @@ -3018,6 +3019,16 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) { // declarations after definitions. ++I; continue; + } else if (isa(NewAttribute)) { + // Elevate latent uses of the sycl_kernel_entry_point attribute to an + // error since the definition will have already been created without + // the semantic effects of the attribute having been applied. + S.Diag(NewAttribute->getLocation(), + diag::err_sycl_entry_point_after_definition); + S.Diag(Def->getLocation(), diag::note_previous_definition); + cast(NewAttribute)->setInvalidAttr(); + ++I; + continue; } S.Diag(NewAttribute->getLocation(), @@ -12142,8 +12153,8 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, if (LangOpts.OpenMP) OpenMP().ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(NewFD); - if (LangOpts.isSYCL() && NewFD->hasAttr()) - getASTContext().registerSYCLEntryPointFunction(NewFD); + if (NewFD->hasAttr()) + SYCL().CheckSYCLEntryPointFunctionDecl(NewFD); // Semantic checking for this function declaration (in isolation). @@ -15975,6 +15986,25 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, CheckCoroutineWrapper(FD); } + // Diagnose invalid SYCL kernel entry point function declarations. + if (FD && !FD->isInvalidDecl() && FD->hasAttr()) { + SYCLKernelEntryPointAttr *SKEPAttr = + FD->getAttr(); + if (FD->isDefaulted()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*defaulted function*/ 3; + SKEPAttr->setInvalidAttr(); + } else if (FD->isDeleted()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*deleted function*/ 2; + SKEPAttr->setInvalidAttr(); + } else if (FSI->isCoroutine()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*coroutine*/ 7; + SKEPAttr->setInvalidAttr(); + } + } + { // Do not call PopExpressionEvaluationContext() if it is a lambda because // one is already popped when finishing the lambda in BuildLambdaExpr(). diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index a67c0b2b367d1a..f2c3a816b3b5d3 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -24,6 +24,7 @@ #include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaOpenMP.h" +#include "clang/Sema/SemaSYCL.h" #include "clang/Sema/Template.h" #include "llvm/ADT/STLExtras.h" #include @@ -1948,6 +1949,10 @@ ExprResult Sema::BuildCaptureInit(const Capture &Cap, ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) { LambdaScopeInfo LSI = *cast(FunctionScopes.back()); + + if (LSI.CallOperator->hasAttr()) + SYCL().CheckSYCLEntryPointFunctionDecl(LSI.CallOperator); + ActOnFinishFunctionBody(LSI.CallOperator, Body); return BuildLambdaExpr(StartLoc, Body->getEndLoc(), &LSI); diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index d4fddeb01d0fc8..ce53990fdcb18f 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -10,7 +10,9 @@ #include "clang/Sema/SemaSYCL.h" #include "clang/AST/Mangle.h" +#include "clang/AST/SYCLKernelInfo.h" #include "clang/AST/TypeOrdering.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Sema/Attr.h" #include "clang/Sema/ParsedAttr.h" #include "clang/Sema/Sema.h" @@ -206,3 +208,157 @@ void SemaSYCL::handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL) { D->addAttr(::new (SemaRef.Context) SYCLKernelEntryPointAttr(SemaRef.Context, AL, TSI)); } + +// Given a potentially qualified type, SourceLocationForUserDeclaredType() +// returns the source location of the canonical declaration of the unqualified +// desugared user declared type, if any. For non-user declared types, an +// invalid source location is returned. The intended usage of this function +// is to identify an appropriate source location, if any, for a +// "entity declared here" diagnostic note. +static SourceLocation SourceLocationForUserDeclaredType(QualType QT) { + SourceLocation Loc; + const Type *T = QT->getUnqualifiedDesugaredType(); + if (const TagType *TT = dyn_cast(T)) + Loc = TT->getDecl()->getLocation(); + else if (const ObjCInterfaceType *ObjCIT = dyn_cast(T)) + Loc = ObjCIT->getDecl()->getLocation(); + return Loc; +} + +static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc, + QualType KernelName) { + assert(!KernelName->isDependentType()); + + if (!KernelName->isStructureOrClassType()) { + // SYCL 2020 section 5.2, "Naming of kernels", only requires that the + // kernel name be a C++ typename. However, the definition of "kernel name" + // in the glossary states that a kernel name is a class type. Neither + // section explicitly states whether the kernel name type can be + // cv-qualified. For now, kernel name types are required to be class types + // and that they may be cv-qualified. The following issue requests + // clarification from the SYCL WG. + // https://github.com/KhronosGroup/SYCL-Docs/issues/568 + S.Diag(Loc, diag::warn_sycl_kernel_name_not_a_class_type) << KernelName; + SourceLocation DeclTypeLoc = SourceLocationForUserDeclaredType(KernelName); + if (DeclTypeLoc.isValid()) + S.Diag(DeclTypeLoc, diag::note_entity_declared_at) << KernelName; + return true; + } + + return false; +} + +void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) { + // Ensure that all attributes present on the declaration are consistent + // and warn about any redundant ones. + SYCLKernelEntryPointAttr *SKEPAttr = nullptr; + for (auto *SAI : FD->specific_attrs()) { + if (!SKEPAttr) { + SKEPAttr = SAI; + continue; + } + if (!getASTContext().hasSameType(SAI->getKernelName(), + SKEPAttr->getKernelName())) { + Diag(SAI->getLocation(), diag::err_sycl_entry_point_invalid_redeclaration) + << SAI->getKernelName() << SKEPAttr->getKernelName(); + Diag(SKEPAttr->getLocation(), diag::note_previous_attribute); + SAI->setInvalidAttr(); + } else { + Diag(SAI->getLocation(), + diag::warn_sycl_entry_point_redundant_declaration); + Diag(SKEPAttr->getLocation(), diag::note_previous_attribute); + } + } + assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute"); + + // Ensure the kernel name type is valid. + if (!SKEPAttr->getKernelName()->isDependentType() && + CheckSYCLKernelName(SemaRef, SKEPAttr->getLocation(), + SKEPAttr->getKernelName())) + SKEPAttr->setInvalidAttr(); + + // Ensure that an attribute present on the previous declaration + // matches the one on this declaration. + FunctionDecl *PrevFD = FD->getPreviousDecl(); + if (PrevFD && !PrevFD->isInvalidDecl()) { + const auto *PrevSKEPAttr = PrevFD->getAttr(); + if (PrevSKEPAttr && !PrevSKEPAttr->isInvalidAttr()) { + if (!getASTContext().hasSameType(SKEPAttr->getKernelName(), + PrevSKEPAttr->getKernelName())) { + Diag(SKEPAttr->getLocation(), + diag::err_sycl_entry_point_invalid_redeclaration) + << SKEPAttr->getKernelName() << PrevSKEPAttr->getKernelName(); + Diag(PrevSKEPAttr->getLocation(), diag::note_previous_decl) << PrevFD; + SKEPAttr->setInvalidAttr(); + } + } + } + + if (const auto *MD = dyn_cast(FD)) { + if (!MD->isStatic()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*non-static member function*/ 0; + SKEPAttr->setInvalidAttr(); + } + } + + if (FD->isVariadic()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*variadic function*/ 1; + SKEPAttr->setInvalidAttr(); + } + + if (FD->isDefaulted()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*defaulted function*/ 3; + SKEPAttr->setInvalidAttr(); + } else if (FD->isDeleted()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*deleted function*/ 2; + SKEPAttr->setInvalidAttr(); + } + + if (FD->isConsteval()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*consteval function*/ 5; + SKEPAttr->setInvalidAttr(); + } else if (FD->isConstexpr()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*constexpr function*/ 4; + SKEPAttr->setInvalidAttr(); + } + + if (FD->isNoReturn()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid) + << /*function declared with the 'noreturn' attribute*/ 6; + SKEPAttr->setInvalidAttr(); + } + + if (FD->getReturnType()->isUndeducedType()) { + Diag(SKEPAttr->getLocation(), + diag::err_sycl_entry_point_deduced_return_type); + SKEPAttr->setInvalidAttr(); + } else if (!FD->getReturnType()->isDependentType() && + !FD->getReturnType()->isVoidType()) { + Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_return_type); + SKEPAttr->setInvalidAttr(); + } + + if (!FD->isInvalidDecl() && !FD->isTemplated() && + !SKEPAttr->isInvalidAttr()) { + const SYCLKernelInfo *SKI = + getASTContext().findSYCLKernelInfo(SKEPAttr->getKernelName()); + if (SKI) { + if (!declaresSameEntity(FD, SKI->getKernelEntryPointDecl())) { + // FIXME: This diagnostic should include the origin of the kernel + // FIXME: names; not just the locations of the conflicting declarations. + Diag(FD->getLocation(), diag::err_sycl_kernel_name_conflict); + Diag(SKI->getKernelEntryPointDecl()->getLocation(), + diag::note_previous_declaration); + SKEPAttr->setInvalidAttr(); + } + } else { + getASTContext().registerSYCLEntryPointFunction(FD); + } + } +} diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 8c60e85c93d70e..dee5169ae5723a 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1134,8 +1134,19 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { // the presence of a sycl_kernel_entry_point attribute, register it so that // associated metadata is recreated. if (FD->hasAttr()) { + auto *SKEPAttr = FD->getAttr(); ASTContext &C = Reader.getContext(); - C.registerSYCLEntryPointFunction(FD); + const SYCLKernelInfo *SKI = C.findSYCLKernelInfo(SKEPAttr->getKernelName()); + if (SKI) { + if (!declaresSameEntity(FD, SKI->getKernelEntryPointDecl())) { + Reader.Diag(FD->getLocation(), diag::err_sycl_kernel_name_conflict); + Reader.Diag(SKI->getKernelEntryPointDecl()->getLocation(), + diag::note_previous_declaration); + SKEPAttr->setInvalidAttr(); + } + } else { + C.registerSYCLEntryPointFunction(FD); + } } } diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp b/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp index c351f3b7d03eab..0189cf0402d3a3 100644 --- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp +++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp @@ -107,14 +107,29 @@ void skep5>(long) { // CHECK: | |-TemplateArgument type 'long' // CHECK: | `-SYCLKernelEntryPointAttr {{.*}} KN<5, 2> +// FIXME: C++23 [temp.expl.spec]p12 states: +// FIXME: ... Similarly, attributes appearing in the declaration of a template +// FIXME: have no effect on an explicit specialization of that template. +// FIXME: Clang currently instantiates a function template specialization from +// FIXME: the function template declaration and links it as a previous +// FIXME: declaration of an explicit specialization. The instantiated +// FIXME: declaration includes attributes instantiated from the function +// FIXME: template declaration. When the instantiated declaration and the +// FIXME: explicit specialization both specify a sycl_kernel_entry_point +// FIXME: attribute with different kernel name types, a spurious diagnostic +// FIXME: is issued. The following test case is incorrectly diagnosed as +// FIXME: having conflicting kernel name types (KN<5,3> vs the incorrectly +// FIXME: inherited KN<5,-1>). +#if 0 template<> [[clang::sycl_kernel_entry_point(KN<5,3>)]] void skep5>(long long) { } -// CHECK: |-FunctionDecl {{.*}} prev {{.*}} skep5 'void (long long)' explicit_specialization -// CHECK-NEXT: | |-TemplateArgument type 'KN<5, -1>' -// CHECK: | |-TemplateArgument type 'long long' -// CHECK: | `-SYCLKernelEntryPointAttr {{.*}} KN<5, 3> +// FIXME-CHECK: |-FunctionDecl {{.*}} prev {{.*}} skep5 'void (long long)' explicit_specialization +// FIXME-CHECK-NEXT: | |-TemplateArgument type 'KN<5, -1>' +// FIXME-CHECK: | |-TemplateArgument type 'long long' +// FIXME-CHECK: | `-SYCLKernelEntryPointAttr {{.*}} KN<5, 3> +#endif template void skep5>(int); // Checks are located with the primary template declaration above. diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp new file mode 100644 index 00000000000000..5b3cf9853173dd --- /dev/null +++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp @@ -0,0 +1,352 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-device -verify %s + +// These tests validate appertainment for the sycl_kernel_entry_point attribute. + +#if __cplusplus >= 202002L +// Mock coroutine support. +namespace std { + +template +struct coroutine_handle { + template + coroutine_handle(const coroutine_handle&); + static coroutine_handle from_address(void *addr); +}; + +template +struct coroutine_traits { + struct suspend_never { + bool await_ready() const noexcept; + void await_suspend(std::coroutine_handle<>) const noexcept; + void await_resume() const noexcept; + }; + struct promise_type { + void get_return_object() noexcept; + suspend_never initial_suspend() const noexcept; + suspend_never final_suspend() const noexcept; + void return_void() noexcept; + void unhandled_exception() noexcept; + }; +}; + +} +#endif + +// A unique kernel name type is required for each declared kernel entry point. +template struct KN; + + +//////////////////////////////////////////////////////////////////////////////// +// Valid declarations. +//////////////////////////////////////////////////////////////////////////////// + +// Function declaration with GNU attribute spelling +__attribute__((sycl_kernel_entry_point(KN<1>))) +void ok1(); + +// Function declaration with Clang attribute spelling. +[[clang::sycl_kernel_entry_point(KN<2>)]] +void ok2(); + +// Function definition. +[[clang::sycl_kernel_entry_point(KN<3>)]] +void ok3() {} + +// Function template definition. +template +[[clang::sycl_kernel_entry_point(KNT)]] +void ok4(T) {} + +// Function template explicit specialization. +template<> +[[clang::sycl_kernel_entry_point(KN<4,1>)]] +void ok4>(int) {} + +// Function template explicit instantiation. +template void ok4, long>(long); + +namespace NS { +// Function declaration at namespace scope. +[[clang::sycl_kernel_entry_point(KN<5>)]] +void ok5(); +} + +struct S6 { + // Static member function declaration. + [[clang::sycl_kernel_entry_point(KN<6>)]] + static void ok6(); +}; + +// Dependent hidden friend definition. +template +struct S7 { + [[clang::sycl_kernel_entry_point(KNT)]] + friend void ok7(S7) {} +}; +void test_ok7() { + ok7(S7>{}); +} + +// Non-dependent hidden friend definition. +struct S8Base {}; +template +struct S8 : S8Base { + [[clang::sycl_kernel_entry_point(KN<8>)]] + friend void ok8(const S8Base&) {} +}; +void test_ok8() { + ok8(S8{}); +} + +// The sycl_kernel_entry_point attribute must match across declarations and +// cannot be added for the first time after a definition. +[[clang::sycl_kernel_entry_point(KN<9>)]] +void ok9(); +[[clang::sycl_kernel_entry_point(KN<9>)]] +void ok9(); +[[clang::sycl_kernel_entry_point(KN<10>)]] +void ok10(); +void ok10() {} +void ok11(); +[[clang::sycl_kernel_entry_point(KN<11>)]] +void ok11() {} + +using VOID = void; +[[clang::sycl_kernel_entry_point(KN<12>)]] +VOID ok12(); +[[clang::sycl_kernel_entry_point(KN<13>)]] +const void ok13(); + +#if __cplusplus >= 202302L +auto ok14 = [] [[clang::sycl_kernel_entry_point(KN<14>)]] static -> void {}; +#endif + +template +struct S15 { + // Don't diagnose a dependent return type as a non-void type. + [[clang::sycl_kernel_entry_point(KNT)]] + static T ok15(); +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Invalid declarations. +//////////////////////////////////////////////////////////////////////////////// + +// The sycl_kernel_entry_point attribute cannot appertain to main() because +// main() has a non-void return type. However, if the requirement for a void +// return type were to be relaxed or if an allowance was made for main() to +// return void (as gcc allows in some modes and as has been proposed to WG21 +// on occassion), main() still can't function as a SYCL kernel entry point, +// so this test ensures such attempted uses of the attribute are rejected. +struct Smain; +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions with a 'void' return type}} +[[clang::sycl_kernel_entry_point(Smain)]] +int main(); + +template struct BADKN; + +struct B1 { + // Non-static data member declaration. + // expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} + [[clang::sycl_kernel_entry_point(BADKN<1>)]] + int bad1; +}; + +struct B2 { + // Static data member declaration. + // expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} + [[clang::sycl_kernel_entry_point(BADKN<2>)]] + static int bad2; +}; + +struct B3 { + // Non-static member function declaration. + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}} + [[clang::sycl_kernel_entry_point(BADKN<3>)]] + void bad3(); +}; + +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +namespace bad4 [[clang::sycl_kernel_entry_point(BADKN<4>)]] {} + +#if __cplusplus >= 202002L +// expected-error@+2 {{'sycl_kernel_entry_point' attribute only applies to functions}} +template +concept bad5 [[clang::sycl_kernel_entry_point(BADKN<5>)]] = true; +#endif + +// Type alias declarations. +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +typedef void bad6 [[clang::sycl_kernel_entry_point(BADKN<6>)]] (); +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +using bad7 [[clang::sycl_kernel_entry_point(BADKN<7>)]] = void(); +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +using bad8 [[clang::sycl_kernel_entry_point(BADKN<8>)]] = int; +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to types}} +using bad9 = int [[clang::sycl_kernel_entry_point(BADKN<9>)]]; +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to types}} +using bad10 = int() [[clang::sycl_kernel_entry_point(BADKN<10>)]]; + +// Variable declaration. +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +[[clang::sycl_kernel_entry_point(BADKN<11>)]] +int bad11; + +// Class declaration. +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +struct [[clang::sycl_kernel_entry_point(BADKN<12>)]] bad12; + +// Enumeration declaration. +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +enum [[clang::sycl_kernel_entry_point(BADKN<13>)]] bad13 {}; + +// Enumerator. +// expected-error@+2 {{'sycl_kernel_entry_point' attribute only applies to functions}} +enum { + bad14 [[clang::sycl_kernel_entry_point(BADKN<14>)]] +}; + +// Attribute added after the definition. +// expected-error@+3 {{'sycl_kernel_entry_point' attribute cannot be added to a function after the function is defined}} +// expected-note@+1 {{previous definition is here}} +void bad15() {} +[[clang::sycl_kernel_entry_point(BADKN<15>)]] +void bad15(); + +// The function must return void. +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions with a 'void' return type}} +[[clang::sycl_kernel_entry_point(BADKN<16>)]] +int bad16(); + +// Function parameters. +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +void bad17(void (fp [[clang::sycl_kernel_entry_point(BADKN<17>)]])()); + +// Function template parameters. +// FIXME: Clang currently ignores attributes that appear in template parameters +// FIXME: and the C++ standard is unclear regarding whether such attributes are +// FIXME: permitted. P3324 (Attributes for namespace aliases, template +// FIXME: parameters, and lambda captures) seeks to clarify the situation. +// FIXME-expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}} +template)]])()> +void bad18(); + +#if __cplusplus >= 202002L +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a coroutine}} +[[clang::sycl_kernel_entry_point(BADKN<19>)]] +void bad19() { + co_return; +} +#endif + +struct B20 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}} + [[clang::sycl_kernel_entry_point(BADKN<20>)]] + B20(); +}; + +struct B21 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}} + [[clang::sycl_kernel_entry_point(BADKN<21>)]] + ~B21(); +}; + +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a variadic function}} +[[clang::sycl_kernel_entry_point(BADKN<22>)]] +void bad22(...); + +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a deleted function}} +[[clang::sycl_kernel_entry_point(BADKN<23>)]] +void bad23() = delete; + +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a constexpr function}} +[[clang::sycl_kernel_entry_point(BADKN<24>)]] +constexpr void bad24() {} + +#if __cplusplus >= 202002L +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a consteval function}} +[[clang::sycl_kernel_entry_point(BADKN<25>)]] +consteval void bad25() {} +#endif + +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a function declared with the 'noreturn' attribute}} +[[clang::sycl_kernel_entry_point(BADKN<26>)]] +[[noreturn]] void bad26(); + +// expected-error@+3 {{attribute 'target' multiversioning cannot be combined with attribute 'sycl_kernel_entry_point'}} +__attribute__((target("avx"))) void bad27(); +[[clang::sycl_kernel_entry_point(BADKN<27>)]] +__attribute__((target("sse4.2"))) void bad27(); + +template +struct B28 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a deleted function}} + [[clang::sycl_kernel_entry_point(KNT)]] + friend void bad28() = delete; +}; + +#if __cplusplus >= 202002L +template +struct B29 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a defaulted function}} + [[clang::sycl_kernel_entry_point(KNT)]] + friend T operator==(B29, B29) = default; +}; +#endif + +#if __cplusplus >= 202002L +template +struct B30 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a coroutine}} + [[clang::sycl_kernel_entry_point(KNT)]] + friend void bad30() { co_return; } +}; +#endif + +template +struct B31 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a variadic function}} + [[clang::sycl_kernel_entry_point(KNT)]] + friend void bad31(...) {} +}; + +template +struct B32 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a constexpr function}} + [[clang::sycl_kernel_entry_point(KNT)]] + friend constexpr void bad32() {} +}; + +#if __cplusplus >= 202002L +template +struct B33 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a consteval function}} + [[clang::sycl_kernel_entry_point(KNT)]] + friend consteval void bad33() {} +}; +#endif + +template +struct B34 { + // expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a function declared with the 'noreturn' attribute}} + [[clang::sycl_kernel_entry_point(KNT)]] + [[noreturn]] friend void bad34() {} +}; + +#if __cplusplus >= 202302L +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}} +auto bad35 = [] [[clang::sycl_kernel_entry_point(BADKN<35>)]] -> void {}; +#endif + +#if __cplusplus >= 202302L +// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions with a non-deduced 'void' return type}} +auto bad36 = [] [[clang::sycl_kernel_entry_point(BADKN<36>)]] static {}; +#endif + +#if __cplusplus >= 202302L +// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a coroutine}} +auto bad37 = [] [[clang::sycl_kernel_entry_point(BADKN<37>)]] static -> void { co_return; }; +#endif diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp index c63d241163e618..14b5d2746631d6 100644 --- a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp +++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp @@ -120,18 +120,3 @@ template concept C = true; // expected-error@+1 {{expected a type}} [[clang::sycl_kernel_entry_point(C)]] void bad11(); #endif - -struct B12; // #B12-decl -// FIXME: C++23 [temp.expl.spec]p12 states: -// FIXME: ... Similarly, attributes appearing in the declaration of a template -// FIXME: have no effect on an explicit specialization of that template. -// FIXME: Clang currently instantiates and propagates attributes from a function -// FIXME: template to its explicit specializations resulting in the following -// FIXME: spurious error. -// expected-error@+4 {{incomplete type 'B12' named in nested name specifier}} -// expected-note@+5 {{in instantiation of function template specialization 'bad12' requested here}} -// expected-note@#B12-decl {{forward declaration of 'B12'}} -template -[[clang::sycl_kernel_entry_point(typename T::not_found)]] void bad12() {} -template<> -void bad12() {} diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp new file mode 100644 index 00000000000000..83c3e5ca267ab2 --- /dev/null +++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-module.cpp @@ -0,0 +1,104 @@ +// Test that SYCL kernel name conflicts that occur across module boundaries are +// properly diagnosed and that declarations are properly merged so that spurious +// conflicts are not reported. + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t \ +// RUN: -std=c++17 -fsycl-is-host %t/test.cpp -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t \ +// RUN: -std=c++17 -fsycl-is-device %t/test.cpp -verify + +#--- module.modulemap +module M1 { header "m1.h" } +module M2 { header "m2.h" } + + +#--- common.h +template struct KN; + +[[clang::sycl_kernel_entry_point(KN<1>)]] +void common_test1() {} + +template +[[clang::sycl_kernel_entry_point(T)]] +void common_test2() {} +template void common_test2>(); + + +#--- m1.h +#include "common.h" + +[[clang::sycl_kernel_entry_point(KN<3>)]] +void m1_test3() {} // << expected previous declaration note here. + +template +[[clang::sycl_kernel_entry_point(T)]] +void m1_test4() {} // << expected previous declaration note here. +template void m1_test4>(); + +[[clang::sycl_kernel_entry_point(KN<5>)]] +void m1_test5() {} // << expected previous declaration note here. + +template +[[clang::sycl_kernel_entry_point(T)]] +void m1_test6() {} // << expected previous declaration note here. +template void m1_test6>(); + + +#--- m2.h +#include "common.h" + +[[clang::sycl_kernel_entry_point(KN<3>)]] +void m2_test3() {} // << expected kernel name conflict here. + +template +[[clang::sycl_kernel_entry_point(T)]] +void m2_test4() {} // << expected kernel name conflict here. +template void m2_test4>(); + +[[clang::sycl_kernel_entry_point(KN<7>)]] +void m2_test7() {} // << expected previous declaration note here. + +template +[[clang::sycl_kernel_entry_point(T)]] +void m2_test8() {} // << expected previous declaration note here. +template void m2_test8>(); + + +#--- test.cpp +#include "m1.h" +#include "m2.h" + +// Expected diagnostics for m1_test3() and m2_test3(): +// expected-error@m2.h:4 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@m1.h:12 {{previous declaration is here}} + +// Expected diagnostics for m1_test4>() and m2_test4>(): +// expected-error@m2.h:8 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@m1.h:16 {{previous declaration is here}} + +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@m1.h:4 {{previous declaration is here}} +[[clang::sycl_kernel_entry_point(KN<5>)]] +void test5() {} + +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@m1.h:8 {{previous declaration is here}} +[[clang::sycl_kernel_entry_point(KN<6>)]] +void test6() {} + +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@m2.h:12 {{previous declaration is here}} +[[clang::sycl_kernel_entry_point(KN<7>)]] +void test7() {} + +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@m2.h:16 {{previous declaration is here}} +[[clang::sycl_kernel_entry_point(KN<8>)]] +void test8() {} + +void f() { + common_test1(); + common_test2>(); +} diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp new file mode 100644 index 00000000000000..0814d898d1c0e0 --- /dev/null +++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name-pch.cpp @@ -0,0 +1,36 @@ +// Test that SYCL kernel name conflicts that occur across PCH boundaries are +// properly diagnosed. + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -std=c++17 -fsycl-is-host -emit-pch -x c++-header \ +// RUN: %t/pch.h -o %t/pch.h.host.pch +// RUN: %clang_cc1 -std=c++17 -fsycl-is-host -verify \ +// RUN: -include-pch %t/pch.h.host.pch %t/test.cpp +// RUN: %clang_cc1 -std=c++17 -fsycl-is-device -emit-pch -x c++-header \ +// RUN: %t/pch.h -o %t/pch.h.device.pch +// RUN: %clang_cc1 -std=c++17 -fsycl-is-device -verify \ +// RUN: -include-pch %t/pch.h.device.pch %t/test.cpp + +#--- pch.h +template struct KN; + +[[clang::sycl_kernel_entry_point(KN<1>)]] +void pch_test1() {} // << expected previous declaration note here. + +template +[[clang::sycl_kernel_entry_point(T)]] +void pch_test2() {} // << expected previous declaration note here. +template void pch_test2>(); + + +#--- test.cpp +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@pch.h:4 {{previous declaration is here}} +[[clang::sycl_kernel_entry_point(KN<1>)]] +void test1() {} + +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@pch.h:8 {{previous declaration is here}} +[[clang::sycl_kernel_entry_point(KN<2>)]] +void test2() {} diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp new file mode 100644 index 00000000000000..78dd89696c02d4 --- /dev/null +++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp @@ -0,0 +1,118 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s + +// These tests validate that the kernel name type argument provided to the +// sycl_kernel_entry_point attribute meets the requirements of a SYCL kernel +// name as described in section 5.2, "Naming of kernels", of the SYCL 2020 +// specification. + +struct S1; +// expected-warning@+3 {{redundant 'sycl_kernel_entry_point' attribute}} +// expected-note@+1 {{previous attribute is here}} +[[clang::sycl_kernel_entry_point(S1), + clang::sycl_kernel_entry_point(S1)]] +void ok1(); + +// expected-error@+1 {{'int' is not a valid SYCL kernel name type; a non-union class type is required}} +[[clang::sycl_kernel_entry_point(int)]] void bad2(); + +// expected-error@+1 {{'int ()' is not a valid SYCL kernel name type; a non-union class type is required}} +[[clang::sycl_kernel_entry_point(int())]] void bad3(); + +// expected-error@+1 {{'int (*)()' is not a valid SYCL kernel name type; a non-union class type is required}} +[[clang::sycl_kernel_entry_point(int(*)())]] void bad4(); + +// expected-error@+1 {{'int (&)()' is not a valid SYCL kernel name type; a non-union class type is required}} +[[clang::sycl_kernel_entry_point(int(&)())]] void bad5(); + +// expected-error@+1 {{'decltype(nullptr)' (aka 'std::nullptr_t') is not a valid SYCL kernel name type; a non-union class type is required}} +[[clang::sycl_kernel_entry_point(decltype(nullptr))]] void bad6(); + +union U7; // #U7-decl +// expected-error@+2 {{'U7' is not a valid SYCL kernel name type; a non-union class type is required}} +// expected-note@#U7-decl {{'U7' declared here}} +[[clang::sycl_kernel_entry_point(U7)]] void bad7(); + +enum E8 {}; // #E8-decl +// expected-error@+2 {{'E8' is not a valid SYCL kernel name type; a non-union class type is required}} +// expected-note@#E8-decl {{'E8' declared here}} +[[clang::sycl_kernel_entry_point(E8)]] void bad8(); + +enum E9 : int; // #E9-decl +// expected-error@+2 {{'E9' is not a valid SYCL kernel name type; a non-union class type is required}} +// expected-note@#E9-decl {{'E9' declared here}} +[[clang::sycl_kernel_entry_point(E9)]] void bad9(); + +struct B10 { + struct MS; +}; +// FIXME-expected-error@+1 {{'sycl_kernel_entry_point' attribute argument must be a forward declarable class type}} +[[clang::sycl_kernel_entry_point(B10::MS)]] void bad10(); + +struct B11 { + struct MS; +}; +// FIXME-expected-error@+3 {{'sycl_kernel_entry_point' attribute argument must be a forward declarable class type}} +template +[[clang::sycl_kernel_entry_point(typename T::MS)]] void bad11() {} +template void bad11(); + +template +[[clang::sycl_kernel_entry_point(T)]] void bad12(); +void f12() { + // FIXME-expected-error@+2 {{'sycl_kernel_entry_point' attribute argument must be a forward declarable class type}} + struct LS; + bad12(); +} + +struct B13_1; +struct B13_2; +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument does not match prior declaration: 'B13_2' vs 'B13_1'}} +// expected-note@+1 {{'bad13' declared here}} +[[clang::sycl_kernel_entry_point(B13_1)]] void bad13(); +[[clang::sycl_kernel_entry_point(B13_2)]] void bad13() {} + +struct B14_1; +struct B14_2; +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument does not match prior declaration: 'B14_2' vs 'B14_1'}} +// expected-note@+1 {{previous attribute is here}} +[[clang::sycl_kernel_entry_point(B14_1), + clang::sycl_kernel_entry_point(B14_2)]] +void bad14(); + +struct B15; +// expected-error@+3 {{'sycl_kernel_entry_point' kernel name argument conflicts with a previous declaration}} +// expected-note@+1 {{previous declaration is here}} +[[clang::sycl_kernel_entry_point(B15)]] void bad15_1(); +[[clang::sycl_kernel_entry_point(B15)]] void bad15_2(); + +struct B16_1; +struct B16_2; +// expected-error@+4 {{'sycl_kernel_entry_point' kernel name argument does not match prior declaration: 'B16_2' vs 'B16_1'}} +// expected-note@+1 {{'bad16' declared here}} +[[clang::sycl_kernel_entry_point(B16_1)]] void bad16(); +void bad16(); // The attribute from the previous declaration is inherited. +[[clang::sycl_kernel_entry_point(B16_2)]] void bad16(); + +template +struct B17 { + // expected-error@+1 {{'int' is not a valid SYCL kernel name type; a non-union class type is required}} + [[clang::sycl_kernel_entry_point(int)]] + static void bad17(); +}; + +template +struct B18 { + // expected-error@+1 {{'int' is not a valid SYCL kernel name type; a non-union class type is required}} + [[clang::sycl_kernel_entry_point(int)]] + friend void bad18() {} +}; + +template +struct B19 { + // expected-error@+1 {{'int' is not a valid SYCL kernel name type; a non-union class type is required}} + [[clang::sycl_kernel_entry_point(KNT)]] + friend void bad19() {} +}; +// expected-note@+1 {{in instantiation of template class 'B19' requested here}} +B19 b19; diff --git a/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp new file mode 100644 index 00000000000000..4c615704196299 --- /dev/null +++ b/clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s + +// These tests are intended to validate that a sycl_kernel_entry_point attribute +// appearing in the declaration of a function template does not affect overload +// resolution or cause spurious errors during overload resolution due to either +// a substitution failure in the attribute argument or a semantic check of the +// attribute during instantiation of a specialization unless that specialization +// is selected by overload resolution. + +// FIXME: C++23 [temp.expl.spec]p12 states: +// FIXME: ... Similarly, attributes appearing in the declaration of a template +// FIXME: have no effect on an explicit specialization of that template. +// FIXME: Clang currently instantiates and propagates attributes from a function +// FIXME: template to its explicit specializations resulting in the following +// FIXME: spurious error. +struct S1; // #S1-decl +// expected-error@+4 {{incomplete type 'S1' named in nested name specifier}} +// expected-note@+5 {{in instantiation of function template specialization 'ok1' requested here}} +// expected-note@#S1-decl {{forward declaration of 'S1'}} +template +[[clang::sycl_kernel_entry_point(typename T::invalid)]] void ok1() {} +template<> +void ok1() {} +void test_ok1() { + // ok1() is not a call to a SYCL kernel entry point function. + ok1(); +} + +// FIXME: The sycl_kernel_entry_point attribute should not be instantiated +// FIXME: until after overload resolution has completed. +struct S2; // #S2-decl +// expected-error@+6 {{incomplete type 'S2' named in nested name specifier}} +// expected-note@+10 {{in instantiation of function template specialization 'ok2' requested here}} +// expected-note@#S2-decl {{forward declaration of 'S2'}} +template +[[clang::sycl_kernel_entry_point(T)]] void ok2(int) {} +template +[[clang::sycl_kernel_entry_point(typename T::invalid)]] void ok2(long) {} +void test_ok2() { + // ok2(int) is a better match and is therefore selected by overload + // resolution; the attempted instantiation of ok2(long) should not produce + // an error for the substitution failure into the attribute argument. + ok2(2); +} + +// FIXME: The sycl_kernel_entry_point attribute should not be instantiated +// FIXME: until after overload resolution has completed. +struct S3; +struct Select3 { + using bad_type = int; + using good_type = S3; +}; +// expected-error@+5 {{'typename Select3::bad_type' (aka 'int') is not a valid SYCL kernel name type; a non-union class type is required}} +// expected-note@+9 {{in instantiation of function template specialization 'ok3' requested here}} +template +[[clang::sycl_kernel_entry_point(typename T::good_type)]] void ok3(int) {} +template +[[clang::sycl_kernel_entry_point(typename T::bad_type)]] void ok3(long) {} +void test_ok3() { + // ok3(int) is a better match and is therefore selected by overload + // resolution; the attempted instantiation of ok3(long) should not produce + // an error for the invalid kernel name provided as the attribute argument. + ok3(2); +}