| //===- SemaSYCL.cpp - Semantic Analysis for SYCL constructs ---------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // This implements Semantic Analysis for SYCL constructs. |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/Mangle.h" |
| #include "clang/Sema/Sema.h" |
| #include "clang/Sema/SemaDiagnostic.h" |
| |
| using namespace clang; |
| |
| // ----------------------------------------------------------------------------- |
| // SYCL device specific diagnostics implementation |
| // ----------------------------------------------------------------------------- |
| |
| Sema::SemaDiagnosticBuilder Sema::SYCLDiagIfDeviceCode(SourceLocation Loc, |
| unsigned DiagID) { |
| assert(getLangOpts().SYCLIsDevice && |
| "Should only be called during SYCL compilation"); |
| FunctionDecl *FD = dyn_cast<FunctionDecl>(getCurLexicalContext()); |
| SemaDiagnosticBuilder::Kind DiagKind = [this, FD] { |
| if (!FD) |
| return SemaDiagnosticBuilder::K_Nop; |
| if (getEmissionStatus(FD) == Sema::FunctionEmissionStatus::Emitted) |
| return SemaDiagnosticBuilder::K_ImmediateWithCallStack; |
| return SemaDiagnosticBuilder::K_Deferred; |
| }(); |
| return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, FD, *this); |
| } |
| |
| bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) { |
| assert(getLangOpts().SYCLIsDevice && |
| "Should only be called during SYCL compilation"); |
| assert(Callee && "Callee may not be null."); |
| |
| // Errors in an unevaluated context don't need to be generated, |
| // so we can safely skip them. |
| if (isUnevaluatedContext() || isConstantEvaluated()) |
| return true; |
| |
| SemaDiagnosticBuilder::Kind DiagKind = SemaDiagnosticBuilder::K_Nop; |
| |
| return DiagKind != SemaDiagnosticBuilder::K_Immediate && |
| DiagKind != SemaDiagnosticBuilder::K_ImmediateWithCallStack; |
| } |
| |
| static bool isZeroSizedArray(Sema &SemaRef, QualType Ty) { |
| if (const auto *CAT = SemaRef.getASTContext().getAsConstantArrayType(Ty)) |
| return CAT->getSize() == 0; |
| return false; |
| } |
| |
| void Sema::deepTypeCheckForSYCLDevice(SourceLocation UsedAt, |
| llvm::DenseSet<QualType> Visited, |
| ValueDecl *DeclToCheck) { |
| assert(getLangOpts().SYCLIsDevice && |
| "Should only be called during SYCL compilation"); |
| // Emit notes only for the first discovered declaration of unsupported type |
| // to avoid mess of notes. This flag is to track that error already happened. |
| bool NeedToEmitNotes = true; |
| |
| auto Check = [&](QualType TypeToCheck, const ValueDecl *D) { |
| bool ErrorFound = false; |
| if (isZeroSizedArray(*this, TypeToCheck)) { |
| SYCLDiagIfDeviceCode(UsedAt, diag::err_typecheck_zero_array_size) << 1; |
| ErrorFound = true; |
| } |
| // Checks for other types can also be done here. |
| if (ErrorFound) { |
| if (NeedToEmitNotes) { |
| if (auto *FD = dyn_cast<FieldDecl>(D)) |
| SYCLDiagIfDeviceCode(FD->getLocation(), |
| diag::note_illegal_field_declared_here) |
| << FD->getType()->isPointerType() << FD->getType(); |
| else |
| SYCLDiagIfDeviceCode(D->getLocation(), diag::note_declared_at); |
| } |
| } |
| |
| return ErrorFound; |
| }; |
| |
| // In case we have a Record used do the DFS for a bad field. |
| SmallVector<const ValueDecl *, 4> StackForRecursion; |
| StackForRecursion.push_back(DeclToCheck); |
| |
| // While doing DFS save how we get there to emit a nice set of notes. |
| SmallVector<const FieldDecl *, 4> History; |
| History.push_back(nullptr); |
| |
| do { |
| const ValueDecl *Next = StackForRecursion.pop_back_val(); |
| if (!Next) { |
| assert(!History.empty()); |
| // Found a marker, we have gone up a level. |
| History.pop_back(); |
| continue; |
| } |
| QualType NextTy = Next->getType(); |
| |
| if (!Visited.insert(NextTy).second) |
| continue; |
| |
| auto EmitHistory = [&]() { |
| // The first element is always nullptr. |
| for (uint64_t Index = 1; Index < History.size(); ++Index) { |
| SYCLDiagIfDeviceCode(History[Index]->getLocation(), |
| diag::note_within_field_of_type) |
| << History[Index]->getType(); |
| } |
| }; |
| |
| if (Check(NextTy, Next)) { |
| if (NeedToEmitNotes) |
| EmitHistory(); |
| NeedToEmitNotes = false; |
| } |
| |
| // In case pointer/array/reference type is met get pointee type, then |
| // proceed with that type. |
| while (NextTy->isAnyPointerType() || NextTy->isArrayType() || |
| NextTy->isReferenceType()) { |
| if (NextTy->isArrayType()) |
| NextTy = QualType{NextTy->getArrayElementTypeNoTypeQual(), 0}; |
| else |
| NextTy = NextTy->getPointeeType(); |
| if (Check(NextTy, Next)) { |
| if (NeedToEmitNotes) |
| EmitHistory(); |
| NeedToEmitNotes = false; |
| } |
| } |
| |
| if (const auto *RecDecl = NextTy->getAsRecordDecl()) { |
| if (auto *NextFD = dyn_cast<FieldDecl>(Next)) |
| History.push_back(NextFD); |
| // When nullptr is discovered, this means we've gone back up a level, so |
| // the history should be cleaned. |
| StackForRecursion.push_back(nullptr); |
| llvm::copy(RecDecl->fields(), std::back_inserter(StackForRecursion)); |
| } |
| } while (!StackForRecursion.empty()); |
| } |