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