r284549 - DR1330: instantiate exception-specifications when "needed". We previously did

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 23 15:59:16 PDT 2017


On 23 August 2017 at 08:01, Alex L via cfe-commits <
cfe-commits at lists.llvm.org> wrote:

> Hi Richard,
>
> It looks like this commit caused PR34298 (https://bugs.llvm.org/show_bu
> g.cgi?id=34298). Now Clang fails to compile a libc++ std::function member
> variable with an incomplete return type. Libstdc++ works though, so I
> assume that the right way to fix this is to change libc++?
>

I've added some thoughts to the bug. It appears that the rejected code is
not required to work per the C++ standard rules, but it really ought to,
and this looks like a libc++ bug rather than a Clang bug. (Though the notes
we produce aren't particularly clear as to how we got there, sadly.)


> Cheers,
> Alex
>
> On 19 October 2016 at 00:39, Richard Smith via cfe-commits <
> cfe-commits at lists.llvm.org> wrote:
>
>> Author: rsmith
>> Date: Tue Oct 18 18:39:12 2016
>> New Revision: 284549
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=284549&view=rev
>> Log:
>> 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.
>>
>> Modified:
>>     cfe/trunk/lib/Sema/SemaExpr.cpp
>>     cfe/trunk/lib/Sema/SemaOverload.cpp
>>     cfe/trunk/test/CXX/drs/dr13xx.cpp
>>     cfe/trunk/test/SemaCXX/constant-expression-cxx1z.cpp
>>     cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp
>>     cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
>>     cfe/trunk/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp
>>     cfe/trunk/www/cxx_dr_status.html
>>
>> Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaE
>> xpr.cpp?rev=284549&r1=284548&r2=284549&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
>> +++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Oct 18 18:39:12 2016
>> @@ -2889,6 +2889,14 @@ ExprResult Sema::BuildDeclarationNameExp
>>
>>    {
>>      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(Source
>>         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<Functio
>> nProtoType>();
>> +  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(Source
>>    // 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<Functio
>> nProtoType>();
>> -  if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType()))
>> -    ResolveExceptionSpec(Loc, FPT);
>> -
>>    // Implicit instantiation of function templates and member functions of
>>    // class templates.
>>    if (Func->isImplicitlyInstantiable()) {
>>
>> Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaO
>> verload.cpp?rev=284549&r1=284548&r2=284549&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
>> +++ cfe/trunk/lib/Sema/SemaOverload.cpp Tue Oct 18 18:39:12 2016
>> @@ -13166,6 +13166,13 @@ Expr *Sema::FixOverloadedFunctionReferen
>>                                         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;
>>
>> Modified: cfe/trunk/test/CXX/drs/dr13xx.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/
>> dr13xx.cpp?rev=284549&r1=284548&r2=284549&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/test/CXX/drs/dr13xx.cpp (original)
>> +++ cfe/trunk/test/CXX/drs/dr13xx.cpp Tue Oct 18 18:39:12 2016
>> @@ -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 at -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 at -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}}
>>
>> Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx1z.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/
>> constant-expression-cxx1z.cpp?rev=284549&r1=284548&r2=284549&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/test/SemaCXX/constant-expression-cxx1z.cpp (original)
>> +++ cfe/trunk/test/SemaCXX/constant-expression-cxx1z.cpp Tue Oct 18
>> 18:39:12 2016
>> @@ -25,3 +25,15 @@ namespace BaseClassAggregateInit {
>>    constexpr D d5 = { __INT_MAX__ }; // expected-error {{must be
>> initialized by a constant expression}}
>>    // expected-note-re at -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());
>> +}
>>
>> Modified: cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/
>> cxx0x-defaulted-functions.cpp?rev=284549&r1=284548&r2=284549&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp (original)
>> +++ cfe/trunk/test/SemaCXX/cxx0x-defaulted-functions.cpp Tue Oct 18
>> 18:39:12 2016
>> @@ -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 {
>>
>> Modified: cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/
>> libstdcxx_pair_swap_hack.cpp?rev=284549&r1=284548&r2=284549&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp (original)
>> +++ cfe/trunk/test/SemaCXX/libstdcxx_pair_swap_hack.cpp Tue Oct 18
>> 18:39:12 2016
>> @@ -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 at -1{{uses itself}} expected-note at -1{{in
>> instantiation of}}
>>    };
>>
>>    CLASS<int, int> pi;
>>
>> Modified: cfe/trunk/test/SemaTemplate/instantiate-exception-spec-cxx11
>> .cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTempl
>> ate/instantiate-exception-spec-cxx11.cpp?rev=284549&r1=
>> 284548&r2=284549&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp
>> (original)
>> +++ cfe/trunk/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp Tue
>> Oct 18 18:39:12 2016
>> @@ -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
>>
>>  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 {
>>
>> Modified: cfe/trunk/www/cxx_dr_status.html
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_dr_sta
>> tus.html?rev=284549&r1=284548&r2=284549&view=diff
>> ============================================================
>> ==================
>> --- cfe/trunk/www/cxx_dr_status.html (original)
>> +++ cfe/trunk/www/cxx_dr_status.html Tue Oct 18 18:39:12 2016
>> @@ -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_defect
>> s.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>
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20170823/afd8381b/attachment-0001.html>


More information about the cfe-commits mailing list