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