PR31244: Use the exception specification from the callee's type directly to

compute whether a call is noexcept, even if we can't map the callee expression
to a called declaration.

llvm-svn: 288558
This commit is contained in:
Richard Smith 2016-12-03 00:29:06 +00:00
parent 465bd0fc00
commit 3a8f13ac23
2 changed files with 43 additions and 17 deletions

View File

@ -944,24 +944,37 @@ static CanThrowResult canSubExprsThrow(Sema &S, const Expr *E) {
}
static CanThrowResult canCalleeThrow(Sema &S, const Expr *E, const Decl *D) {
assert(D && "Expected decl");
// See if we can get a function type from the decl somehow.
const ValueDecl *VD = dyn_cast<ValueDecl>(D);
if (!VD) {
// In C++17, we may have a canonical exception specification. If so, use it.
if (auto *FT = E->getType().getCanonicalType()->getAs<FunctionProtoType>())
return FT->isNothrow(S.Context) ? CT_Cannot : CT_Can;
// If we have no clue what we're calling, assume the worst.
return CT_Can;
}
// As an extension, we assume that __attribute__((nothrow)) functions don't
// throw.
if (isa<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
if (D && isa<FunctionDecl>(D) && D->hasAttr<NoThrowAttr>())
return CT_Cannot;
QualType T = VD->getType();
QualType T;
// In C++1z, just look at the function type of the callee.
if (S.getLangOpts().CPlusPlus1z && isa<CallExpr>(E)) {
E = cast<CallExpr>(E)->getCallee();
T = E->getType();
if (T->isSpecificPlaceholderType(BuiltinType::BoundMember)) {
// Sadly we don't preserve the actual type as part of the "bound member"
// placeholder, so we need to reconstruct it.
E = E->IgnoreParenImpCasts();
// Could be a call to a pointer-to-member or a plain member access.
if (auto *Op = dyn_cast<BinaryOperator>(E)) {
assert(Op->getOpcode() == BO_PtrMemD || Op->getOpcode() == BO_PtrMemI);
T = Op->getRHS()->getType()
->castAs<MemberPointerType>()->getPointeeType();
} else {
T = cast<MemberExpr>(E)->getMemberDecl()->getType();
}
}
} else if (const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D))
T = VD->getType();
else
// If we have no clue what we're calling, assume the worst.
return CT_Can;
const FunctionProtoType *FT;
if ((FT = T->getAs<FunctionProtoType>())) {
} else if (const PointerType *PT = T->getAs<PointerType>())
@ -1053,10 +1066,8 @@ CanThrowResult Sema::canThrow(const Expr *E) {
CT = CT_Dependent;
else if (isa<CXXPseudoDestructorExpr>(CE->getCallee()->IgnoreParens()))
CT = CT_Cannot;
else if (CE->getCalleeDecl())
CT = canCalleeThrow(*this, E, CE->getCalleeDecl());
else
CT = CT_Can;
CT = canCalleeThrow(*this, E, CE->getCalleeDecl());
if (CT == CT_Can)
return CT;
return mergeCanThrow(CT, canSubExprsThrow(*this, E));

View File

@ -30,6 +30,21 @@ auto deduce_auto_from_noexcept_function_ptr_b = redecl4<false>;
using DeducedType_b = decltype(deduce_auto_from_noexcept_function_ptr_b);
using DeducedType_b = void (*)(int);
static_assert(noexcept(init_with_exact_type_a(0)));
static_assert(noexcept((+init_with_exact_type_a)(0)));
static_assert(!noexcept(init_with_exact_type_b(0)));
static_assert(!noexcept((+init_with_exact_type_b)(0)));
// Don't look through casts, use the direct type of the expression.
// FIXME: static_cast here would be reasonable, but is not currently permitted.
static_assert(noexcept(static_cast<decltype(init_with_exact_type_a)>(init_with_exact_type_b)(0))); // expected-error {{is not allowed}}
static_assert(noexcept(reinterpret_cast<decltype(init_with_exact_type_a)>(init_with_exact_type_b)(0)));
static_assert(!noexcept(static_cast<decltype(init_with_exact_type_b)>(init_with_exact_type_a)(0)));
template<bool B> auto get_fn() noexcept -> void (*)() noexcept(B) {}
static_assert(noexcept(get_fn<true>()()));
static_assert(!noexcept(get_fn<false>()()));
namespace DependentDefaultCtorExceptionSpec {
template<typename> struct T { static const bool value = true; };