diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/AnalysisBasedWarnings.cpp | 53 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExceptionSpec.cpp | 160 |
2 files changed, 109 insertions, 104 deletions
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 0033edf326a..e67f94c6383 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -287,48 +287,21 @@ enum ThrowState { FoundPathWithNoThrowOutFunction, }; -static bool isThrowCaught(const CXXThrowExpr *Throw, - const CXXCatchStmt *Catch) { - const Type *CaughtType = Catch->getCaughtType().getTypePtrOrNull(); - if (!CaughtType) - return true; - const Type *ThrowType = nullptr; - if (Throw->getSubExpr()) - ThrowType = Throw->getSubExpr()->getType().getTypePtrOrNull(); - if (!ThrowType) - return false; - if (ThrowType->isReferenceType()) - ThrowType = ThrowType->castAs<ReferenceType>() - ->getPointeeType() - ->getUnqualifiedDesugaredType(); - if (CaughtType->isReferenceType()) - CaughtType = CaughtType->castAs<ReferenceType>() - ->getPointeeType() - ->getUnqualifiedDesugaredType(); - if (ThrowType->isPointerType() && CaughtType->isPointerType()) { - ThrowType = ThrowType->getPointeeType()->getUnqualifiedDesugaredType(); - CaughtType = CaughtType->getPointeeType()->getUnqualifiedDesugaredType(); - } - if (CaughtType == ThrowType) - return true; - const CXXRecordDecl *CaughtAsRecordType = - CaughtType->getAsCXXRecordDecl(); - const CXXRecordDecl *ThrowTypeAsRecordType = ThrowType->getAsCXXRecordDecl(); - if (CaughtAsRecordType && ThrowTypeAsRecordType) - return ThrowTypeAsRecordType->isDerivedFrom(CaughtAsRecordType); - return false; -} - -static bool isThrowCaughtByHandlers(const CXXThrowExpr *CE, +static bool isThrowCaughtByHandlers(Sema &S, + const CXXThrowExpr *CE, const CXXTryStmt *TryStmt) { for (unsigned H = 0, E = TryStmt->getNumHandlers(); H < E; ++H) { - if (isThrowCaught(CE, TryStmt->getHandler(H))) + QualType Caught = TryStmt->getHandler(H)->getCaughtType(); + if (Caught.isNull() || // catch (...) catches everything + (CE->getSubExpr() && // throw; is only caught by ... + S.handlerCanCatch(Caught, CE->getSubExpr()->getType()))) return true; } return false; } -static bool doesThrowEscapePath(CFGBlock Block, SourceLocation &OpLoc) { +static bool doesThrowEscapePath(Sema &S, CFGBlock Block, + SourceLocation &OpLoc) { for (const auto &B : Block) { if (B.getKind() != CFGElement::Statement) continue; @@ -342,7 +315,7 @@ static bool doesThrowEscapePath(CFGBlock Block, SourceLocation &OpLoc) { continue; if (const auto *Terminator = dyn_cast_or_null<CXXTryStmt>(I->getTerminator())) - if (isThrowCaughtByHandlers(CE, Terminator)) + if (isThrowCaughtByHandlers(S, CE, Terminator)) return false; } return true; @@ -350,8 +323,8 @@ static bool doesThrowEscapePath(CFGBlock Block, SourceLocation &OpLoc) { return false; } -static bool hasThrowOutNonThrowingFunc(SourceLocation &OpLoc, CFG *BodyCFG) { - +static bool hasThrowOutNonThrowingFunc(Sema &S, SourceLocation &OpLoc, + CFG *BodyCFG) { unsigned ExitID = BodyCFG->getExit().getBlockID(); SmallVector<ThrowState, 16> States(BodyCFG->getNumBlockIDs(), @@ -369,7 +342,7 @@ static bool hasThrowOutNonThrowingFunc(SourceLocation &OpLoc, CFG *BodyCFG) { if (ExitID == ID) continue; - if (doesThrowEscapePath(*CurBlock, OpLoc)) + if (doesThrowEscapePath(S, *CurBlock, OpLoc)) CurState = FoundPathForThrow; } @@ -419,7 +392,7 @@ static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD, if (BodyCFG->getExit().pred_empty()) return; SourceLocation OpLoc; - if (hasThrowOutNonThrowingFunc(OpLoc, BodyCFG)) + if (hasThrowOutNonThrowingFunc(S, OpLoc, BodyCFG)) EmitDiagForCXXThrowInNonThrowingFunc(S, OpLoc, FD); } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 67d1b02d1fc..ebe93750a79 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -626,6 +626,90 @@ bool Sema::CheckEquivalentExceptionSpec(const PartialDiagnostic &DiagID, New, NewLoc); } +bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) { + // [except.handle]p3: + // A handler is a match for an exception object of type E if: + + // HandlerType must be ExceptionType or derived from it, or pointer or + // reference to such types. + const ReferenceType *RefTy = HandlerType->getAs<ReferenceType>(); + if (RefTy) + HandlerType = RefTy->getPointeeType(); + + // -- the handler is of type cv T or cv T& and E and T are the same type + if (Context.hasSameUnqualifiedType(ExceptionType, HandlerType)) + return true; + + // FIXME: ObjC pointer types? + if (HandlerType->isPointerType() || HandlerType->isMemberPointerType()) { + if (RefTy && (!HandlerType.isConstQualified() || + HandlerType.isVolatileQualified())) + return false; + + // -- the handler is of type cv T or const T& where T is a pointer or + // pointer to member type and E is std::nullptr_t + if (ExceptionType->isNullPtrType()) + return true; + + // -- the handler is of type cv T or const T& where T is a pointer or + // pointer to member type and E is a pointer or pointer to member type + // that can be converted to T by one or more of + // -- a qualification conversion + // -- a function pointer conversion + bool LifetimeConv; + QualType Result; + // FIXME: Should we treat the exception as catchable if a lifetime + // conversion is required? + if (IsQualificationConversion(ExceptionType, HandlerType, false, + LifetimeConv) || + IsFunctionConversion(ExceptionType, HandlerType, Result)) + return true; + + // -- a standard pointer conversion [...] + if (!ExceptionType->isPointerType() || !HandlerType->isPointerType()) + return false; + + // Handle the "qualification conversion" portion. + Qualifiers EQuals, HQuals; + ExceptionType = Context.getUnqualifiedArrayType( + ExceptionType->getPointeeType(), EQuals); + HandlerType = Context.getUnqualifiedArrayType( + HandlerType->getPointeeType(), HQuals); + if (!HQuals.compatiblyIncludes(EQuals)) + return false; + + if (HandlerType->isVoidType() && ExceptionType->isObjectType()) + return true; + + // The only remaining case is a derived-to-base conversion. + } + + // -- the handler is of type cg T or cv T& and T is an unambiguous public + // base class of E + if (!ExceptionType->isRecordType() || !HandlerType->isRecordType()) + return false; + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + if (!IsDerivedFrom(SourceLocation(), ExceptionType, HandlerType, Paths) || + Paths.isAmbiguous(Context.getCanonicalType(HandlerType))) + return false; + + // Do this check from a context without privileges. + switch (CheckBaseClassAccess(SourceLocation(), HandlerType, ExceptionType, + Paths.front(), + /*Diagnostic*/ 0, + /*ForceCheck*/ true, + /*ForceUnprivileged*/ true)) { + case AR_accessible: return true; + case AR_inaccessible: return false; + case AR_dependent: + llvm_unreachable("access check dependent for unprivileged context"); + case AR_delayed: + llvm_unreachable("access check delayed in non-declaration"); + } + llvm_unreachable("unexpected access check result"); +} + /// CheckExceptionSpecSubset - Check whether the second function type's /// exception specification is a subset (or equivalent) of the first function /// type. This is used by override and pointer assignment checks. @@ -722,75 +806,23 @@ bool Sema::CheckExceptionSpecSubset(const PartialDiagnostic &DiagID, "Exception spec subset: non-dynamic case slipped through."); // Neither contains everything or nothing. Do a proper comparison. - for (const auto &SubI : Subset->exceptions()) { - // Take one type from the subset. - QualType CanonicalSubT = Context.getCanonicalType(SubI); - // Unwrap pointers and references so that we can do checks within a class - // hierarchy. Don't unwrap member pointers; they don't have hierarchy - // conversions on the pointee. - bool SubIsPointer = false; - if (const ReferenceType *RefTy = CanonicalSubT->getAs<ReferenceType>()) - CanonicalSubT = RefTy->getPointeeType(); - if (const PointerType *PtrTy = CanonicalSubT->getAs<PointerType>()) { - CanonicalSubT = PtrTy->getPointeeType(); - SubIsPointer = true; - } - bool SubIsClass = CanonicalSubT->isRecordType(); - CanonicalSubT = CanonicalSubT.getLocalUnqualifiedType(); - - CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, - /*DetectVirtual=*/false); + for (QualType SubI : Subset->exceptions()) { + if (const ReferenceType *RefTy = SubI->getAs<ReferenceType>()) + SubI = RefTy->getPointeeType(); - bool Contained = false; // Make sure it's in the superset. - for (const auto &SuperI : Superset->exceptions()) { - QualType CanonicalSuperT = Context.getCanonicalType(SuperI); - // SubT must be SuperT or derived from it, or pointer or reference to - // such types. - if (const ReferenceType *RefTy = CanonicalSuperT->getAs<ReferenceType>()) - CanonicalSuperT = RefTy->getPointeeType(); - if (SubIsPointer) { - if (const PointerType *PtrTy = CanonicalSuperT->getAs<PointerType>()) - CanonicalSuperT = PtrTy->getPointeeType(); - else { - continue; - } - } - CanonicalSuperT = CanonicalSuperT.getLocalUnqualifiedType(); - // If the types are the same, move on to the next type in the subset. - if (CanonicalSubT == CanonicalSuperT) { + bool Contained = false; + for (QualType SuperI : Superset->exceptions()) { + // [except.spec]p5: + // the target entity shall allow at least the exceptions allowed by the + // source + // + // We interpret this as meaning that a handler for some target type would + // catch an exception of each source type. + if (handlerCanCatch(SuperI, SubI)) { Contained = true; break; } - - // Otherwise we need to check the inheritance. - if (!SubIsClass || !CanonicalSuperT->isRecordType()) - continue; - - Paths.clear(); - if (!IsDerivedFrom(SubLoc, CanonicalSubT, CanonicalSuperT, Paths)) - continue; - - if (Paths.isAmbiguous(Context.getCanonicalType(CanonicalSuperT))) - continue; - - // Do this check from a context without privileges. - switch (CheckBaseClassAccess(SourceLocation(), - CanonicalSuperT, CanonicalSubT, - Paths.front(), - /*Diagnostic*/ 0, - /*ForceCheck*/ true, - /*ForceUnprivileged*/ true)) { - case AR_accessible: break; - case AR_inaccessible: continue; - case AR_dependent: - llvm_unreachable("access check dependent for unprivileged context"); - case AR_delayed: - llvm_unreachable("access check delayed in non-declaration"); - } - - Contained = true; - break; } if (!Contained) { Diag(SubLoc, DiagID); |