1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
//===- 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());
}
|