1 | //===--- SemaBase.h - Common utilities for semantic analysis-----*- C++ -*-===// |
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 | // |
9 | // This file defines the SemaBase class, which provides utilities for Sema |
10 | // and its parts like SemaOpenACC. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_CLANG_SEMA_SEMABASE_H |
15 | #define LLVM_CLANG_SEMA_SEMABASE_H |
16 | |
17 | #include "clang/AST/Decl.h" |
18 | #include "clang/AST/Redeclarable.h" |
19 | #include "clang/Basic/Diagnostic.h" |
20 | #include "clang/Basic/PartialDiagnostic.h" |
21 | #include "clang/Basic/SourceLocation.h" |
22 | #include "clang/Sema/Ownership.h" |
23 | #include "llvm/ADT/DenseMap.h" |
24 | #include <optional> |
25 | #include <type_traits> |
26 | #include <utility> |
27 | #include <vector> |
28 | |
29 | namespace clang { |
30 | |
31 | class ASTContext; |
32 | class DiagnosticsEngine; |
33 | class LangOptions; |
34 | class Sema; |
35 | |
36 | class SemaBase { |
37 | public: |
38 | SemaBase(Sema &S); |
39 | |
40 | Sema &SemaRef; |
41 | |
42 | ASTContext &getASTContext() const; |
43 | DiagnosticsEngine &getDiagnostics() const; |
44 | const LangOptions &getLangOpts() const; |
45 | DeclContext *getCurContext() const; |
46 | |
47 | /// Helper class that creates diagnostics with optional |
48 | /// template instantiation stacks. |
49 | /// |
50 | /// This class provides a wrapper around the basic DiagnosticBuilder |
51 | /// class that emits diagnostics. ImmediateDiagBuilder is |
52 | /// responsible for emitting the diagnostic (as DiagnosticBuilder |
53 | /// does) and, if the diagnostic comes from inside a template |
54 | /// instantiation, printing the template instantiation stack as |
55 | /// well. |
56 | class ImmediateDiagBuilder : public DiagnosticBuilder { |
57 | Sema &SemaRef; |
58 | unsigned DiagID; |
59 | |
60 | public: |
61 | ImmediateDiagBuilder(DiagnosticBuilder &DB, Sema &SemaRef, unsigned DiagID) |
62 | : DiagnosticBuilder(DB), SemaRef(SemaRef), DiagID(DiagID) {} |
63 | ImmediateDiagBuilder(DiagnosticBuilder &&DB, Sema &SemaRef, unsigned DiagID) |
64 | : DiagnosticBuilder(DB), SemaRef(SemaRef), DiagID(DiagID) {} |
65 | |
66 | // This is a cunning lie. DiagnosticBuilder actually performs move |
67 | // construction in its copy constructor (but due to varied uses, it's not |
68 | // possible to conveniently express this as actual move construction). So |
69 | // the default copy ctor here is fine, because the base class disables the |
70 | // source anyway, so the user-defined ~ImmediateDiagBuilder is a safe no-op |
71 | // in that case anwyay. |
72 | ImmediateDiagBuilder(const ImmediateDiagBuilder &) = default; |
73 | |
74 | ~ImmediateDiagBuilder(); |
75 | |
76 | /// Teach operator<< to produce an object of the correct type. |
77 | template <typename T> |
78 | friend const ImmediateDiagBuilder & |
79 | operator<<(const ImmediateDiagBuilder &Diag, const T &Value) { |
80 | const DiagnosticBuilder &BaseDiag = Diag; |
81 | BaseDiag << Value; |
82 | return Diag; |
83 | } |
84 | |
85 | // It is necessary to limit this to rvalue reference to avoid calling this |
86 | // function with a bitfield lvalue argument since non-const reference to |
87 | // bitfield is not allowed. |
88 | template <typename T, |
89 | typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>> |
90 | const ImmediateDiagBuilder &operator<<(T &&V) const { |
91 | const DiagnosticBuilder &BaseDiag = *this; |
92 | BaseDiag << std::move(V); |
93 | return *this; |
94 | } |
95 | }; |
96 | |
97 | /// A generic diagnostic builder for errors which may or may not be deferred. |
98 | /// |
99 | /// In CUDA, there exist constructs (e.g. variable-length arrays, try/catch) |
100 | /// which are not allowed to appear inside __device__ functions and are |
101 | /// allowed to appear in __host__ __device__ functions only if the host+device |
102 | /// function is never codegen'ed. |
103 | /// |
104 | /// To handle this, we use the notion of "deferred diagnostics", where we |
105 | /// attach a diagnostic to a FunctionDecl that's emitted iff it's codegen'ed. |
106 | /// |
107 | /// This class lets you emit either a regular diagnostic, a deferred |
108 | /// diagnostic, or no diagnostic at all, according to an argument you pass to |
109 | /// its constructor, thus simplifying the process of creating these "maybe |
110 | /// deferred" diagnostics. |
111 | class SemaDiagnosticBuilder { |
112 | public: |
113 | enum Kind { |
114 | /// Emit no diagnostics. |
115 | K_Nop, |
116 | /// Emit the diagnostic immediately (i.e., behave like Sema::Diag()). |
117 | K_Immediate, |
118 | /// Emit the diagnostic immediately, and, if it's a warning or error, also |
119 | /// emit a call stack showing how this function can be reached by an a |
120 | /// priori known-emitted function. |
121 | K_ImmediateWithCallStack, |
122 | /// Create a deferred diagnostic, which is emitted only if the function |
123 | /// it's attached to is codegen'ed. Also emit a call stack as with |
124 | /// K_ImmediateWithCallStack. |
125 | K_Deferred |
126 | }; |
127 | |
128 | SemaDiagnosticBuilder(Kind K, SourceLocation Loc, unsigned DiagID, |
129 | const FunctionDecl *Fn, Sema &S); |
130 | SemaDiagnosticBuilder(SemaDiagnosticBuilder &&D); |
131 | SemaDiagnosticBuilder(const SemaDiagnosticBuilder &) = default; |
132 | |
133 | // The copy and move assignment operator is defined as deleted pending |
134 | // further motivation. |
135 | SemaDiagnosticBuilder &operator=(const SemaDiagnosticBuilder &) = delete; |
136 | SemaDiagnosticBuilder &operator=(SemaDiagnosticBuilder &&) = delete; |
137 | |
138 | ~SemaDiagnosticBuilder(); |
139 | |
140 | bool isImmediate() const { return ImmediateDiag.has_value(); } |
141 | |
142 | /// Convertible to bool: True if we immediately emitted an error, false if |
143 | /// we didn't emit an error or we created a deferred error. |
144 | /// |
145 | /// Example usage: |
146 | /// |
147 | /// if (SemaDiagnosticBuilder(...) << foo << bar) |
148 | /// return ExprError(); |
149 | /// |
150 | /// But see DiagIfDeviceCode() and DiagIfHostCode() -- you probably |
151 | /// want to use these instead of creating a SemaDiagnosticBuilder yourself. |
152 | operator bool() const { return isImmediate(); } |
153 | |
154 | template <typename T> |
155 | friend const SemaDiagnosticBuilder & |
156 | operator<<(const SemaDiagnosticBuilder &Diag, const T &Value) { |
157 | if (Diag.ImmediateDiag) |
158 | *Diag.ImmediateDiag << Value; |
159 | else if (Diag.PartialDiagId) |
160 | Diag.getDeviceDeferredDiags()[Diag.Fn][*Diag.PartialDiagId].second |
161 | << Value; |
162 | return Diag; |
163 | } |
164 | |
165 | // It is necessary to limit this to rvalue reference to avoid calling this |
166 | // function with a bitfield lvalue argument since non-const reference to |
167 | // bitfield is not allowed. |
168 | template <typename T, |
169 | typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>> |
170 | const SemaDiagnosticBuilder &operator<<(T &&V) const { |
171 | if (ImmediateDiag) |
172 | *ImmediateDiag << std::move(V); |
173 | else if (PartialDiagId) |
174 | getDeviceDeferredDiags()[Fn][*PartialDiagId].second << std::move(V); |
175 | return *this; |
176 | } |
177 | |
178 | friend const SemaDiagnosticBuilder & |
179 | operator<<(const SemaDiagnosticBuilder &Diag, const PartialDiagnostic &PD); |
180 | |
181 | void AddFixItHint(const FixItHint &Hint) const; |
182 | |
183 | friend ExprResult ExprError(const SemaDiagnosticBuilder &) { |
184 | return ExprError(); |
185 | } |
186 | friend StmtResult StmtError(const SemaDiagnosticBuilder &) { |
187 | return StmtError(); |
188 | } |
189 | operator ExprResult() const { return ExprError(); } |
190 | operator StmtResult() const { return StmtError(); } |
191 | operator TypeResult() const { return TypeError(); } |
192 | operator DeclResult() const { return DeclResult(true); } |
193 | operator MemInitResult() const { return MemInitResult(true); } |
194 | |
195 | using DeferredDiagnosticsType = |
196 | llvm::DenseMap<CanonicalDeclPtr<const FunctionDecl>, |
197 | std::vector<PartialDiagnosticAt>>; |
198 | |
199 | private: |
200 | Sema &S; |
201 | SourceLocation Loc; |
202 | unsigned DiagID; |
203 | const FunctionDecl *Fn; |
204 | bool ShowCallStack; |
205 | |
206 | // Invariant: At most one of these Optionals has a value. |
207 | // FIXME: Switch these to a Variant once that exists. |
208 | std::optional<ImmediateDiagBuilder> ImmediateDiag; |
209 | std::optional<unsigned> PartialDiagId; |
210 | |
211 | DeferredDiagnosticsType &getDeviceDeferredDiags() const; |
212 | }; |
213 | |
214 | /// Emit a diagnostic. |
215 | SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, |
216 | bool DeferHint = false); |
217 | |
218 | /// Emit a partial diagnostic. |
219 | SemaDiagnosticBuilder Diag(SourceLocation Loc, const PartialDiagnostic &PD, |
220 | bool DeferHint = false); |
221 | |
222 | /// Emit a compatibility diagnostic. |
223 | SemaDiagnosticBuilder DiagCompat(SourceLocation Loc, unsigned CompatDiagId, |
224 | bool DeferHint = false); |
225 | |
226 | /// Build a partial diagnostic. |
227 | PartialDiagnostic PDiag(unsigned DiagID = 0); |
228 | }; |
229 | |
230 | } // namespace clang |
231 | |
232 | #endif |
233 | |