From: Richard Smith Date: Tue, 18 Oct 2016 23:39:12 +0000 (+0000) Subject: DR1330: instantiate exception-specifications when "needed". We previously did X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=84a0b6dba179126639a68a6925d9d0ceb0bb250a;p=platform%2Fupstream%2Fllvm.git 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 --- diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 4363885..40b5512 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2889,6 +2889,14 @@ ExprResult Sema::BuildDeclarationNameExpr( { QualType type = VD->getType(); + if (auto *FPT = type->getAs()) { + // 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(); + 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(); - if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) - ResolveExceptionSpec(Loc, FPT); - // Implicit instantiation of function templates and member functions of // class templates. if (Func->isImplicitlyInstantiable()) { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 460e53f..6ed68a9 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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()) + ResolveExceptionSpec(E->getExprLoc(), FPT); + if (UnresolvedLookupExpr *ULE = dyn_cast(E)) { // FIXME: avoid copy. TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = nullptr; diff --git a/clang/test/CXX/drs/dr13xx.cpp b/clang/test/CXX/drs/dr13xx.cpp index 8c3e7f2..b7d3a34 100644 --- a/clang/test/CXX/drs/dr13xx.cpp +++ b/clang/test/CXX/drs/dr13xx.cpp @@ -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 + 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 bi; // ok + + struct P { + typedef int type; + static const int value = true; + }; + + void (B

::*bpf1)() throw(B

::T, int) = &B

::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

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

::*bpf2)() throw(int) = &B

::f; // expected-error-re {{{{not superset|different exception spec}}}} + void (B

::*bpf3)() = &B

::f; + +#if __cplusplus >= 201103L + static_assert(noexcept(B

().g()), ""); + struct Q { static const int value = false; }; + static_assert(!noexcept(B().g()), ""); +#endif + + template 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()); // expected-note {{instantiation of}} +#if __cplusplus >= 201103L + decltype(f()) f2; // expected-note {{instantiation of}} + bool f3 = noexcept(f()); // expected-note {{instantiation of}} +#endif + template int f(); // expected-note {{instantiation of}} + + template struct C { + C() throw(typename T::type); // expected-error 1-2{{prior to '::'}} + }; + struct D : C {}; // 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 {}; // 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}} diff --git a/clang/test/SemaCXX/constant-expression-cxx1z.cpp b/clang/test/SemaCXX/constant-expression-cxx1z.cpp index e84de44..9aab999 100644 --- a/clang/test/SemaCXX/constant-expression-cxx1z.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx1z.cpp @@ -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 constexpr bool f() noexcept(true) { return true; } + static_assert(f()); + + template struct A { + constexpr bool f() noexcept(true) { return true; } + constexpr bool g() { return f(); } + }; + static_assert(A().f()); + static_assert(A().g()); +} diff --git a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp index 8225b01..f4bbce8 100644 --- a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp +++ b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp @@ -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 c; // expected-note 2{{instantiation of}} struct DelayImplicit { - Error 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 e; // expected-note 6{{instantiation of}} }; + Error *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()))); void test2(decltype(declval() = declval())); void test3(decltype(DelayImplicit(declval()))); - // 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 e; - }; - OdrUse use; // expected-note {{implicit default constructor for 'DefaultedFnExceptionSpec::OdrUse' first required here}} + // Any odr-use needs the exception specification. + void f(Error *p) { + *p = *p; // expected-note {{instantiation of}} + } } namespace PR13527 { diff --git a/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp b/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp index 6d783fe..1a92bf9 100644 --- a/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp +++ b/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp @@ -69,6 +69,7 @@ namespace sad { template 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 pi; diff --git a/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp b/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp index 6e8323d..c5290ef 100644 --- a/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp +++ b/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp @@ -30,8 +30,6 @@ template 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 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 - static int f() noexcept(noexcept(A().f("boo!"))) { return 0; } // \ - // expected-note {{instantiation of exception spec}} - typedef decltype(f()) X; + template static void f() throw(typename T::X); + typedef decltype(f()) X; // expected-error {{exception specification is not available}} }; - int test2() { - S().f(); // ok - S().f(); // expected-note {{instantiation of exception spec}} - } + struct T { + template static void f() throw(T**); + typedef decltype(f()) X; // expected-error {{exception specification is not available}} + }; template struct U { diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 6fac953..73238b4 100644 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -3613,7 +3613,7 @@ and POD class 595 dup Exception specifications in templates instantiated from class bodies - Duplicate of 1330 + Duplicate of 1330 596 @@ -7795,7 +7795,7 @@ and POD class 1330 CD3 Delayed instantiation of noexcept specifiers - Unknown + SVN (C++11 onwards) 1331