13 changes: 13 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14437,6 +14437,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();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5441,6 +5441,31 @@ BlockDecl *BlockDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) BlockDecl(nullptr, SourceLocation());
}


OutlinedFunctionDecl::OutlinedFunctionDecl(DeclContext *DC, unsigned NumParams)
: Decl(OutlinedFunction, DC, SourceLocation()), DeclContext(OutlinedFunction),
NumParams(NumParams), BodyAndNothrow(nullptr, false) {}

OutlinedFunctionDecl *OutlinedFunctionDecl::Create(ASTContext &C, DeclContext *DC,
unsigned NumParams) {
return new (C, DC, additionalSizeToAlloc<ImplicitParamDecl *>(NumParams))
OutlinedFunctionDecl(DC, NumParams);
}

OutlinedFunctionDecl *OutlinedFunctionDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID,
unsigned NumParams) {
return new (C, ID, additionalSizeToAlloc<ImplicitParamDecl *>(NumParams))
OutlinedFunctionDecl(nullptr, NumParams);
}

Stmt *OutlinedFunctionDecl::getBody() const { return BodyAndNothrow.getPointer(); }
void OutlinedFunctionDecl::setBody(Stmt *B) { BodyAndNothrow.setPointer(B); }

bool OutlinedFunctionDecl::isNothrow() const { return BodyAndNothrow.getInt(); }
void OutlinedFunctionDecl::setNothrow(bool Nothrow) { BodyAndNothrow.setInt(Nothrow); }


CapturedDecl::CapturedDecl(DeclContext *DC, unsigned NumParams)
: Decl(Captured, DC, SourceLocation()), DeclContext(Captured),
NumParams(NumParams), ContextParam(0), BodyAndNothrow(nullptr, false) {}
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case PragmaDetectMismatch:
case Block:
case Captured:
case OutlinedFunction:
case TranslationUnit:
case ExternCContext:
case Decomposition:
Expand Down Expand Up @@ -1240,6 +1241,8 @@ template <class T> static Decl *getNonClosureContext(T *D) {
return getNonClosureContext(BD->getParent());
if (auto *CD = dyn_cast<CapturedDecl>(D))
return getNonClosureContext(CD->getParent());
if (auto *OFD = dyn_cast<OutlinedFunctionDecl>(D))
return getNonClosureContext(OFD->getParent());
return nullptr;
}

Expand Down Expand Up @@ -1432,6 +1435,7 @@ DeclContext *DeclContext::getPrimaryContext() {
case Decl::TopLevelStmt:
case Decl::Block:
case Decl::Captured:
case Decl::OutlinedFunction:
case Decl::OMPDeclareReduction:
case Decl::OMPDeclareMapper:
case Decl::RequiresExprBody:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/Type.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/LLVM.h"
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
Expand Down Expand Up @@ -583,6 +584,10 @@ void StmtPrinter::VisitCapturedStmt(CapturedStmt *Node) {
PrintStmt(Node->getCapturedDecl()->getBody());
}

void StmtPrinter::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *Node) {
PrintStmt(Node->getOutlinedFunctionDecl()->getBody());
}

void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) {
Indent() << "@try";
if (auto *TS = dyn_cast<CompoundStmt>(Node->getTryBody())) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,10 @@ void StmtProfiler::VisitCapturedStmt(const CapturedStmt *S) {
VisitStmt(S);
}

void StmtProfiler::VisitSYCLKernelCallStmt(const SYCLKernelCallStmt *S) {
VisitStmt(S);
}

void StmtProfiler::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) {
VisitStmt(S);
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
case Decl::Friend:
case Decl::FriendTemplate:
case Decl::Block:
case Decl::OutlinedFunction:
case Decl::Captured:
case Decl::UsingShadow:
case Decl::ConstructorUsingShadow:
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
case Stmt::DefaultStmtClass:
case Stmt::CaseStmtClass:
case Stmt::SEHLeaveStmtClass:
case Stmt::SYCLKernelCallStmtClass:
llvm_unreachable("should have emitted these statements as simple");

#define STMT(Type, Base)
Expand Down Expand Up @@ -495,6 +496,23 @@ bool CodeGenFunction::EmitSimpleStmt(const Stmt *S,
case Stmt::SEHLeaveStmtClass:
EmitSEHLeaveStmt(cast<SEHLeaveStmt>(*S));
break;
case Stmt::SYCLKernelCallStmtClass:
// SYCL kernel call statements are generated as wrappers around the body
// of functions declared with the sycl_kernel_entry_point attribute. Such
// functions are used to specify how a SYCL kernel (a function object) is
// to be invoked; the SYCL kernel call statement contains a transformed
// variation of the function body and is used to generate a SYCL kernel
// caller function; a function that serves as the device side entry point
// used to execute the SYCL kernel. The sycl_kernel_entry_point attributed
// function is invoked by host code in order to trigger emission of the
// device side SYCL kernel caller function and to generate metadata needed
// by SYCL run-time library implementations; the function is otherwise
// intended to have no effect. As such, the function body is not evaluated
// as part of the invocation during host compilation (and the function
// should not be called or emitted during device compilation); the SYCL
// kernel call statement is thus handled as a null statement for the
// purpose of code generation.
break;
}
return true;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/Type.h"
#include "clang/Basic/ABI.h"
#include "clang/Basic/CapturedStmt.h"
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/JumpDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/BitVector.h"
Expand Down
47 changes: 46 additions & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "clang/Sema/SemaPPC.h"
#include "clang/Sema/SemaRISCV.h"
#include "clang/Sema/SemaSwift.h"
#include "clang/Sema/SemaSYCL.h"
#include "clang/Sema/SemaWasm.h"
#include "clang/Sema/Template.h"
#include "llvm/ADT/STLForwardCompat.h"
Expand Down Expand Up @@ -3020,6 +3021,16 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
// declarations after definitions.
++I;
continue;
} else if (isa<SYCLKernelEntryPointAttr>(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);
New->setInvalidDecl();
++I;
continue;
}

S.Diag(NewAttribute->getLocation(),
Expand Down Expand Up @@ -12095,7 +12106,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
OpenMP().ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(NewFD);

if (LangOpts.isSYCL() && NewFD->hasAttr<SYCLKernelEntryPointAttr>())
getASTContext().registerSYCLEntryPointFunction(NewFD);
SYCL().CheckSYCLEntryPointFunctionDecl(NewFD);

// Semantic checking for this function declaration (in isolation).

Expand Down Expand Up @@ -12329,6 +12340,13 @@ void Sema::CheckMain(FunctionDecl *FD, const DeclSpec &DS) {
return;
}

if (getLangOpts().isSYCL() && FD->hasAttr<SYCLKernelEntryPointAttr>()) {
Diag(FD->getAttr<SYCLKernelEntryPointAttr>()->getLocation(),
diag::err_sycl_entry_point_on_main);
FD->setInvalidDecl();
return;
}

// Functions named main in hlsl are default entries, but don't have specific
// signatures they are required to conform to.
if (getLangOpts().HLSL)
Expand Down Expand Up @@ -15888,6 +15906,33 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
CheckCoroutineWrapper(FD);
}

// Diagnose invalid SYCL kernel entry point function declarations
// and build SYCLKernelCallStmts for valid ones.
if (FD && !FD->isInvalidDecl() && !FD->isTemplated() &&
FD->hasAttr<SYCLKernelEntryPointAttr>()) {
if (FD->isDeleted()) {
Diag(FD->getAttr<SYCLKernelEntryPointAttr>()->getLocation(),
diag::err_sycl_entry_point_invalid)
<< /*deleted function*/2;
FD->setInvalidDecl();
} else if (FD->isDefaulted()) {
Diag(FD->getAttr<SYCLKernelEntryPointAttr>()->getLocation(),
diag::err_sycl_entry_point_invalid)
<< /*defaulted function*/3;
FD->setInvalidDecl();
} else if (FSI->isCoroutine()) {
Diag(FD->getAttr<SYCLKernelEntryPointAttr>()->getLocation(),
diag::err_sycl_entry_point_invalid)
<< /*coroutine*/7;
FD->setInvalidDecl();
} else if (Body) {
StmtResult SR = SYCL().BuildSYCLKernelCallStmt(FD, Body);
if (SR.isInvalid())
return nullptr;
Body = SR.get();
}
}

{
// Do not call PopExpressionEvaluationContext() if it is a lambda because
// one is already popped when finishing the lambda in BuildLambdaExpr().
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaExceptionSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
case Stmt::AttributedStmtClass:
case Stmt::BreakStmtClass:
case Stmt::CapturedStmtClass:
case Stmt::SYCLKernelCallStmtClass:
case Stmt::CaseStmtClass:
case Stmt::CompoundStmtClass:
case Stmt::ContinueStmtClass:
Expand Down
227 changes: 227 additions & 0 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
// This implements Semantic Analysis for SYCL constructs.
//===----------------------------------------------------------------------===//

#include "TreeTransform.h"
#include "clang/Sema/SemaSYCL.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/StmtSYCL.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"
Expand Down Expand Up @@ -207,3 +211,226 @@ void SemaSYCL::handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (SemaRef.Context)
SYCLKernelEntryPointAttr(SemaRef.Context, AL, TSI));
}

static SourceLocation SourceLocationForType(QualType QT) {
SourceLocation Loc;
const Type *T = QT->getUnqualifiedDesugaredType();
if (const TagType *TT = dyn_cast<TagType>(T))
Loc = TT->getDecl()->getLocation();
else if (const ObjCInterfaceType *ObjCIT = dyn_cast<ObjCInterfaceType>(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 = SourceLocationForType(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.
const SYCLKernelEntryPointAttr *SKEPAttr = nullptr;
for (auto SAI = FD->specific_attr_begin<SYCLKernelEntryPointAttr>();
SAI != FD->specific_attr_end<SYCLKernelEntryPointAttr>();
++SAI) {
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);
FD->setInvalidDecl();
} 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())) {
FD->setInvalidDecl();
}

// 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<SYCLKernelEntryPointAttr>();
if (PrevSKEPAttr) {
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;;
FD->setInvalidDecl();
}
}
}

if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
if (!MD->isStatic()) {
Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
<< /*non-static member function*/0;
FD->setInvalidDecl();
}
}
if (FD->isVariadic()) {
Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
<< /*variadic function*/1;
FD->setInvalidDecl();
}
if (FD->isConsteval()) {
Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
<< /*consteval function*/5;
FD->setInvalidDecl();
} else if (FD->isConstexpr()) {
Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
<< /*constexpr function*/4;
FD->setInvalidDecl();
}
if (FD->isNoReturn()) {
Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
<< /*noreturn function*/6;
FD->setInvalidDecl();
}

if (!FD->getReturnType()->isVoidType()) {
Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_return_type);
FD->setInvalidDecl();
}

if (!FD->isInvalidDecl() && !FD->isTemplated()) {
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);
FD->setInvalidDecl();
}
} else {
getASTContext().registerSYCLEntryPointFunction(FD);
}
}
}

namespace {

// The body of a function declared with the [[sycl_kernel_entry_point]]
// attribute is cloned and transformed to substitute references to the original
// function parameters with references to replacement variables that stand in
// for SYCL kernel parameters or local variables that reconstitute a decomposed
// SYCL kernel argument.
class OutlinedFunctionDeclBodyInstantiator
: public TreeTransform<OutlinedFunctionDeclBodyInstantiator> {
public:
using ParmDeclMap = llvm::DenseMap<ParmVarDecl*, VarDecl*>;

OutlinedFunctionDeclBodyInstantiator(Sema &S, ParmDeclMap &M)
: TreeTransform<OutlinedFunctionDeclBodyInstantiator>(S),
SemaRef(S), MapRef(M) {}

// A new set of AST nodes is always required.
bool AlwaysRebuild() {
return true;
}

// Transform ParmVarDecl references to the supplied replacement variables.
ExprResult TransformDeclRefExpr(DeclRefExpr *DRE) {
const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl());
if (PVD) {
ParmDeclMap::iterator I = MapRef.find(PVD);
if (I != MapRef.end()) {
VarDecl *VD = I->second;
assert(SemaRef.getASTContext().hasSameUnqualifiedType(PVD->getType(),
VD->getType()));
assert(!VD->getType().isMoreQualifiedThan(PVD->getType()));
VD->setIsUsed();
return DeclRefExpr::Create(
SemaRef.getASTContext(), DRE->getQualifierLoc(),
DRE->getTemplateKeywordLoc(), VD, false, DRE->getNameInfo(),
DRE->getType(), DRE->getValueKind());
}
}
return DRE;
}

private:
Sema &SemaRef;
ParmDeclMap &MapRef;
};

} // unnamed namespace

StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD, Stmt *Body) {
assert(!FD->isInvalidDecl());
assert(!FD->isTemplated());
assert(FD->hasPrototype());

const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");

// Ensure that the kernel name was previously registered and that the
// stored declaration matches.
const SYCLKernelInfo &SKI =
getASTContext().getSYCLKernelInfo(SKEPAttr->getKernelName());
assert(declaresSameEntity(SKI.getKernelEntryPointDecl(), FD) &&
"SYCL kernel name conflict");

using ParmDeclMap = OutlinedFunctionDeclBodyInstantiator::ParmDeclMap;
ParmDeclMap ParmMap;

assert(SemaRef.CurContext == FD);
OutlinedFunctionDecl *OFD =
OutlinedFunctionDecl::Create(getASTContext(), FD, FD->getNumParams());
unsigned i = 0;
for (ParmVarDecl *PVD : FD->parameters()) {
ImplicitParamDecl *IPD =
ImplicitParamDecl::Create(getASTContext(), OFD, SourceLocation(),
PVD->getIdentifier(), PVD->getType(),
ImplicitParamKind::Other);
OFD->setParam(i, IPD);
ParmMap[PVD] = IPD;
++i;
}

OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap);
Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(Body).get();
OFD->setBody(OFDBody);
OFD->setNothrow();
Stmt *NewBody = new (getASTContext()) SYCLKernelCallStmt(Body, OFD);

return NewBody;
}
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,8 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
IdResolver.AddDecl(Param);
}

ProcessDeclAttributes(S, Param, D);

// C++0x [temp.param]p9:
// A default template-argument may be specified for any kind of
// template-parameter that is not a template parameter pack.
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/OpenMPKinds.h"
#include "clang/Sema/Designator.h"
Expand Down Expand Up @@ -16920,6 +16921,16 @@ TreeTransform<Derived>::TransformCapturedStmt(CapturedStmt *S) {
return getSema().ActOnCapturedRegionEnd(Body.get());
}

template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
// SYCLKernelCallStmt nodes are inserted upon completion of a (non-template)
// function definition or instantiation of a function template specialization
// and will therefore never appear in a dependent context.
llvm_unreachable("SYCL kernel call statement cannot appear in dependent "
"context");
}

template <typename Derived>
ExprResult TreeTransform<Derived>::TransformHLSLOutArgExpr(HLSLOutArgExpr *E) {
// We can transform the base expression and allow argument resolution to fill
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ serialization::getDefinitiveDeclContext(const DeclContext *DC) {
case Decl::CXXConversion:
case Decl::ObjCMethod:
case Decl::Block:
case Decl::OutlinedFunction:
case Decl::Captured:
// Objective C categories, category implementations, and class
// implementations can only be defined in one place.
Expand Down Expand Up @@ -439,6 +440,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::FriendTemplate:
case Decl::StaticAssert:
case Decl::Block:
case Decl::OutlinedFunction:
case Decl::Captured:
case Decl::Import:
case Decl::OMPThreadPrivate:
Expand Down
31 changes: 28 additions & 3 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
void VisitFriendTemplateDecl(FriendTemplateDecl *D);
void VisitStaticAssertDecl(StaticAssertDecl *D);
void VisitBlockDecl(BlockDecl *BD);
void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D);
void VisitCapturedDecl(CapturedDecl *CD);
void VisitEmptyDecl(EmptyDecl *D);
void VisitLifetimeExtendedTemporaryDecl(LifetimeExtendedTemporaryDecl *D);
Expand Down Expand Up @@ -477,9 +478,9 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
void VisitOMPDeclareMapperDecl(OMPDeclareMapperDecl *D);
void VisitOMPRequiresDecl(OMPRequiresDecl *D);
void VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D);
};
};

} // namespace clang
} // namespace clang

namespace {

Expand Down Expand Up @@ -1160,8 +1161,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<SYCLKernelEntryPointAttr>()) {
const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
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);
}
} else {
C.registerSYCLEntryPointFunction(FD);
}
}
}

Expand Down Expand Up @@ -1807,6 +1819,16 @@ void ASTDeclReader::VisitBlockDecl(BlockDecl *BD) {
BD->setCaptures(Reader.getContext(), captures, capturesCXXThis);
}

void ASTDeclReader::VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D) {
// NumParams is deserialized by OutlinedFunctionDecl::CreateDeserialized().
VisitDecl(D);
D->setNothrow(Record.readInt() != 0);
for (unsigned I = 0; I < D->NumParams; ++I) {
D->setParam(I, readDeclAs<ImplicitParamDecl>());
}
D->setBody(cast_or_null<Stmt>(Record.readStmt()));
}

void ASTDeclReader::VisitCapturedDecl(CapturedDecl *CD) {
VisitDecl(CD);
unsigned ContextParamPos = Record.readInt();
Expand Down Expand Up @@ -4097,6 +4119,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
case DECL_TEMPLATE_PARAM_OBJECT:
D = TemplateParamObjectDecl::CreateDeserialized(Context, ID);
break;
case DECL_OUTLINEDFUNCTION:
D = OutlinedFunctionDecl::CreateDeserialized(Context, ID, Record.readInt());
break;
case DECL_CAPTURED:
D = CapturedDecl::CreateDeserialized(Context, ID, Record.readInt());
break;
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Serialization/ASTReaderStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
Expand Down Expand Up @@ -528,6 +529,12 @@ void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) {
}
}

void ASTStmtReader::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
VisitStmt(S);
S->setOriginalStmt(Record.readSubStmt());
S->setOutlinedFunctionDecl(readDeclAs<OutlinedFunctionDecl>());
}

void ASTStmtReader::VisitExpr(Expr *E) {
VisitStmt(E);
CurrentUnpackingBits.emplace(Record.readInt());
Expand Down Expand Up @@ -3050,6 +3057,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
Context, Record[ASTStmtReader::NumStmtFields]);
break;

case STMT_SYCLKERNELCALL:
S = new (Context) SYCLKernelCallStmt(Empty);
break;

case EXPR_CONSTANT:
S = ConstantExpr::CreateEmpty(
Context, static_cast<ConstantResultStorageKind>(
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ namespace clang {
void VisitFriendTemplateDecl(FriendTemplateDecl *D);
void VisitStaticAssertDecl(StaticAssertDecl *D);
void VisitBlockDecl(BlockDecl *D);
void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D);
void VisitCapturedDecl(CapturedDecl *D);
void VisitEmptyDecl(EmptyDecl *D);
void VisitLifetimeExtendedTemporaryDecl(LifetimeExtendedTemporaryDecl *D);
Expand Down Expand Up @@ -1327,6 +1328,16 @@ void ASTDeclWriter::VisitBlockDecl(BlockDecl *D) {
Code = serialization::DECL_BLOCK;
}

void ASTDeclWriter::VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D) {
Record.push_back(D->getNumParams());
VisitDecl(D);
Record.push_back(D->isNothrow() ? 1 : 0);
for (unsigned I = 0; I < D->getNumParams(); ++I)
Record.AddDeclRef(D->getParam(I));
Record.AddStmt(D->getBody());
Code = serialization::DECL_OUTLINEDFUNCTION;
}

void ASTDeclWriter::VisitCapturedDecl(CapturedDecl *CD) {
Record.push_back(CD->getNumParams());
VisitDecl(CD);
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Serialization/ASTWriterStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,14 @@ void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) {
Code = serialization::STMT_CAPTURED;
}

void ASTStmtWriter::VisitSYCLKernelCallStmt(SYCLKernelCallStmt *S) {
VisitStmt(S);
Record.AddStmt(S->getOriginalStmt());
Record.AddDeclRef(S->getOutlinedFunctionDecl());

Code = serialization::STMT_SYCLKERNELCALL;
}

void ASTStmtWriter::VisitExpr(Expr *E) {
VisitStmt(E);

Expand Down
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPParallelGenericLoopDirectiveClass:
case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
case Stmt::CapturedStmtClass:
case Stmt::SYCLKernelCallStmtClass:
case Stmt::OpenACCComputeConstructClass:
case Stmt::OpenACCLoopConstructClass:
case Stmt::OMPUnrollDirectiveClass:
Expand Down
275 changes: 275 additions & 0 deletions clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
// Tests without serialization:
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-device \
// RUN: -ast-dump %s \
// RUN: | FileCheck --match-full-lines %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-host \
// RUN: -ast-dump %s \
// RUN: | FileCheck --match-full-lines %s
//
// Tests with serialization:
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-device \
// RUN: -emit-pch -o %t %s
// RUN: %clang_cc1 -x c++ -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-device \
// RUN: -include-pch %t -ast-dump-all /dev/null \
// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
// RUN: | FileCheck --match-full-lines %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-host \
// RUN: -emit-pch -o %t %s
// RUN: %clang_cc1 -x c++ -std=c++17 -triple x86_64-unknown-unknown -fsycl-is-host \
// RUN: -include-pch %t -ast-dump-all /dev/null \
// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
// RUN: | FileCheck --match-full-lines %s

// These tests validate the AST body produced for functions declared with the
// sycl_kernel_entry_point attribute.

// CHECK: TranslationUnitDecl {{.*}}

// A unique kernel name type is required for each declared kernel entry point.
template<int> struct KN;

// A unique invocable type for use with each declared kernel entry point.
template<int> struct K {
template<typename... Ts>
void operator()(Ts...) const {}
};


[[clang::sycl_kernel_entry_point(KN<1>)]]
void skep1() {
}
// CHECK: |-FunctionDecl {{.*}} skep1 'void ()'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<1>

template<typename KNT, typename KT>
[[clang::sycl_kernel_entry_point(KNT)]]
void skep2(KT k) {
k();
}
template
void skep2<KN<2>>(K<2>);
// CHECK: |-FunctionTemplateDecl {{.*}} skep2
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KNT
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KT
// CHECK-NEXT: | |-FunctionDecl {{.*}} skep2 'void (KT)'
// CHECK-NEXT: | | |-ParmVarDecl {{.*}} k 'KT'
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CallExpr {{.*}} '<dependent type>'
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
// CHECK-NEXT: | | `-SYCLKernelEntryPointAttr {{.*}} KNT

// CHECK-NEXT: | `-FunctionDecl {{.*}} skep2 'void (K<2>)' explicit_instantiation_definition
// CHECK-NEXT: | |-TemplateArgument type 'KN<2>'
// CHECK-NEXT: | | `-RecordType {{.*}} 'KN<2>'
// CHECK-NEXT: | | `-ClassTemplateSpecialization {{.*}} 'KN'
// CHECK-NEXT: | |-TemplateArgument type 'K<2>'
// CHECK-NEXT: | | `-RecordType {{.*}} 'K<2>'
// CHECK-NEXT: | | `-ClassTemplateSpecialization {{.*}} 'K'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} k 'K<2>'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<2>' lvalue ParmVar {{.*}} 'k' 'K<2>'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'K<2>'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'K<2>' lvalue ImplicitParam {{.*}} 'k' 'K<2>'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<2>

template<typename KNT, typename KT>
[[clang::sycl_kernel_entry_point(KNT)]]
void skep3(KT k) {
k();
}
template<>
[[clang::sycl_kernel_entry_point(KN<3>)]]
void skep3<KN<3>>(K<3> k) {
k();
}
// CHECK: |-FunctionTemplateDecl {{.*}} skep3
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KNT
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} KT
// CHECK-NEXT: | |-FunctionDecl {{.*}} skep3 'void (KT)'
// CHECK-NEXT: | | |-ParmVarDecl {{.*}} k 'KT'
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CallExpr {{.*}} '<dependent type>'
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'KT' lvalue ParmVar {{.*}} 'k' 'KT'
// CHECK-NEXT: | | `-SYCLKernelEntryPointAttr {{.*}} KNT

// CHECK-NEXT: | `-Function {{.*}} 'skep3' 'void (K<3>)'
// CHECK-NEXT: |-FunctionDecl {{.*}} skep3 'void (K<3>)' explicit_specialization
// CHECK-NEXT: | |-TemplateArgument type 'KN<3>'
// CHECK-NEXT: | | `-RecordType {{.*}} 'KN<3>'
// CHECK-NEXT: | | `-ClassTemplateSpecialization {{.*}} 'KN'
// CHECK-NEXT: | |-TemplateArgument type 'K<3>'
// CHECK-NEXT: | | `-RecordType {{.*}} 'K<3>'
// CHECK-NEXT: | | `-ClassTemplateSpecialization {{.*}} 'K'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} k 'K<3>'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<3>' lvalue ParmVar {{.*}} 'k' 'K<3>'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'K<3>'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'K<3>' lvalue ImplicitParam {{.*}} 'k' 'K<3>'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<3>

[[clang::sycl_kernel_entry_point(KN<4>)]]
void skep4(K<4> k, int p1, int p2) {
k(p1, p2);
}
// CHECK: |-FunctionDecl {{.*}} skep4 'void (K<4>, int, int)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} k 'K<4>'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} p1 'int'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} p2 'int'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int) const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int) const'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'K<4>' lvalue ParmVar {{.*}} 'k' 'K<4>'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p1' 'int'
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'p2' 'int'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'K<4>'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used p1 'int'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used p2 'int'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int) const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int) const'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<4>' lvalue ImplicitParam {{.*}} 'k' 'K<4>'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue ImplicitParam {{.*}} 'p1' 'int'
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'int' lvalue ImplicitParam {{.*}} 'p2' 'int'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<4>

[[clang::sycl_kernel_entry_point(KN<5>)]]
void skep5(int unused1, K<5> k, int unused2, int p, int unused3) {
static int slv = 0;
int lv = 4;
k(slv, 1, p, 3, lv, 5, []{ return 6; });
}
// CHECK: |-FunctionDecl {{.*}} skep5 'void (int, K<5>, int, int, int)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} unused1 'int'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} used k 'K<5>'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} unused2 'int'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} used p 'int'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} unused3 'int'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit unused1 'int'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'K<5>'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit unused2 'int'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used p 'int'
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit unused3 'int'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | |-DeclStmt {{.*}}
// CHECK-NEXT: | | | `-VarDecl {{.*}} used slv 'int' static cinit
// CHECK-NEXT: | | | `-IntegerLiteral {{.*}} 'int' 0
// CHECK-NEXT: | | |-DeclStmt {{.*}}
// CHECK-NEXT: | | | `-VarDecl {{.*}} used lv 'int' cinit
// CHECK-NEXT: | | | `-IntegerLiteral {{.*}} 'int' 4
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)(int, int, int, int, int, int, (lambda {{.*}}) const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void (int, int, int, int, int, int, (lambda {{.*}})) const' lvalue CXXMethod {{.*}} 'operator()' 'void (int, int, int, int, int, int, (lambda {{.*}})) const'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const K<5>' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'K<5>' lvalue ImplicitParam {{.*}} 'k' 'K<5>'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'slv' 'int'
// CHECK-NEXT: | | |-IntegerLiteral {{.*}} 'int' 1
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue ImplicitParam {{.*}} 'p' 'int'
// CHECK-NEXT: | | |-IntegerLiteral {{.*}} 'int' 3
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'lv' 'int'
// CHECK-NEXT: | | |-IntegerLiteral {{.*}} 'int' 5
// CHECK-NEXT: | | `-LambdaExpr {{.*}} '(lambda {{.*}})'
// CHECK: | `-SYCLKernelEntryPointAttr {{.*}} KN<5>

struct S6 {
void operator()() const;
};
[[clang::sycl_kernel_entry_point(KN<6>)]]
void skep6(const S6 &k) {
k();
}
// CHECK: |-FunctionDecl {{.*}} skep6 'void (const S6 &)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} used k 'const S6 &'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'const S6' lvalue ParmVar {{.*}} 'k' 'const S6 &'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'const S6 &'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'const S6' lvalue ImplicitParam {{.*}} 'k' 'const S6 &'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<6>

// Parameter types are not required to be complete at the point of a
// non-defining declaration.
struct S7;
[[clang::sycl_kernel_entry_point(KN<7>)]]
void skep7(S7 k);
struct S7 {
void operator()() const;
};
[[clang::sycl_kernel_entry_point(KN<7>)]]
void skep7(S7 k) {
k();
}
// CHECK: |-FunctionDecl {{.*}} skep7 'void (S7)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} k 'S7'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<7>
// CHECK: |-FunctionDecl {{.*}} prev {{.*}} skep7 'void (S7)'
// CHECK-NEXT: | |-ParmVarDecl {{.*}} used k 'S7'
// CHECK-NEXT: | |-SYCLKernelCallStmt {{.*}}
// CHECK-NEXT: | | |-CompoundStmt {{.*}}
// CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const S7' lvalue <NoOp>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'S7' lvalue ParmVar {{.*}} 'k' 'S7'
// CHECK-NEXT: | | `-OutlinedFunctionDecl {{.*}}
// CHECK-NEXT: | | |-ImplicitParamDecl {{.*}} implicit used k 'S7'
// CHECK-NEXT: | | `-CompoundStmt {{.*}}
// CHECK-NEXT: | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
// CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'void (*)() const' <FunctionToPointerDecay>
// CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod {{.*}} 'operator()' 'void () const'
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const S7' lvalue <NoOp>
// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'S7' lvalue ImplicitParam {{.*}} 'k' 'S7'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<7>


void the_end() {}
// CHECK: `-FunctionDecl {{.*}} the_end 'void ()'
29 changes: 21 additions & 8 deletions clang/test/ASTSYCL/ast-dump-sycl-kernel-entry-point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,29 @@ void skep5<KN<5,2>>(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<KN<5,-1>>(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<KN<5,4>>(int);
// Checks are located with the primary template declaration above.
Expand All @@ -128,16 +143,14 @@ void skep6() {
// CHECK: |-FunctionDecl {{.*}} skep6 'void ()'
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<6>
// CHECK-NEXT: |-FunctionDecl {{.*}} prev {{.*}} skep6 'void ()'
// CHECK-NEXT: | |-CompoundStmt {{.*}}
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<6>
// CHECK: | `-SYCLKernelEntryPointAttr {{.*}} KN<6>

// Ensure that matching attributes from the same declaration are ok.
[[clang::sycl_kernel_entry_point(KN<7>), clang::sycl_kernel_entry_point(KN<7>)]]
void skep7() {
}
// CHECK: |-FunctionDecl {{.*}} skep7 'void ()'
// CHECK-NEXT: | |-CompoundStmt {{.*}}
// CHECK-NEXT: | |-SYCLKernelEntryPointAttr {{.*}} KN<7>
// CHECK: | |-SYCLKernelEntryPointAttr {{.*}} KN<7>
// CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<7>

void the_end() {}
Expand Down
249 changes: 249 additions & 0 deletions clang/test/SemaSYCL/sycl-kernel-entry-point-attr-appertainment.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// 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 appertainment for the sycl_kernel_entry_point attribute.

#if __cplusplus >= 202002L
// Mock coroutine support.
namespace std {

template<typename Promise = void>
struct coroutine_handle {
template<typename T>
coroutine_handle(const coroutine_handle<T>&);
static coroutine_handle from_address(void *addr);
};

template<typename R, typename... Args>
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<int, int = 0> 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<typename KNT, typename T>
[[clang::sycl_kernel_entry_point(KNT)]]
void ok4(T) {}

// Function template explicit specialization.
template<>
[[clang::sycl_kernel_entry_point(KN<4,1>)]]
void ok4<KN<4,1>>(int) {}

// Function template explicit instantiation.
template void ok4<KN<4,2>, 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 friend function.
template<typename KNT>
struct S7 {
[[clang::sycl_kernel_entry_point(KNT)]]
friend void ok7(S7) {}
};
void test_ok7() {
ok7(S7<KN<7>>{});
}

// 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<8>)]]
void ok8();
[[clang::sycl_kernel_entry_point(KN<8>)]]
void ok8();
[[clang::sycl_kernel_entry_point(KN<9>)]]
void ok9();
void ok9() {}
void ok10();
[[clang::sycl_kernel_entry_point(KN<10>)]]
void ok10() {}

using VOID = void;
[[clang::sycl_kernel_entry_point(KN<11>)]]
VOID ok11();
[[clang::sycl_kernel_entry_point(KN<12>)]]
const void ok12();


////////////////////////////////////////////////////////////////////////////////
// Invalid declarations.
////////////////////////////////////////////////////////////////////////////////

struct Smain;
// expected-error@+1 {{'main' cannot be declared with the 'sycl_kernel_entry_point' attribute}}
[[clang::sycl_kernel_entry_point(Smain)]]
int main();

template<int> 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<typename>
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.
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
template<void (fp [[clang::sycl_kernel_entry_point(BADKN<18>)]])()>
void bad18();

#if __cplusplus >= 202002L
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a coroutine function}}
[[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 noreturn function}}
[[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();
15 changes: 0 additions & 15 deletions clang/test/SemaSYCL/sycl-kernel-entry-point-attr-grammar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,18 +120,3 @@ template<typename> concept C = true;
// expected-error@+1 {{expected a type}}
[[clang::sycl_kernel_entry_point(C<int>)]] 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<B12>' requested here}}
// expected-note@#B12-decl {{forward declaration of 'B12'}}
template<typename T>
[[clang::sycl_kernel_entry_point(typename T::not_found)]] void bad12() {}
template<>
void bad12<B12>() {}
Original file line number Diff line number Diff line change
@@ -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<int> struct KN;

[[clang::sycl_kernel_entry_point(KN<1>)]]
void common_test1() {}

template<typename T>
[[clang::sycl_kernel_entry_point(T)]]
void common_test2() {}
template void common_test2<KN<2>>();


#--- m1.h
#include "common.h"

[[clang::sycl_kernel_entry_point(KN<3>)]]
void m1_test3() {} // << expected previous declaration note here.

template<typename T>
[[clang::sycl_kernel_entry_point(T)]]
void m1_test4() {} // << expected previous declaration note here.
template void m1_test4<KN<4>>();

[[clang::sycl_kernel_entry_point(KN<5>)]]
void m1_test5() {} // << expected previous declaration note here.

template<typename T>
[[clang::sycl_kernel_entry_point(T)]]
void m1_test6() {} // << expected previous declaration note here.
template void m1_test6<KN<6>>();


#--- m2.h
#include "common.h"

[[clang::sycl_kernel_entry_point(KN<3>)]]
void m2_test3() {} // << expected kernel name conflict here.

template<typename T>
[[clang::sycl_kernel_entry_point(T)]]
void m2_test4() {} // << expected kernel name conflict here.
template void m2_test4<KN<4>>();

[[clang::sycl_kernel_entry_point(KN<7>)]]
void m2_test7() {} // << expected previous declaration note here.

template<typename T>
[[clang::sycl_kernel_entry_point(T)]]
void m2_test8() {} // << expected previous declaration note here.
template void m2_test8<KN<8>>();


#--- 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<KN<4>>() and m2_test4<KN<4>>():
// 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<KN<2>>();
}
Original file line number Diff line number Diff line change
@@ -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<int> struct KN;

[[clang::sycl_kernel_entry_point(KN<1>)]]
void pch_test1() {} // << expected previous declaration note here.

template<typename T>
[[clang::sycl_kernel_entry_point(T)]]
void pch_test2() {} // << expected previous declaration note here.
template void pch_test2<KN<2>>();


#--- 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() {}
87 changes: 87 additions & 0 deletions clang/test/SemaSYCL/sycl-kernel-entry-point-attr-kernel-name.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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 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 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 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 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 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 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 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 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<typename T>
[[clang::sycl_kernel_entry_point(typename T::MS)]] void bad11() {}
template void bad11<B11>();

template<typename T>
[[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<LS>();
}

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();
38 changes: 38 additions & 0 deletions clang/test/SemaSYCL/sycl-kernel-entry-point-attr-sfinae.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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 substitution failures that occur in an argument of
// a sycl_kernel_entry_point attribute occur in the immediate context and
// therefore influence 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<S1>' requested here}}
// expected-note@#S1-decl {{forward declaration of 'S1'}}
template<typename T>
[[clang::sycl_kernel_entry_point(typename T::invalid)]] void ok1() {}
template<>
void ok1<S1>() {}

// FIXME: A substitution failure that occurs during instantiation of an
// FIXME: attribute specifier sequence should make the declaration invalid for
// FIXME: overload resolution but should not elicit a diagnostic (assuming
// FIXME: another overload candidate is selected).
struct S2; // #S2-decl
// expected-error@+4 {{incomplete type 'S2' named in nested name specifier}}
// expected-note@+7 {{in instantiation of function template specialization 'ok2<S2>' requested here}}
// expected-note@#S2-decl {{forward declaration of 'S2'}}
template<typename T>
[[clang::sycl_kernel_entry_point(typename T::invalid)]] void ok2(int) {}
template<typename T>
[[clang::sycl_kernel_entry_point(T)]] void ok2(long) {}
void test_ok2() {
ok2<S2>(2); // ok2(int) would be a better match, but SFINAE should result
// in overload resolution selecting ok2(long).
}
1 change: 1 addition & 0 deletions clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7077,6 +7077,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
case Decl::TopLevelStmt:
case Decl::StaticAssert:
case Decl::Block:
case Decl::OutlinedFunction:
case Decl::Captured:
case Decl::OMPCapturedExpr:
case Decl::Label: // FIXME: Is this right??
Expand Down
4 changes: 4 additions & 0 deletions clang/tools/libclang/CXCursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
K = CXCursor_UnexposedStmt;
break;

case Stmt::SYCLKernelCallStmtClass:
K = CXCursor_UnexposedStmt;
break;

case Stmt::IntegerLiteralClass:
K = CXCursor_IntegerLiteral;
break;
Expand Down