1//===- SemaSYCL.cpp - Semantic Analysis for SYCL constructs ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// This implements Semantic Analysis for SYCL constructs.
9//===----------------------------------------------------------------------===//
10
11#include "clang/AST/Mangle.h"
12#include "clang/Sema/Sema.h"
13#include "clang/Sema/SemaDiagnostic.h"
14
15using namespace clang;
16
17// -----------------------------------------------------------------------------
18// SYCL device specific diagnostics implementation
19// -----------------------------------------------------------------------------
20
21Sema::SemaDiagnosticBuilder Sema::SYCLDiagIfDeviceCode(SourceLocation Loc,
22                                                       unsigned DiagID) {
23  assert(getLangOpts().SYCLIsDevice &&
24         "Should only be called during SYCL compilation");
25  FunctionDecl *FD = dyn_cast<FunctionDecl>(getCurLexicalContext());
26  SemaDiagnosticBuilder::Kind DiagKind = [this, FD] {
27    if (!FD)
28      return SemaDiagnosticBuilder::K_Nop;
29    if (getEmissionStatus(FD) == Sema::FunctionEmissionStatus::Emitted)
30      return SemaDiagnosticBuilder::K_ImmediateWithCallStack;
31    return SemaDiagnosticBuilder::K_Deferred;
32  }();
33  return SemaDiagnosticBuilder(DiagKind, Loc, DiagID, FD, *this);
34}
35
36bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) {
37  assert(getLangOpts().SYCLIsDevice &&
38         "Should only be called during SYCL compilation");
39  assert(Callee && "Callee may not be null.");
40
41  // Errors in an unevaluated context don't need to be generated,
42  // so we can safely skip them.
43  if (isUnevaluatedContext() || isConstantEvaluated())
44    return true;
45
46  SemaDiagnosticBuilder::Kind DiagKind = SemaDiagnosticBuilder::K_Nop;
47
48  return DiagKind != SemaDiagnosticBuilder::K_Immediate &&
49         DiagKind != SemaDiagnosticBuilder::K_ImmediateWithCallStack;
50}
51
52static bool isZeroSizedArray(Sema &SemaRef, QualType Ty) {
53  if (const auto *CAT = SemaRef.getASTContext().getAsConstantArrayType(Ty))
54    return CAT->getSize() == 0;
55  return false;
56}
57
58void Sema::deepTypeCheckForSYCLDevice(SourceLocation UsedAt,
59                                      llvm::DenseSet<QualType> Visited,
60                                      ValueDecl *DeclToCheck) {
61  assert(getLangOpts().SYCLIsDevice &&
62         "Should only be called during SYCL compilation");
63  // Emit notes only for the first discovered declaration of unsupported type
64  // to avoid mess of notes. This flag is to track that error already happened.
65  bool NeedToEmitNotes = true;
66
67  auto Check = [&](QualType TypeToCheck, const ValueDecl *D) {
68    bool ErrorFound = false;
69    if (isZeroSizedArray(*this, TypeToCheck)) {
70      SYCLDiagIfDeviceCode(UsedAt, diag::err_typecheck_zero_array_size) << 1;
71      ErrorFound = true;
72    }
73    // Checks for other types can also be done here.
74    if (ErrorFound) {
75      if (NeedToEmitNotes) {
76        if (auto *FD = dyn_cast<FieldDecl>(D))
77          SYCLDiagIfDeviceCode(FD->getLocation(),
78                               diag::note_illegal_field_declared_here)
79              << FD->getType()->isPointerType() << FD->getType();
80        else
81          SYCLDiagIfDeviceCode(D->getLocation(), diag::note_declared_at);
82      }
83    }
84
85    return ErrorFound;
86  };
87
88  // In case we have a Record used do the DFS for a bad field.
89  SmallVector<const ValueDecl *, 4> StackForRecursion;
90  StackForRecursion.push_back(DeclToCheck);
91
92  // While doing DFS save how we get there to emit a nice set of notes.
93  SmallVector<const FieldDecl *, 4> History;
94  History.push_back(nullptr);
95
96  do {
97    const ValueDecl *Next = StackForRecursion.pop_back_val();
98    if (!Next) {
99      assert(!History.empty());
100      // Found a marker, we have gone up a level.
101      History.pop_back();
102      continue;
103    }
104    QualType NextTy = Next->getType();
105
106    if (!Visited.insert(NextTy).second)
107      continue;
108
109    auto EmitHistory = [&]() {
110      // The first element is always nullptr.
111      for (uint64_t Index = 1; Index < History.size(); ++Index) {
112        SYCLDiagIfDeviceCode(History[Index]->getLocation(),
113                             diag::note_within_field_of_type)
114            << History[Index]->getType();
115      }
116    };
117
118    if (Check(NextTy, Next)) {
119      if (NeedToEmitNotes)
120        EmitHistory();
121      NeedToEmitNotes = false;
122    }
123
124    // In case pointer/array/reference type is met get pointee type, then
125    // proceed with that type.
126    while (NextTy->isAnyPointerType() || NextTy->isArrayType() ||
127           NextTy->isReferenceType()) {
128      if (NextTy->isArrayType())
129        NextTy = QualType{NextTy->getArrayElementTypeNoTypeQual(), 0};
130      else
131        NextTy = NextTy->getPointeeType();
132      if (Check(NextTy, Next)) {
133        if (NeedToEmitNotes)
134          EmitHistory();
135        NeedToEmitNotes = false;
136      }
137    }
138
139    if (const auto *RecDecl = NextTy->getAsRecordDecl()) {
140      if (auto *NextFD = dyn_cast<FieldDecl>(Next))
141        History.push_back(NextFD);
142      // When nullptr is discovered, this means we've gone back up a level, so
143      // the history should be cleaned.
144      StackForRecursion.push_back(nullptr);
145      llvm::copy(RecDecl->fields(), std::back_inserter(StackForRecursion));
146    }
147  } while (!StackForRecursion.empty());
148}
149