llvm-project/clang/lib/Sema/SemaSYCL.cpp
Vlad Serebrennikov 27d37ee4d0 [clang][NFC] Clean up Sema headers
When various `Sema*.h` and `Sema*.cpp` files were created, cleanup of
`Sema.h` includes and forward declarations was left for the later.
Now's the time. This commit touches `Sema.h` and Sema components:
1. Unused includes are removed.
2. Unused forward declarations are removed.
3. Missing includes are added (those files are largely IWYU-clean now).
4. Includes were converted into forward declarations where possible.

As this commit focuses on headers, all changes to `.cpp` files were
minimal, and were aiming at keeping everything buildable.
2024-08-17 14:57:59 +03:00

201 lines
7.2 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/Sema/SemaSYCL.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Sema/Attr.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
// -----------------------------------------------------------------------------
// SYCL device specific diagnostics implementation
// -----------------------------------------------------------------------------
SemaSYCL::SemaSYCL(Sema &S) : SemaBase(S) {}
Sema::SemaDiagnosticBuilder SemaSYCL::DiagIfDeviceCode(SourceLocation Loc,
unsigned DiagID) {
assert(getLangOpts().SYCLIsDevice &&
"Should only be called during SYCL compilation");
FunctionDecl *FD = dyn_cast<FunctionDecl>(SemaRef.getCurLexicalContext());
SemaDiagnosticBuilder::Kind DiagKind = [this, FD] {
if (!FD)
return SemaDiagnosticBuilder::K_Nop;
if (SemaRef.getEmissionStatus(FD) == Sema::FunctionEmissionStatus::Emitted)
return SemaDiagnosticBuilder::K_ImmediateWithCallStack;
return SemaDiagnosticBuilder::K_Deferred;
}();
return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, FD, SemaRef);
}
static bool isZeroSizedArray(SemaSYCL &S, QualType Ty) {
if (const auto *CAT = S.getASTContext().getAsConstantArrayType(Ty))
return CAT->isZeroSize();
return false;
}
void SemaSYCL::deepTypeCheckForDevice(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)) {
DiagIfDeviceCode(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))
DiagIfDeviceCode(FD->getLocation(),
diag::note_illegal_field_declared_here)
<< FD->getType()->isPointerType() << FD->getType();
else
DiagIfDeviceCode(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) {
DiagIfDeviceCode(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());
}
ExprResult SemaSYCL::BuildUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen,
TypeSourceInfo *TSI) {
return SYCLUniqueStableNameExpr::Create(getASTContext(), OpLoc, LParen,
RParen, TSI);
}
ExprResult SemaSYCL::ActOnUniqueStableNameExpr(SourceLocation OpLoc,
SourceLocation LParen,
SourceLocation RParen,
ParsedType ParsedTy) {
TypeSourceInfo *TSI = nullptr;
QualType Ty = SemaRef.GetTypeFromParser(ParsedTy, &TSI);
if (Ty.isNull())
return ExprError();
if (!TSI)
TSI = getASTContext().getTrivialTypeSourceInfo(Ty, LParen);
return BuildUniqueStableNameExpr(OpLoc, LParen, RParen, TSI);
}
void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) {
// The 'sycl_kernel' attribute applies only to function templates.
const auto *FD = cast<FunctionDecl>(D);
const FunctionTemplateDecl *FT = FD->getDescribedFunctionTemplate();
assert(FT && "Function template is expected");
// Function template must have at least two template parameters.
const TemplateParameterList *TL = FT->getTemplateParameters();
if (TL->size() < 2) {
Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_template_params);
return;
}
// Template parameters must be typenames.
for (unsigned I = 0; I < 2; ++I) {
const NamedDecl *TParam = TL->getParam(I);
if (isa<NonTypeTemplateParmDecl>(TParam)) {
Diag(FT->getLocation(),
diag::warn_sycl_kernel_invalid_template_param_type);
return;
}
}
// Function must have at least one argument.
if (getFunctionOrMethodNumParams(D) != 1) {
Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_function_params);
return;
}
// Function must return void.
QualType RetTy = getFunctionOrMethodResultType(D);
if (!RetTy->isVoidType()) {
Diag(FT->getLocation(), diag::warn_sycl_kernel_return_type);
return;
}
handleSimpleAttribute<SYCLKernelAttr>(*this, D, AL);
}