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 | |
15 | using namespace clang; |
16 | |
17 | // ----------------------------------------------------------------------------- |
18 | // SYCL device specific diagnostics implementation |
19 | // ----------------------------------------------------------------------------- |
20 | |
21 | Sema::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(Decl: 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 | |
36 | static bool isZeroSizedArray(Sema &SemaRef, QualType Ty) { |
37 | if (const auto *CAT = SemaRef.getASTContext().getAsConstantArrayType(T: Ty)) |
38 | return CAT->getSize() == 0; |
39 | return false; |
40 | } |
41 | |
42 | void Sema::deepTypeCheckForSYCLDevice(SourceLocation UsedAt, |
43 | llvm::DenseSet<QualType> Visited, |
44 | ValueDecl *DeclToCheck) { |
45 | assert(getLangOpts().SYCLIsDevice && |
46 | "Should only be called during SYCL compilation" ); |
47 | // Emit notes only for the first discovered declaration of unsupported type |
48 | // to avoid mess of notes. This flag is to track that error already happened. |
49 | bool NeedToEmitNotes = true; |
50 | |
51 | auto Check = [&](QualType TypeToCheck, const ValueDecl *D) { |
52 | bool ErrorFound = false; |
53 | if (isZeroSizedArray(SemaRef&: *this, Ty: TypeToCheck)) { |
54 | SYCLDiagIfDeviceCode(UsedAt, diag::err_typecheck_zero_array_size) << 1; |
55 | ErrorFound = true; |
56 | } |
57 | // Checks for other types can also be done here. |
58 | if (ErrorFound) { |
59 | if (NeedToEmitNotes) { |
60 | if (auto *FD = dyn_cast<FieldDecl>(D)) |
61 | SYCLDiagIfDeviceCode(FD->getLocation(), |
62 | diag::note_illegal_field_declared_here) |
63 | << FD->getType()->isPointerType() << FD->getType(); |
64 | else |
65 | SYCLDiagIfDeviceCode(D->getLocation(), diag::note_declared_at); |
66 | } |
67 | } |
68 | |
69 | return ErrorFound; |
70 | }; |
71 | |
72 | // In case we have a Record used do the DFS for a bad field. |
73 | SmallVector<const ValueDecl *, 4> StackForRecursion; |
74 | StackForRecursion.push_back(Elt: DeclToCheck); |
75 | |
76 | // While doing DFS save how we get there to emit a nice set of notes. |
77 | SmallVector<const FieldDecl *, 4> History; |
78 | History.push_back(Elt: nullptr); |
79 | |
80 | do { |
81 | const ValueDecl *Next = StackForRecursion.pop_back_val(); |
82 | if (!Next) { |
83 | assert(!History.empty()); |
84 | // Found a marker, we have gone up a level. |
85 | History.pop_back(); |
86 | continue; |
87 | } |
88 | QualType NextTy = Next->getType(); |
89 | |
90 | if (!Visited.insert(V: NextTy).second) |
91 | continue; |
92 | |
93 | auto EmitHistory = [&]() { |
94 | // The first element is always nullptr. |
95 | for (uint64_t Index = 1; Index < History.size(); ++Index) { |
96 | SYCLDiagIfDeviceCode(History[Index]->getLocation(), |
97 | diag::note_within_field_of_type) |
98 | << History[Index]->getType(); |
99 | } |
100 | }; |
101 | |
102 | if (Check(NextTy, Next)) { |
103 | if (NeedToEmitNotes) |
104 | EmitHistory(); |
105 | NeedToEmitNotes = false; |
106 | } |
107 | |
108 | // In case pointer/array/reference type is met get pointee type, then |
109 | // proceed with that type. |
110 | while (NextTy->isAnyPointerType() || NextTy->isArrayType() || |
111 | NextTy->isReferenceType()) { |
112 | if (NextTy->isArrayType()) |
113 | NextTy = QualType{NextTy->getArrayElementTypeNoTypeQual(), 0}; |
114 | else |
115 | NextTy = NextTy->getPointeeType(); |
116 | if (Check(NextTy, Next)) { |
117 | if (NeedToEmitNotes) |
118 | EmitHistory(); |
119 | NeedToEmitNotes = false; |
120 | } |
121 | } |
122 | |
123 | if (const auto *RecDecl = NextTy->getAsRecordDecl()) { |
124 | if (auto *NextFD = dyn_cast<FieldDecl>(Val: Next)) |
125 | History.push_back(Elt: NextFD); |
126 | // When nullptr is discovered, this means we've gone back up a level, so |
127 | // the history should be cleaned. |
128 | StackForRecursion.push_back(Elt: nullptr); |
129 | llvm::copy(Range: RecDecl->fields(), Out: std::back_inserter(x&: StackForRecursion)); |
130 | } |
131 | } while (!StackForRecursion.empty()); |
132 | } |
133 | |