forked from OSchip/llvm-project
DR1330: instantiate exception-specifications when "needed". We previously did
not instantiate exception specifications of functions if they were only used in unevaluated contexts (other than 'noexcept' expressions). In C++17 onwards, this becomes essential since the exception specification is now part of the function's type. Note that this means that constructs like the following no longer work: struct A { static T f() noexcept(...); decltype(f()) *p; }; ... because the decltype expression now needs the exception specification of 'f', which has not yet been parsed. llvm-svn: 284549
This commit is contained in:
parent
832c383b25
commit
84a0b6dba1
|
@ -2889,6 +2889,14 @@ ExprResult Sema::BuildDeclarationNameExpr(
|
|||
|
||||
{
|
||||
QualType type = VD->getType();
|
||||
if (auto *FPT = type->getAs<FunctionProtoType>()) {
|
||||
// C++ [except.spec]p17:
|
||||
// An exception-specification is considered to be needed when:
|
||||
// - in an expression, the function is the unique lookup result or
|
||||
// the selected member of a set of overloaded functions.
|
||||
ResolveExceptionSpec(Loc, FPT);
|
||||
type = VD->getType();
|
||||
}
|
||||
ExprValueKind valueKind = VK_RValue;
|
||||
|
||||
switch (D->getKind()) {
|
||||
|
@ -13138,6 +13146,19 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
|
|||
Func->getMemberSpecializationInfo()))
|
||||
checkSpecializationVisibility(Loc, Func);
|
||||
|
||||
// C++14 [except.spec]p17:
|
||||
// An exception-specification is considered to be needed when:
|
||||
// - the function is odr-used or, if it appears in an unevaluated operand,
|
||||
// would be odr-used if the expression were potentially-evaluated;
|
||||
//
|
||||
// Note, we do this even if MightBeOdrUse is false. That indicates that the
|
||||
// function is a pure virtual function we're calling, and in that case the
|
||||
// function was selected by overload resolution and we need to resolve its
|
||||
// exception specification for a different reason.
|
||||
const FunctionProtoType *FPT = Func->getType()->getAs<FunctionProtoType>();
|
||||
if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
|
||||
ResolveExceptionSpec(Loc, FPT);
|
||||
|
||||
// If we don't need to mark the function as used, and we don't need to
|
||||
// try to provide a definition, there's nothing more to do.
|
||||
if ((Func->isUsed(/*CheckUsedAttr=*/false) || !OdrUse) &&
|
||||
|
@ -13196,12 +13217,6 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
|
|||
// FIXME: Is this really right?
|
||||
if (CurContext == Func) return;
|
||||
|
||||
// Resolve the exception specification for any function which is
|
||||
// used: CodeGen will need it.
|
||||
const FunctionProtoType *FPT = Func->getType()->getAs<FunctionProtoType>();
|
||||
if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
|
||||
ResolveExceptionSpec(Loc, FPT);
|
||||
|
||||
// Implicit instantiation of function templates and member functions of
|
||||
// class templates.
|
||||
if (Func->isImplicitlyInstantiable()) {
|
||||
|
|
|
@ -13166,6 +13166,13 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
|
|||
UnOp->getOperatorLoc());
|
||||
}
|
||||
|
||||
// C++ [except.spec]p17:
|
||||
// An exception-specification is considered to be needed when:
|
||||
// - in an expression the function is the unique lookup result or the
|
||||
// selected member of a set of overloaded functions
|
||||
if (auto *FPT = Fn->getType()->getAs<FunctionProtoType>())
|
||||
ResolveExceptionSpec(E->getExprLoc(), FPT);
|
||||
|
||||
if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(E)) {
|
||||
// FIXME: avoid copy.
|
||||
TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = nullptr;
|
||||
|
|
|
@ -3,6 +3,91 @@
|
|||
// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++1z %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
|
||||
namespace dr1330 { // dr1330: 4.0 c++11
|
||||
// exception-specifications are parsed in a context where the class is complete.
|
||||
struct A {
|
||||
void f() throw(T) {}
|
||||
struct T {};
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
void g() noexcept(&a == b) {}
|
||||
static int a;
|
||||
static constexpr int *b = &a;
|
||||
#endif
|
||||
};
|
||||
|
||||
void (A::*af1)() throw(A::T) = &A::f;
|
||||
void (A::*af2)() throw() = &A::f; // expected-error-re {{{{not superset|different exception spec}}}}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
static_assert(noexcept(A().g()), "");
|
||||
#endif
|
||||
|
||||
// Likewise, they're instantiated separately from an enclosing class template.
|
||||
template<typename U>
|
||||
struct B {
|
||||
void f() throw(T, typename U::type) {}
|
||||
struct T {};
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
void g() noexcept(&a == b && U::value) {}
|
||||
static int a;
|
||||
static constexpr int *b = &a;
|
||||
#endif
|
||||
};
|
||||
|
||||
B<int> bi; // ok
|
||||
|
||||
struct P {
|
||||
typedef int type;
|
||||
static const int value = true;
|
||||
};
|
||||
|
||||
void (B<P>::*bpf1)() throw(B<P>::T, int) = &B<P>::f;
|
||||
#if __cplusplus < 201103L
|
||||
// expected-error@-2 {{not superset}}
|
||||
// FIXME: We only delay instantiation in C++11 onwards. In C++98, something
|
||||
// weird happens: instantiation of B<P> fails because it references T before
|
||||
// it's instantiated, but the diagnostic is suppressed in
|
||||
// Sema::FindInstantiatedDecl because we've already hit an error. This is
|
||||
// obviously a bad way to react to this situation; we should still producing
|
||||
// the "T has not yet been instantiated" error here, rather than giving
|
||||
// confusing errors later on.
|
||||
#endif
|
||||
void (B<P>::*bpf2)() throw(int) = &B<P>::f; // expected-error-re {{{{not superset|different exception spec}}}}
|
||||
void (B<P>::*bpf3)() = &B<P>::f;
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
static_assert(noexcept(B<P>().g()), "");
|
||||
struct Q { static const int value = false; };
|
||||
static_assert(!noexcept(B<Q>().g()), "");
|
||||
#endif
|
||||
|
||||
template<typename T> int f() throw(typename T::error) { return 0; } // expected-error 1-4{{prior to '::'}} expected-note 0-1{{instantiation of}}
|
||||
// An exception-specification is needed even if the function is only used in
|
||||
// an unevaluated operand.
|
||||
int f1 = sizeof(f<int>()); // expected-note {{instantiation of}}
|
||||
#if __cplusplus >= 201103L
|
||||
decltype(f<char>()) f2; // expected-note {{instantiation of}}
|
||||
bool f3 = noexcept(f<float>()); // expected-note {{instantiation of}}
|
||||
#endif
|
||||
template int f<short>(); // expected-note {{instantiation of}}
|
||||
|
||||
template<typename T> struct C {
|
||||
C() throw(typename T::type); // expected-error 1-2{{prior to '::'}}
|
||||
};
|
||||
struct D : C<void> {}; // ok
|
||||
#if __cplusplus < 201103L
|
||||
// expected-note@-2 {{instantiation of}}
|
||||
#endif
|
||||
void f(D &d) { d = d; } // ok
|
||||
|
||||
// FIXME: In C++11 onwards, we should also note the declaration of 'e' as the
|
||||
// line that triggers the use of E::E()'s exception specification.
|
||||
struct E : C<int> {}; // expected-note {{in instantiation of}}
|
||||
E e;
|
||||
}
|
||||
|
||||
namespace dr1346 { // dr1346: 3.5
|
||||
auto a(1); // expected-error 0-1{{extension}}
|
||||
auto b(1, 2); // expected-error {{multiple expressions}} expected-error 0-1{{extension}}
|
||||
|
|
|
@ -25,3 +25,15 @@ namespace BaseClassAggregateInit {
|
|||
constexpr D d5 = { __INT_MAX__ }; // expected-error {{must be initialized by a constant expression}}
|
||||
// expected-note-re@-1 {{in call to 'A({{.*}})'}}
|
||||
}
|
||||
|
||||
namespace NoexceptFunctionTypes {
|
||||
template<typename T> constexpr bool f() noexcept(true) { return true; }
|
||||
static_assert(f<int>());
|
||||
|
||||
template<typename T> struct A {
|
||||
constexpr bool f() noexcept(true) { return true; }
|
||||
constexpr bool g() { return f(); }
|
||||
};
|
||||
static_assert(A<int>().f());
|
||||
static_assert(A<int>().g());
|
||||
}
|
||||
|
|
|
@ -89,30 +89,34 @@ namespace DefaultedFnExceptionSpec {
|
|||
struct Error {
|
||||
void f() noexcept(T::error);
|
||||
|
||||
Error() noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
|
||||
Error(const Error&) noexcept(T::error);
|
||||
Error(Error&&) noexcept(T::error);
|
||||
Error &operator=(const Error&) noexcept(T::error);
|
||||
Error &operator=(Error&&) noexcept(T::error);
|
||||
~Error() noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
|
||||
Error() noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} expected-error {{type 'char'}}
|
||||
Error(const Error&) noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
|
||||
Error(Error&&) noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
|
||||
Error &operator=(const Error&) noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} expected-error {{type 'double'}}
|
||||
Error &operator=(Error&&) noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
|
||||
~Error() noexcept(T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} expected-error {{type 'char'}}
|
||||
};
|
||||
|
||||
Error<char> c; // expected-note 2{{instantiation of}}
|
||||
struct DelayImplicit {
|
||||
Error<int> e;
|
||||
// FIXME: The location of this note is terrible. The instantiation was
|
||||
// triggered by the uses of the functions in the decltype expressions below.
|
||||
Error<int> e; // expected-note 6{{instantiation of}}
|
||||
};
|
||||
Error<float> *e;
|
||||
|
||||
// Don't instantiate the exception specification here.
|
||||
// An exception specification is needed if the exception specification for a
|
||||
// a defaulted special member function that calls the function is needed.
|
||||
// Use in an unevaluated operand still results in the exception spec being
|
||||
// needed.
|
||||
void test1(decltype(declval<DelayImplicit>() = DelayImplicit(DelayImplicit())));
|
||||
void test2(decltype(declval<DelayImplicit>() = declval<const DelayImplicit>()));
|
||||
void test3(decltype(DelayImplicit(declval<const DelayImplicit>())));
|
||||
|
||||
// Any odr-use causes the exception specification to be evaluated.
|
||||
struct OdrUse { // \
|
||||
expected-note {{instantiation of exception specification for 'Error'}} \
|
||||
expected-note {{instantiation of exception specification for '~Error'}}
|
||||
Error<int> e;
|
||||
};
|
||||
OdrUse use; // expected-note {{implicit default constructor for 'DefaultedFnExceptionSpec::OdrUse' first required here}}
|
||||
// Any odr-use needs the exception specification.
|
||||
void f(Error<double> *p) {
|
||||
*p = *p; // expected-note {{instantiation of}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace PR13527 {
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace sad {
|
|||
|
||||
template<typename A, typename B> struct CLASS {
|
||||
void swap(CLASS &other) noexcept(noexcept(swap(*this, other))); // expected-error {{too many arguments}} expected-note {{declared here}}
|
||||
// expected-error@-1{{uses itself}} expected-note@-1{{in instantiation of}}
|
||||
};
|
||||
|
||||
CLASS<int, int> pi;
|
||||
|
|
|
@ -30,8 +30,6 @@ template<unsigned N> struct S {
|
|||
// expected-error {{no member named 'recurse'}} \
|
||||
// expected-note 9{{instantiation of exception spec}}
|
||||
};
|
||||
decltype(S<0>::recurse()) *pVoid1 = 0; // ok, exception spec not needed
|
||||
decltype(&S<0>::recurse) pFn = 0; // ok, exception spec not needed
|
||||
|
||||
template<> struct S<10> {};
|
||||
void (*pFn2)() noexcept = &S<0>::recurse; // expected-note {{instantiation of exception spec}} expected-error {{not superset}}
|
||||
|
@ -39,7 +37,7 @@ void (*pFn2)() noexcept = &S<0>::recurse; // expected-note {{instantiation of ex
|
|||
|
||||
namespace dr1330_example {
|
||||
template <class T> struct A {
|
||||
void f(...) throw (typename T::X); // expected-error {{'int'}}
|
||||
void f(...) throw (typename T::X);
|
||||
void f(int);
|
||||
};
|
||||
|
||||
|
@ -48,16 +46,14 @@ namespace dr1330_example {
|
|||
}
|
||||
|
||||
struct S {
|
||||
template<typename T>
|
||||
static int f() noexcept(noexcept(A<T>().f("boo!"))) { return 0; } // \
|
||||
// expected-note {{instantiation of exception spec}}
|
||||
typedef decltype(f<S>()) X;
|
||||
template<typename T> static void f() throw(typename T::X);
|
||||
typedef decltype(f<S>()) X; // expected-error {{exception specification is not available}}
|
||||
};
|
||||
|
||||
int test2() {
|
||||
S().f<S>(); // ok
|
||||
S().f<int>(); // expected-note {{instantiation of exception spec}}
|
||||
}
|
||||
struct T {
|
||||
template<typename T> static void f() throw(T**);
|
||||
typedef decltype(f<S>()) X; // expected-error {{exception specification is not available}}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct U {
|
||||
|
|
|
@ -3613,7 +3613,7 @@ and <I>POD class</I></td>
|
|||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#595">595</a></td>
|
||||
<td>dup</td>
|
||||
<td>Exception specifications in templates instantiated from class bodies</td>
|
||||
<td class="none" align="center">Duplicate of <a href="#1330">1330</a></td>
|
||||
<td class="svn" align="center">Duplicate of <a href="#1330">1330</a></td>
|
||||
</tr>
|
||||
<tr class="open" id="596">
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#596">596</a></td>
|
||||
|
@ -7795,7 +7795,7 @@ and <I>POD class</I></td>
|
|||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1330">1330</a></td>
|
||||
<td>CD3</td>
|
||||
<td>Delayed instantiation of <TT>noexcept</TT> specifiers</td>
|
||||
<td class="none" align="center">Unknown</td>
|
||||
<td class="svn" align="center">SVN (C++11 onwards)</td>
|
||||
</tr>
|
||||
<tr class="open" id="1331">
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1331">1331</a></td>
|
||||
|
|
Loading…
Reference in New Issue