File: | clang/lib/Sema/SemaExceptionSpec.cpp |
Warning: | line 390, column 9 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--- SemaExceptionSpec.cpp - C++ Exception Specifications ---*- 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 provides Sema routines for C++ exception specification testing. | ||||
10 | // | ||||
11 | //===----------------------------------------------------------------------===// | ||||
12 | |||||
13 | #include "clang/Sema/SemaInternal.h" | ||||
14 | #include "clang/AST/ASTMutationListener.h" | ||||
15 | #include "clang/AST/CXXInheritance.h" | ||||
16 | #include "clang/AST/Expr.h" | ||||
17 | #include "clang/AST/ExprCXX.h" | ||||
18 | #include "clang/AST/StmtObjC.h" | ||||
19 | #include "clang/AST/TypeLoc.h" | ||||
20 | #include "clang/Basic/Diagnostic.h" | ||||
21 | #include "clang/Basic/SourceManager.h" | ||||
22 | #include "llvm/ADT/SmallPtrSet.h" | ||||
23 | #include "llvm/ADT/SmallString.h" | ||||
24 | |||||
25 | namespace clang { | ||||
26 | |||||
27 | static const FunctionProtoType *GetUnderlyingFunction(QualType T) | ||||
28 | { | ||||
29 | if (const PointerType *PtrTy = T->getAs<PointerType>()) | ||||
30 | T = PtrTy->getPointeeType(); | ||||
31 | else if (const ReferenceType *RefTy = T->getAs<ReferenceType>()) | ||||
32 | T = RefTy->getPointeeType(); | ||||
33 | else if (const MemberPointerType *MPTy = T->getAs<MemberPointerType>()) | ||||
34 | T = MPTy->getPointeeType(); | ||||
35 | return T->getAs<FunctionProtoType>(); | ||||
36 | } | ||||
37 | |||||
38 | /// HACK: 2014-11-14 libstdc++ had a bug where it shadows std::swap with a | ||||
39 | /// member swap function then tries to call std::swap unqualified from the | ||||
40 | /// exception specification of that function. This function detects whether | ||||
41 | /// we're in such a case and turns off delay-parsing of exception | ||||
42 | /// specifications. Libstdc++ 6.1 (released 2016-04-27) appears to have | ||||
43 | /// resolved it as side-effect of commit ddb63209a8d (2015-06-05). | ||||
44 | bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) { | ||||
45 | auto *RD = dyn_cast<CXXRecordDecl>(CurContext); | ||||
46 | |||||
47 | // All the problem cases are member functions named "swap" within class | ||||
48 | // templates declared directly within namespace std or std::__debug or | ||||
49 | // std::__profile. | ||||
50 | if (!RD || !RD->getIdentifier() || !RD->getDescribedClassTemplate() || | ||||
51 | !D.getIdentifier() || !D.getIdentifier()->isStr("swap")) | ||||
52 | return false; | ||||
53 | |||||
54 | auto *ND = dyn_cast<NamespaceDecl>(RD->getDeclContext()); | ||||
55 | if (!ND) | ||||
56 | return false; | ||||
57 | |||||
58 | bool IsInStd = ND->isStdNamespace(); | ||||
59 | if (!IsInStd) { | ||||
60 | // This isn't a direct member of namespace std, but it might still be | ||||
61 | // libstdc++'s std::__debug::array or std::__profile::array. | ||||
62 | IdentifierInfo *II = ND->getIdentifier(); | ||||
63 | if (!II || !(II->isStr("__debug") || II->isStr("__profile")) || | ||||
64 | !ND->isInStdNamespace()) | ||||
65 | return false; | ||||
66 | } | ||||
67 | |||||
68 | // Only apply this hack within a system header. | ||||
69 | if (!Context.getSourceManager().isInSystemHeader(D.getBeginLoc())) | ||||
70 | return false; | ||||
71 | |||||
72 | return llvm::StringSwitch<bool>(RD->getIdentifier()->getName()) | ||||
73 | .Case("array", true) | ||||
74 | .Case("pair", IsInStd) | ||||
75 | .Case("priority_queue", IsInStd) | ||||
76 | .Case("stack", IsInStd) | ||||
77 | .Case("queue", IsInStd) | ||||
78 | .Default(false); | ||||
79 | } | ||||
80 | |||||
81 | ExprResult Sema::ActOnNoexceptSpec(Expr *NoexceptExpr, | ||||
82 | ExceptionSpecificationType &EST) { | ||||
83 | |||||
84 | if (NoexceptExpr->isTypeDependent() || | ||||
85 | NoexceptExpr->containsUnexpandedParameterPack()) { | ||||
86 | EST = EST_DependentNoexcept; | ||||
87 | return NoexceptExpr; | ||||
88 | } | ||||
89 | |||||
90 | llvm::APSInt Result; | ||||
91 | ExprResult Converted = CheckConvertedConstantExpression( | ||||
92 | NoexceptExpr, Context.BoolTy, Result, CCEK_Noexcept); | ||||
93 | |||||
94 | if (Converted.isInvalid()) { | ||||
95 | EST = EST_NoexceptFalse; | ||||
96 | // Fill in an expression of 'false' as a fixup. | ||||
97 | auto *BoolExpr = new (Context) | ||||
98 | CXXBoolLiteralExpr(false, Context.BoolTy, NoexceptExpr->getBeginLoc()); | ||||
99 | llvm::APSInt Value{1}; | ||||
100 | Value = 0; | ||||
101 | return ConstantExpr::Create(Context, BoolExpr, APValue{Value}); | ||||
102 | } | ||||
103 | |||||
104 | if (Converted.get()->isValueDependent()) { | ||||
105 | EST = EST_DependentNoexcept; | ||||
106 | return Converted; | ||||
107 | } | ||||
108 | |||||
109 | if (!Converted.isInvalid()) | ||||
110 | EST = !Result ? EST_NoexceptFalse : EST_NoexceptTrue; | ||||
111 | return Converted; | ||||
112 | } | ||||
113 | |||||
114 | /// CheckSpecifiedExceptionType - Check if the given type is valid in an | ||||
115 | /// exception specification. Incomplete types, or pointers to incomplete types | ||||
116 | /// other than void are not allowed. | ||||
117 | /// | ||||
118 | /// \param[in,out] T The exception type. This will be decayed to a pointer type | ||||
119 | /// when the input is an array or a function type. | ||||
120 | bool Sema::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) { | ||||
121 | // C++11 [except.spec]p2: | ||||
122 | // A type cv T, "array of T", or "function returning T" denoted | ||||
123 | // in an exception-specification is adjusted to type T, "pointer to T", or | ||||
124 | // "pointer to function returning T", respectively. | ||||
125 | // | ||||
126 | // We also apply this rule in C++98. | ||||
127 | if (T->isArrayType()) | ||||
128 | T = Context.getArrayDecayedType(T); | ||||
129 | else if (T->isFunctionType()) | ||||
130 | T = Context.getPointerType(T); | ||||
131 | |||||
132 | int Kind = 0; | ||||
133 | QualType PointeeT = T; | ||||
134 | if (const PointerType *PT = T->getAs<PointerType>()) { | ||||
135 | PointeeT = PT->getPointeeType(); | ||||
136 | Kind = 1; | ||||
137 | |||||
138 | // cv void* is explicitly permitted, despite being a pointer to an | ||||
139 | // incomplete type. | ||||
140 | if (PointeeT->isVoidType()) | ||||
141 | return false; | ||||
142 | } else if (const ReferenceType *RT = T->getAs<ReferenceType>()) { | ||||
143 | PointeeT = RT->getPointeeType(); | ||||
144 | Kind = 2; | ||||
145 | |||||
146 | if (RT->isRValueReferenceType()) { | ||||
147 | // C++11 [except.spec]p2: | ||||
148 | // A type denoted in an exception-specification shall not denote [...] | ||||
149 | // an rvalue reference type. | ||||
150 | Diag(Range.getBegin(), diag::err_rref_in_exception_spec) | ||||
151 | << T << Range; | ||||
152 | return true; | ||||
153 | } | ||||
154 | } | ||||
155 | |||||
156 | // C++11 [except.spec]p2: | ||||
157 | // A type denoted in an exception-specification shall not denote an | ||||
158 | // incomplete type other than a class currently being defined [...]. | ||||
159 | // A type denoted in an exception-specification shall not denote a | ||||
160 | // pointer or reference to an incomplete type, other than (cv) void* or a | ||||
161 | // pointer or reference to a class currently being defined. | ||||
162 | // In Microsoft mode, downgrade this to a warning. | ||||
163 | unsigned DiagID = diag::err_incomplete_in_exception_spec; | ||||
164 | bool ReturnValueOnError = true; | ||||
165 | if (getLangOpts().MSVCCompat) { | ||||
166 | DiagID = diag::ext_incomplete_in_exception_spec; | ||||
167 | ReturnValueOnError = false; | ||||
168 | } | ||||
169 | if (!(PointeeT->isRecordType() && | ||||
170 | PointeeT->castAs<RecordType>()->isBeingDefined()) && | ||||
171 | RequireCompleteType(Range.getBegin(), PointeeT, DiagID, Kind, Range)) | ||||
172 | return ReturnValueOnError; | ||||
173 | |||||
174 | // The MSVC compatibility mode doesn't extend to sizeless types, | ||||
175 | // so diagnose them separately. | ||||
176 | if (PointeeT->isSizelessType() && Kind != 1) { | ||||
177 | Diag(Range.getBegin(), diag::err_sizeless_in_exception_spec) | ||||
178 | << (Kind == 2 ? 1 : 0) << PointeeT << Range; | ||||
179 | return true; | ||||
180 | } | ||||
181 | |||||
182 | return false; | ||||
183 | } | ||||
184 | |||||
185 | /// CheckDistantExceptionSpec - Check if the given type is a pointer or pointer | ||||
186 | /// to member to a function with an exception specification. This means that | ||||
187 | /// it is invalid to add another level of indirection. | ||||
188 | bool Sema::CheckDistantExceptionSpec(QualType T) { | ||||
189 | // C++17 removes this rule in favor of putting exception specifications into | ||||
190 | // the type system. | ||||
191 | if (getLangOpts().CPlusPlus17) | ||||
192 | return false; | ||||
193 | |||||
194 | if (const PointerType *PT = T->getAs<PointerType>()) | ||||
195 | T = PT->getPointeeType(); | ||||
196 | else if (const MemberPointerType *PT = T->getAs<MemberPointerType>()) | ||||
197 | T = PT->getPointeeType(); | ||||
198 | else | ||||
199 | return false; | ||||
200 | |||||
201 | const FunctionProtoType *FnT = T->getAs<FunctionProtoType>(); | ||||
202 | if (!FnT) | ||||
203 | return false; | ||||
204 | |||||
205 | return FnT->hasExceptionSpec(); | ||||
206 | } | ||||
207 | |||||
208 | const FunctionProtoType * | ||||
209 | Sema::ResolveExceptionSpec(SourceLocation Loc, const FunctionProtoType *FPT) { | ||||
210 | if (FPT->getExceptionSpecType() == EST_Unparsed) { | ||||
211 | Diag(Loc, diag::err_exception_spec_not_parsed); | ||||
212 | return nullptr; | ||||
213 | } | ||||
214 | |||||
215 | if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) | ||||
216 | return FPT; | ||||
217 | |||||
218 | FunctionDecl *SourceDecl = FPT->getExceptionSpecDecl(); | ||||
219 | const FunctionProtoType *SourceFPT = | ||||
220 | SourceDecl->getType()->castAs<FunctionProtoType>(); | ||||
221 | |||||
222 | // If the exception specification has already been resolved, just return it. | ||||
223 | if (!isUnresolvedExceptionSpec(SourceFPT->getExceptionSpecType())) | ||||
224 | return SourceFPT; | ||||
225 | |||||
226 | // Compute or instantiate the exception specification now. | ||||
227 | if (SourceFPT->getExceptionSpecType() == EST_Unevaluated) | ||||
228 | EvaluateImplicitExceptionSpec(Loc, SourceDecl); | ||||
229 | else | ||||
230 | InstantiateExceptionSpec(Loc, SourceDecl); | ||||
231 | |||||
232 | const FunctionProtoType *Proto = | ||||
233 | SourceDecl->getType()->castAs<FunctionProtoType>(); | ||||
234 | if (Proto->getExceptionSpecType() == clang::EST_Unparsed) { | ||||
235 | Diag(Loc, diag::err_exception_spec_not_parsed); | ||||
236 | Proto = nullptr; | ||||
237 | } | ||||
238 | return Proto; | ||||
239 | } | ||||
240 | |||||
241 | void | ||||
242 | Sema::UpdateExceptionSpec(FunctionDecl *FD, | ||||
243 | const FunctionProtoType::ExceptionSpecInfo &ESI) { | ||||
244 | // If we've fully resolved the exception specification, notify listeners. | ||||
245 | if (!isUnresolvedExceptionSpec(ESI.Type)) | ||||
246 | if (auto *Listener = getASTMutationListener()) | ||||
247 | Listener->ResolvedExceptionSpec(FD); | ||||
248 | |||||
249 | for (FunctionDecl *Redecl : FD->redecls()) | ||||
250 | Context.adjustExceptionSpec(Redecl, ESI); | ||||
251 | } | ||||
252 | |||||
253 | static bool exceptionSpecNotKnownYet(const FunctionDecl *FD) { | ||||
254 | auto *MD = dyn_cast<CXXMethodDecl>(FD); | ||||
255 | if (!MD
| ||||
256 | return false; | ||||
257 | |||||
258 | auto EST = MD->getType()->castAs<FunctionProtoType>()->getExceptionSpecType(); | ||||
259 | return EST == EST_Unparsed || | ||||
260 | (EST == EST_Unevaluated && MD->getParent()->isBeingDefined()); | ||||
261 | } | ||||
262 | |||||
263 | static bool CheckEquivalentExceptionSpecImpl( | ||||
264 | Sema &S, const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID, | ||||
265 | const FunctionProtoType *Old, SourceLocation OldLoc, | ||||
266 | const FunctionProtoType *New, SourceLocation NewLoc, | ||||
267 | bool *MissingExceptionSpecification = nullptr, | ||||
268 | bool *MissingEmptyExceptionSpecification = nullptr, | ||||
269 | bool AllowNoexceptAllMatchWithNoSpec = false, bool IsOperatorNew = false); | ||||
270 | |||||
271 | /// Determine whether a function has an implicitly-generated exception | ||||
272 | /// specification. | ||||
273 | static bool hasImplicitExceptionSpec(FunctionDecl *Decl) { | ||||
274 | if (!isa<CXXDestructorDecl>(Decl) && | ||||
275 | Decl->getDeclName().getCXXOverloadedOperator() != OO_Delete && | ||||
276 | Decl->getDeclName().getCXXOverloadedOperator() != OO_Array_Delete) | ||||
277 | return false; | ||||
278 | |||||
279 | // For a function that the user didn't declare: | ||||
280 | // - if this is a destructor, its exception specification is implicit. | ||||
281 | // - if this is 'operator delete' or 'operator delete[]', the exception | ||||
282 | // specification is as-if an explicit exception specification was given | ||||
283 | // (per [basic.stc.dynamic]p2). | ||||
284 | if (!Decl->getTypeSourceInfo()) | ||||
285 | return isa<CXXDestructorDecl>(Decl); | ||||
286 | |||||
287 | auto *Ty = Decl->getTypeSourceInfo()->getType()->castAs<FunctionProtoType>(); | ||||
288 | return !Ty->hasExceptionSpec(); | ||||
289 | } | ||||
290 | |||||
291 | bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { | ||||
292 | // Just completely ignore this under -fno-exceptions prior to C++17. | ||||
293 | // In C++17 onwards, the exception specification is part of the type and | ||||
294 | // we will diagnose mismatches anyway, so it's better to check for them here. | ||||
295 | if (!getLangOpts().CXXExceptions && !getLangOpts().CPlusPlus17) | ||||
| |||||
296 | return false; | ||||
297 | |||||
298 | OverloadedOperatorKind OO = New->getDeclName().getCXXOverloadedOperator(); | ||||
299 | bool IsOperatorNew = OO
| ||||
300 | bool MissingExceptionSpecification = false; | ||||
301 | bool MissingEmptyExceptionSpecification = false; | ||||
302 | |||||
303 | unsigned DiagID = diag::err_mismatched_exception_spec; | ||||
304 | bool ReturnValueOnError = true; | ||||
305 | if (getLangOpts().MSVCCompat) { | ||||
306 | DiagID = diag::ext_mismatched_exception_spec; | ||||
307 | ReturnValueOnError = false; | ||||
308 | } | ||||
309 | |||||
310 | // If we're befriending a member function of a class that's currently being | ||||
311 | // defined, we might not be able to work out its exception specification yet. | ||||
312 | // If not, defer the check until later. | ||||
313 | if (exceptionSpecNotKnownYet(Old) || exceptionSpecNotKnownYet(New)) { | ||||
314 | DelayedEquivalentExceptionSpecChecks.push_back({New, Old}); | ||||
315 | return false; | ||||
316 | } | ||||
317 | |||||
318 | // Check the types as written: they must match before any exception | ||||
319 | // specification adjustment is applied. | ||||
320 | if (!CheckEquivalentExceptionSpecImpl( | ||||
321 | *this, PDiag(DiagID), PDiag(diag::note_previous_declaration), | ||||
322 | Old->getType()->getAs<FunctionProtoType>(), Old->getLocation(), | ||||
323 | New->getType()->getAs<FunctionProtoType>(), New->getLocation(), | ||||
324 | &MissingExceptionSpecification, &MissingEmptyExceptionSpecification, | ||||
325 | /*AllowNoexceptAllMatchWithNoSpec=*/true, IsOperatorNew)) { | ||||
326 | // C++11 [except.spec]p4 [DR1492]: | ||||
327 | // If a declaration of a function has an implicit | ||||
328 | // exception-specification, other declarations of the function shall | ||||
329 | // not specify an exception-specification. | ||||
330 | if (getLangOpts().CPlusPlus11 && getLangOpts().CXXExceptions && | ||||
331 | hasImplicitExceptionSpec(Old) != hasImplicitExceptionSpec(New)) { | ||||
332 | Diag(New->getLocation(), diag::ext_implicit_exception_spec_mismatch) | ||||
333 | << hasImplicitExceptionSpec(Old); | ||||
334 | if (Old->getLocation().isValid()) | ||||
335 | Diag(Old->getLocation(), diag::note_previous_declaration); | ||||
336 | } | ||||
337 | return false; | ||||
338 | } | ||||
339 | |||||
340 | // The failure was something other than an missing exception | ||||
341 | // specification; return an error, except in MS mode where this is a warning. | ||||
342 | if (!MissingExceptionSpecification) | ||||
343 | return ReturnValueOnError; | ||||
344 | |||||
345 | const FunctionProtoType *NewProto = | ||||
346 | New->getType()->castAs<FunctionProtoType>(); | ||||
347 | |||||
348 | // The new function declaration is only missing an empty exception | ||||
349 | // specification "throw()". If the throw() specification came from a | ||||
350 | // function in a system header that has C linkage, just add an empty | ||||
351 | // exception specification to the "new" declaration. Note that C library | ||||
352 | // implementations are permitted to add these nothrow exception | ||||
353 | // specifications. | ||||
354 | // | ||||
355 | // Likewise if the old function is a builtin. | ||||
356 | if (MissingEmptyExceptionSpecification && NewProto && | ||||
357 | (Old->getLocation().isInvalid() || | ||||
358 | Context.getSourceManager().isInSystemHeader(Old->getLocation()) || | ||||
359 | Old->getBuiltinID()) && | ||||
360 | Old->isExternC()) { | ||||
361 | New->setType(Context.getFunctionType( | ||||
362 | NewProto->getReturnType(), NewProto->getParamTypes(), | ||||
363 | NewProto->getExtProtoInfo().withExceptionSpec(EST_DynamicNone))); | ||||
364 | return false; | ||||
365 | } | ||||
366 | |||||
367 | const FunctionProtoType *OldProto = | ||||
368 | Old->getType()->castAs<FunctionProtoType>(); | ||||
369 | |||||
370 | FunctionProtoType::ExceptionSpecInfo ESI = OldProto->getExceptionSpecType(); | ||||
371 | if (ESI.Type == EST_Dynamic) { | ||||
372 | // FIXME: What if the exceptions are described in terms of the old | ||||
373 | // prototype's parameters? | ||||
374 | ESI.Exceptions = OldProto->exceptions(); | ||||
375 | } | ||||
376 | |||||
377 | if (ESI.Type == EST_NoexceptFalse) | ||||
378 | ESI.Type = EST_None; | ||||
379 | if (ESI.Type == EST_NoexceptTrue) | ||||
380 | ESI.Type = EST_BasicNoexcept; | ||||
381 | |||||
382 | // For dependent noexcept, we can't just take the expression from the old | ||||
383 | // prototype. It likely contains references to the old prototype's parameters. | ||||
384 | if (ESI.Type == EST_DependentNoexcept) { | ||||
385 | New->setInvalidDecl(); | ||||
386 | } else { | ||||
387 | // Update the type of the function with the appropriate exception | ||||
388 | // specification. | ||||
389 | New->setType(Context.getFunctionType( | ||||
390 | NewProto->getReturnType(), NewProto->getParamTypes(), | ||||
| |||||
391 | NewProto->getExtProtoInfo().withExceptionSpec(ESI))); | ||||
392 | } | ||||
393 | |||||
394 | if (getLangOpts().MSVCCompat && ESI.Type != EST_DependentNoexcept) { | ||||
395 | // Allow missing exception specifications in redeclarations as an extension. | ||||
396 | DiagID = diag::ext_ms_missing_exception_specification; | ||||
397 | ReturnValueOnError = false; | ||||
398 | } else if (New->isReplaceableGlobalAllocationFunction() && | ||||
399 | ESI.Type != EST_DependentNoexcept) { | ||||
400 | // Allow missing exception specifications in redeclarations as an extension, | ||||
401 | // when declaring a replaceable global allocation function. | ||||
402 | DiagID = diag::ext_missing_exception_specification; | ||||
403 | ReturnValueOnError = false; | ||||
404 | } else if (ESI.Type == EST_NoThrow) { | ||||
405 | // Allow missing attribute 'nothrow' in redeclarations, since this is a very | ||||
406 | // common omission. | ||||
407 | DiagID = diag::ext_missing_exception_specification; | ||||
408 | ReturnValueOnError = false; | ||||
409 | } else { | ||||
410 | DiagID = diag::err_missing_exception_specification; | ||||
411 | ReturnValueOnError = true; | ||||
412 | } | ||||
413 | |||||
414 | // Warn about the lack of exception specification. | ||||
415 | SmallString<128> ExceptionSpecString; | ||||
416 | llvm::raw_svector_ostream OS(ExceptionSpecString); | ||||
417 | switch (OldProto->getExceptionSpecType()) { | ||||
418 | case EST_DynamicNone: | ||||
419 | OS << "throw()"; | ||||
420 | break; | ||||
421 | |||||
422 | case EST_Dynamic: { | ||||
423 | OS << "throw("; | ||||
424 | bool OnFirstException = true; | ||||
425 | for (const auto &E : OldProto->exceptions()) { | ||||
426 | if (OnFirstException) | ||||
427 | OnFirstException = false; | ||||
428 | else | ||||
429 | OS << ", "; | ||||
430 | |||||
431 | OS << E.getAsString(getPrintingPolicy()); | ||||
432 | } | ||||
433 | OS << ")"; | ||||
434 | break; | ||||
435 | } | ||||
436 | |||||
437 | case EST_BasicNoexcept: | ||||
438 | OS << "noexcept"; | ||||
439 | break; | ||||
440 | |||||
441 | case EST_DependentNoexcept: | ||||
442 | case EST_NoexceptFalse: | ||||
443 | case EST_NoexceptTrue: | ||||
444 | OS << "noexcept("; | ||||
445 | assert(OldProto->getNoexceptExpr() != nullptr && "Expected non-null Expr")(static_cast <bool> (OldProto->getNoexceptExpr() != nullptr && "Expected non-null Expr") ? void (0) : __assert_fail ("OldProto->getNoexceptExpr() != nullptr && \"Expected non-null Expr\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 445, __extension__ __PRETTY_FUNCTION__)); | ||||
446 | OldProto->getNoexceptExpr()->printPretty(OS, nullptr, getPrintingPolicy()); | ||||
447 | OS << ")"; | ||||
448 | break; | ||||
449 | case EST_NoThrow: | ||||
450 | OS <<"__attribute__((nothrow))"; | ||||
451 | break; | ||||
452 | case EST_None: | ||||
453 | case EST_MSAny: | ||||
454 | case EST_Unevaluated: | ||||
455 | case EST_Uninstantiated: | ||||
456 | case EST_Unparsed: | ||||
457 | llvm_unreachable("This spec type is compatible with none.")::llvm::llvm_unreachable_internal("This spec type is compatible with none." , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 457); | ||||
458 | } | ||||
459 | |||||
460 | SourceLocation FixItLoc; | ||||
461 | if (TypeSourceInfo *TSInfo = New->getTypeSourceInfo()) { | ||||
462 | TypeLoc TL = TSInfo->getTypeLoc().IgnoreParens(); | ||||
463 | // FIXME: Preserve enough information so that we can produce a correct fixit | ||||
464 | // location when there is a trailing return type. | ||||
465 | if (auto FTLoc = TL.getAs<FunctionProtoTypeLoc>()) | ||||
466 | if (!FTLoc.getTypePtr()->hasTrailingReturn()) | ||||
467 | FixItLoc = getLocForEndOfToken(FTLoc.getLocalRangeEnd()); | ||||
468 | } | ||||
469 | |||||
470 | if (FixItLoc.isInvalid()) | ||||
471 | Diag(New->getLocation(), DiagID) | ||||
472 | << New << OS.str(); | ||||
473 | else { | ||||
474 | Diag(New->getLocation(), DiagID) | ||||
475 | << New << OS.str() | ||||
476 | << FixItHint::CreateInsertion(FixItLoc, " " + OS.str().str()); | ||||
477 | } | ||||
478 | |||||
479 | if (Old->getLocation().isValid()) | ||||
480 | Diag(Old->getLocation(), diag::note_previous_declaration); | ||||
481 | |||||
482 | return ReturnValueOnError; | ||||
483 | } | ||||
484 | |||||
485 | /// CheckEquivalentExceptionSpec - Check if the two types have equivalent | ||||
486 | /// exception specifications. Exception specifications are equivalent if | ||||
487 | /// they allow exactly the same set of exception types. It does not matter how | ||||
488 | /// that is achieved. See C++ [except.spec]p2. | ||||
489 | bool Sema::CheckEquivalentExceptionSpec( | ||||
490 | const FunctionProtoType *Old, SourceLocation OldLoc, | ||||
491 | const FunctionProtoType *New, SourceLocation NewLoc) { | ||||
492 | if (!getLangOpts().CXXExceptions) | ||||
493 | return false; | ||||
494 | |||||
495 | unsigned DiagID = diag::err_mismatched_exception_spec; | ||||
496 | if (getLangOpts().MSVCCompat) | ||||
497 | DiagID = diag::ext_mismatched_exception_spec; | ||||
498 | bool Result = CheckEquivalentExceptionSpecImpl( | ||||
499 | *this, PDiag(DiagID), PDiag(diag::note_previous_declaration), | ||||
500 | Old, OldLoc, New, NewLoc); | ||||
501 | |||||
502 | // In Microsoft mode, mismatching exception specifications just cause a warning. | ||||
503 | if (getLangOpts().MSVCCompat) | ||||
504 | return false; | ||||
505 | return Result; | ||||
506 | } | ||||
507 | |||||
508 | /// CheckEquivalentExceptionSpec - Check if the two types have compatible | ||||
509 | /// exception specifications. See C++ [except.spec]p3. | ||||
510 | /// | ||||
511 | /// \return \c false if the exception specifications match, \c true if there is | ||||
512 | /// a problem. If \c true is returned, either a diagnostic has already been | ||||
513 | /// produced or \c *MissingExceptionSpecification is set to \c true. | ||||
514 | static bool CheckEquivalentExceptionSpecImpl( | ||||
515 | Sema &S, const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID, | ||||
516 | const FunctionProtoType *Old, SourceLocation OldLoc, | ||||
517 | const FunctionProtoType *New, SourceLocation NewLoc, | ||||
518 | bool *MissingExceptionSpecification, | ||||
519 | bool *MissingEmptyExceptionSpecification, | ||||
520 | bool AllowNoexceptAllMatchWithNoSpec, bool IsOperatorNew) { | ||||
521 | if (MissingExceptionSpecification) | ||||
522 | *MissingExceptionSpecification = false; | ||||
523 | |||||
524 | if (MissingEmptyExceptionSpecification) | ||||
525 | *MissingEmptyExceptionSpecification = false; | ||||
526 | |||||
527 | Old = S.ResolveExceptionSpec(NewLoc, Old); | ||||
528 | if (!Old) | ||||
529 | return false; | ||||
530 | New = S.ResolveExceptionSpec(NewLoc, New); | ||||
531 | if (!New) | ||||
532 | return false; | ||||
533 | |||||
534 | // C++0x [except.spec]p3: Two exception-specifications are compatible if: | ||||
535 | // - both are non-throwing, regardless of their form, | ||||
536 | // - both have the form noexcept(constant-expression) and the constant- | ||||
537 | // expressions are equivalent, | ||||
538 | // - both are dynamic-exception-specifications that have the same set of | ||||
539 | // adjusted types. | ||||
540 | // | ||||
541 | // C++0x [except.spec]p12: An exception-specification is non-throwing if it is | ||||
542 | // of the form throw(), noexcept, or noexcept(constant-expression) where the | ||||
543 | // constant-expression yields true. | ||||
544 | // | ||||
545 | // C++0x [except.spec]p4: If any declaration of a function has an exception- | ||||
546 | // specifier that is not a noexcept-specification allowing all exceptions, | ||||
547 | // all declarations [...] of that function shall have a compatible | ||||
548 | // exception-specification. | ||||
549 | // | ||||
550 | // That last point basically means that noexcept(false) matches no spec. | ||||
551 | // It's considered when AllowNoexceptAllMatchWithNoSpec is true. | ||||
552 | |||||
553 | ExceptionSpecificationType OldEST = Old->getExceptionSpecType(); | ||||
554 | ExceptionSpecificationType NewEST = New->getExceptionSpecType(); | ||||
555 | |||||
556 | assert(!isUnresolvedExceptionSpec(OldEST) &&(static_cast <bool> (!isUnresolvedExceptionSpec(OldEST) && !isUnresolvedExceptionSpec(NewEST) && "Shouldn't see unknown exception specifications here" ) ? void (0) : __assert_fail ("!isUnresolvedExceptionSpec(OldEST) && !isUnresolvedExceptionSpec(NewEST) && \"Shouldn't see unknown exception specifications here\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 558, __extension__ __PRETTY_FUNCTION__)) | ||||
557 | !isUnresolvedExceptionSpec(NewEST) &&(static_cast <bool> (!isUnresolvedExceptionSpec(OldEST) && !isUnresolvedExceptionSpec(NewEST) && "Shouldn't see unknown exception specifications here" ) ? void (0) : __assert_fail ("!isUnresolvedExceptionSpec(OldEST) && !isUnresolvedExceptionSpec(NewEST) && \"Shouldn't see unknown exception specifications here\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 558, __extension__ __PRETTY_FUNCTION__)) | ||||
558 | "Shouldn't see unknown exception specifications here")(static_cast <bool> (!isUnresolvedExceptionSpec(OldEST) && !isUnresolvedExceptionSpec(NewEST) && "Shouldn't see unknown exception specifications here" ) ? void (0) : __assert_fail ("!isUnresolvedExceptionSpec(OldEST) && !isUnresolvedExceptionSpec(NewEST) && \"Shouldn't see unknown exception specifications here\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 558, __extension__ __PRETTY_FUNCTION__)); | ||||
559 | |||||
560 | CanThrowResult OldCanThrow = Old->canThrow(); | ||||
561 | CanThrowResult NewCanThrow = New->canThrow(); | ||||
562 | |||||
563 | // Any non-throwing specifications are compatible. | ||||
564 | if (OldCanThrow == CT_Cannot && NewCanThrow == CT_Cannot) | ||||
565 | return false; | ||||
566 | |||||
567 | // Any throws-anything specifications are usually compatible. | ||||
568 | if (OldCanThrow == CT_Can && OldEST != EST_Dynamic && | ||||
569 | NewCanThrow == CT_Can && NewEST != EST_Dynamic) { | ||||
570 | // The exception is that the absence of an exception specification only | ||||
571 | // matches noexcept(false) for functions, as described above. | ||||
572 | if (!AllowNoexceptAllMatchWithNoSpec && | ||||
573 | ((OldEST == EST_None && NewEST == EST_NoexceptFalse) || | ||||
574 | (OldEST == EST_NoexceptFalse && NewEST == EST_None))) { | ||||
575 | // This is the disallowed case. | ||||
576 | } else { | ||||
577 | return false; | ||||
578 | } | ||||
579 | } | ||||
580 | |||||
581 | // C++14 [except.spec]p3: | ||||
582 | // Two exception-specifications are compatible if [...] both have the form | ||||
583 | // noexcept(constant-expression) and the constant-expressions are equivalent | ||||
584 | if (OldEST == EST_DependentNoexcept && NewEST == EST_DependentNoexcept) { | ||||
585 | llvm::FoldingSetNodeID OldFSN, NewFSN; | ||||
586 | Old->getNoexceptExpr()->Profile(OldFSN, S.Context, true); | ||||
587 | New->getNoexceptExpr()->Profile(NewFSN, S.Context, true); | ||||
588 | if (OldFSN == NewFSN) | ||||
589 | return false; | ||||
590 | } | ||||
591 | |||||
592 | // Dynamic exception specifications with the same set of adjusted types | ||||
593 | // are compatible. | ||||
594 | if (OldEST == EST_Dynamic && NewEST == EST_Dynamic) { | ||||
595 | bool Success = true; | ||||
596 | // Both have a dynamic exception spec. Collect the first set, then compare | ||||
597 | // to the second. | ||||
598 | llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes; | ||||
599 | for (const auto &I : Old->exceptions()) | ||||
600 | OldTypes.insert(S.Context.getCanonicalType(I).getUnqualifiedType()); | ||||
601 | |||||
602 | for (const auto &I : New->exceptions()) { | ||||
603 | CanQualType TypePtr = S.Context.getCanonicalType(I).getUnqualifiedType(); | ||||
604 | if (OldTypes.count(TypePtr)) | ||||
605 | NewTypes.insert(TypePtr); | ||||
606 | else { | ||||
607 | Success = false; | ||||
608 | break; | ||||
609 | } | ||||
610 | } | ||||
611 | |||||
612 | if (Success && OldTypes.size() == NewTypes.size()) | ||||
613 | return false; | ||||
614 | } | ||||
615 | |||||
616 | // As a special compatibility feature, under C++0x we accept no spec and | ||||
617 | // throw(std::bad_alloc) as equivalent for operator new and operator new[]. | ||||
618 | // This is because the implicit declaration changed, but old code would break. | ||||
619 | if (S.getLangOpts().CPlusPlus11 && IsOperatorNew) { | ||||
620 | const FunctionProtoType *WithExceptions = nullptr; | ||||
621 | if (OldEST == EST_None && NewEST == EST_Dynamic) | ||||
622 | WithExceptions = New; | ||||
623 | else if (OldEST == EST_Dynamic && NewEST == EST_None) | ||||
624 | WithExceptions = Old; | ||||
625 | if (WithExceptions && WithExceptions->getNumExceptions() == 1) { | ||||
626 | // One has no spec, the other throw(something). If that something is | ||||
627 | // std::bad_alloc, all conditions are met. | ||||
628 | QualType Exception = *WithExceptions->exception_begin(); | ||||
629 | if (CXXRecordDecl *ExRecord = Exception->getAsCXXRecordDecl()) { | ||||
630 | IdentifierInfo* Name = ExRecord->getIdentifier(); | ||||
631 | if (Name && Name->getName() == "bad_alloc") { | ||||
632 | // It's called bad_alloc, but is it in std? | ||||
633 | if (ExRecord->isInStdNamespace()) { | ||||
634 | return false; | ||||
635 | } | ||||
636 | } | ||||
637 | } | ||||
638 | } | ||||
639 | } | ||||
640 | |||||
641 | // If the caller wants to handle the case that the new function is | ||||
642 | // incompatible due to a missing exception specification, let it. | ||||
643 | if (MissingExceptionSpecification && OldEST != EST_None && | ||||
644 | NewEST == EST_None) { | ||||
645 | // The old type has an exception specification of some sort, but | ||||
646 | // the new type does not. | ||||
647 | *MissingExceptionSpecification = true; | ||||
648 | |||||
649 | if (MissingEmptyExceptionSpecification && OldCanThrow == CT_Cannot) { | ||||
650 | // The old type has a throw() or noexcept(true) exception specification | ||||
651 | // and the new type has no exception specification, and the caller asked | ||||
652 | // to handle this itself. | ||||
653 | *MissingEmptyExceptionSpecification = true; | ||||
654 | } | ||||
655 | |||||
656 | return true; | ||||
657 | } | ||||
658 | |||||
659 | S.Diag(NewLoc, DiagID); | ||||
660 | if (NoteID.getDiagID() != 0 && OldLoc.isValid()) | ||||
661 | S.Diag(OldLoc, NoteID); | ||||
662 | return true; | ||||
663 | } | ||||
664 | |||||
665 | bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, | ||||
666 | const PartialDiagnostic &NoteID, | ||||
667 | const FunctionProtoType *Old, | ||||
668 | SourceLocation OldLoc, | ||||
669 | const FunctionProtoType *New, | ||||
670 | SourceLocation NewLoc) { | ||||
671 | if (!getLangOpts().CXXExceptions) | ||||
672 | return false; | ||||
673 | return CheckEquivalentExceptionSpecImpl(*this, DiagID, NoteID, Old, OldLoc, | ||||
674 | New, NewLoc); | ||||
675 | } | ||||
676 | |||||
677 | bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) { | ||||
678 | // [except.handle]p3: | ||||
679 | // A handler is a match for an exception object of type E if: | ||||
680 | |||||
681 | // HandlerType must be ExceptionType or derived from it, or pointer or | ||||
682 | // reference to such types. | ||||
683 | const ReferenceType *RefTy = HandlerType->getAs<ReferenceType>(); | ||||
684 | if (RefTy) | ||||
685 | HandlerType = RefTy->getPointeeType(); | ||||
686 | |||||
687 | // -- the handler is of type cv T or cv T& and E and T are the same type | ||||
688 | if (Context.hasSameUnqualifiedType(ExceptionType, HandlerType)) | ||||
689 | return true; | ||||
690 | |||||
691 | // FIXME: ObjC pointer types? | ||||
692 | if (HandlerType->isPointerType() || HandlerType->isMemberPointerType()) { | ||||
693 | if (RefTy && (!HandlerType.isConstQualified() || | ||||
694 | HandlerType.isVolatileQualified())) | ||||
695 | return false; | ||||
696 | |||||
697 | // -- the handler is of type cv T or const T& where T is a pointer or | ||||
698 | // pointer to member type and E is std::nullptr_t | ||||
699 | if (ExceptionType->isNullPtrType()) | ||||
700 | return true; | ||||
701 | |||||
702 | // -- the handler is of type cv T or const T& where T is a pointer or | ||||
703 | // pointer to member type and E is a pointer or pointer to member type | ||||
704 | // that can be converted to T by one or more of | ||||
705 | // -- a qualification conversion | ||||
706 | // -- a function pointer conversion | ||||
707 | bool LifetimeConv; | ||||
708 | QualType Result; | ||||
709 | // FIXME: Should we treat the exception as catchable if a lifetime | ||||
710 | // conversion is required? | ||||
711 | if (IsQualificationConversion(ExceptionType, HandlerType, false, | ||||
712 | LifetimeConv) || | ||||
713 | IsFunctionConversion(ExceptionType, HandlerType, Result)) | ||||
714 | return true; | ||||
715 | |||||
716 | // -- a standard pointer conversion [...] | ||||
717 | if (!ExceptionType->isPointerType() || !HandlerType->isPointerType()) | ||||
718 | return false; | ||||
719 | |||||
720 | // Handle the "qualification conversion" portion. | ||||
721 | Qualifiers EQuals, HQuals; | ||||
722 | ExceptionType = Context.getUnqualifiedArrayType( | ||||
723 | ExceptionType->getPointeeType(), EQuals); | ||||
724 | HandlerType = Context.getUnqualifiedArrayType( | ||||
725 | HandlerType->getPointeeType(), HQuals); | ||||
726 | if (!HQuals.compatiblyIncludes(EQuals)) | ||||
727 | return false; | ||||
728 | |||||
729 | if (HandlerType->isVoidType() && ExceptionType->isObjectType()) | ||||
730 | return true; | ||||
731 | |||||
732 | // The only remaining case is a derived-to-base conversion. | ||||
733 | } | ||||
734 | |||||
735 | // -- the handler is of type cg T or cv T& and T is an unambiguous public | ||||
736 | // base class of E | ||||
737 | if (!ExceptionType->isRecordType() || !HandlerType->isRecordType()) | ||||
738 | return false; | ||||
739 | CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, | ||||
740 | /*DetectVirtual=*/false); | ||||
741 | if (!IsDerivedFrom(SourceLocation(), ExceptionType, HandlerType, Paths) || | ||||
742 | Paths.isAmbiguous(Context.getCanonicalType(HandlerType))) | ||||
743 | return false; | ||||
744 | |||||
745 | // Do this check from a context without privileges. | ||||
746 | switch (CheckBaseClassAccess(SourceLocation(), HandlerType, ExceptionType, | ||||
747 | Paths.front(), | ||||
748 | /*Diagnostic*/ 0, | ||||
749 | /*ForceCheck*/ true, | ||||
750 | /*ForceUnprivileged*/ true)) { | ||||
751 | case AR_accessible: return true; | ||||
752 | case AR_inaccessible: return false; | ||||
753 | case AR_dependent: | ||||
754 | llvm_unreachable("access check dependent for unprivileged context")::llvm::llvm_unreachable_internal("access check dependent for unprivileged context" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 754); | ||||
755 | case AR_delayed: | ||||
756 | llvm_unreachable("access check delayed in non-declaration")::llvm::llvm_unreachable_internal("access check delayed in non-declaration" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 756); | ||||
757 | } | ||||
758 | llvm_unreachable("unexpected access check result")::llvm::llvm_unreachable_internal("unexpected access check result" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 758); | ||||
759 | } | ||||
760 | |||||
761 | /// CheckExceptionSpecSubset - Check whether the second function type's | ||||
762 | /// exception specification is a subset (or equivalent) of the first function | ||||
763 | /// type. This is used by override and pointer assignment checks. | ||||
764 | bool Sema::CheckExceptionSpecSubset(const PartialDiagnostic &DiagID, | ||||
765 | const PartialDiagnostic &NestedDiagID, | ||||
766 | const PartialDiagnostic &NoteID, | ||||
767 | const PartialDiagnostic &NoThrowDiagID, | ||||
768 | const FunctionProtoType *Superset, | ||||
769 | SourceLocation SuperLoc, | ||||
770 | const FunctionProtoType *Subset, | ||||
771 | SourceLocation SubLoc) { | ||||
772 | |||||
773 | // Just auto-succeed under -fno-exceptions. | ||||
774 | if (!getLangOpts().CXXExceptions) | ||||
775 | return false; | ||||
776 | |||||
777 | // FIXME: As usual, we could be more specific in our error messages, but | ||||
778 | // that better waits until we've got types with source locations. | ||||
779 | |||||
780 | if (!SubLoc.isValid()) | ||||
781 | SubLoc = SuperLoc; | ||||
782 | |||||
783 | // Resolve the exception specifications, if needed. | ||||
784 | Superset = ResolveExceptionSpec(SuperLoc, Superset); | ||||
785 | if (!Superset) | ||||
786 | return false; | ||||
787 | Subset = ResolveExceptionSpec(SubLoc, Subset); | ||||
788 | if (!Subset) | ||||
789 | return false; | ||||
790 | |||||
791 | ExceptionSpecificationType SuperEST = Superset->getExceptionSpecType(); | ||||
792 | ExceptionSpecificationType SubEST = Subset->getExceptionSpecType(); | ||||
793 | assert(!isUnresolvedExceptionSpec(SuperEST) &&(static_cast <bool> (!isUnresolvedExceptionSpec(SuperEST ) && !isUnresolvedExceptionSpec(SubEST) && "Shouldn't see unknown exception specifications here" ) ? void (0) : __assert_fail ("!isUnresolvedExceptionSpec(SuperEST) && !isUnresolvedExceptionSpec(SubEST) && \"Shouldn't see unknown exception specifications here\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 795, __extension__ __PRETTY_FUNCTION__)) | ||||
794 | !isUnresolvedExceptionSpec(SubEST) &&(static_cast <bool> (!isUnresolvedExceptionSpec(SuperEST ) && !isUnresolvedExceptionSpec(SubEST) && "Shouldn't see unknown exception specifications here" ) ? void (0) : __assert_fail ("!isUnresolvedExceptionSpec(SuperEST) && !isUnresolvedExceptionSpec(SubEST) && \"Shouldn't see unknown exception specifications here\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 795, __extension__ __PRETTY_FUNCTION__)) | ||||
795 | "Shouldn't see unknown exception specifications here")(static_cast <bool> (!isUnresolvedExceptionSpec(SuperEST ) && !isUnresolvedExceptionSpec(SubEST) && "Shouldn't see unknown exception specifications here" ) ? void (0) : __assert_fail ("!isUnresolvedExceptionSpec(SuperEST) && !isUnresolvedExceptionSpec(SubEST) && \"Shouldn't see unknown exception specifications here\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 795, __extension__ __PRETTY_FUNCTION__)); | ||||
796 | |||||
797 | // If there are dependent noexcept specs, assume everything is fine. Unlike | ||||
798 | // with the equivalency check, this is safe in this case, because we don't | ||||
799 | // want to merge declarations. Checks after instantiation will catch any | ||||
800 | // omissions we make here. | ||||
801 | if (SuperEST == EST_DependentNoexcept || SubEST == EST_DependentNoexcept) | ||||
802 | return false; | ||||
803 | |||||
804 | CanThrowResult SuperCanThrow = Superset->canThrow(); | ||||
805 | CanThrowResult SubCanThrow = Subset->canThrow(); | ||||
806 | |||||
807 | // If the superset contains everything or the subset contains nothing, we're | ||||
808 | // done. | ||||
809 | if ((SuperCanThrow == CT_Can && SuperEST != EST_Dynamic) || | ||||
810 | SubCanThrow == CT_Cannot) | ||||
811 | return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc, | ||||
812 | Subset, SubLoc); | ||||
813 | |||||
814 | // Allow __declspec(nothrow) to be missing on redeclaration as an extension in | ||||
815 | // some cases. | ||||
816 | if (NoThrowDiagID.getDiagID() != 0 && SubCanThrow == CT_Can && | ||||
817 | SuperCanThrow == CT_Cannot && SuperEST == EST_NoThrow) { | ||||
818 | Diag(SubLoc, NoThrowDiagID); | ||||
819 | if (NoteID.getDiagID() != 0) | ||||
820 | Diag(SuperLoc, NoteID); | ||||
821 | return true; | ||||
822 | } | ||||
823 | |||||
824 | // If the subset contains everything or the superset contains nothing, we've | ||||
825 | // failed. | ||||
826 | if ((SubCanThrow == CT_Can && SubEST != EST_Dynamic) || | ||||
827 | SuperCanThrow == CT_Cannot) { | ||||
828 | Diag(SubLoc, DiagID); | ||||
829 | if (NoteID.getDiagID() != 0) | ||||
830 | Diag(SuperLoc, NoteID); | ||||
831 | return true; | ||||
832 | } | ||||
833 | |||||
834 | assert(SuperEST == EST_Dynamic && SubEST == EST_Dynamic &&(static_cast <bool> (SuperEST == EST_Dynamic && SubEST == EST_Dynamic && "Exception spec subset: non-dynamic case slipped through." ) ? void (0) : __assert_fail ("SuperEST == EST_Dynamic && SubEST == EST_Dynamic && \"Exception spec subset: non-dynamic case slipped through.\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 835, __extension__ __PRETTY_FUNCTION__)) | ||||
835 | "Exception spec subset: non-dynamic case slipped through.")(static_cast <bool> (SuperEST == EST_Dynamic && SubEST == EST_Dynamic && "Exception spec subset: non-dynamic case slipped through." ) ? void (0) : __assert_fail ("SuperEST == EST_Dynamic && SubEST == EST_Dynamic && \"Exception spec subset: non-dynamic case slipped through.\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 835, __extension__ __PRETTY_FUNCTION__)); | ||||
836 | |||||
837 | // Neither contains everything or nothing. Do a proper comparison. | ||||
838 | for (QualType SubI : Subset->exceptions()) { | ||||
839 | if (const ReferenceType *RefTy = SubI->getAs<ReferenceType>()) | ||||
840 | SubI = RefTy->getPointeeType(); | ||||
841 | |||||
842 | // Make sure it's in the superset. | ||||
843 | bool Contained = false; | ||||
844 | for (QualType SuperI : Superset->exceptions()) { | ||||
845 | // [except.spec]p5: | ||||
846 | // the target entity shall allow at least the exceptions allowed by the | ||||
847 | // source | ||||
848 | // | ||||
849 | // We interpret this as meaning that a handler for some target type would | ||||
850 | // catch an exception of each source type. | ||||
851 | if (handlerCanCatch(SuperI, SubI)) { | ||||
852 | Contained = true; | ||||
853 | break; | ||||
854 | } | ||||
855 | } | ||||
856 | if (!Contained) { | ||||
857 | Diag(SubLoc, DiagID); | ||||
858 | if (NoteID.getDiagID() != 0) | ||||
859 | Diag(SuperLoc, NoteID); | ||||
860 | return true; | ||||
861 | } | ||||
862 | } | ||||
863 | // We've run half the gauntlet. | ||||
864 | return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc, | ||||
865 | Subset, SubLoc); | ||||
866 | } | ||||
867 | |||||
868 | static bool | ||||
869 | CheckSpecForTypesEquivalent(Sema &S, const PartialDiagnostic &DiagID, | ||||
870 | const PartialDiagnostic &NoteID, QualType Target, | ||||
871 | SourceLocation TargetLoc, QualType Source, | ||||
872 | SourceLocation SourceLoc) { | ||||
873 | const FunctionProtoType *TFunc = GetUnderlyingFunction(Target); | ||||
874 | if (!TFunc) | ||||
875 | return false; | ||||
876 | const FunctionProtoType *SFunc = GetUnderlyingFunction(Source); | ||||
877 | if (!SFunc) | ||||
878 | return false; | ||||
879 | |||||
880 | return S.CheckEquivalentExceptionSpec(DiagID, NoteID, TFunc, TargetLoc, | ||||
881 | SFunc, SourceLoc); | ||||
882 | } | ||||
883 | |||||
884 | /// CheckParamExceptionSpec - Check if the parameter and return types of the | ||||
885 | /// two functions have equivalent exception specs. This is part of the | ||||
886 | /// assignment and override compatibility check. We do not check the parameters | ||||
887 | /// of parameter function pointers recursively, as no sane programmer would | ||||
888 | /// even be able to write such a function type. | ||||
889 | bool Sema::CheckParamExceptionSpec(const PartialDiagnostic &DiagID, | ||||
890 | const PartialDiagnostic &NoteID, | ||||
891 | const FunctionProtoType *Target, | ||||
892 | SourceLocation TargetLoc, | ||||
893 | const FunctionProtoType *Source, | ||||
894 | SourceLocation SourceLoc) { | ||||
895 | auto RetDiag = DiagID; | ||||
896 | RetDiag << 0; | ||||
897 | if (CheckSpecForTypesEquivalent( | ||||
898 | *this, RetDiag, PDiag(), | ||||
899 | Target->getReturnType(), TargetLoc, Source->getReturnType(), | ||||
900 | SourceLoc)) | ||||
901 | return true; | ||||
902 | |||||
903 | // We shouldn't even be testing this unless the arguments are otherwise | ||||
904 | // compatible. | ||||
905 | assert(Target->getNumParams() == Source->getNumParams() &&(static_cast <bool> (Target->getNumParams() == Source ->getNumParams() && "Functions have different argument counts." ) ? void (0) : __assert_fail ("Target->getNumParams() == Source->getNumParams() && \"Functions have different argument counts.\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 906, __extension__ __PRETTY_FUNCTION__)) | ||||
906 | "Functions have different argument counts.")(static_cast <bool> (Target->getNumParams() == Source ->getNumParams() && "Functions have different argument counts." ) ? void (0) : __assert_fail ("Target->getNumParams() == Source->getNumParams() && \"Functions have different argument counts.\"" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 906, __extension__ __PRETTY_FUNCTION__)); | ||||
907 | for (unsigned i = 0, E = Target->getNumParams(); i != E; ++i) { | ||||
908 | auto ParamDiag = DiagID; | ||||
909 | ParamDiag << 1; | ||||
910 | if (CheckSpecForTypesEquivalent( | ||||
911 | *this, ParamDiag, PDiag(), | ||||
912 | Target->getParamType(i), TargetLoc, Source->getParamType(i), | ||||
913 | SourceLoc)) | ||||
914 | return true; | ||||
915 | } | ||||
916 | return false; | ||||
917 | } | ||||
918 | |||||
919 | bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) { | ||||
920 | // First we check for applicability. | ||||
921 | // Target type must be a function, function pointer or function reference. | ||||
922 | const FunctionProtoType *ToFunc = GetUnderlyingFunction(ToType); | ||||
923 | if (!ToFunc || ToFunc->hasDependentExceptionSpec()) | ||||
924 | return false; | ||||
925 | |||||
926 | // SourceType must be a function or function pointer. | ||||
927 | const FunctionProtoType *FromFunc = GetUnderlyingFunction(From->getType()); | ||||
928 | if (!FromFunc || FromFunc->hasDependentExceptionSpec()) | ||||
929 | return false; | ||||
930 | |||||
931 | unsigned DiagID = diag::err_incompatible_exception_specs; | ||||
932 | unsigned NestedDiagID = diag::err_deep_exception_specs_differ; | ||||
933 | // This is not an error in C++17 onwards, unless the noexceptness doesn't | ||||
934 | // match, but in that case we have a full-on type mismatch, not just a | ||||
935 | // type sugar mismatch. | ||||
936 | if (getLangOpts().CPlusPlus17) { | ||||
937 | DiagID = diag::warn_incompatible_exception_specs; | ||||
938 | NestedDiagID = diag::warn_deep_exception_specs_differ; | ||||
939 | } | ||||
940 | |||||
941 | // Now we've got the correct types on both sides, check their compatibility. | ||||
942 | // This means that the source of the conversion can only throw a subset of | ||||
943 | // the exceptions of the target, and any exception specs on arguments or | ||||
944 | // return types must be equivalent. | ||||
945 | // | ||||
946 | // FIXME: If there is a nested dependent exception specification, we should | ||||
947 | // not be checking it here. This is fine: | ||||
948 | // template<typename T> void f() { | ||||
949 | // void (*p)(void (*) throw(T)); | ||||
950 | // void (*q)(void (*) throw(int)) = p; | ||||
951 | // } | ||||
952 | // ... because it might be instantiated with T=int. | ||||
953 | return CheckExceptionSpecSubset( | ||||
954 | PDiag(DiagID), PDiag(NestedDiagID), PDiag(), PDiag(), ToFunc, | ||||
955 | From->getSourceRange().getBegin(), FromFunc, SourceLocation()) && | ||||
956 | !getLangOpts().CPlusPlus17; | ||||
957 | } | ||||
958 | |||||
959 | bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, | ||||
960 | const CXXMethodDecl *Old) { | ||||
961 | // If the new exception specification hasn't been parsed yet, skip the check. | ||||
962 | // We'll get called again once it's been parsed. | ||||
963 | if (New->getType()->castAs<FunctionProtoType>()->getExceptionSpecType() == | ||||
964 | EST_Unparsed) | ||||
965 | return false; | ||||
966 | |||||
967 | // Don't check uninstantiated template destructors at all. We can only | ||||
968 | // synthesize correct specs after the template is instantiated. | ||||
969 | if (isa<CXXDestructorDecl>(New) && New->getParent()->isDependentType()) | ||||
970 | return false; | ||||
971 | |||||
972 | // If the old exception specification hasn't been parsed yet, or the new | ||||
973 | // exception specification can't be computed yet, remember that we need to | ||||
974 | // perform this check when we get to the end of the outermost | ||||
975 | // lexically-surrounding class. | ||||
976 | if (exceptionSpecNotKnownYet(Old) || exceptionSpecNotKnownYet(New)) { | ||||
977 | DelayedOverridingExceptionSpecChecks.push_back({New, Old}); | ||||
978 | return false; | ||||
979 | } | ||||
980 | |||||
981 | unsigned DiagID = diag::err_override_exception_spec; | ||||
982 | if (getLangOpts().MSVCCompat) | ||||
983 | DiagID = diag::ext_override_exception_spec; | ||||
984 | return CheckExceptionSpecSubset(PDiag(DiagID), | ||||
985 | PDiag(diag::err_deep_exception_specs_differ), | ||||
986 | PDiag(diag::note_overridden_virtual_function), | ||||
987 | PDiag(diag::ext_override_exception_spec), | ||||
988 | Old->getType()->castAs<FunctionProtoType>(), | ||||
989 | Old->getLocation(), | ||||
990 | New->getType()->castAs<FunctionProtoType>(), | ||||
991 | New->getLocation()); | ||||
992 | } | ||||
993 | |||||
994 | static CanThrowResult canSubStmtsThrow(Sema &Self, const Stmt *S) { | ||||
995 | CanThrowResult R = CT_Cannot; | ||||
996 | for (const Stmt *SubStmt : S->children()) { | ||||
997 | if (!SubStmt) | ||||
998 | continue; | ||||
999 | R = mergeCanThrow(R, Self.canThrow(SubStmt)); | ||||
1000 | if (R == CT_Can) | ||||
1001 | break; | ||||
1002 | } | ||||
1003 | return R; | ||||
1004 | } | ||||
1005 | |||||
1006 | CanThrowResult Sema::canCalleeThrow(Sema &S, const Expr *E, const Decl *D, | ||||
1007 | SourceLocation Loc) { | ||||
1008 | // As an extension, we assume that __attribute__((nothrow)) functions don't | ||||
1009 | // throw. | ||||
1010 | if (D && isa<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>()) | ||||
1011 | return CT_Cannot; | ||||
1012 | |||||
1013 | QualType T; | ||||
1014 | |||||
1015 | // In C++1z, just look at the function type of the callee. | ||||
1016 | if (S.getLangOpts().CPlusPlus17 && E && isa<CallExpr>(E)) { | ||||
1017 | E = cast<CallExpr>(E)->getCallee(); | ||||
1018 | T = E->getType(); | ||||
1019 | if (T->isSpecificPlaceholderType(BuiltinType::BoundMember)) { | ||||
1020 | // Sadly we don't preserve the actual type as part of the "bound member" | ||||
1021 | // placeholder, so we need to reconstruct it. | ||||
1022 | E = E->IgnoreParenImpCasts(); | ||||
1023 | |||||
1024 | // Could be a call to a pointer-to-member or a plain member access. | ||||
1025 | if (auto *Op = dyn_cast<BinaryOperator>(E)) { | ||||
1026 | assert(Op->getOpcode() == BO_PtrMemD || Op->getOpcode() == BO_PtrMemI)(static_cast <bool> (Op->getOpcode() == BO_PtrMemD || Op->getOpcode() == BO_PtrMemI) ? void (0) : __assert_fail ("Op->getOpcode() == BO_PtrMemD || Op->getOpcode() == BO_PtrMemI" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 1026, __extension__ __PRETTY_FUNCTION__)); | ||||
1027 | T = Op->getRHS()->getType() | ||||
1028 | ->castAs<MemberPointerType>()->getPointeeType(); | ||||
1029 | } else { | ||||
1030 | T = cast<MemberExpr>(E)->getMemberDecl()->getType(); | ||||
1031 | } | ||||
1032 | } | ||||
1033 | } else if (const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D)) | ||||
1034 | T = VD->getType(); | ||||
1035 | else | ||||
1036 | // If we have no clue what we're calling, assume the worst. | ||||
1037 | return CT_Can; | ||||
1038 | |||||
1039 | const FunctionProtoType *FT; | ||||
1040 | if ((FT = T->getAs<FunctionProtoType>())) { | ||||
1041 | } else if (const PointerType *PT = T->getAs<PointerType>()) | ||||
1042 | FT = PT->getPointeeType()->getAs<FunctionProtoType>(); | ||||
1043 | else if (const ReferenceType *RT = T->getAs<ReferenceType>()) | ||||
1044 | FT = RT->getPointeeType()->getAs<FunctionProtoType>(); | ||||
1045 | else if (const MemberPointerType *MT = T->getAs<MemberPointerType>()) | ||||
1046 | FT = MT->getPointeeType()->getAs<FunctionProtoType>(); | ||||
1047 | else if (const BlockPointerType *BT = T->getAs<BlockPointerType>()) | ||||
1048 | FT = BT->getPointeeType()->getAs<FunctionProtoType>(); | ||||
1049 | |||||
1050 | if (!FT) | ||||
1051 | return CT_Can; | ||||
1052 | |||||
1053 | if (Loc.isValid() || (Loc.isInvalid() && E)) | ||||
1054 | FT = S.ResolveExceptionSpec(Loc.isInvalid() ? E->getBeginLoc() : Loc, FT); | ||||
1055 | if (!FT) | ||||
1056 | return CT_Can; | ||||
1057 | |||||
1058 | return FT->canThrow(); | ||||
1059 | } | ||||
1060 | |||||
1061 | static CanThrowResult canVarDeclThrow(Sema &Self, const VarDecl *VD) { | ||||
1062 | CanThrowResult CT = CT_Cannot; | ||||
1063 | |||||
1064 | // Initialization might throw. | ||||
1065 | if (!VD->isUsableInConstantExpressions(Self.Context)) | ||||
1066 | if (const Expr *Init = VD->getInit()) | ||||
1067 | CT = mergeCanThrow(CT, Self.canThrow(Init)); | ||||
1068 | |||||
1069 | // Destructor might throw. | ||||
1070 | if (VD->needsDestruction(Self.Context) == QualType::DK_cxx_destructor) { | ||||
1071 | if (auto *RD = | ||||
1072 | VD->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl()) { | ||||
1073 | if (auto *Dtor = RD->getDestructor()) { | ||||
1074 | CT = mergeCanThrow( | ||||
1075 | CT, Sema::canCalleeThrow(Self, nullptr, Dtor, VD->getLocation())); | ||||
1076 | } | ||||
1077 | } | ||||
1078 | } | ||||
1079 | |||||
1080 | // If this is a decomposition declaration, bindings might throw. | ||||
1081 | if (auto *DD = dyn_cast<DecompositionDecl>(VD)) | ||||
1082 | for (auto *B : DD->bindings()) | ||||
1083 | if (auto *HD = B->getHoldingVar()) | ||||
1084 | CT = mergeCanThrow(CT, canVarDeclThrow(Self, HD)); | ||||
1085 | |||||
1086 | return CT; | ||||
1087 | } | ||||
1088 | |||||
1089 | static CanThrowResult canDynamicCastThrow(const CXXDynamicCastExpr *DC) { | ||||
1090 | if (DC->isTypeDependent()) | ||||
1091 | return CT_Dependent; | ||||
1092 | |||||
1093 | if (!DC->getTypeAsWritten()->isReferenceType()) | ||||
1094 | return CT_Cannot; | ||||
1095 | |||||
1096 | if (DC->getSubExpr()->isTypeDependent()) | ||||
1097 | return CT_Dependent; | ||||
1098 | |||||
1099 | return DC->getCastKind() == clang::CK_Dynamic? CT_Can : CT_Cannot; | ||||
1100 | } | ||||
1101 | |||||
1102 | static CanThrowResult canTypeidThrow(Sema &S, const CXXTypeidExpr *DC) { | ||||
1103 | if (DC->isTypeOperand()) | ||||
1104 | return CT_Cannot; | ||||
1105 | |||||
1106 | Expr *Op = DC->getExprOperand(); | ||||
1107 | if (Op->isTypeDependent()) | ||||
1108 | return CT_Dependent; | ||||
1109 | |||||
1110 | const RecordType *RT = Op->getType()->getAs<RecordType>(); | ||||
1111 | if (!RT) | ||||
1112 | return CT_Cannot; | ||||
1113 | |||||
1114 | if (!cast<CXXRecordDecl>(RT->getDecl())->isPolymorphic()) | ||||
1115 | return CT_Cannot; | ||||
1116 | |||||
1117 | if (Op->Classify(S.Context).isPRValue()) | ||||
1118 | return CT_Cannot; | ||||
1119 | |||||
1120 | return CT_Can; | ||||
1121 | } | ||||
1122 | |||||
1123 | CanThrowResult Sema::canThrow(const Stmt *S) { | ||||
1124 | // C++ [expr.unary.noexcept]p3: | ||||
1125 | // [Can throw] if in a potentially-evaluated context the expression would | ||||
1126 | // contain: | ||||
1127 | switch (S->getStmtClass()) { | ||||
1128 | case Expr::ConstantExprClass: | ||||
1129 | return canThrow(cast<ConstantExpr>(S)->getSubExpr()); | ||||
1130 | |||||
1131 | case Expr::CXXThrowExprClass: | ||||
1132 | // - a potentially evaluated throw-expression | ||||
1133 | return CT_Can; | ||||
1134 | |||||
1135 | case Expr::CXXDynamicCastExprClass: { | ||||
1136 | // - a potentially evaluated dynamic_cast expression dynamic_cast<T>(v), | ||||
1137 | // where T is a reference type, that requires a run-time check | ||||
1138 | auto *CE = cast<CXXDynamicCastExpr>(S); | ||||
1139 | // FIXME: Properly determine whether a variably-modified type can throw. | ||||
1140 | if (CE->getType()->isVariablyModifiedType()) | ||||
1141 | return CT_Can; | ||||
1142 | CanThrowResult CT = canDynamicCastThrow(CE); | ||||
1143 | if (CT == CT_Can) | ||||
1144 | return CT; | ||||
1145 | return mergeCanThrow(CT, canSubStmtsThrow(*this, CE)); | ||||
1146 | } | ||||
1147 | |||||
1148 | case Expr::CXXTypeidExprClass: | ||||
1149 | // - a potentially evaluated typeid expression applied to a glvalue | ||||
1150 | // expression whose type is a polymorphic class type | ||||
1151 | return canTypeidThrow(*this, cast<CXXTypeidExpr>(S)); | ||||
1152 | |||||
1153 | // - a potentially evaluated call to a function, member function, function | ||||
1154 | // pointer, or member function pointer that does not have a non-throwing | ||||
1155 | // exception-specification | ||||
1156 | case Expr::CallExprClass: | ||||
1157 | case Expr::CXXMemberCallExprClass: | ||||
1158 | case Expr::CXXOperatorCallExprClass: | ||||
1159 | case Expr::UserDefinedLiteralClass: { | ||||
1160 | const CallExpr *CE = cast<CallExpr>(S); | ||||
1161 | CanThrowResult CT; | ||||
1162 | if (CE->isTypeDependent()) | ||||
1163 | CT = CT_Dependent; | ||||
1164 | else if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens())) | ||||
1165 | CT = CT_Cannot; | ||||
1166 | else | ||||
1167 | CT = canCalleeThrow(*this, CE, CE->getCalleeDecl()); | ||||
1168 | if (CT == CT_Can) | ||||
1169 | return CT; | ||||
1170 | return mergeCanThrow(CT, canSubStmtsThrow(*this, CE)); | ||||
1171 | } | ||||
1172 | |||||
1173 | case Expr::CXXConstructExprClass: | ||||
1174 | case Expr::CXXTemporaryObjectExprClass: { | ||||
1175 | auto *CE = cast<CXXConstructExpr>(S); | ||||
1176 | // FIXME: Properly determine whether a variably-modified type can throw. | ||||
1177 | if (CE->getType()->isVariablyModifiedType()) | ||||
1178 | return CT_Can; | ||||
1179 | CanThrowResult CT = canCalleeThrow(*this, CE, CE->getConstructor()); | ||||
1180 | if (CT == CT_Can) | ||||
1181 | return CT; | ||||
1182 | return mergeCanThrow(CT, canSubStmtsThrow(*this, CE)); | ||||
1183 | } | ||||
1184 | |||||
1185 | case Expr::CXXInheritedCtorInitExprClass: { | ||||
1186 | auto *ICIE = cast<CXXInheritedCtorInitExpr>(S); | ||||
1187 | return canCalleeThrow(*this, ICIE, ICIE->getConstructor()); | ||||
1188 | } | ||||
1189 | |||||
1190 | case Expr::LambdaExprClass: { | ||||
1191 | const LambdaExpr *Lambda = cast<LambdaExpr>(S); | ||||
1192 | CanThrowResult CT = CT_Cannot; | ||||
1193 | for (LambdaExpr::const_capture_init_iterator | ||||
1194 | Cap = Lambda->capture_init_begin(), | ||||
1195 | CapEnd = Lambda->capture_init_end(); | ||||
1196 | Cap != CapEnd; ++Cap) | ||||
1197 | CT = mergeCanThrow(CT, canThrow(*Cap)); | ||||
1198 | return CT; | ||||
1199 | } | ||||
1200 | |||||
1201 | case Expr::CXXNewExprClass: { | ||||
1202 | auto *NE = cast<CXXNewExpr>(S); | ||||
1203 | CanThrowResult CT; | ||||
1204 | if (NE->isTypeDependent()) | ||||
1205 | CT = CT_Dependent; | ||||
1206 | else | ||||
1207 | CT = canCalleeThrow(*this, NE, NE->getOperatorNew()); | ||||
1208 | if (CT == CT_Can) | ||||
1209 | return CT; | ||||
1210 | return mergeCanThrow(CT, canSubStmtsThrow(*this, NE)); | ||||
1211 | } | ||||
1212 | |||||
1213 | case Expr::CXXDeleteExprClass: { | ||||
1214 | auto *DE = cast<CXXDeleteExpr>(S); | ||||
1215 | CanThrowResult CT; | ||||
1216 | QualType DTy = DE->getDestroyedType(); | ||||
1217 | if (DTy.isNull() || DTy->isDependentType()) { | ||||
1218 | CT = CT_Dependent; | ||||
1219 | } else { | ||||
1220 | CT = canCalleeThrow(*this, DE, DE->getOperatorDelete()); | ||||
1221 | if (const RecordType *RT = DTy->getAs<RecordType>()) { | ||||
1222 | const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); | ||||
1223 | const CXXDestructorDecl *DD = RD->getDestructor(); | ||||
1224 | if (DD) | ||||
1225 | CT = mergeCanThrow(CT, canCalleeThrow(*this, DE, DD)); | ||||
1226 | } | ||||
1227 | if (CT == CT_Can) | ||||
1228 | return CT; | ||||
1229 | } | ||||
1230 | return mergeCanThrow(CT, canSubStmtsThrow(*this, DE)); | ||||
1231 | } | ||||
1232 | |||||
1233 | case Expr::CXXBindTemporaryExprClass: { | ||||
1234 | auto *BTE = cast<CXXBindTemporaryExpr>(S); | ||||
1235 | // The bound temporary has to be destroyed again, which might throw. | ||||
1236 | CanThrowResult CT = | ||||
1237 | canCalleeThrow(*this, BTE, BTE->getTemporary()->getDestructor()); | ||||
1238 | if (CT == CT_Can) | ||||
1239 | return CT; | ||||
1240 | return mergeCanThrow(CT, canSubStmtsThrow(*this, BTE)); | ||||
1241 | } | ||||
1242 | |||||
1243 | case Expr::PseudoObjectExprClass: { | ||||
1244 | auto *POE = cast<PseudoObjectExpr>(S); | ||||
1245 | CanThrowResult CT = CT_Cannot; | ||||
1246 | for (const Expr *E : POE->semantics()) { | ||||
1247 | CT = mergeCanThrow(CT, canThrow(E)); | ||||
1248 | if (CT == CT_Can) | ||||
1249 | break; | ||||
1250 | } | ||||
1251 | return CT; | ||||
1252 | } | ||||
1253 | |||||
1254 | // ObjC message sends are like function calls, but never have exception | ||||
1255 | // specs. | ||||
1256 | case Expr::ObjCMessageExprClass: | ||||
1257 | case Expr::ObjCPropertyRefExprClass: | ||||
1258 | case Expr::ObjCSubscriptRefExprClass: | ||||
1259 | return CT_Can; | ||||
1260 | |||||
1261 | // All the ObjC literals that are implemented as calls are | ||||
1262 | // potentially throwing unless we decide to close off that | ||||
1263 | // possibility. | ||||
1264 | case Expr::ObjCArrayLiteralClass: | ||||
1265 | case Expr::ObjCDictionaryLiteralClass: | ||||
1266 | case Expr::ObjCBoxedExprClass: | ||||
1267 | return CT_Can; | ||||
1268 | |||||
1269 | // Many other things have subexpressions, so we have to test those. | ||||
1270 | // Some are simple: | ||||
1271 | case Expr::CoawaitExprClass: | ||||
1272 | case Expr::ConditionalOperatorClass: | ||||
1273 | case Expr::CoyieldExprClass: | ||||
1274 | case Expr::CXXRewrittenBinaryOperatorClass: | ||||
1275 | case Expr::CXXStdInitializerListExprClass: | ||||
1276 | case Expr::DesignatedInitExprClass: | ||||
1277 | case Expr::DesignatedInitUpdateExprClass: | ||||
1278 | case Expr::ExprWithCleanupsClass: | ||||
1279 | case Expr::ExtVectorElementExprClass: | ||||
1280 | case Expr::InitListExprClass: | ||||
1281 | case Expr::ArrayInitLoopExprClass: | ||||
1282 | case Expr::MemberExprClass: | ||||
1283 | case Expr::ObjCIsaExprClass: | ||||
1284 | case Expr::ObjCIvarRefExprClass: | ||||
1285 | case Expr::ParenExprClass: | ||||
1286 | case Expr::ParenListExprClass: | ||||
1287 | case Expr::ShuffleVectorExprClass: | ||||
1288 | case Expr::StmtExprClass: | ||||
1289 | case Expr::ConvertVectorExprClass: | ||||
1290 | case Expr::VAArgExprClass: | ||||
1291 | return canSubStmtsThrow(*this, S); | ||||
1292 | |||||
1293 | case Expr::CompoundLiteralExprClass: | ||||
1294 | case Expr::CXXConstCastExprClass: | ||||
1295 | case Expr::CXXAddrspaceCastExprClass: | ||||
1296 | case Expr::CXXReinterpretCastExprClass: | ||||
1297 | case Expr::BuiltinBitCastExprClass: | ||||
1298 | // FIXME: Properly determine whether a variably-modified type can throw. | ||||
1299 | if (cast<Expr>(S)->getType()->isVariablyModifiedType()) | ||||
1300 | return CT_Can; | ||||
1301 | return canSubStmtsThrow(*this, S); | ||||
1302 | |||||
1303 | // Some might be dependent for other reasons. | ||||
1304 | case Expr::ArraySubscriptExprClass: | ||||
1305 | case Expr::MatrixSubscriptExprClass: | ||||
1306 | case Expr::OMPArraySectionExprClass: | ||||
1307 | case Expr::OMPArrayShapingExprClass: | ||||
1308 | case Expr::OMPIteratorExprClass: | ||||
1309 | case Expr::BinaryOperatorClass: | ||||
1310 | case Expr::DependentCoawaitExprClass: | ||||
1311 | case Expr::CompoundAssignOperatorClass: | ||||
1312 | case Expr::CStyleCastExprClass: | ||||
1313 | case Expr::CXXStaticCastExprClass: | ||||
1314 | case Expr::CXXFunctionalCastExprClass: | ||||
1315 | case Expr::ImplicitCastExprClass: | ||||
1316 | case Expr::MaterializeTemporaryExprClass: | ||||
1317 | case Expr::UnaryOperatorClass: { | ||||
1318 | // FIXME: Properly determine whether a variably-modified type can throw. | ||||
1319 | if (auto *CE = dyn_cast<CastExpr>(S)) | ||||
1320 | if (CE->getType()->isVariablyModifiedType()) | ||||
1321 | return CT_Can; | ||||
1322 | CanThrowResult CT = | ||||
1323 | cast<Expr>(S)->isTypeDependent() ? CT_Dependent : CT_Cannot; | ||||
1324 | return mergeCanThrow(CT, canSubStmtsThrow(*this, S)); | ||||
1325 | } | ||||
1326 | |||||
1327 | case Expr::CXXDefaultArgExprClass: | ||||
1328 | return canThrow(cast<CXXDefaultArgExpr>(S)->getExpr()); | ||||
1329 | |||||
1330 | case Expr::CXXDefaultInitExprClass: | ||||
1331 | return canThrow(cast<CXXDefaultInitExpr>(S)->getExpr()); | ||||
1332 | |||||
1333 | case Expr::ChooseExprClass: { | ||||
1334 | auto *CE = cast<ChooseExpr>(S); | ||||
1335 | if (CE->isTypeDependent() || CE->isValueDependent()) | ||||
1336 | return CT_Dependent; | ||||
1337 | return canThrow(CE->getChosenSubExpr()); | ||||
1338 | } | ||||
1339 | |||||
1340 | case Expr::GenericSelectionExprClass: | ||||
1341 | if (cast<GenericSelectionExpr>(S)->isResultDependent()) | ||||
1342 | return CT_Dependent; | ||||
1343 | return canThrow(cast<GenericSelectionExpr>(S)->getResultExpr()); | ||||
1344 | |||||
1345 | // Some expressions are always dependent. | ||||
1346 | case Expr::CXXDependentScopeMemberExprClass: | ||||
1347 | case Expr::CXXUnresolvedConstructExprClass: | ||||
1348 | case Expr::DependentScopeDeclRefExprClass: | ||||
1349 | case Expr::CXXFoldExprClass: | ||||
1350 | case Expr::RecoveryExprClass: | ||||
1351 | return CT_Dependent; | ||||
1352 | |||||
1353 | case Expr::AsTypeExprClass: | ||||
1354 | case Expr::BinaryConditionalOperatorClass: | ||||
1355 | case Expr::BlockExprClass: | ||||
1356 | case Expr::CUDAKernelCallExprClass: | ||||
1357 | case Expr::DeclRefExprClass: | ||||
1358 | case Expr::ObjCBridgedCastExprClass: | ||||
1359 | case Expr::ObjCIndirectCopyRestoreExprClass: | ||||
1360 | case Expr::ObjCProtocolExprClass: | ||||
1361 | case Expr::ObjCSelectorExprClass: | ||||
1362 | case Expr::ObjCAvailabilityCheckExprClass: | ||||
1363 | case Expr::OffsetOfExprClass: | ||||
1364 | case Expr::PackExpansionExprClass: | ||||
1365 | case Expr::SubstNonTypeTemplateParmExprClass: | ||||
1366 | case Expr::SubstNonTypeTemplateParmPackExprClass: | ||||
1367 | case Expr::FunctionParmPackExprClass: | ||||
1368 | case Expr::UnaryExprOrTypeTraitExprClass: | ||||
1369 | case Expr::UnresolvedLookupExprClass: | ||||
1370 | case Expr::UnresolvedMemberExprClass: | ||||
1371 | case Expr::TypoExprClass: | ||||
1372 | // FIXME: Many of the above can throw. | ||||
1373 | return CT_Cannot; | ||||
1374 | |||||
1375 | case Expr::AddrLabelExprClass: | ||||
1376 | case Expr::ArrayTypeTraitExprClass: | ||||
1377 | case Expr::AtomicExprClass: | ||||
1378 | case Expr::TypeTraitExprClass: | ||||
1379 | case Expr::CXXBoolLiteralExprClass: | ||||
1380 | case Expr::CXXNoexceptExprClass: | ||||
1381 | case Expr::CXXNullPtrLiteralExprClass: | ||||
1382 | case Expr::CXXPseudoDestructorExprClass: | ||||
1383 | case Expr::CXXScalarValueInitExprClass: | ||||
1384 | case Expr::CXXThisExprClass: | ||||
1385 | case Expr::CXXUuidofExprClass: | ||||
1386 | case Expr::CharacterLiteralClass: | ||||
1387 | case Expr::ExpressionTraitExprClass: | ||||
1388 | case Expr::FloatingLiteralClass: | ||||
1389 | case Expr::GNUNullExprClass: | ||||
1390 | case Expr::ImaginaryLiteralClass: | ||||
1391 | case Expr::ImplicitValueInitExprClass: | ||||
1392 | case Expr::IntegerLiteralClass: | ||||
1393 | case Expr::FixedPointLiteralClass: | ||||
1394 | case Expr::ArrayInitIndexExprClass: | ||||
1395 | case Expr::NoInitExprClass: | ||||
1396 | case Expr::ObjCEncodeExprClass: | ||||
1397 | case Expr::ObjCStringLiteralClass: | ||||
1398 | case Expr::ObjCBoolLiteralExprClass: | ||||
1399 | case Expr::OpaqueValueExprClass: | ||||
1400 | case Expr::PredefinedExprClass: | ||||
1401 | case Expr::SizeOfPackExprClass: | ||||
1402 | case Expr::StringLiteralClass: | ||||
1403 | case Expr::SourceLocExprClass: | ||||
1404 | case Expr::ConceptSpecializationExprClass: | ||||
1405 | case Expr::RequiresExprClass: | ||||
1406 | // These expressions can never throw. | ||||
1407 | return CT_Cannot; | ||||
1408 | |||||
1409 | case Expr::MSPropertyRefExprClass: | ||||
1410 | case Expr::MSPropertySubscriptExprClass: | ||||
1411 | llvm_unreachable("Invalid class for expression")::llvm::llvm_unreachable_internal("Invalid class for expression" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 1411); | ||||
1412 | |||||
1413 | // Most statements can throw if any substatement can throw. | ||||
1414 | case Stmt::AttributedStmtClass: | ||||
1415 | case Stmt::BreakStmtClass: | ||||
1416 | case Stmt::CapturedStmtClass: | ||||
1417 | case Stmt::CaseStmtClass: | ||||
1418 | case Stmt::CompoundStmtClass: | ||||
1419 | case Stmt::ContinueStmtClass: | ||||
1420 | case Stmt::CoreturnStmtClass: | ||||
1421 | case Stmt::CoroutineBodyStmtClass: | ||||
1422 | case Stmt::CXXCatchStmtClass: | ||||
1423 | case Stmt::CXXForRangeStmtClass: | ||||
1424 | case Stmt::DefaultStmtClass: | ||||
1425 | case Stmt::DoStmtClass: | ||||
1426 | case Stmt::ForStmtClass: | ||||
1427 | case Stmt::GCCAsmStmtClass: | ||||
1428 | case Stmt::GotoStmtClass: | ||||
1429 | case Stmt::IndirectGotoStmtClass: | ||||
1430 | case Stmt::LabelStmtClass: | ||||
1431 | case Stmt::MSAsmStmtClass: | ||||
1432 | case Stmt::MSDependentExistsStmtClass: | ||||
1433 | case Stmt::NullStmtClass: | ||||
1434 | case Stmt::ObjCAtCatchStmtClass: | ||||
1435 | case Stmt::ObjCAtFinallyStmtClass: | ||||
1436 | case Stmt::ObjCAtSynchronizedStmtClass: | ||||
1437 | case Stmt::ObjCAutoreleasePoolStmtClass: | ||||
1438 | case Stmt::ObjCForCollectionStmtClass: | ||||
1439 | case Stmt::OMPAtomicDirectiveClass: | ||||
1440 | case Stmt::OMPBarrierDirectiveClass: | ||||
1441 | case Stmt::OMPCancelDirectiveClass: | ||||
1442 | case Stmt::OMPCancellationPointDirectiveClass: | ||||
1443 | case Stmt::OMPCriticalDirectiveClass: | ||||
1444 | case Stmt::OMPDistributeDirectiveClass: | ||||
1445 | case Stmt::OMPDistributeParallelForDirectiveClass: | ||||
1446 | case Stmt::OMPDistributeParallelForSimdDirectiveClass: | ||||
1447 | case Stmt::OMPDistributeSimdDirectiveClass: | ||||
1448 | case Stmt::OMPFlushDirectiveClass: | ||||
1449 | case Stmt::OMPDepobjDirectiveClass: | ||||
1450 | case Stmt::OMPScanDirectiveClass: | ||||
1451 | case Stmt::OMPForDirectiveClass: | ||||
1452 | case Stmt::OMPForSimdDirectiveClass: | ||||
1453 | case Stmt::OMPMasterDirectiveClass: | ||||
1454 | case Stmt::OMPMasterTaskLoopDirectiveClass: | ||||
1455 | case Stmt::OMPMasterTaskLoopSimdDirectiveClass: | ||||
1456 | case Stmt::OMPOrderedDirectiveClass: | ||||
1457 | case Stmt::OMPCanonicalLoopClass: | ||||
1458 | case Stmt::OMPParallelDirectiveClass: | ||||
1459 | case Stmt::OMPParallelForDirectiveClass: | ||||
1460 | case Stmt::OMPParallelForSimdDirectiveClass: | ||||
1461 | case Stmt::OMPParallelMasterDirectiveClass: | ||||
1462 | case Stmt::OMPParallelMasterTaskLoopDirectiveClass: | ||||
1463 | case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass: | ||||
1464 | case Stmt::OMPParallelSectionsDirectiveClass: | ||||
1465 | case Stmt::OMPSectionDirectiveClass: | ||||
1466 | case Stmt::OMPSectionsDirectiveClass: | ||||
1467 | case Stmt::OMPSimdDirectiveClass: | ||||
1468 | case Stmt::OMPTileDirectiveClass: | ||||
1469 | case Stmt::OMPUnrollDirectiveClass: | ||||
1470 | case Stmt::OMPSingleDirectiveClass: | ||||
1471 | case Stmt::OMPTargetDataDirectiveClass: | ||||
1472 | case Stmt::OMPTargetDirectiveClass: | ||||
1473 | case Stmt::OMPTargetEnterDataDirectiveClass: | ||||
1474 | case Stmt::OMPTargetExitDataDirectiveClass: | ||||
1475 | case Stmt::OMPTargetParallelDirectiveClass: | ||||
1476 | case Stmt::OMPTargetParallelForDirectiveClass: | ||||
1477 | case Stmt::OMPTargetParallelForSimdDirectiveClass: | ||||
1478 | case Stmt::OMPTargetSimdDirectiveClass: | ||||
1479 | case Stmt::OMPTargetTeamsDirectiveClass: | ||||
1480 | case Stmt::OMPTargetTeamsDistributeDirectiveClass: | ||||
1481 | case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: | ||||
1482 | case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: | ||||
1483 | case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: | ||||
1484 | case Stmt::OMPTargetUpdateDirectiveClass: | ||||
1485 | case Stmt::OMPTaskDirectiveClass: | ||||
1486 | case Stmt::OMPTaskgroupDirectiveClass: | ||||
1487 | case Stmt::OMPTaskLoopDirectiveClass: | ||||
1488 | case Stmt::OMPTaskLoopSimdDirectiveClass: | ||||
1489 | case Stmt::OMPTaskwaitDirectiveClass: | ||||
1490 | case Stmt::OMPTaskyieldDirectiveClass: | ||||
1491 | case Stmt::OMPTeamsDirectiveClass: | ||||
1492 | case Stmt::OMPTeamsDistributeDirectiveClass: | ||||
1493 | case Stmt::OMPTeamsDistributeParallelForDirectiveClass: | ||||
1494 | case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: | ||||
1495 | case Stmt::OMPTeamsDistributeSimdDirectiveClass: | ||||
1496 | case Stmt::OMPInteropDirectiveClass: | ||||
1497 | case Stmt::OMPDispatchDirectiveClass: | ||||
1498 | case Stmt::OMPMaskedDirectiveClass: | ||||
1499 | case Stmt::ReturnStmtClass: | ||||
1500 | case Stmt::SEHExceptStmtClass: | ||||
1501 | case Stmt::SEHFinallyStmtClass: | ||||
1502 | case Stmt::SEHLeaveStmtClass: | ||||
1503 | case Stmt::SEHTryStmtClass: | ||||
1504 | case Stmt::SwitchStmtClass: | ||||
1505 | case Stmt::WhileStmtClass: | ||||
1506 | return canSubStmtsThrow(*this, S); | ||||
1507 | |||||
1508 | case Stmt::DeclStmtClass: { | ||||
1509 | CanThrowResult CT = CT_Cannot; | ||||
1510 | for (const Decl *D : cast<DeclStmt>(S)->decls()) { | ||||
1511 | if (auto *VD = dyn_cast<VarDecl>(D)) | ||||
1512 | CT = mergeCanThrow(CT, canVarDeclThrow(*this, VD)); | ||||
1513 | |||||
1514 | // FIXME: Properly determine whether a variably-modified type can throw. | ||||
1515 | if (auto *TND = dyn_cast<TypedefNameDecl>(D)) | ||||
1516 | if (TND->getUnderlyingType()->isVariablyModifiedType()) | ||||
1517 | return CT_Can; | ||||
1518 | if (auto *VD = dyn_cast<ValueDecl>(D)) | ||||
1519 | if (VD->getType()->isVariablyModifiedType()) | ||||
1520 | return CT_Can; | ||||
1521 | } | ||||
1522 | return CT; | ||||
1523 | } | ||||
1524 | |||||
1525 | case Stmt::IfStmtClass: { | ||||
1526 | auto *IS = cast<IfStmt>(S); | ||||
1527 | CanThrowResult CT = CT_Cannot; | ||||
1528 | if (const Stmt *Init = IS->getInit()) | ||||
1529 | CT = mergeCanThrow(CT, canThrow(Init)); | ||||
1530 | if (const Stmt *CondDS = IS->getConditionVariableDeclStmt()) | ||||
1531 | CT = mergeCanThrow(CT, canThrow(CondDS)); | ||||
1532 | CT = mergeCanThrow(CT, canThrow(IS->getCond())); | ||||
1533 | |||||
1534 | // For 'if constexpr', consider only the non-discarded case. | ||||
1535 | // FIXME: We should add a DiscardedStmt marker to the AST. | ||||
1536 | if (Optional<const Stmt *> Case = IS->getNondiscardedCase(Context)) | ||||
1537 | return *Case ? mergeCanThrow(CT, canThrow(*Case)) : CT; | ||||
1538 | |||||
1539 | CanThrowResult Then = canThrow(IS->getThen()); | ||||
1540 | CanThrowResult Else = IS->getElse() ? canThrow(IS->getElse()) : CT_Cannot; | ||||
1541 | if (Then == Else) | ||||
1542 | return mergeCanThrow(CT, Then); | ||||
1543 | |||||
1544 | // For a dependent 'if constexpr', the result is dependent if it depends on | ||||
1545 | // the value of the condition. | ||||
1546 | return mergeCanThrow(CT, IS->isConstexpr() ? CT_Dependent | ||||
1547 | : mergeCanThrow(Then, Else)); | ||||
1548 | } | ||||
1549 | |||||
1550 | case Stmt::CXXTryStmtClass: { | ||||
1551 | auto *TS = cast<CXXTryStmt>(S); | ||||
1552 | // try /*...*/ catch (...) { H } can throw only if H can throw. | ||||
1553 | // Any other try-catch can throw if any substatement can throw. | ||||
1554 | const CXXCatchStmt *FinalHandler = TS->getHandler(TS->getNumHandlers() - 1); | ||||
1555 | if (!FinalHandler->getExceptionDecl()) | ||||
1556 | return canThrow(FinalHandler->getHandlerBlock()); | ||||
1557 | return canSubStmtsThrow(*this, S); | ||||
1558 | } | ||||
1559 | |||||
1560 | case Stmt::ObjCAtThrowStmtClass: | ||||
1561 | return CT_Can; | ||||
1562 | |||||
1563 | case Stmt::ObjCAtTryStmtClass: { | ||||
1564 | auto *TS = cast<ObjCAtTryStmt>(S); | ||||
1565 | |||||
1566 | // @catch(...) need not be last in Objective-C. Walk backwards until we | ||||
1567 | // see one or hit the @try. | ||||
1568 | CanThrowResult CT = CT_Cannot; | ||||
1569 | if (const Stmt *Finally = TS->getFinallyStmt()) | ||||
1570 | CT = mergeCanThrow(CT, canThrow(Finally)); | ||||
1571 | for (unsigned I = TS->getNumCatchStmts(); I != 0; --I) { | ||||
1572 | const ObjCAtCatchStmt *Catch = TS->getCatchStmt(I - 1); | ||||
1573 | CT = mergeCanThrow(CT, canThrow(Catch)); | ||||
1574 | // If we reach a @catch(...), no earlier exceptions can escape. | ||||
1575 | if (Catch->hasEllipsis()) | ||||
1576 | return CT; | ||||
1577 | } | ||||
1578 | |||||
1579 | // Didn't find an @catch(...). Exceptions from the @try body can escape. | ||||
1580 | return mergeCanThrow(CT, canThrow(TS->getTryBody())); | ||||
1581 | } | ||||
1582 | |||||
1583 | case Stmt::SYCLUniqueStableNameExprClass: | ||||
1584 | return CT_Cannot; | ||||
1585 | case Stmt::NoStmtClass: | ||||
1586 | llvm_unreachable("Invalid class for statement")::llvm::llvm_unreachable_internal("Invalid class for statement" , "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 1586); | ||||
1587 | } | ||||
1588 | llvm_unreachable("Bogus StmtClass")::llvm::llvm_unreachable_internal("Bogus StmtClass", "/build/llvm-toolchain-snapshot-14~++20210828111110+16086d47c0d0/clang/lib/Sema/SemaExceptionSpec.cpp" , 1588); | ||||
1589 | } | ||||
1590 | |||||
1591 | } // end namespace clang |