forked from OSchip/llvm-project
149 lines
5.4 KiB
C++
149 lines
5.4 KiB
C++
//===- 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());
|
|
}
|