1 //===--- SemaExceptionSpec.cpp - C++ Exception Specifications ---*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file provides Sema routines for C++ exception specification testing.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "Sema.h"
15 #include "clang/AST/CXXInheritance.h"
16 #include "clang/AST/Expr.h"
17 #include "clang/AST/ExprCXX.h"
18 #include "clang/AST/TypeLoc.h"
19 #include "clang/Lex/Preprocessor.h"
20 #include "clang/Basic/Diagnostic.h"
21 #include "clang/Basic/SourceManager.h"
22 #include "llvm/ADT/SmallPtrSet.h"
23 
24 namespace clang {
25 
26 static const FunctionProtoType *GetUnderlyingFunction(QualType T)
27 {
28   if (const PointerType *PtrTy = T->getAs<PointerType>())
29     T = PtrTy->getPointeeType();
30   else if (const ReferenceType *RefTy = T->getAs<ReferenceType>())
31     T = RefTy->getPointeeType();
32   else if (const MemberPointerType *MPTy = T->getAs<MemberPointerType>())
33     T = MPTy->getPointeeType();
34   return T->getAs<FunctionProtoType>();
35 }
36 
37 /// CheckSpecifiedExceptionType - Check if the given type is valid in an
38 /// exception specification. Incomplete types, or pointers to incomplete types
39 /// other than void are not allowed.
40 bool Sema::CheckSpecifiedExceptionType(QualType T, const SourceRange &Range) {
41 
42   // This check (and the similar one below) deals with issue 437, that changes
43   // C++ 9.2p2 this way:
44   // Within the class member-specification, the class is regarded as complete
45   // within function bodies, default arguments, exception-specifications, and
46   // constructor ctor-initializers (including such things in nested classes).
47   if (T->isRecordType() && T->getAs<RecordType>()->isBeingDefined())
48     return false;
49 
50   // C++ 15.4p2: A type denoted in an exception-specification shall not denote
51   //   an incomplete type.
52   if (RequireCompleteType(Range.getBegin(), T,
53       PDiag(diag::err_incomplete_in_exception_spec) << /*direct*/0 << Range))
54     return true;
55 
56   // C++ 15.4p2: A type denoted in an exception-specification shall not denote
57   //   an incomplete type a pointer or reference to an incomplete type, other
58   //   than (cv) void*.
59   int kind;
60   if (const PointerType* IT = T->getAs<PointerType>()) {
61     T = IT->getPointeeType();
62     kind = 1;
63   } else if (const ReferenceType* IT = T->getAs<ReferenceType>()) {
64     T = IT->getPointeeType();
65     kind = 2;
66   } else
67     return false;
68 
69   // Again as before
70   if (T->isRecordType() && T->getAs<RecordType>()->isBeingDefined())
71     return false;
72 
73   if (!T->isVoidType() && RequireCompleteType(Range.getBegin(), T,
74       PDiag(diag::err_incomplete_in_exception_spec) << kind << Range))
75     return true;
76 
77   return false;
78 }
79 
80 /// CheckDistantExceptionSpec - Check if the given type is a pointer or pointer
81 /// to member to a function with an exception specification. This means that
82 /// it is invalid to add another level of indirection.
83 bool Sema::CheckDistantExceptionSpec(QualType T) {
84   if (const PointerType *PT = T->getAs<PointerType>())
85     T = PT->getPointeeType();
86   else if (const MemberPointerType *PT = T->getAs<MemberPointerType>())
87     T = PT->getPointeeType();
88   else
89     return false;
90 
91   const FunctionProtoType *FnT = T->getAs<FunctionProtoType>();
92   if (!FnT)
93     return false;
94 
95   return FnT->hasExceptionSpec();
96 }
97 
98 bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) {
99   bool MissingExceptionSpecification = false;
100   bool MissingEmptyExceptionSpecification = false;
101   if (!CheckEquivalentExceptionSpec(PDiag(diag::err_mismatched_exception_spec),
102                                     PDiag(diag::note_previous_declaration),
103                                     Old->getType()->getAs<FunctionProtoType>(),
104                                     Old->getLocation(),
105                                     New->getType()->getAs<FunctionProtoType>(),
106                                     New->getLocation(),
107                                     &MissingExceptionSpecification,
108                                     &MissingEmptyExceptionSpecification))
109     return false;
110 
111   // The failure was something other than an empty exception
112   // specification; return an error.
113   if (!MissingExceptionSpecification && !MissingEmptyExceptionSpecification)
114     return true;
115 
116   // The new function declaration is only missing an empty exception
117   // specification "throw()". If the throw() specification came from a
118   // function in a system header that has C linkage, just add an empty
119   // exception specification to the "new" declaration. This is an
120   // egregious workaround for glibc, which adds throw() specifications
121   // to many libc functions as an optimization. Unfortunately, that
122   // optimization isn't permitted by the C++ standard, so we're forced
123   // to work around it here.
124   if (MissingEmptyExceptionSpecification &&
125       isa<FunctionProtoType>(New->getType()) &&
126       (Old->getLocation().isInvalid() ||
127        Context.getSourceManager().isInSystemHeader(Old->getLocation())) &&
128       Old->isExternC()) {
129     const FunctionProtoType *NewProto
130       = cast<FunctionProtoType>(New->getType());
131     QualType NewType = Context.getFunctionType(NewProto->getResultType(),
132                                                NewProto->arg_type_begin(),
133                                                NewProto->getNumArgs(),
134                                                NewProto->isVariadic(),
135                                                NewProto->getTypeQuals(),
136                                                true, false, 0, 0,
137                                                NewProto->getExtInfo());
138     New->setType(NewType);
139     return false;
140   }
141 
142   if (MissingExceptionSpecification && isa<FunctionProtoType>(New->getType())) {
143     const FunctionProtoType *NewProto
144       = cast<FunctionProtoType>(New->getType());
145     const FunctionProtoType *OldProto
146       = Old->getType()->getAs<FunctionProtoType>();
147 
148     // Update the type of the function with the appropriate exception
149     // specification.
150     QualType NewType = Context.getFunctionType(NewProto->getResultType(),
151                                                NewProto->arg_type_begin(),
152                                                NewProto->getNumArgs(),
153                                                NewProto->isVariadic(),
154                                                NewProto->getTypeQuals(),
155                                                OldProto->hasExceptionSpec(),
156                                                OldProto->hasAnyExceptionSpec(),
157                                                OldProto->getNumExceptions(),
158                                                OldProto->exception_begin(),
159                                                NewProto->getExtInfo());
160     New->setType(NewType);
161 
162     // If exceptions are disabled, suppress the warning about missing
163     // exception specifications for new and delete operators.
164     if (!getLangOptions().Exceptions) {
165       switch (New->getDeclName().getCXXOverloadedOperator()) {
166       case OO_New:
167       case OO_Array_New:
168       case OO_Delete:
169       case OO_Array_Delete:
170         if (New->getDeclContext()->isTranslationUnit())
171           return false;
172         break;
173 
174       default:
175         break;
176       }
177     }
178 
179     // Warn about the lack of exception specification.
180     llvm::SmallString<128> ExceptionSpecString;
181     llvm::raw_svector_ostream OS(ExceptionSpecString);
182     OS << "throw(";
183     bool OnFirstException = true;
184     for (FunctionProtoType::exception_iterator E = OldProto->exception_begin(),
185                                             EEnd = OldProto->exception_end();
186          E != EEnd;
187          ++E) {
188       if (OnFirstException)
189         OnFirstException = false;
190       else
191         OS << ", ";
192 
193       OS << E->getAsString(Context.PrintingPolicy);
194     }
195     OS << ")";
196     OS.flush();
197 
198     SourceLocation AfterParenLoc;
199     if (TypeSourceInfo *TSInfo = New->getTypeSourceInfo()) {
200       TypeLoc TL = TSInfo->getTypeLoc();
201       if (const FunctionTypeLoc *FTLoc = dyn_cast<FunctionTypeLoc>(&TL))
202         AfterParenLoc = PP.getLocForEndOfToken(FTLoc->getRParenLoc());
203     }
204 
205     if (AfterParenLoc.isInvalid())
206       Diag(New->getLocation(), diag::warn_missing_exception_specification)
207         << New << OS.str();
208     else {
209       // FIXME: This will get more complicated with C++0x
210       // late-specified return types.
211       Diag(New->getLocation(), diag::warn_missing_exception_specification)
212         << New << OS.str()
213         << FixItHint::CreateInsertion(AfterParenLoc, " " + OS.str().str());
214     }
215 
216     if (!Old->getLocation().isInvalid())
217       Diag(Old->getLocation(), diag::note_previous_declaration);
218 
219     return false;
220   }
221 
222   Diag(New->getLocation(), diag::err_mismatched_exception_spec);
223   Diag(Old->getLocation(), diag::note_previous_declaration);
224   return true;
225 }
226 
227 /// CheckEquivalentExceptionSpec - Check if the two types have equivalent
228 /// exception specifications. Exception specifications are equivalent if
229 /// they allow exactly the same set of exception types. It does not matter how
230 /// that is achieved. See C++ [except.spec]p2.
231 bool Sema::CheckEquivalentExceptionSpec(
232     const FunctionProtoType *Old, SourceLocation OldLoc,
233     const FunctionProtoType *New, SourceLocation NewLoc) {
234   return CheckEquivalentExceptionSpec(
235                                     PDiag(diag::err_mismatched_exception_spec),
236                                       PDiag(diag::note_previous_declaration),
237                                       Old, OldLoc, New, NewLoc);
238 }
239 
240 /// CheckEquivalentExceptionSpec - Check if the two types have equivalent
241 /// exception specifications. Exception specifications are equivalent if
242 /// they allow exactly the same set of exception types. It does not matter how
243 /// that is achieved. See C++ [except.spec]p2.
244 bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID,
245                                         const PartialDiagnostic & NoteID,
246                                         const FunctionProtoType *Old,
247                                         SourceLocation OldLoc,
248                                         const FunctionProtoType *New,
249                                         SourceLocation NewLoc,
250                                         bool *MissingExceptionSpecification,
251                                      bool *MissingEmptyExceptionSpecification)  {
252   // Just completely ignore this under -fno-exceptions.
253   if (!getLangOptions().Exceptions)
254     return false;
255 
256   if (MissingExceptionSpecification)
257     *MissingExceptionSpecification = false;
258 
259   if (MissingEmptyExceptionSpecification)
260     *MissingEmptyExceptionSpecification = false;
261 
262   bool OldAny = !Old->hasExceptionSpec() || Old->hasAnyExceptionSpec();
263   bool NewAny = !New->hasExceptionSpec() || New->hasAnyExceptionSpec();
264   if (OldAny && NewAny)
265     return false;
266   if (OldAny || NewAny) {
267     if (MissingExceptionSpecification && Old->hasExceptionSpec() &&
268         !New->hasExceptionSpec()) {
269       // The old type has an exception specification of some sort, but
270       // the new type does not.
271       *MissingExceptionSpecification = true;
272 
273       if (MissingEmptyExceptionSpecification &&
274           !Old->hasAnyExceptionSpec() && Old->getNumExceptions() == 0) {
275         // The old type has a throw() exception specification and the
276         // new type has no exception specification, and the caller asked
277         // to handle this itself.
278         *MissingEmptyExceptionSpecification = true;
279       }
280 
281       return true;
282     }
283 
284     Diag(NewLoc, DiagID);
285     if (NoteID.getDiagID() != 0)
286       Diag(OldLoc, NoteID);
287     return true;
288   }
289 
290   bool Success = true;
291   // Both have a definite exception spec. Collect the first set, then compare
292   // to the second.
293   llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes;
294   for (FunctionProtoType::exception_iterator I = Old->exception_begin(),
295        E = Old->exception_end(); I != E; ++I)
296     OldTypes.insert(Context.getCanonicalType(*I).getUnqualifiedType());
297 
298   for (FunctionProtoType::exception_iterator I = New->exception_begin(),
299        E = New->exception_end(); I != E && Success; ++I) {
300     CanQualType TypePtr = Context.getCanonicalType(*I).getUnqualifiedType();
301     if(OldTypes.count(TypePtr))
302       NewTypes.insert(TypePtr);
303     else
304       Success = false;
305   }
306 
307   Success = Success && OldTypes.size() == NewTypes.size();
308 
309   if (Success) {
310     return false;
311   }
312   Diag(NewLoc, DiagID);
313   if (NoteID.getDiagID() != 0)
314     Diag(OldLoc, NoteID);
315   return true;
316 }
317 
318 /// CheckExceptionSpecSubset - Check whether the second function type's
319 /// exception specification is a subset (or equivalent) of the first function
320 /// type. This is used by override and pointer assignment checks.
321 bool Sema::CheckExceptionSpecSubset(
322     const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID,
323     const FunctionProtoType *Superset, SourceLocation SuperLoc,
324     const FunctionProtoType *Subset, SourceLocation SubLoc) {
325 
326   // Just auto-succeed under -fno-exceptions.
327   if (!getLangOptions().Exceptions)
328     return false;
329 
330   // FIXME: As usual, we could be more specific in our error messages, but
331   // that better waits until we've got types with source locations.
332 
333   if (!SubLoc.isValid())
334     SubLoc = SuperLoc;
335 
336   // If superset contains everything, we're done.
337   if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec())
338     return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
339 
340   // It does not. If the subset contains everything, we've failed.
341   if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) {
342     Diag(SubLoc, DiagID);
343     if (NoteID.getDiagID() != 0)
344       Diag(SuperLoc, NoteID);
345     return true;
346   }
347 
348   // Neither contains everything. Do a proper comparison.
349   for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(),
350        SubE = Subset->exception_end(); SubI != SubE; ++SubI) {
351     // Take one type from the subset.
352     QualType CanonicalSubT = Context.getCanonicalType(*SubI);
353     // Unwrap pointers and references so that we can do checks within a class
354     // hierarchy. Don't unwrap member pointers; they don't have hierarchy
355     // conversions on the pointee.
356     bool SubIsPointer = false;
357     if (const ReferenceType *RefTy = CanonicalSubT->getAs<ReferenceType>())
358       CanonicalSubT = RefTy->getPointeeType();
359     if (const PointerType *PtrTy = CanonicalSubT->getAs<PointerType>()) {
360       CanonicalSubT = PtrTy->getPointeeType();
361       SubIsPointer = true;
362     }
363     bool SubIsClass = CanonicalSubT->isRecordType();
364     CanonicalSubT = CanonicalSubT.getLocalUnqualifiedType();
365 
366     CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
367                        /*DetectVirtual=*/false);
368 
369     bool Contained = false;
370     // Make sure it's in the superset.
371     for (FunctionProtoType::exception_iterator SuperI =
372            Superset->exception_begin(), SuperE = Superset->exception_end();
373          SuperI != SuperE; ++SuperI) {
374       QualType CanonicalSuperT = Context.getCanonicalType(*SuperI);
375       // SubT must be SuperT or derived from it, or pointer or reference to
376       // such types.
377       if (const ReferenceType *RefTy = CanonicalSuperT->getAs<ReferenceType>())
378         CanonicalSuperT = RefTy->getPointeeType();
379       if (SubIsPointer) {
380         if (const PointerType *PtrTy = CanonicalSuperT->getAs<PointerType>())
381           CanonicalSuperT = PtrTy->getPointeeType();
382         else {
383           continue;
384         }
385       }
386       CanonicalSuperT = CanonicalSuperT.getLocalUnqualifiedType();
387       // If the types are the same, move on to the next type in the subset.
388       if (CanonicalSubT == CanonicalSuperT) {
389         Contained = true;
390         break;
391       }
392 
393       // Otherwise we need to check the inheritance.
394       if (!SubIsClass || !CanonicalSuperT->isRecordType())
395         continue;
396 
397       Paths.clear();
398       if (!IsDerivedFrom(CanonicalSubT, CanonicalSuperT, Paths))
399         continue;
400 
401       if (Paths.isAmbiguous(Context.getCanonicalType(CanonicalSuperT)))
402         continue;
403 
404       // Do this check from a context without privileges.
405       switch (CheckBaseClassAccess(SourceLocation(),
406                                    CanonicalSuperT, CanonicalSubT,
407                                    Paths.front(),
408                                    /*Diagnostic*/ 0,
409                                    /*ForceCheck*/ true,
410                                    /*ForceUnprivileged*/ true)) {
411       case AR_accessible: break;
412       case AR_inaccessible: continue;
413       case AR_dependent:
414         llvm_unreachable("access check dependent for unprivileged context");
415         break;
416       case AR_delayed:
417         llvm_unreachable("access check delayed in non-declaration");
418         break;
419       }
420 
421       Contained = true;
422       break;
423     }
424     if (!Contained) {
425       Diag(SubLoc, DiagID);
426       if (NoteID.getDiagID() != 0)
427         Diag(SuperLoc, NoteID);
428       return true;
429     }
430   }
431   // We've run half the gauntlet.
432   return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc);
433 }
434 
435 static bool CheckSpecForTypesEquivalent(Sema &S,
436     const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID,
437     QualType Target, SourceLocation TargetLoc,
438     QualType Source, SourceLocation SourceLoc)
439 {
440   const FunctionProtoType *TFunc = GetUnderlyingFunction(Target);
441   if (!TFunc)
442     return false;
443   const FunctionProtoType *SFunc = GetUnderlyingFunction(Source);
444   if (!SFunc)
445     return false;
446 
447   return S.CheckEquivalentExceptionSpec(DiagID, NoteID, TFunc, TargetLoc,
448                                         SFunc, SourceLoc);
449 }
450 
451 /// CheckParamExceptionSpec - Check if the parameter and return types of the
452 /// two functions have equivalent exception specs. This is part of the
453 /// assignment and override compatibility check. We do not check the parameters
454 /// of parameter function pointers recursively, as no sane programmer would
455 /// even be able to write such a function type.
456 bool Sema::CheckParamExceptionSpec(const PartialDiagnostic & NoteID,
457     const FunctionProtoType *Target, SourceLocation TargetLoc,
458     const FunctionProtoType *Source, SourceLocation SourceLoc)
459 {
460   if (CheckSpecForTypesEquivalent(*this,
461                            PDiag(diag::err_deep_exception_specs_differ) << 0,
462                                   PDiag(),
463                                   Target->getResultType(), TargetLoc,
464                                   Source->getResultType(), SourceLoc))
465     return true;
466 
467   // We shouldn't even be testing this unless the arguments are otherwise
468   // compatible.
469   assert(Target->getNumArgs() == Source->getNumArgs() &&
470          "Functions have different argument counts.");
471   for (unsigned i = 0, E = Target->getNumArgs(); i != E; ++i) {
472     if (CheckSpecForTypesEquivalent(*this,
473                            PDiag(diag::err_deep_exception_specs_differ) << 1,
474                                     PDiag(),
475                                     Target->getArgType(i), TargetLoc,
476                                     Source->getArgType(i), SourceLoc))
477       return true;
478   }
479   return false;
480 }
481 
482 bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType)
483 {
484   // First we check for applicability.
485   // Target type must be a function, function pointer or function reference.
486   const FunctionProtoType *ToFunc = GetUnderlyingFunction(ToType);
487   if (!ToFunc)
488     return false;
489 
490   // SourceType must be a function or function pointer.
491   const FunctionProtoType *FromFunc = GetUnderlyingFunction(From->getType());
492   if (!FromFunc)
493     return false;
494 
495   // Now we've got the correct types on both sides, check their compatibility.
496   // This means that the source of the conversion can only throw a subset of
497   // the exceptions of the target, and any exception specs on arguments or
498   // return types must be equivalent.
499   return CheckExceptionSpecSubset(PDiag(diag::err_incompatible_exception_specs),
500                                   PDiag(), ToFunc,
501                                   From->getSourceRange().getBegin(),
502                                   FromFunc, SourceLocation());
503 }
504 
505 bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
506                                                 const CXXMethodDecl *Old) {
507   return CheckExceptionSpecSubset(PDiag(diag::err_override_exception_spec),
508                                   PDiag(diag::note_overridden_virtual_function),
509                                   Old->getType()->getAs<FunctionProtoType>(),
510                                   Old->getLocation(),
511                                   New->getType()->getAs<FunctionProtoType>(),
512                                   New->getLocation());
513 }
514 
515 } // end namespace clang
516