[PATCH] Add DiagnosticSuppressionScope RAII class

Richard Smith richard at metafoo.co.uk
Thu Oct 30 18:06:00 PDT 2014


On Wed, Oct 29, 2014 at 6:10 AM, Brad King <brad.king at kitware.com> wrote:

> On 10/28/2014 05:46 PM, Richard Smith wrote:
> > Right; that's how the C++ standard says SFINAE works.
>
> Sure, but I'm not performing a SFINAE test.  That's why SFINAETrap is not
> sufficient.  I'm not trying to replace SFINAETrap.  I'm trying to provide
> a new primitive that is useful to tools based on Clang to achieve things
> that are currently not possible.
>
> > if you suppress errors here, things will go wrong later
> > (later tests which happen to look at validity of the same thing can
> > get incorrect answers).
>
> IIUC a declaration that has been marked invalid is considered to not exist
> by such later tests.  See below.
>
> > The problem is with a case like:
> >
> > template<typename T> struct A {
> >   A() { T::error; }
> > };
> > struct B : A<int> {};
> > struct C : A<int> {};
> >
> > Trying to create B::B() will trigger the instantiation of A<int>::A(),
> > which will fail with a non-local error. We'll mark the function as
> invalid,
> > and you'll determine that B::B() doesn't work. Then you'll try C::C(),
> > which will *succeed*, because we'll silently skip A<int>::A() because
> we've
> > already marked it invalid.
>
> Clang calls setInvalidDecl on A::A() after failing to instantiate it for
> B::B().  Then C::C() fails too because A::A() does not exist anymore.
>

OK, I can reproduce that behavior for a testcase like:

template<typename T> struct A {
  A() { T::error; }
};
struct B : A<int> {};
template<typename T> struct C : A<T> { C() : A<int>() {} };
B b; C<int> c;

We produce:

<stdin>:2:9: error: type 'int' cannot be used prior to '::' because it has
no members
  A() { T::error; }
        ^
<stdin>:4:8: note: in instantiation of member function 'A<int>::A'
requested here
struct B : A<int> {};
       ^
<stdin>:5:46: error: no matching constructor for initialization of 'A<int>'
template<typename T> struct C : A<T> { C() : A<int>() {} };
                                             ^
<stdin>:6:13: note: in instantiation of member function 'C<int>::C'
requested here
B b; C<int> c;
            ^
<stdin>:1:29: note: candidate constructor (the implicit copy constructor)
not viable: requires 1 argument, but 0 were provided
template<typename T> struct A {
                            ^

This is a bug; we should not produce the second error message.


> I hacked my tool to take out the DiagnosticSuppressionScope for the
> DefaultConstructor case and to print out a description of each implicit
> member before attempting to define it.  The result on the above code is:
>
> --------------------------------------------------------------------------
> DefaultConstructor: B
> test.cpp:2:9: error: type 'int' cannot be used prior to '::' because it has
>       no members
>   A() { T::error; }
>         ^
> test.cpp:4:8: note: in instantiation of member function 'A<int>::A'
> requested
>       here
> struct B : A<int> {};
>        ^
> DefaultConstructor: C
> test.cpp:5:8: error: implicit default constructor for 'C' must explicitly
>       initialize the base class 'A<int>' which does not have a default
>       constructor
> struct C : A<int> {};
>        ^
> --------------------------------------------------------------------------
>
> One can see that both B::B() and C::C() get errors as desired.


That is not guaranteed, and will no longer be the case when we fix the bug
described above.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20141030/d34a7db1/attachment.html>


More information about the cfe-commits mailing list