Skip to content

Commit

Permalink
No -fsanitize=function warning when calling noexcept function through…
Browse files Browse the repository at this point in the history
… non-noexcept pointer in C++17

As discussed in the mail thread <https://groups.google.com/a/isocpp.org/forum/
#!topic/std-discussion/T64_dW3WKUk> "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
  • Loading branch information
stbergmann committed Jan 5, 2018
1 parent 0b1c457 commit 7ad9adf
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 25 deletions.
7 changes: 7 additions & 0 deletions include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// \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);
Expand Down
26 changes: 12 additions & 14 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ParenType>(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<AttributedType>(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<FunctionProtoType>(Orig);
return Context.getFunctionType(
return getFunctionType(
Proto->getReturnType(), Proto->getParamTypes(),
Proto->getExtProtoInfo().withExceptionSpec(ESI));
}
Expand All @@ -2610,16 +2608,16 @@ 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(
FunctionDecl *FD, const FunctionProtoType::ExceptionSpecInfo &ESI,
bool AsWritten) {
// Update the type.
QualType Updated =
getFunctionTypeWithExceptionSpec(*this, FD->getType(), ESI);
getFunctionTypeWithExceptionSpec(FD->getType(), ESI);
FD->setType(Updated);

if (!AsWritten)
Expand All @@ -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
Expand Down
11 changes: 8 additions & 3 deletions lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4479,8 +4479,7 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee

CalleeType = getContext().getCanonicalType(CalleeType);

const auto *FnType =
cast<FunctionType>(cast<PointerType>(CalleeType)->getPointeeType());
auto PointeeType = cast<PointerType>(CalleeType)->getPointeeType();

CGCallee Callee = OrigCallee;

Expand All @@ -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);
Expand Down Expand Up @@ -4530,6 +4533,8 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
}
}

const auto *FnType = cast<FunctionType>(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) &&
Expand Down
7 changes: 6 additions & 1 deletion lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,8 +924,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
if (getLangOpts().CPlusPlus && SanOpts.has(SanitizerKind::Function)) {
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(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,
Expand Down
4 changes: 1 addition & 3 deletions lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3435,9 +3435,7 @@ static unsigned extractPBaseFlags(ASTContext &Ctx, QualType &Type) {
if (auto *Proto = Type->getAs<FunctionProtoType>()) {
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);
}
}

Expand Down
6 changes: 2 additions & 4 deletions lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1475,10 +1475,8 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
const auto *ToFPT = cast<FunctionProtoType>(ToFn);
if (FromFPT->isNothrow(Context) && !ToFPT->isNothrow(Context)) {
FromFn = cast<FunctionType>(
Context.getFunctionType(FromFPT->getReturnType(),
FromFPT->getParamTypes(),
FromFPT->getExtProtoInfo().withExceptionSpec(
FunctionProtoType::ExceptionSpecInfo()))
Context.getFunctionTypeWithExceptionSpec(QualType(FromFPT, 0),
EST_None)
.getTypePtr());
Changed = true;
}
Expand Down

0 comments on commit 7ad9adf

Please sign in to comment.