From 7ad9adf8c0a3b96aa60a2557c81f1de71d4ca2a8 Mon Sep 17 00:00:00 2001 From: Stephan Bergmann Date: Fri, 5 Jan 2018 07:57:12 +0000 Subject: [PATCH] No -fsanitize=function warning when calling noexcept function through non-noexcept pointer in C++17 As discussed in the mail thread "Calling noexcept function throug non- noexcept pointer is undefined behavior?", such a call should not be UB. However, Clang currently warns about it. This change removes exception specifications from the function types recorded for -fsanitize=function, both in the functions themselves and at the call sites. That means that calling a non-noexcept function through a noexcept pointer will also not be flagged as UB. In the review of this change, that was deemed acceptable, at least for now. (See the "TODO" in compiler-rt test/ubsan/TestCases/TypeCheck/Function/function.cpp.) To remove exception specifications from types, the existing internal ASTContext::getFunctionTypeWithExceptionSpec was made public, and some places otherwise unrelated to this change have been adapted to call it, too. This is the cfe part of a patch covering both cfe and compiler-rt. Differential Revision: https://reviews.llvm.org/D40720 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@321859 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 7 +++++++ lib/AST/ASTContext.cpp | 26 ++++++++++++-------------- lib/CodeGen/CGExpr.cpp | 11 ++++++++--- lib/CodeGen/CodeGenFunction.cpp | 7 ++++++- lib/CodeGen/ItaniumCXXABI.cpp | 4 +--- lib/Sema/SemaOverload.cpp | 6 ++---- 6 files changed, 36 insertions(+), 25 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index a5d080035d..8ddb1385bf 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1162,6 +1162,13 @@ public: /// \brief Change the result type of a function type once it is deduced. void adjustDeducedFunctionResultType(FunctionDecl *FD, QualType ResultType); + /// Get a function type and produce the equivalent function type with the + /// specified exception specification. Type sugar that can be present on a + /// declaration of a function with an exception specification is permitted + /// and preserved. Other type sugar (for instance, typedefs) is not. + QualType getFunctionTypeWithExceptionSpec( + QualType Orig, const FunctionProtoType::ExceptionSpecInfo &ESI); + /// \brief Determine whether two function types are the same, ignoring /// exception specifications in cases where they're part of the type. bool hasSameFunctionTypeIgnoringExceptionSpec(QualType T, QualType U); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 3dc961d4f1..b9a23ed964 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2582,26 +2582,24 @@ void ASTContext::adjustDeducedFunctionResultType(FunctionDecl *FD, /// specified exception specification. Type sugar that can be present on a /// declaration of a function with an exception specification is permitted /// and preserved. Other type sugar (for instance, typedefs) is not. -static QualType getFunctionTypeWithExceptionSpec( - ASTContext &Context, QualType Orig, - const FunctionProtoType::ExceptionSpecInfo &ESI) { +QualType ASTContext::getFunctionTypeWithExceptionSpec( + QualType Orig, const FunctionProtoType::ExceptionSpecInfo &ESI) { // Might have some parens. if (auto *PT = dyn_cast(Orig)) - return Context.getParenType( - getFunctionTypeWithExceptionSpec(Context, PT->getInnerType(), ESI)); + return getParenType( + getFunctionTypeWithExceptionSpec(PT->getInnerType(), ESI)); // Might have a calling-convention attribute. if (auto *AT = dyn_cast(Orig)) - return Context.getAttributedType( + return getAttributedType( AT->getAttrKind(), - getFunctionTypeWithExceptionSpec(Context, AT->getModifiedType(), ESI), - getFunctionTypeWithExceptionSpec(Context, AT->getEquivalentType(), - ESI)); + getFunctionTypeWithExceptionSpec(AT->getModifiedType(), ESI), + getFunctionTypeWithExceptionSpec(AT->getEquivalentType(), ESI)); // Anything else must be a function type. Rebuild it with the new exception // specification. const FunctionProtoType *Proto = cast(Orig); - return Context.getFunctionType( + return getFunctionType( Proto->getReturnType(), Proto->getParamTypes(), Proto->getExtProtoInfo().withExceptionSpec(ESI)); } @@ -2610,8 +2608,8 @@ bool ASTContext::hasSameFunctionTypeIgnoringExceptionSpec(QualType T, QualType U) { return hasSameType(T, U) || (getLangOpts().CPlusPlus17 && - hasSameType(getFunctionTypeWithExceptionSpec(*this, T, EST_None), - getFunctionTypeWithExceptionSpec(*this, U, EST_None))); + hasSameType(getFunctionTypeWithExceptionSpec(T, EST_None), + getFunctionTypeWithExceptionSpec(U, EST_None))); } void ASTContext::adjustExceptionSpec( @@ -2619,7 +2617,7 @@ void ASTContext::adjustExceptionSpec( bool AsWritten) { // Update the type. QualType Updated = - getFunctionTypeWithExceptionSpec(*this, FD->getType(), ESI); + getFunctionTypeWithExceptionSpec(FD->getType(), ESI); FD->setType(Updated); if (!AsWritten) @@ -2630,7 +2628,7 @@ void ASTContext::adjustExceptionSpec( // If the type and the type-as-written differ, we may need to update // the type-as-written too. if (TSInfo->getType() != FD->getType()) - Updated = getFunctionTypeWithExceptionSpec(*this, TSInfo->getType(), ESI); + Updated = getFunctionTypeWithExceptionSpec(TSInfo->getType(), ESI); // FIXME: When we get proper type location information for exceptions, // we'll also have to rebuild the TypeSourceInfo. For now, we just patch diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 3f81be00f3..3a46bbfe20 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -4479,8 +4479,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee CalleeType = getContext().getCanonicalType(CalleeType); - const auto *FnType = - cast(cast(CalleeType)->getPointeeType()); + auto PointeeType = cast(CalleeType)->getPointeeType(); CGCallee Callee = OrigCallee; @@ -4489,8 +4488,12 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee if (llvm::Constant *PrefixSig = CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) { SanitizerScope SanScope(this); + // Remove any (C++17) exception specifications, to allow calling e.g. a + // noexcept function through a non-noexcept pointer. + auto ProtoTy = + getContext().getFunctionTypeWithExceptionSpec(PointeeType, EST_None); llvm::Constant *FTRTTIConst = - CGM.GetAddrOfRTTIDescriptor(QualType(FnType, 0), /*ForEH=*/true); + CGM.GetAddrOfRTTIDescriptor(ProtoTy, /*ForEH=*/true); llvm::Type *PrefixStructTyElems[] = {PrefixSig->getType(), Int32Ty}; llvm::StructType *PrefixStructTy = llvm::StructType::get( CGM.getLLVMContext(), PrefixStructTyElems, /*isPacked=*/true); @@ -4530,6 +4533,8 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee } } + const auto *FnType = cast(PointeeType); + // If we are checking indirect calls and this call is indirect, check that the // function pointer is a member of the bit set for the function type. if (SanOpts.has(SanitizerKind::CFIICall) && diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 664b510784..6fb21b1573 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -924,8 +924,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, if (getLangOpts().CPlusPlus && SanOpts.has(SanitizerKind::Function)) { if (const FunctionDecl *FD = dyn_cast_or_null(D)) { if (llvm::Constant *PrologueSig = getPrologueSignature(CGM, FD)) { + // Remove any (C++17) exception specifications, to allow calling e.g. a + // noexcept function through a non-noexcept pointer. + auto ProtoTy = + getContext().getFunctionTypeWithExceptionSpec(FD->getType(), + EST_None); llvm::Constant *FTRTTIConst = - CGM.GetAddrOfRTTIDescriptor(FD->getType(), /*ForEH=*/true); + CGM.GetAddrOfRTTIDescriptor(ProtoTy, /*ForEH=*/true); llvm::Constant *FTRTTIConstEncoded = EncodeAddrForUseInPrologue(Fn, FTRTTIConst); llvm::Constant *PrologueStructElems[] = {PrologueSig, diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index c375b82ea9..d2538e9e85 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -3435,9 +3435,7 @@ static unsigned extractPBaseFlags(ASTContext &Ctx, QualType &Type) { if (auto *Proto = Type->getAs()) { if (Proto->isNothrow(Ctx)) { Flags |= ItaniumRTTIBuilder::PTI_Noexcept; - Type = Ctx.getFunctionType( - Proto->getReturnType(), Proto->getParamTypes(), - Proto->getExtProtoInfo().withExceptionSpec(EST_None)); + Type = Ctx.getFunctionTypeWithExceptionSpec(Type, EST_None); } } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index f2b1963df1..0543490322 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1475,10 +1475,8 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, const auto *ToFPT = cast(ToFn); if (FromFPT->isNothrow(Context) && !ToFPT->isNothrow(Context)) { FromFn = cast( - Context.getFunctionType(FromFPT->getReturnType(), - FromFPT->getParamTypes(), - FromFPT->getExtProtoInfo().withExceptionSpec( - FunctionProtoType::ExceptionSpecInfo())) + Context.getFunctionTypeWithExceptionSpec(QualType(FromFPT, 0), + EST_None) .getTypePtr()); Changed = true; } -- 2.50.1