480 lines · cpp
1//===- SemaSYCL.cpp - Semantic Analysis for SYCL constructs ---------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8// This implements Semantic Analysis for SYCL constructs.9//===----------------------------------------------------------------------===//10 11#include "clang/Sema/SemaSYCL.h"12#include "TreeTransform.h"13#include "clang/AST/Mangle.h"14#include "clang/AST/SYCLKernelInfo.h"15#include "clang/AST/StmtSYCL.h"16#include "clang/AST/TypeOrdering.h"17#include "clang/Basic/Diagnostic.h"18#include "clang/Sema/Attr.h"19#include "clang/Sema/ParsedAttr.h"20#include "clang/Sema/Sema.h"21 22using namespace clang;23 24// -----------------------------------------------------------------------------25// SYCL device specific diagnostics implementation26// -----------------------------------------------------------------------------27 28SemaSYCL::SemaSYCL(Sema &S) : SemaBase(S) {}29 30Sema::SemaDiagnosticBuilder SemaSYCL::DiagIfDeviceCode(SourceLocation Loc,31 unsigned DiagID) {32 assert(getLangOpts().SYCLIsDevice &&33 "Should only be called during SYCL compilation");34 FunctionDecl *FD = dyn_cast<FunctionDecl>(SemaRef.getCurLexicalContext());35 SemaDiagnosticBuilder::Kind DiagKind = [this, FD] {36 if (!FD)37 return SemaDiagnosticBuilder::K_Nop;38 if (SemaRef.getEmissionStatus(FD) == Sema::FunctionEmissionStatus::Emitted)39 return SemaDiagnosticBuilder::K_ImmediateWithCallStack;40 return SemaDiagnosticBuilder::K_Deferred;41 }();42 return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, FD, SemaRef);43}44 45static bool isZeroSizedArray(SemaSYCL &S, QualType Ty) {46 if (const auto *CAT = S.getASTContext().getAsConstantArrayType(Ty))47 return CAT->isZeroSize();48 return false;49}50 51void SemaSYCL::deepTypeCheckForDevice(SourceLocation UsedAt,52 llvm::DenseSet<QualType> Visited,53 ValueDecl *DeclToCheck) {54 assert(getLangOpts().SYCLIsDevice &&55 "Should only be called during SYCL compilation");56 // Emit notes only for the first discovered declaration of unsupported type57 // to avoid mess of notes. This flag is to track that error already happened.58 bool NeedToEmitNotes = true;59 60 auto Check = [&](QualType TypeToCheck, const ValueDecl *D) {61 bool ErrorFound = false;62 if (isZeroSizedArray(*this, TypeToCheck)) {63 DiagIfDeviceCode(UsedAt, diag::err_typecheck_zero_array_size) << 1;64 ErrorFound = true;65 }66 // Checks for other types can also be done here.67 if (ErrorFound) {68 if (NeedToEmitNotes) {69 if (auto *FD = dyn_cast<FieldDecl>(D))70 DiagIfDeviceCode(FD->getLocation(),71 diag::note_illegal_field_declared_here)72 << FD->getType()->isPointerType() << FD->getType();73 else74 DiagIfDeviceCode(D->getLocation(), diag::note_declared_at);75 }76 }77 78 return ErrorFound;79 };80 81 // In case we have a Record used do the DFS for a bad field.82 SmallVector<const ValueDecl *, 4> StackForRecursion;83 StackForRecursion.push_back(DeclToCheck);84 85 // While doing DFS save how we get there to emit a nice set of notes.86 SmallVector<const FieldDecl *, 4> History;87 History.push_back(nullptr);88 89 do {90 const ValueDecl *Next = StackForRecursion.pop_back_val();91 if (!Next) {92 assert(!History.empty());93 // Found a marker, we have gone up a level.94 History.pop_back();95 continue;96 }97 QualType NextTy = Next->getType();98 99 if (!Visited.insert(NextTy).second)100 continue;101 102 auto EmitHistory = [&]() {103 // The first element is always nullptr.104 for (uint64_t Index = 1; Index < History.size(); ++Index) {105 DiagIfDeviceCode(History[Index]->getLocation(),106 diag::note_within_field_of_type)107 << History[Index]->getType();108 }109 };110 111 if (Check(NextTy, Next)) {112 if (NeedToEmitNotes)113 EmitHistory();114 NeedToEmitNotes = false;115 }116 117 // In case pointer/array/reference type is met get pointee type, then118 // proceed with that type.119 while (NextTy->isAnyPointerType() || NextTy->isArrayType() ||120 NextTy->isReferenceType()) {121 if (NextTy->isArrayType())122 NextTy = QualType{NextTy->getArrayElementTypeNoTypeQual(), 0};123 else124 NextTy = NextTy->getPointeeType();125 if (Check(NextTy, Next)) {126 if (NeedToEmitNotes)127 EmitHistory();128 NeedToEmitNotes = false;129 }130 }131 132 if (const auto *RecDecl = NextTy->getAsRecordDecl()) {133 if (auto *NextFD = dyn_cast<FieldDecl>(Next))134 History.push_back(NextFD);135 // When nullptr is discovered, this means we've gone back up a level, so136 // the history should be cleaned.137 StackForRecursion.push_back(nullptr);138 llvm::append_range(StackForRecursion, RecDecl->fields());139 }140 } while (!StackForRecursion.empty());141}142 143ExprResult SemaSYCL::BuildUniqueStableNameExpr(SourceLocation OpLoc,144 SourceLocation LParen,145 SourceLocation RParen,146 TypeSourceInfo *TSI) {147 return SYCLUniqueStableNameExpr::Create(getASTContext(), OpLoc, LParen,148 RParen, TSI);149}150 151ExprResult SemaSYCL::ActOnUniqueStableNameExpr(SourceLocation OpLoc,152 SourceLocation LParen,153 SourceLocation RParen,154 ParsedType ParsedTy) {155 TypeSourceInfo *TSI = nullptr;156 QualType Ty = SemaRef.GetTypeFromParser(ParsedTy, &TSI);157 158 if (Ty.isNull())159 return ExprError();160 if (!TSI)161 TSI = getASTContext().getTrivialTypeSourceInfo(Ty, LParen);162 163 return BuildUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);164}165 166void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) {167 // The 'sycl_kernel' attribute applies only to function templates.168 const auto *FD = cast<FunctionDecl>(D);169 const FunctionTemplateDecl *FT = FD->getDescribedFunctionTemplate();170 assert(FT && "Function template is expected");171 172 // Function template must have at least two template parameters.173 const TemplateParameterList *TL = FT->getTemplateParameters();174 if (TL->size() < 2) {175 Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_template_params);176 return;177 }178 179 // Template parameters must be typenames.180 for (unsigned I = 0; I < 2; ++I) {181 const NamedDecl *TParam = TL->getParam(I);182 if (isa<NonTypeTemplateParmDecl>(TParam)) {183 Diag(FT->getLocation(),184 diag::warn_sycl_kernel_invalid_template_param_type);185 return;186 }187 }188 189 // Function must have at least one argument.190 if (getFunctionOrMethodNumParams(D) != 1) {191 Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_function_params);192 return;193 }194 195 // Function must return void.196 QualType RetTy = getFunctionOrMethodResultType(D);197 if (!RetTy->isVoidType()) {198 Diag(FT->getLocation(), diag::warn_sycl_kernel_return_type);199 return;200 }201 202 handleSimpleAttribute<SYCLKernelAttr>(*this, D, AL);203}204 205void SemaSYCL::handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL) {206 ParsedType PT = AL.getTypeArg();207 TypeSourceInfo *TSI = nullptr;208 (void)SemaRef.GetTypeFromParser(PT, &TSI);209 assert(TSI && "no type source info for attribute argument");210 D->addAttr(::new (SemaRef.Context)211 SYCLKernelEntryPointAttr(SemaRef.Context, AL, TSI));212}213 214// Given a potentially qualified type, SourceLocationForUserDeclaredType()215// returns the source location of the canonical declaration of the unqualified216// desugared user declared type, if any. For non-user declared types, an217// invalid source location is returned. The intended usage of this function218// is to identify an appropriate source location, if any, for a219// "entity declared here" diagnostic note.220static SourceLocation SourceLocationForUserDeclaredType(QualType QT) {221 SourceLocation Loc;222 const Type *T = QT->getUnqualifiedDesugaredType();223 if (const TagType *TT = dyn_cast<TagType>(T))224 Loc = TT->getDecl()->getLocation();225 else if (const auto *ObjCIT = dyn_cast<ObjCInterfaceType>(T))226 Loc = ObjCIT->getDecl()->getLocation();227 return Loc;228}229 230static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc,231 QualType KernelName) {232 assert(!KernelName->isDependentType());233 234 if (!KernelName->isStructureOrClassType()) {235 // SYCL 2020 section 5.2, "Naming of kernels", only requires that the236 // kernel name be a C++ typename. However, the definition of "kernel name"237 // in the glossary states that a kernel name is a class type. Neither238 // section explicitly states whether the kernel name type can be239 // cv-qualified. For now, kernel name types are required to be class types240 // and that they may be cv-qualified. The following issue requests241 // clarification from the SYCL WG.242 // https://github.com/KhronosGroup/SYCL-Docs/issues/568243 S.Diag(Loc, diag::warn_sycl_kernel_name_not_a_class_type) << KernelName;244 SourceLocation DeclTypeLoc = SourceLocationForUserDeclaredType(KernelName);245 if (DeclTypeLoc.isValid())246 S.Diag(DeclTypeLoc, diag::note_entity_declared_at) << KernelName;247 return true;248 }249 250 return false;251}252 253void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) {254 const auto *SEAttr = FD->getAttr<SYCLExternalAttr>();255 assert(SEAttr && "Missing sycl_external attribute");256 if (!FD->isInvalidDecl() && !FD->isTemplated()) {257 if (!FD->isExternallyVisible())258 if (!FD->isFunctionTemplateSpecialization() ||259 FD->getTemplateSpecializationInfo()->isExplicitSpecialization())260 Diag(SEAttr->getLocation(), diag::err_sycl_external_invalid_linkage)261 << SEAttr;262 }263 if (FD->isDeletedAsWritten()) {264 Diag(SEAttr->getLocation(),265 diag::err_sycl_external_invalid_deleted_function)266 << SEAttr;267 }268}269 270void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {271 // Ensure that all attributes present on the declaration are consistent272 // and warn about any redundant ones.273 SYCLKernelEntryPointAttr *SKEPAttr = nullptr;274 for (auto *SAI : FD->specific_attrs<SYCLKernelEntryPointAttr>()) {275 if (!SKEPAttr) {276 SKEPAttr = SAI;277 continue;278 }279 if (!getASTContext().hasSameType(SAI->getKernelName(),280 SKEPAttr->getKernelName())) {281 Diag(SAI->getLocation(), diag::err_sycl_entry_point_invalid_redeclaration)282 << SKEPAttr << SAI->getKernelName() << SKEPAttr->getKernelName();283 Diag(SKEPAttr->getLocation(), diag::note_previous_attribute);284 SAI->setInvalidAttr();285 } else {286 Diag(SAI->getLocation(),287 diag::warn_sycl_entry_point_redundant_declaration)288 << SAI;289 Diag(SKEPAttr->getLocation(), diag::note_previous_attribute);290 }291 }292 assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");293 294 // Ensure the kernel name type is valid.295 if (!SKEPAttr->getKernelName()->isDependentType() &&296 CheckSYCLKernelName(SemaRef, SKEPAttr->getLocation(),297 SKEPAttr->getKernelName()))298 SKEPAttr->setInvalidAttr();299 300 // Ensure that an attribute present on the previous declaration301 // matches the one on this declaration.302 FunctionDecl *PrevFD = FD->getPreviousDecl();303 if (PrevFD && !PrevFD->isInvalidDecl()) {304 const auto *PrevSKEPAttr = PrevFD->getAttr<SYCLKernelEntryPointAttr>();305 if (PrevSKEPAttr && !PrevSKEPAttr->isInvalidAttr()) {306 if (!getASTContext().hasSameType(SKEPAttr->getKernelName(),307 PrevSKEPAttr->getKernelName())) {308 Diag(SKEPAttr->getLocation(),309 diag::err_sycl_entry_point_invalid_redeclaration)310 << SKEPAttr << SKEPAttr->getKernelName()311 << PrevSKEPAttr->getKernelName();312 Diag(PrevSKEPAttr->getLocation(), diag::note_previous_decl) << PrevFD;313 SKEPAttr->setInvalidAttr();314 }315 }316 }317 318 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {319 if (!MD->isStatic()) {320 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)321 << SKEPAttr << /*non-static member function*/ 0;322 SKEPAttr->setInvalidAttr();323 }324 }325 326 if (FD->isVariadic()) {327 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)328 << SKEPAttr << /*variadic function*/ 1;329 SKEPAttr->setInvalidAttr();330 }331 332 if (FD->isDefaulted()) {333 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)334 << SKEPAttr << /*defaulted function*/ 3;335 SKEPAttr->setInvalidAttr();336 } else if (FD->isDeleted()) {337 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)338 << SKEPAttr << /*deleted function*/ 2;339 SKEPAttr->setInvalidAttr();340 }341 342 if (FD->isConsteval()) {343 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)344 << SKEPAttr << /*consteval function*/ 5;345 SKEPAttr->setInvalidAttr();346 } else if (FD->isConstexpr()) {347 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)348 << SKEPAttr << /*constexpr function*/ 4;349 SKEPAttr->setInvalidAttr();350 }351 352 if (FD->isNoReturn()) {353 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)354 << SKEPAttr << /*function declared with the 'noreturn' attribute*/ 6;355 SKEPAttr->setInvalidAttr();356 }357 358 if (FD->getReturnType()->isUndeducedType()) {359 Diag(SKEPAttr->getLocation(),360 diag::err_sycl_entry_point_deduced_return_type)361 << SKEPAttr;362 SKEPAttr->setInvalidAttr();363 } else if (!FD->getReturnType()->isDependentType() &&364 !FD->getReturnType()->isVoidType()) {365 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_return_type)366 << SKEPAttr;367 SKEPAttr->setInvalidAttr();368 }369 370 if (!FD->isInvalidDecl() && !FD->isTemplated() &&371 !SKEPAttr->isInvalidAttr()) {372 const SYCLKernelInfo *SKI =373 getASTContext().findSYCLKernelInfo(SKEPAttr->getKernelName());374 if (SKI) {375 if (!declaresSameEntity(FD, SKI->getKernelEntryPointDecl())) {376 // FIXME: This diagnostic should include the origin of the kernel377 // FIXME: names; not just the locations of the conflicting declarations.378 Diag(FD->getLocation(), diag::err_sycl_kernel_name_conflict)379 << SKEPAttr;380 Diag(SKI->getKernelEntryPointDecl()->getLocation(),381 diag::note_previous_declaration);382 SKEPAttr->setInvalidAttr();383 }384 } else {385 getASTContext().registerSYCLEntryPointFunction(FD);386 }387 }388}389 390namespace {391 392// The body of a function declared with the [[sycl_kernel_entry_point]]393// attribute is cloned and transformed to substitute references to the original394// function parameters with references to replacement variables that stand in395// for SYCL kernel parameters or local variables that reconstitute a decomposed396// SYCL kernel argument.397class OutlinedFunctionDeclBodyInstantiator398 : public TreeTransform<OutlinedFunctionDeclBodyInstantiator> {399public:400 using ParmDeclMap = llvm::DenseMap<ParmVarDecl *, VarDecl *>;401 402 OutlinedFunctionDeclBodyInstantiator(Sema &S, ParmDeclMap &M)403 : TreeTransform<OutlinedFunctionDeclBodyInstantiator>(S), SemaRef(S),404 MapRef(M) {}405 406 // A new set of AST nodes is always required.407 bool AlwaysRebuild() { return true; }408 409 // Transform ParmVarDecl references to the supplied replacement variables.410 ExprResult TransformDeclRefExpr(DeclRefExpr *DRE) {411 const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl());412 if (PVD) {413 ParmDeclMap::iterator I = MapRef.find(PVD);414 if (I != MapRef.end()) {415 VarDecl *VD = I->second;416 assert(SemaRef.getASTContext().hasSameUnqualifiedType(PVD->getType(),417 VD->getType()));418 assert(!VD->getType().isMoreQualifiedThan(PVD->getType(),419 SemaRef.getASTContext()));420 VD->setIsUsed();421 return DeclRefExpr::Create(422 SemaRef.getASTContext(), DRE->getQualifierLoc(),423 DRE->getTemplateKeywordLoc(), VD, false, DRE->getNameInfo(),424 DRE->getType(), DRE->getValueKind());425 }426 }427 return DRE;428 }429 430private:431 Sema &SemaRef;432 ParmDeclMap &MapRef;433};434 435} // unnamed namespace436 437StmtResult SemaSYCL::BuildSYCLKernelCallStmt(FunctionDecl *FD,438 CompoundStmt *Body) {439 assert(!FD->isInvalidDecl());440 assert(!FD->isTemplated());441 assert(FD->hasPrototype());442 443 const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();444 assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");445 assert(!SKEPAttr->isInvalidAttr() &&446 "sycl_kernel_entry_point attribute is invalid");447 448 // Ensure that the kernel name was previously registered and that the449 // stored declaration matches.450 const SYCLKernelInfo &SKI =451 getASTContext().getSYCLKernelInfo(SKEPAttr->getKernelName());452 assert(declaresSameEntity(SKI.getKernelEntryPointDecl(), FD) &&453 "SYCL kernel name conflict");454 (void)SKI;455 456 using ParmDeclMap = OutlinedFunctionDeclBodyInstantiator::ParmDeclMap;457 ParmDeclMap ParmMap;458 459 assert(SemaRef.CurContext == FD);460 OutlinedFunctionDecl *OFD =461 OutlinedFunctionDecl::Create(getASTContext(), FD, FD->getNumParams());462 unsigned i = 0;463 for (ParmVarDecl *PVD : FD->parameters()) {464 ImplicitParamDecl *IPD = ImplicitParamDecl::Create(465 getASTContext(), OFD, SourceLocation(), PVD->getIdentifier(),466 PVD->getType(), ImplicitParamKind::Other);467 OFD->setParam(i, IPD);468 ParmMap[PVD] = IPD;469 ++i;470 }471 472 OutlinedFunctionDeclBodyInstantiator OFDBodyInstantiator(SemaRef, ParmMap);473 Stmt *OFDBody = OFDBodyInstantiator.TransformStmt(Body).get();474 OFD->setBody(OFDBody);475 OFD->setNothrow();476 Stmt *NewBody = new (getASTContext()) SYCLKernelCallStmt(Body, OFD);477 478 return NewBody;479}480