clang 20.0.0git
SemaSYCL.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8// This implements Semantic Analysis for SYCL constructs.
9//===----------------------------------------------------------------------===//
10
11#include "clang/Sema/SemaSYCL.h"
12#include "clang/AST/Mangle.h"
16#include "clang/Sema/Attr.h"
18#include "clang/Sema/Sema.h"
19
20using namespace clang;
21
22// -----------------------------------------------------------------------------
23// SYCL device specific diagnostics implementation
24// -----------------------------------------------------------------------------
25
27
29 unsigned DiagID) {
30 assert(getLangOpts().SYCLIsDevice &&
31 "Should only be called during SYCL compilation");
32 FunctionDecl *FD = dyn_cast<FunctionDecl>(SemaRef.getCurLexicalContext());
33 SemaDiagnosticBuilder::Kind DiagKind = [this, FD] {
34 if (!FD)
35 return SemaDiagnosticBuilder::K_Nop;
37 return SemaDiagnosticBuilder::K_ImmediateWithCallStack;
38 return SemaDiagnosticBuilder::K_Deferred;
39 }();
40 return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, FD, SemaRef);
41}
42
43static bool isZeroSizedArray(SemaSYCL &S, QualType Ty) {
44 if (const auto *CAT = S.getASTContext().getAsConstantArrayType(Ty))
45 return CAT->isZeroSize();
46 return false;
47}
48
50 llvm::DenseSet<QualType> Visited,
51 ValueDecl *DeclToCheck) {
52 assert(getLangOpts().SYCLIsDevice &&
53 "Should only be called during SYCL compilation");
54 // Emit notes only for the first discovered declaration of unsupported type
55 // to avoid mess of notes. This flag is to track that error already happened.
56 bool NeedToEmitNotes = true;
57
58 auto Check = [&](QualType TypeToCheck, const ValueDecl *D) {
59 bool ErrorFound = false;
60 if (isZeroSizedArray(*this, TypeToCheck)) {
61 DiagIfDeviceCode(UsedAt, diag::err_typecheck_zero_array_size) << 1;
62 ErrorFound = true;
63 }
64 // Checks for other types can also be done here.
65 if (ErrorFound) {
66 if (NeedToEmitNotes) {
67 if (auto *FD = dyn_cast<FieldDecl>(D))
68 DiagIfDeviceCode(FD->getLocation(),
69 diag::note_illegal_field_declared_here)
70 << FD->getType()->isPointerType() << FD->getType();
71 else
72 DiagIfDeviceCode(D->getLocation(), diag::note_declared_at);
73 }
74 }
75
76 return ErrorFound;
77 };
78
79 // In case we have a Record used do the DFS for a bad field.
80 SmallVector<const ValueDecl *, 4> StackForRecursion;
81 StackForRecursion.push_back(DeclToCheck);
82
83 // While doing DFS save how we get there to emit a nice set of notes.
85 History.push_back(nullptr);
86
87 do {
88 const ValueDecl *Next = StackForRecursion.pop_back_val();
89 if (!Next) {
90 assert(!History.empty());
91 // Found a marker, we have gone up a level.
92 History.pop_back();
93 continue;
94 }
95 QualType NextTy = Next->getType();
96
97 if (!Visited.insert(NextTy).second)
98 continue;
99
100 auto EmitHistory = [&]() {
101 // The first element is always nullptr.
102 for (uint64_t Index = 1; Index < History.size(); ++Index) {
103 DiagIfDeviceCode(History[Index]->getLocation(),
104 diag::note_within_field_of_type)
105 << History[Index]->getType();
106 }
107 };
108
109 if (Check(NextTy, Next)) {
110 if (NeedToEmitNotes)
111 EmitHistory();
112 NeedToEmitNotes = false;
113 }
114
115 // In case pointer/array/reference type is met get pointee type, then
116 // proceed with that type.
117 while (NextTy->isAnyPointerType() || NextTy->isArrayType() ||
118 NextTy->isReferenceType()) {
119 if (NextTy->isArrayType())
120 NextTy = QualType{NextTy->getArrayElementTypeNoTypeQual(), 0};
121 else
122 NextTy = NextTy->getPointeeType();
123 if (Check(NextTy, Next)) {
124 if (NeedToEmitNotes)
125 EmitHistory();
126 NeedToEmitNotes = false;
127 }
128 }
129
130 if (const auto *RecDecl = NextTy->getAsRecordDecl()) {
131 if (auto *NextFD = dyn_cast<FieldDecl>(Next))
132 History.push_back(NextFD);
133 // When nullptr is discovered, this means we've gone back up a level, so
134 // the history should be cleaned.
135 StackForRecursion.push_back(nullptr);
136 llvm::copy(RecDecl->fields(), std::back_inserter(StackForRecursion));
137 }
138 } while (!StackForRecursion.empty());
139}
140
142 SourceLocation LParen,
143 SourceLocation RParen,
144 TypeSourceInfo *TSI) {
145 return SYCLUniqueStableNameExpr::Create(getASTContext(), OpLoc, LParen,
146 RParen, TSI);
147}
148
150 SourceLocation LParen,
151 SourceLocation RParen,
152 ParsedType ParsedTy) {
153 TypeSourceInfo *TSI = nullptr;
154 QualType Ty = SemaRef.GetTypeFromParser(ParsedTy, &TSI);
155
156 if (Ty.isNull())
157 return ExprError();
158 if (!TSI)
159 TSI = getASTContext().getTrivialTypeSourceInfo(Ty, LParen);
160
161 return BuildUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
162}
163
165 // The 'sycl_kernel' attribute applies only to function templates.
166 const auto *FD = cast<FunctionDecl>(D);
167 const FunctionTemplateDecl *FT = FD->getDescribedFunctionTemplate();
168 assert(FT && "Function template is expected");
169
170 // Function template must have at least two template parameters.
172 if (TL->size() < 2) {
173 Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_template_params);
174 return;
175 }
176
177 // Template parameters must be typenames.
178 for (unsigned I = 0; I < 2; ++I) {
179 const NamedDecl *TParam = TL->getParam(I);
180 if (isa<NonTypeTemplateParmDecl>(TParam)) {
181 Diag(FT->getLocation(),
182 diag::warn_sycl_kernel_invalid_template_param_type);
183 return;
184 }
185 }
186
187 // Function must have at least one argument.
189 Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_function_params);
190 return;
191 }
192
193 // Function must return void.
195 if (!RetTy->isVoidType()) {
196 Diag(FT->getLocation(), diag::warn_sycl_kernel_return_type);
197 return;
198 }
199
200 handleSimpleAttribute<SYCLKernelAttr>(*this, D, AL);
201}
202
204 ParsedType PT = AL.getTypeArg();
205 TypeSourceInfo *TSI = nullptr;
206 (void)SemaRef.GetTypeFromParser(PT, &TSI);
207 assert(TSI && "no type source info for attribute argument");
208 D->addAttr(::new (SemaRef.Context)
209 SYCLKernelEntryPointAttr(SemaRef.Context, AL, TSI));
210}
211
212// Given a potentially qualified type, SourceLocationForUserDeclaredType()
213// returns the source location of the canonical declaration of the unqualified
214// desugared user declared type, if any. For non-user declared types, an
215// invalid source location is returned. The intended usage of this function
216// is to identify an appropriate source location, if any, for a
217// "entity declared here" diagnostic note.
220 const Type *T = QT->getUnqualifiedDesugaredType();
221 if (const TagType *TT = dyn_cast<TagType>(T))
222 Loc = TT->getDecl()->getLocation();
223 else if (const ObjCInterfaceType *ObjCIT = dyn_cast<ObjCInterfaceType>(T))
224 Loc = ObjCIT->getDecl()->getLocation();
225 return Loc;
226}
227
229 QualType KernelName) {
230 assert(!KernelName->isDependentType());
231
232 if (!KernelName->isStructureOrClassType()) {
233 // SYCL 2020 section 5.2, "Naming of kernels", only requires that the
234 // kernel name be a C++ typename. However, the definition of "kernel name"
235 // in the glossary states that a kernel name is a class type. Neither
236 // section explicitly states whether the kernel name type can be
237 // cv-qualified. For now, kernel name types are required to be class types
238 // and that they may be cv-qualified. The following issue requests
239 // clarification from the SYCL WG.
240 // https://github.com/KhronosGroup/SYCL-Docs/issues/568
241 S.Diag(Loc, diag::warn_sycl_kernel_name_not_a_class_type) << KernelName;
242 SourceLocation DeclTypeLoc = SourceLocationForUserDeclaredType(KernelName);
243 if (DeclTypeLoc.isValid())
244 S.Diag(DeclTypeLoc, diag::note_entity_declared_at) << KernelName;
245 return true;
246 }
247
248 return false;
249}
250
252 // Ensure that all attributes present on the declaration are consistent
253 // and warn about any redundant ones.
254 SYCLKernelEntryPointAttr *SKEPAttr = nullptr;
255 for (auto *SAI : FD->specific_attrs<SYCLKernelEntryPointAttr>()) {
256 if (!SKEPAttr) {
257 SKEPAttr = SAI;
258 continue;
259 }
260 if (!getASTContext().hasSameType(SAI->getKernelName(),
261 SKEPAttr->getKernelName())) {
262 Diag(SAI->getLocation(), diag::err_sycl_entry_point_invalid_redeclaration)
263 << SAI->getKernelName() << SKEPAttr->getKernelName();
264 Diag(SKEPAttr->getLocation(), diag::note_previous_attribute);
265 SAI->setInvalidAttr();
266 } else {
267 Diag(SAI->getLocation(),
268 diag::warn_sycl_entry_point_redundant_declaration);
269 Diag(SKEPAttr->getLocation(), diag::note_previous_attribute);
270 }
271 }
272 assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");
273
274 // Ensure the kernel name type is valid.
275 if (!SKEPAttr->getKernelName()->isDependentType() &&
276 CheckSYCLKernelName(SemaRef, SKEPAttr->getLocation(),
277 SKEPAttr->getKernelName()))
278 SKEPAttr->setInvalidAttr();
279
280 // Ensure that an attribute present on the previous declaration
281 // matches the one on this declaration.
282 FunctionDecl *PrevFD = FD->getPreviousDecl();
283 if (PrevFD && !PrevFD->isInvalidDecl()) {
284 const auto *PrevSKEPAttr = PrevFD->getAttr<SYCLKernelEntryPointAttr>();
285 if (PrevSKEPAttr && !PrevSKEPAttr->isInvalidAttr()) {
286 if (!getASTContext().hasSameType(SKEPAttr->getKernelName(),
287 PrevSKEPAttr->getKernelName())) {
288 Diag(SKEPAttr->getLocation(),
289 diag::err_sycl_entry_point_invalid_redeclaration)
290 << SKEPAttr->getKernelName() << PrevSKEPAttr->getKernelName();
291 Diag(PrevSKEPAttr->getLocation(), diag::note_previous_decl) << PrevFD;
292 SKEPAttr->setInvalidAttr();
293 }
294 }
295 }
296
297 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
298 if (!MD->isStatic()) {
299 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
300 << /*non-static member function*/ 0;
301 SKEPAttr->setInvalidAttr();
302 }
303 }
304
305 if (FD->isVariadic()) {
306 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
307 << /*variadic function*/ 1;
308 SKEPAttr->setInvalidAttr();
309 }
310
311 if (FD->isDefaulted()) {
312 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
313 << /*defaulted function*/ 3;
314 SKEPAttr->setInvalidAttr();
315 } else if (FD->isDeleted()) {
316 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
317 << /*deleted function*/ 2;
318 SKEPAttr->setInvalidAttr();
319 }
320
321 if (FD->isConsteval()) {
322 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
323 << /*consteval function*/ 5;
324 SKEPAttr->setInvalidAttr();
325 } else if (FD->isConstexpr()) {
326 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
327 << /*constexpr function*/ 4;
328 SKEPAttr->setInvalidAttr();
329 }
330
331 if (FD->isNoReturn()) {
332 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_invalid)
333 << /*function declared with the 'noreturn' attribute*/ 6;
334 SKEPAttr->setInvalidAttr();
335 }
336
337 if (FD->getReturnType()->isUndeducedType()) {
338 Diag(SKEPAttr->getLocation(),
339 diag::err_sycl_entry_point_deduced_return_type);
340 SKEPAttr->setInvalidAttr();
341 } else if (!FD->getReturnType()->isDependentType() &&
342 !FD->getReturnType()->isVoidType()) {
343 Diag(SKEPAttr->getLocation(), diag::err_sycl_entry_point_return_type);
344 SKEPAttr->setInvalidAttr();
345 }
346
347 if (!FD->isInvalidDecl() && !FD->isTemplated() &&
348 !SKEPAttr->isInvalidAttr()) {
349 const SYCLKernelInfo *SKI =
350 getASTContext().findSYCLKernelInfo(SKEPAttr->getKernelName());
351 if (SKI) {
353 // FIXME: This diagnostic should include the origin of the kernel
354 // FIXME: names; not just the locations of the conflicting declarations.
355 Diag(FD->getLocation(), diag::err_sycl_kernel_name_conflict);
357 diag::note_previous_declaration);
358 SKEPAttr->setInvalidAttr();
359 }
360 } else {
362 }
363 }
364}
Defines the Diagnostic-related interfaces.
const Decl * D
llvm::DenseSet< const void * > Visited
Definition: HTMLLogger.cpp:145
This file declares types used to describe SYCL kernels.
SourceLocation Loc
Definition: SemaObjC.cpp:759
static SourceLocation SourceLocationForUserDeclaredType(QualType QT)
Definition: SemaSYCL.cpp:218
static bool isZeroSizedArray(SemaSYCL &S, QualType Ty)
Definition: SemaSYCL.cpp:43
static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc, QualType KernelName)
Definition: SemaSYCL.cpp:228
This file declares semantic analysis for SYCL constructs.
Allows QualTypes to be sorted and hence used in maps and sets.
const ConstantArrayType * getAsConstantArrayType(QualType T) const
Definition: ASTContext.h:2915
bool hasSameType(QualType T1, QualType T2) const
Determine whether the given types T1 and T2 are equivalent.
Definition: ASTContext.h:2732
TypeSourceInfo * getTrivialTypeSourceInfo(QualType T, SourceLocation Loc=SourceLocation()) const
Allocate a TypeSourceInfo where all locations have been initialized to a given location,...
void registerSYCLEntryPointFunction(FunctionDecl *FD)
Generates and stores SYCL kernel metadata for the provided SYCL kernel entry point function.
const SYCLKernelInfo * findSYCLKernelInfo(QualType T) const
Returns a pointer to the metadata generated from the corresponding SYCLkernel entry point if the prov...
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
T * getAttr() const
Definition: DeclBase.h:576
void addAttr(Attr *A)
Definition: DeclBase.cpp:1010
bool isTemplated() const
Determine whether this declaration is a templated entity (whether it is.
Definition: DeclBase.cpp:281
bool isInvalidDecl() const
Definition: DeclBase.h:591
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
Definition: DeclBase.h:562
SourceLocation getLocation() const
Definition: DeclBase.h:442
Represents a function declaration or definition.
Definition: Decl.h:1935
bool isNoReturn() const
Determines whether this function is known to be 'noreturn', through an attribute on its declaration o...
Definition: Decl.cpp:3531
QualType getReturnType() const
Definition: Decl.h:2720
bool isVariadic() const
Whether this function is variadic.
Definition: Decl.cpp:3096
bool isDeleted() const
Whether this function has been deleted.
Definition: Decl.h:2468
bool isConstexpr() const
Whether this is a (C++11) constexpr function or constexpr constructor.
Definition: Decl.h:2398
bool isDefaulted() const
Whether this function is defaulted.
Definition: Decl.h:2313
bool isConsteval() const
Definition: Decl.h:2410
Declaration of a template function.
Definition: DeclTemplate.h:959
This represents a decl that may have a name.
Definition: Decl.h:253
Interfaces are the core concept in Objective-C for object oriented design.
Definition: Type.h:7529
ParsedAttr - Represents a syntactic attribute.
Definition: ParsedAttr.h:129
const ParsedType & getTypeArg() const
Definition: ParsedAttr.h:474
A (possibly-)qualified type.
Definition: Type.h:929
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: Type.h:996
decl_type * getPreviousDecl()
Return the previous declaration of this declaration or NULL if this is the first declaration.
Definition: Redeclarable.h:203
const FunctionDecl * getKernelEntryPointDecl() const
static SYCLUniqueStableNameExpr * Create(const ASTContext &Ctx, SourceLocation OpLoc, SourceLocation LParen, SourceLocation RParen, TypeSourceInfo *TSI)
Definition: Expr.cpp:577
A generic diagnostic builder for errors which may or may not be deferred.
Definition: SemaBase.h:110
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, bool DeferHint=false)
Emit a diagnostic.
Definition: SemaBase.cpp:60
ASTContext & getASTContext() const
Definition: SemaBase.cpp:9
Sema & SemaRef
Definition: SemaBase.h:40
const LangOptions & getLangOpts() const
Definition: SemaBase.cpp:11
SemaDiagnosticBuilder DiagIfDeviceCode(SourceLocation Loc, unsigned DiagID)
Creates a SemaDiagnosticBuilder that emits the diagnostic if the current context is "used as device c...
Definition: SemaSYCL.cpp:28
ExprResult BuildUniqueStableNameExpr(SourceLocation OpLoc, SourceLocation LParen, SourceLocation RParen, TypeSourceInfo *TSI)
Definition: SemaSYCL.cpp:141
void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL)
Definition: SemaSYCL.cpp:203
void deepTypeCheckForDevice(SourceLocation UsedAt, llvm::DenseSet< QualType > Visited, ValueDecl *DeclToCheck)
Definition: SemaSYCL.cpp:49
void handleKernelAttr(Decl *D, const ParsedAttr &AL)
Definition: SemaSYCL.cpp:164
SemaSYCL(Sema &S)
Definition: SemaSYCL.cpp:26
void CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD)
Definition: SemaSYCL.cpp:251
ExprResult ActOnUniqueStableNameExpr(SourceLocation OpLoc, SourceLocation LParen, SourceLocation RParen, ParsedType ParsedTy)
Definition: SemaSYCL.cpp:149
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:464
ASTContext & Context
Definition: Sema.h:909
ASTContext & getASTContext() const
Definition: Sema.h:532
DeclContext * getCurLexicalContext() const
Definition: Sema.h:736
FunctionEmissionStatus getEmissionStatus(const FunctionDecl *Decl, bool Final=false)
Definition: SemaDecl.cpp:20357
static QualType GetTypeFromParser(ParsedType Ty, TypeSourceInfo **TInfo=nullptr)
Definition: SemaType.cpp:2751
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
TemplateParameterList * getTemplateParameters() const
Get the list of template parameters.
Definition: DeclTemplate.h:418
Stores a list of template parameters for a TemplateDecl and its derived classes.
Definition: DeclTemplate.h:73
NamedDecl * getParam(unsigned Idx)
Definition: DeclTemplate.h:147
A container of type source information.
Definition: Type.h:7907
The base class of the type hierarchy.
Definition: Type.h:1828
bool isVoidType() const
Definition: Type.h:8515
bool isArrayType() const
Definition: Type.h:8263
bool isReferenceType() const
Definition: Type.h:8209
const Type * getArrayElementTypeNoTypeQual() const
If this is an array type, return the element type of the array, potentially with type qualifiers miss...
Definition: Type.cpp:460
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:738
bool isDependentType() const
Whether this type is a dependent type, meaning that its definition somehow depends on a template para...
Definition: Type.h:2706
bool isUndeducedType() const
Determine whether this type is an undeduced type, meaning that it somehow involves a C++11 'auto' typ...
Definition: Type.h:8649
bool isStructureOrClassType() const
Definition: Type.cpp:690
bool isAnyPointerType() const
Definition: Type.h:8199
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
Definition: Type.cpp:638
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition: Type.cpp:1920
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition: Decl.h:671
The JSON file list parser is used to communicate input to InstallAPI.
QualType getFunctionOrMethodResultType(const Decl *D)
Definition: Attr.h:98
ExprResult ExprError()
Definition: Ownership.h:264
const FunctionProtoType * T
unsigned getFunctionOrMethodNumParams(const Decl *D)
getFunctionOrMethodNumParams - Return number of function or method parameters.
Definition: Attr.h:64
bool declaresSameEntity(const Decl *D1, const Decl *D2)
Determine whether two declarations declare the same entity.
Definition: DeclBase.h:1274