Skip to content

[SYCL] Basic diagnostics for the sycl_kernel_entry_point attribute. #120327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -3360,6 +3360,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// 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
//===--------------------------------------------------------------------===//
Expand Down
13 changes: 12 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
27 changes: 27 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -12408,6 +12408,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<DiagGroup<"nonportable-sycl">>, DefaultError;
def warn_sycl_entry_point_redundant_declaration : Warning<
"redundant 'sycl_kernel_entry_point' attribute">, InGroup<RedundantAttribute>;
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<IgnoredAttributes>;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/SemaSYCL.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14463,6 +14463,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
36 changes: 33 additions & 3 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<AliasAttr>(NewAttribute) || isa<IFuncAttr>(NewAttribute)) {
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(New)) {
Expand Down Expand Up @@ -3018,6 +3019,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);
cast<SYCLKernelEntryPointAttr>(NewAttribute)->setInvalidAttr();
++I;
continue;
}

S.Diag(NewAttribute->getLocation(),
Expand Down Expand Up @@ -12145,8 +12156,8 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
if (LangOpts.OpenMP)
OpenMP().ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(NewFD);

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

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

Expand Down Expand Up @@ -15978,6 +15989,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>()) {
SYCLKernelEntryPointAttr *SKEPAttr =
FD->getAttr<SYCLKernelEntryPointAttr>();
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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When these get this high, an enum typically makes sense instead of comments (which get lost/etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you point me to an example? I haven't been able to find one. It doesn't look like there is a way to declare an enum with the diagnostic definition. Without the ability to do so, I don't see how adding an intermediate enum somewhere else improves the situation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there isn't a convenient way of doing so in the diagnostics engine unfortunately. I'd love for us to have that! But typically we just define it elsewhere. For large lists like this it can make it significantly more readable, and much less likely to not be updated because of a copy/paste error (which happens OFTEN).

See AttributeArgumentNType and AttributeDeclKind for some examples, but they are kind of sprinkled in quite a few places, Richard had me do some a few times IIRC for function multiversioning. Sema::checkTargetVersionAttr has some more local versions of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing me to those examples. For both AttributeArgumentNType and AttributeDeclKind, the production of the diagnostic is more complicated. For the first case, the diagnostic is produced in a lambda expression and the %select index is passed as an argument. For the second case, the %select index is computed by mapping from another enumeration using a switch statement. An enum definitely seems warranted for those cases. Similarly, for Sema::checkTargetVersionAttr, there are multiple %select indices involved in each diagnostic and the local enumeration does help with readability.

For simple diagnostics, where the %select index doesn't require computation and where the same index values are not used in multiple places, I think indirection through an enumeration isn't helpful, so I haven't made a change. If you still think a change is warranted, perhaps we can get a second opinion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still disagree strongly. Any time where there are more than 2-3 options, and are used in more than 1 or 2 places, it vastly improves readability and prevents bugs of printing the wrong thing. We can have a second opinion from Aaron, but this is something pretty important to me as code-owner.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a judgement call either way, IMO. Personally, I dislike using one-shot enumerations because they're overkill for what they're used for. The in-line comment saying "this value corresponds to that selection" is plenty to keep the code readable. However, I do like using enumerations when the enum is used for selecting the diagnostic and some other purpose. e.g.,

AwesomeSauce Result = CheckSomeCondition(Whatever);
if (Result != AwesomeSauce::Good)
  Diag(Loc, diag::dont_do_that) << Result;
else
  Diag(Loc, diag::congrats) << AwesomeSauce::Fantastic;

In this specific case, I lean towards not needing the enumeration because then we have to figure out where that lives (Are we making Sema even more complicated because the diagnostics are in multiple files? Are we adding a new header? Something else?), and that will lead to inconsistency as we add new diagnostics because some folks will add unscoped enums, others will use scoped enums, and the enums will likely live in various different places in the source.

That said, coming up with some tablegen syntax that allows someone to associate enumerations with the diagnostic definition itself would be a pretty interesting idea to explore because then we'd get something that could be consistently applied and doesn't require as much maintenance and review effort. e.g.,

def err_sycl_entry_point_invalid : Error<
  "'sycl_kernel_entry_point' attribute cannot be applied to a"
  " %enum_select{"
  "%enum{NonStaticMemberFunc}non-static member function|"
  "%enum{VariadicFunc}variadic function|"
  "%enum{DeletedFunc}deleted function|"
  "%enum{DefaultedFunc}defaulted function|...<you get the idea>}0">;

that code then be used like:

Diag(Loc, diag::err_sycl_entry_point_invalid) << diag::err_sycl_entry_point_invalid::NonStaticMemberFunction;

(Though I think we may want to find some better place for the enum to live so that uses in calls to Diag() are more succinct. Obviously there's a lot of design work left to be done with the idea.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @AaronBallman. I like the suggested tablegen approach and would be happy to use that if someone (other than me) implemented it!

I share your perspective on one-shot enumerations. I'll leave the code as is for now. @erichkeane, as a compromise, I would be happy to split this diagnostic into eight distinct ones to avoid the situation all together. I don't think we gain much by using a single diagnostic in the first place. Let me know if you would prefer that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer we don't split into separate diagnostics; that increases overhead and maintenance burden for very little gain IMO. We generally prefer using %select when possible (it makes it easier to reword diagnostics, fix typos, localize, etc and it avoids adding extra strings to the binary when string merging isn't possible).

SKEPAttr->setInvalidAttr();
}
}

{
// Do not call PopExpressionEvaluationContext() if it is a lambda because
// one is already popped when finishing the lambda in BuildLambdaExpr().
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <optional>
Expand Down Expand Up @@ -1948,6 +1949,10 @@ ExprResult Sema::BuildCaptureInit(const Capture &Cap,

ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) {
LambdaScopeInfo LSI = *cast<LambdaScopeInfo>(FunctionScopes.back());

if (LSI.CallOperator->hasAttr<SYCLKernelEntryPointAttr>())
SYCL().CheckSYCLEntryPointFunctionDecl(LSI.CallOperator);

ActOnFinishFunctionBody(LSI.CallOperator, Body);

return BuildLambdaExpr(StartLoc, Body->getEndLoc(), &LSI);
Expand Down
156 changes: 156 additions & 0 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<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 = 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<SYCLKernelEntryPointAttr>()) {
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<SYCLKernelEntryPointAttr>();
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<CXXMethodDecl>(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);
}
}
}
13 changes: 12 additions & 1 deletion clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SYCLKernelEntryPointAttr>()) {
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);
SKEPAttr->setInvalidAttr();
}
} else {
C.registerSYCLEntryPointFunction(FD);
}
}
}

Expand Down
Loading
Loading