xref: /openbsd-src/gnu/llvm/clang/lib/Sema/SemaSYCL.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1ec727ea7Spatrick //===- SemaSYCL.cpp - Semantic Analysis for SYCL constructs ---------------===//
2ec727ea7Spatrick //
3ec727ea7Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ec727ea7Spatrick // See https://llvm.org/LICENSE.txt for license information.
5ec727ea7Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ec727ea7Spatrick //
7ec727ea7Spatrick //===----------------------------------------------------------------------===//
8ec727ea7Spatrick // This implements Semantic Analysis for SYCL constructs.
9ec727ea7Spatrick //===----------------------------------------------------------------------===//
10ec727ea7Spatrick 
11a9ac8606Spatrick #include "clang/AST/Mangle.h"
12ec727ea7Spatrick #include "clang/Sema/Sema.h"
13ec727ea7Spatrick #include "clang/Sema/SemaDiagnostic.h"
14ec727ea7Spatrick 
15ec727ea7Spatrick using namespace clang;
16ec727ea7Spatrick 
17ec727ea7Spatrick // -----------------------------------------------------------------------------
18ec727ea7Spatrick // SYCL device specific diagnostics implementation
19ec727ea7Spatrick // -----------------------------------------------------------------------------
20ec727ea7Spatrick 
SYCLDiagIfDeviceCode(SourceLocation Loc,unsigned DiagID)21a9ac8606Spatrick Sema::SemaDiagnosticBuilder Sema::SYCLDiagIfDeviceCode(SourceLocation Loc,
22ec727ea7Spatrick                                                        unsigned DiagID) {
23ec727ea7Spatrick   assert(getLangOpts().SYCLIsDevice &&
24ec727ea7Spatrick          "Should only be called during SYCL compilation");
25ec727ea7Spatrick   FunctionDecl *FD = dyn_cast<FunctionDecl>(getCurLexicalContext());
26a9ac8606Spatrick   SemaDiagnosticBuilder::Kind DiagKind = [this, FD] {
27ec727ea7Spatrick     if (!FD)
28a9ac8606Spatrick       return SemaDiagnosticBuilder::K_Nop;
29ec727ea7Spatrick     if (getEmissionStatus(FD) == Sema::FunctionEmissionStatus::Emitted)
30a9ac8606Spatrick       return SemaDiagnosticBuilder::K_ImmediateWithCallStack;
31a9ac8606Spatrick     return SemaDiagnosticBuilder::K_Deferred;
32ec727ea7Spatrick   }();
33a9ac8606Spatrick   return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, FD, *this);
34ec727ea7Spatrick }
35ec727ea7Spatrick 
checkSYCLDeviceFunction(SourceLocation Loc,FunctionDecl * Callee)36ec727ea7Spatrick bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) {
37ec727ea7Spatrick   assert(getLangOpts().SYCLIsDevice &&
38ec727ea7Spatrick          "Should only be called during SYCL compilation");
39ec727ea7Spatrick   assert(Callee && "Callee may not be null.");
40ec727ea7Spatrick 
41*12c85518Srobert   // Errors in an unevaluated context don't need to be generated,
42ec727ea7Spatrick   // so we can safely skip them.
43ec727ea7Spatrick   if (isUnevaluatedContext() || isConstantEvaluated())
44ec727ea7Spatrick     return true;
45ec727ea7Spatrick 
46a9ac8606Spatrick   SemaDiagnosticBuilder::Kind DiagKind = SemaDiagnosticBuilder::K_Nop;
47ec727ea7Spatrick 
48a9ac8606Spatrick   return DiagKind != SemaDiagnosticBuilder::K_Immediate &&
49a9ac8606Spatrick          DiagKind != SemaDiagnosticBuilder::K_ImmediateWithCallStack;
50a9ac8606Spatrick }
51a9ac8606Spatrick 
isZeroSizedArray(Sema & SemaRef,QualType Ty)52*12c85518Srobert static bool isZeroSizedArray(Sema &SemaRef, QualType Ty) {
53*12c85518Srobert   if (const auto *CAT = SemaRef.getASTContext().getAsConstantArrayType(Ty))
54*12c85518Srobert     return CAT->getSize() == 0;
55*12c85518Srobert   return false;
56a9ac8606Spatrick }
57a9ac8606Spatrick 
deepTypeCheckForSYCLDevice(SourceLocation UsedAt,llvm::DenseSet<QualType> Visited,ValueDecl * DeclToCheck)58*12c85518Srobert void Sema::deepTypeCheckForSYCLDevice(SourceLocation UsedAt,
59*12c85518Srobert                                       llvm::DenseSet<QualType> Visited,
60*12c85518Srobert                                       ValueDecl *DeclToCheck) {
61*12c85518Srobert   assert(getLangOpts().SYCLIsDevice &&
62*12c85518Srobert          "Should only be called during SYCL compilation");
63*12c85518Srobert   // Emit notes only for the first discovered declaration of unsupported type
64*12c85518Srobert   // to avoid mess of notes. This flag is to track that error already happened.
65*12c85518Srobert   bool NeedToEmitNotes = true;
66*12c85518Srobert 
67*12c85518Srobert   auto Check = [&](QualType TypeToCheck, const ValueDecl *D) {
68*12c85518Srobert     bool ErrorFound = false;
69*12c85518Srobert     if (isZeroSizedArray(*this, TypeToCheck)) {
70*12c85518Srobert       SYCLDiagIfDeviceCode(UsedAt, diag::err_typecheck_zero_array_size) << 1;
71*12c85518Srobert       ErrorFound = true;
72*12c85518Srobert     }
73*12c85518Srobert     // Checks for other types can also be done here.
74*12c85518Srobert     if (ErrorFound) {
75*12c85518Srobert       if (NeedToEmitNotes) {
76*12c85518Srobert         if (auto *FD = dyn_cast<FieldDecl>(D))
77*12c85518Srobert           SYCLDiagIfDeviceCode(FD->getLocation(),
78*12c85518Srobert                                diag::note_illegal_field_declared_here)
79*12c85518Srobert               << FD->getType()->isPointerType() << FD->getType();
80*12c85518Srobert         else
81*12c85518Srobert           SYCLDiagIfDeviceCode(D->getLocation(), diag::note_declared_at);
82*12c85518Srobert       }
83*12c85518Srobert     }
84*12c85518Srobert 
85*12c85518Srobert     return ErrorFound;
86a9ac8606Spatrick   };
87a9ac8606Spatrick 
88*12c85518Srobert   // In case we have a Record used do the DFS for a bad field.
89*12c85518Srobert   SmallVector<const ValueDecl *, 4> StackForRecursion;
90*12c85518Srobert   StackForRecursion.push_back(DeclToCheck);
91*12c85518Srobert 
92*12c85518Srobert   // While doing DFS save how we get there to emit a nice set of notes.
93*12c85518Srobert   SmallVector<const FieldDecl *, 4> History;
94*12c85518Srobert   History.push_back(nullptr);
95*12c85518Srobert 
96*12c85518Srobert   do {
97*12c85518Srobert     const ValueDecl *Next = StackForRecursion.pop_back_val();
98*12c85518Srobert     if (!Next) {
99*12c85518Srobert       assert(!History.empty());
100*12c85518Srobert       // Found a marker, we have gone up a level.
101*12c85518Srobert       History.pop_back();
102*12c85518Srobert       continue;
103*12c85518Srobert     }
104*12c85518Srobert     QualType NextTy = Next->getType();
105*12c85518Srobert 
106*12c85518Srobert     if (!Visited.insert(NextTy).second)
107*12c85518Srobert       continue;
108*12c85518Srobert 
109*12c85518Srobert     auto EmitHistory = [&]() {
110*12c85518Srobert       // The first element is always nullptr.
111*12c85518Srobert       for (uint64_t Index = 1; Index < History.size(); ++Index) {
112*12c85518Srobert         SYCLDiagIfDeviceCode(History[Index]->getLocation(),
113*12c85518Srobert                              diag::note_within_field_of_type)
114*12c85518Srobert             << History[Index]->getType();
115*12c85518Srobert       }
116*12c85518Srobert     };
117*12c85518Srobert 
118*12c85518Srobert     if (Check(NextTy, Next)) {
119*12c85518Srobert       if (NeedToEmitNotes)
120*12c85518Srobert         EmitHistory();
121*12c85518Srobert       NeedToEmitNotes = false;
122*12c85518Srobert     }
123*12c85518Srobert 
124*12c85518Srobert     // In case pointer/array/reference type is met get pointee type, then
125*12c85518Srobert     // proceed with that type.
126*12c85518Srobert     while (NextTy->isAnyPointerType() || NextTy->isArrayType() ||
127*12c85518Srobert            NextTy->isReferenceType()) {
128*12c85518Srobert       if (NextTy->isArrayType())
129*12c85518Srobert         NextTy = QualType{NextTy->getArrayElementTypeNoTypeQual(), 0};
130*12c85518Srobert       else
131*12c85518Srobert         NextTy = NextTy->getPointeeType();
132*12c85518Srobert       if (Check(NextTy, Next)) {
133*12c85518Srobert         if (NeedToEmitNotes)
134*12c85518Srobert           EmitHistory();
135*12c85518Srobert         NeedToEmitNotes = false;
136*12c85518Srobert       }
137*12c85518Srobert     }
138*12c85518Srobert 
139*12c85518Srobert     if (const auto *RecDecl = NextTy->getAsRecordDecl()) {
140*12c85518Srobert       if (auto *NextFD = dyn_cast<FieldDecl>(Next))
141*12c85518Srobert         History.push_back(NextFD);
142*12c85518Srobert       // When nullptr is discovered, this means we've gone back up a level, so
143*12c85518Srobert       // the history should be cleaned.
144*12c85518Srobert       StackForRecursion.push_back(nullptr);
145*12c85518Srobert       llvm::copy(RecDecl->fields(), std::back_inserter(StackForRecursion));
146*12c85518Srobert     }
147*12c85518Srobert   } while (!StackForRecursion.empty());
148ec727ea7Spatrick }
149