SemaExceptionSpec.cpp revision 90f2dca178e6c2076cd74dc78fb4b22128e0f048
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 24namespace clang { 25 26static 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. 40bool 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. 83bool 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 98bool 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. 231bool 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. 244bool 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 if (MissingExceptionSpecification) 253 *MissingExceptionSpecification = false; 254 255 if (MissingEmptyExceptionSpecification) 256 *MissingEmptyExceptionSpecification = false; 257 258 bool OldAny = !Old->hasExceptionSpec() || Old->hasAnyExceptionSpec(); 259 bool NewAny = !New->hasExceptionSpec() || New->hasAnyExceptionSpec(); 260 if (OldAny && NewAny) 261 return false; 262 if (OldAny || NewAny) { 263 if (MissingExceptionSpecification && Old->hasExceptionSpec() && 264 !New->hasExceptionSpec()) { 265 // The old type has an exception specification of some sort, but 266 // the new type does not. 267 *MissingExceptionSpecification = true; 268 269 if (MissingEmptyExceptionSpecification && 270 !Old->hasAnyExceptionSpec() && Old->getNumExceptions() == 0) { 271 // The old type has a throw() exception specification and the 272 // new type has no exception specification, and the caller asked 273 // to handle this itself. 274 *MissingEmptyExceptionSpecification = true; 275 } 276 277 return true; 278 } 279 280 Diag(NewLoc, DiagID); 281 if (NoteID.getDiagID() != 0) 282 Diag(OldLoc, NoteID); 283 return true; 284 } 285 286 bool Success = true; 287 // Both have a definite exception spec. Collect the first set, then compare 288 // to the second. 289 llvm::SmallPtrSet<CanQualType, 8> OldTypes, NewTypes; 290 for (FunctionProtoType::exception_iterator I = Old->exception_begin(), 291 E = Old->exception_end(); I != E; ++I) 292 OldTypes.insert(Context.getCanonicalType(*I).getUnqualifiedType()); 293 294 for (FunctionProtoType::exception_iterator I = New->exception_begin(), 295 E = New->exception_end(); I != E && Success; ++I) { 296 CanQualType TypePtr = Context.getCanonicalType(*I).getUnqualifiedType(); 297 if(OldTypes.count(TypePtr)) 298 NewTypes.insert(TypePtr); 299 else 300 Success = false; 301 } 302 303 Success = Success && OldTypes.size() == NewTypes.size(); 304 305 if (Success) { 306 return false; 307 } 308 Diag(NewLoc, DiagID); 309 if (NoteID.getDiagID() != 0) 310 Diag(OldLoc, NoteID); 311 return true; 312} 313 314/// CheckExceptionSpecSubset - Check whether the second function type's 315/// exception specification is a subset (or equivalent) of the first function 316/// type. This is used by override and pointer assignment checks. 317bool Sema::CheckExceptionSpecSubset( 318 const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, 319 const FunctionProtoType *Superset, SourceLocation SuperLoc, 320 const FunctionProtoType *Subset, SourceLocation SubLoc) { 321 // FIXME: As usual, we could be more specific in our error messages, but 322 // that better waits until we've got types with source locations. 323 324 if (!SubLoc.isValid()) 325 SubLoc = SuperLoc; 326 327 // If superset contains everything, we're done. 328 if (!Superset->hasExceptionSpec() || Superset->hasAnyExceptionSpec()) 329 return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); 330 331 // It does not. If the subset contains everything, we've failed. 332 if (!Subset->hasExceptionSpec() || Subset->hasAnyExceptionSpec()) { 333 Diag(SubLoc, DiagID); 334 if (NoteID.getDiagID() != 0) 335 Diag(SuperLoc, NoteID); 336 return true; 337 } 338 339 // Neither contains everything. Do a proper comparison. 340 for (FunctionProtoType::exception_iterator SubI = Subset->exception_begin(), 341 SubE = Subset->exception_end(); SubI != SubE; ++SubI) { 342 // Take one type from the subset. 343 QualType CanonicalSubT = Context.getCanonicalType(*SubI); 344 // Unwrap pointers and references so that we can do checks within a class 345 // hierarchy. Don't unwrap member pointers; they don't have hierarchy 346 // conversions on the pointee. 347 bool SubIsPointer = false; 348 if (const ReferenceType *RefTy = CanonicalSubT->getAs<ReferenceType>()) 349 CanonicalSubT = RefTy->getPointeeType(); 350 if (const PointerType *PtrTy = CanonicalSubT->getAs<PointerType>()) { 351 CanonicalSubT = PtrTy->getPointeeType(); 352 SubIsPointer = true; 353 } 354 bool SubIsClass = CanonicalSubT->isRecordType(); 355 CanonicalSubT = CanonicalSubT.getLocalUnqualifiedType(); 356 357 CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, 358 /*DetectVirtual=*/false); 359 360 bool Contained = false; 361 // Make sure it's in the superset. 362 for (FunctionProtoType::exception_iterator SuperI = 363 Superset->exception_begin(), SuperE = Superset->exception_end(); 364 SuperI != SuperE; ++SuperI) { 365 QualType CanonicalSuperT = Context.getCanonicalType(*SuperI); 366 // SubT must be SuperT or derived from it, or pointer or reference to 367 // such types. 368 if (const ReferenceType *RefTy = CanonicalSuperT->getAs<ReferenceType>()) 369 CanonicalSuperT = RefTy->getPointeeType(); 370 if (SubIsPointer) { 371 if (const PointerType *PtrTy = CanonicalSuperT->getAs<PointerType>()) 372 CanonicalSuperT = PtrTy->getPointeeType(); 373 else { 374 continue; 375 } 376 } 377 CanonicalSuperT = CanonicalSuperT.getLocalUnqualifiedType(); 378 // If the types are the same, move on to the next type in the subset. 379 if (CanonicalSubT == CanonicalSuperT) { 380 Contained = true; 381 break; 382 } 383 384 // Otherwise we need to check the inheritance. 385 if (!SubIsClass || !CanonicalSuperT->isRecordType()) 386 continue; 387 388 Paths.clear(); 389 if (!IsDerivedFrom(CanonicalSubT, CanonicalSuperT, Paths)) 390 continue; 391 392 if (Paths.isAmbiguous(Context.getCanonicalType(CanonicalSuperT))) 393 continue; 394 395 // Do this check from a context without privileges. 396 switch (CheckBaseClassAccess(SourceLocation(), 397 CanonicalSuperT, CanonicalSubT, 398 Paths.front(), 399 /*Diagnostic*/ 0, 400 /*ForceCheck*/ true, 401 /*ForceUnprivileged*/ true)) { 402 case AR_accessible: break; 403 case AR_inaccessible: continue; 404 case AR_dependent: 405 llvm_unreachable("access check dependent for unprivileged context"); 406 break; 407 case AR_delayed: 408 llvm_unreachable("access check delayed in non-declaration"); 409 break; 410 } 411 412 Contained = true; 413 break; 414 } 415 if (!Contained) { 416 Diag(SubLoc, DiagID); 417 if (NoteID.getDiagID() != 0) 418 Diag(SuperLoc, NoteID); 419 return true; 420 } 421 } 422 // We've run half the gauntlet. 423 return CheckParamExceptionSpec(NoteID, Superset, SuperLoc, Subset, SubLoc); 424} 425 426static bool CheckSpecForTypesEquivalent(Sema &S, 427 const PartialDiagnostic &DiagID, const PartialDiagnostic & NoteID, 428 QualType Target, SourceLocation TargetLoc, 429 QualType Source, SourceLocation SourceLoc) 430{ 431 const FunctionProtoType *TFunc = GetUnderlyingFunction(Target); 432 if (!TFunc) 433 return false; 434 const FunctionProtoType *SFunc = GetUnderlyingFunction(Source); 435 if (!SFunc) 436 return false; 437 438 return S.CheckEquivalentExceptionSpec(DiagID, NoteID, TFunc, TargetLoc, 439 SFunc, SourceLoc); 440} 441 442/// CheckParamExceptionSpec - Check if the parameter and return types of the 443/// two functions have equivalent exception specs. This is part of the 444/// assignment and override compatibility check. We do not check the parameters 445/// of parameter function pointers recursively, as no sane programmer would 446/// even be able to write such a function type. 447bool Sema::CheckParamExceptionSpec(const PartialDiagnostic & NoteID, 448 const FunctionProtoType *Target, SourceLocation TargetLoc, 449 const FunctionProtoType *Source, SourceLocation SourceLoc) 450{ 451 if (CheckSpecForTypesEquivalent(*this, 452 PDiag(diag::err_deep_exception_specs_differ) << 0, 453 PDiag(), 454 Target->getResultType(), TargetLoc, 455 Source->getResultType(), SourceLoc)) 456 return true; 457 458 // We shouldn't even be testing this unless the arguments are otherwise 459 // compatible. 460 assert(Target->getNumArgs() == Source->getNumArgs() && 461 "Functions have different argument counts."); 462 for (unsigned i = 0, E = Target->getNumArgs(); i != E; ++i) { 463 if (CheckSpecForTypesEquivalent(*this, 464 PDiag(diag::err_deep_exception_specs_differ) << 1, 465 PDiag(), 466 Target->getArgType(i), TargetLoc, 467 Source->getArgType(i), SourceLoc)) 468 return true; 469 } 470 return false; 471} 472 473bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) 474{ 475 // First we check for applicability. 476 // Target type must be a function, function pointer or function reference. 477 const FunctionProtoType *ToFunc = GetUnderlyingFunction(ToType); 478 if (!ToFunc) 479 return false; 480 481 // SourceType must be a function or function pointer. 482 const FunctionProtoType *FromFunc = GetUnderlyingFunction(From->getType()); 483 if (!FromFunc) 484 return false; 485 486 // Now we've got the correct types on both sides, check their compatibility. 487 // This means that the source of the conversion can only throw a subset of 488 // the exceptions of the target, and any exception specs on arguments or 489 // return types must be equivalent. 490 return CheckExceptionSpecSubset(PDiag(diag::err_incompatible_exception_specs), 491 PDiag(), ToFunc, 492 From->getSourceRange().getBegin(), 493 FromFunc, SourceLocation()); 494} 495 496bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, 497 const CXXMethodDecl *Old) { 498 return CheckExceptionSpecSubset(PDiag(diag::err_override_exception_spec), 499 PDiag(diag::note_overridden_virtual_function), 500 Old->getType()->getAs<FunctionProtoType>(), 501 Old->getLocation(), 502 New->getType()->getAs<FunctionProtoType>(), 503 New->getLocation()); 504} 505 506} // end namespace clang 507