15ffd83dbSDimitry Andric //===- SemaSYCL.cpp - Semantic Analysis for SYCL constructs ---------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric // This implements Semantic Analysis for SYCL constructs.
95ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
105ffd83dbSDimitry Andric 
11fe6060f1SDimitry Andric #include "clang/AST/Mangle.h"
125ffd83dbSDimitry Andric #include "clang/Sema/Sema.h"
135ffd83dbSDimitry Andric #include "clang/Sema/SemaDiagnostic.h"
145ffd83dbSDimitry Andric 
155ffd83dbSDimitry Andric using namespace clang;
165ffd83dbSDimitry Andric 
175ffd83dbSDimitry Andric // -----------------------------------------------------------------------------
185ffd83dbSDimitry Andric // SYCL device specific diagnostics implementation
195ffd83dbSDimitry Andric // -----------------------------------------------------------------------------
205ffd83dbSDimitry Andric 
SYCLDiagIfDeviceCode(SourceLocation Loc,unsigned DiagID)21e8d8bef9SDimitry Andric Sema::SemaDiagnosticBuilder Sema::SYCLDiagIfDeviceCode(SourceLocation Loc,
225ffd83dbSDimitry Andric                                                        unsigned DiagID) {
235ffd83dbSDimitry Andric   assert(getLangOpts().SYCLIsDevice &&
245ffd83dbSDimitry Andric          "Should only be called during SYCL compilation");
255ffd83dbSDimitry Andric   FunctionDecl *FD = dyn_cast<FunctionDecl>(getCurLexicalContext());
26e8d8bef9SDimitry Andric   SemaDiagnosticBuilder::Kind DiagKind = [this, FD] {
275ffd83dbSDimitry Andric     if (!FD)
28e8d8bef9SDimitry Andric       return SemaDiagnosticBuilder::K_Nop;
295ffd83dbSDimitry Andric     if (getEmissionStatus(FD) == Sema::FunctionEmissionStatus::Emitted)
30e8d8bef9SDimitry Andric       return SemaDiagnosticBuilder::K_ImmediateWithCallStack;
31e8d8bef9SDimitry Andric     return SemaDiagnosticBuilder::K_Deferred;
325ffd83dbSDimitry Andric   }();
33e8d8bef9SDimitry Andric   return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, FD, *this);
345ffd83dbSDimitry Andric }
355ffd83dbSDimitry Andric 
isZeroSizedArray(Sema & SemaRef,QualType Ty)36*04eeddc0SDimitry Andric static bool isZeroSizedArray(Sema &SemaRef, QualType Ty) {
37*04eeddc0SDimitry Andric   if (const auto *CAT = SemaRef.getASTContext().getAsConstantArrayType(Ty))
38*04eeddc0SDimitry Andric     return CAT->getSize() == 0;
39*04eeddc0SDimitry Andric   return false;
40*04eeddc0SDimitry Andric }
41*04eeddc0SDimitry Andric 
deepTypeCheckForSYCLDevice(SourceLocation UsedAt,llvm::DenseSet<QualType> Visited,ValueDecl * DeclToCheck)42*04eeddc0SDimitry Andric void Sema::deepTypeCheckForSYCLDevice(SourceLocation UsedAt,
43*04eeddc0SDimitry Andric                                       llvm::DenseSet<QualType> Visited,
44*04eeddc0SDimitry Andric                                       ValueDecl *DeclToCheck) {
45*04eeddc0SDimitry Andric   assert(getLangOpts().SYCLIsDevice &&
46*04eeddc0SDimitry Andric          "Should only be called during SYCL compilation");
47*04eeddc0SDimitry Andric   // Emit notes only for the first discovered declaration of unsupported type
48*04eeddc0SDimitry Andric   // to avoid mess of notes. This flag is to track that error already happened.
49*04eeddc0SDimitry Andric   bool NeedToEmitNotes = true;
50*04eeddc0SDimitry Andric 
51*04eeddc0SDimitry Andric   auto Check = [&](QualType TypeToCheck, const ValueDecl *D) {
52*04eeddc0SDimitry Andric     bool ErrorFound = false;
53*04eeddc0SDimitry Andric     if (isZeroSizedArray(*this, TypeToCheck)) {
54*04eeddc0SDimitry Andric       SYCLDiagIfDeviceCode(UsedAt, diag::err_typecheck_zero_array_size) << 1;
55*04eeddc0SDimitry Andric       ErrorFound = true;
56*04eeddc0SDimitry Andric     }
57*04eeddc0SDimitry Andric     // Checks for other types can also be done here.
58*04eeddc0SDimitry Andric     if (ErrorFound) {
59*04eeddc0SDimitry Andric       if (NeedToEmitNotes) {
60*04eeddc0SDimitry Andric         if (auto *FD = dyn_cast<FieldDecl>(D))
61*04eeddc0SDimitry Andric           SYCLDiagIfDeviceCode(FD->getLocation(),
62*04eeddc0SDimitry Andric                                diag::note_illegal_field_declared_here)
63*04eeddc0SDimitry Andric               << FD->getType()->isPointerType() << FD->getType();
64*04eeddc0SDimitry Andric         else
65*04eeddc0SDimitry Andric           SYCLDiagIfDeviceCode(D->getLocation(), diag::note_declared_at);
66*04eeddc0SDimitry Andric       }
67*04eeddc0SDimitry Andric     }
68*04eeddc0SDimitry Andric 
69*04eeddc0SDimitry Andric     return ErrorFound;
70*04eeddc0SDimitry Andric   };
71*04eeddc0SDimitry Andric 
72*04eeddc0SDimitry Andric   // In case we have a Record used do the DFS for a bad field.
73*04eeddc0SDimitry Andric   SmallVector<const ValueDecl *, 4> StackForRecursion;
74*04eeddc0SDimitry Andric   StackForRecursion.push_back(DeclToCheck);
75*04eeddc0SDimitry Andric 
76*04eeddc0SDimitry Andric   // While doing DFS save how we get there to emit a nice set of notes.
77*04eeddc0SDimitry Andric   SmallVector<const FieldDecl *, 4> History;
78*04eeddc0SDimitry Andric   History.push_back(nullptr);
79*04eeddc0SDimitry Andric 
80*04eeddc0SDimitry Andric   do {
81*04eeddc0SDimitry Andric     const ValueDecl *Next = StackForRecursion.pop_back_val();
82*04eeddc0SDimitry Andric     if (!Next) {
83*04eeddc0SDimitry Andric       assert(!History.empty());
84*04eeddc0SDimitry Andric       // Found a marker, we have gone up a level.
85*04eeddc0SDimitry Andric       History.pop_back();
86*04eeddc0SDimitry Andric       continue;
87*04eeddc0SDimitry Andric     }
88*04eeddc0SDimitry Andric     QualType NextTy = Next->getType();
89*04eeddc0SDimitry Andric 
90*04eeddc0SDimitry Andric     if (!Visited.insert(NextTy).second)
91*04eeddc0SDimitry Andric       continue;
92*04eeddc0SDimitry Andric 
93*04eeddc0SDimitry Andric     auto EmitHistory = [&]() {
94*04eeddc0SDimitry Andric       // The first element is always nullptr.
95*04eeddc0SDimitry Andric       for (uint64_t Index = 1; Index < History.size(); ++Index) {
96*04eeddc0SDimitry Andric         SYCLDiagIfDeviceCode(History[Index]->getLocation(),
97*04eeddc0SDimitry Andric                              diag::note_within_field_of_type)
98*04eeddc0SDimitry Andric             << History[Index]->getType();
99*04eeddc0SDimitry Andric       }
100*04eeddc0SDimitry Andric     };
101*04eeddc0SDimitry Andric 
102*04eeddc0SDimitry Andric     if (Check(NextTy, Next)) {
103*04eeddc0SDimitry Andric       if (NeedToEmitNotes)
104*04eeddc0SDimitry Andric         EmitHistory();
105*04eeddc0SDimitry Andric       NeedToEmitNotes = false;
106*04eeddc0SDimitry Andric     }
107*04eeddc0SDimitry Andric 
108*04eeddc0SDimitry Andric     // In case pointer/array/reference type is met get pointee type, then
109*04eeddc0SDimitry Andric     // proceed with that type.
110*04eeddc0SDimitry Andric     while (NextTy->isAnyPointerType() || NextTy->isArrayType() ||
111*04eeddc0SDimitry Andric            NextTy->isReferenceType()) {
112*04eeddc0SDimitry Andric       if (NextTy->isArrayType())
113*04eeddc0SDimitry Andric         NextTy = QualType{NextTy->getArrayElementTypeNoTypeQual(), 0};
114*04eeddc0SDimitry Andric       else
115*04eeddc0SDimitry Andric         NextTy = NextTy->getPointeeType();
116*04eeddc0SDimitry Andric       if (Check(NextTy, Next)) {
117*04eeddc0SDimitry Andric         if (NeedToEmitNotes)
118*04eeddc0SDimitry Andric           EmitHistory();
119*04eeddc0SDimitry Andric         NeedToEmitNotes = false;
120*04eeddc0SDimitry Andric       }
121*04eeddc0SDimitry Andric     }
122*04eeddc0SDimitry Andric 
123*04eeddc0SDimitry Andric     if (const auto *RecDecl = NextTy->getAsRecordDecl()) {
124*04eeddc0SDimitry Andric       if (auto *NextFD = dyn_cast<FieldDecl>(Next))
125*04eeddc0SDimitry Andric         History.push_back(NextFD);
126*04eeddc0SDimitry Andric       // When nullptr is discovered, this means we've gone back up a level, so
127*04eeddc0SDimitry Andric       // the history should be cleaned.
128*04eeddc0SDimitry Andric       StackForRecursion.push_back(nullptr);
129*04eeddc0SDimitry Andric       llvm::copy(RecDecl->fields(), std::back_inserter(StackForRecursion));
130*04eeddc0SDimitry Andric     }
131*04eeddc0SDimitry Andric   } while (!StackForRecursion.empty());
132*04eeddc0SDimitry Andric }
133