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 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
//===- 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/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);
}
|