[LLVMdev] Clang bug?

Richard Smith richard at metafoo.co.uk
Fri Sep 28 20:22:13 PDT 2012


On Fri, Sep 28, 2012 at 7:35 PM, Howard Hinnant <hhinnant at apple.com> wrote:

> On Sep 28, 2012, at 10:18 PM, Richard Smith <richard at metafoo.co.uk> wrote:
>
> > On Fri, Sep 28, 2012 at 6:45 PM, Howard Hinnant <hhinnant at apple.com>
> wrote:
> > On Sep 28, 2012, at 5:54 PM, Richard Smith <richard at metafoo.co.uk>
> wrote:
> >
> > > Reduced testcase:
> > >
> > > template<typename T> struct A { typedef decltype(T() + 0) type; };
> > > template<typename T> struct B {
> > >   struct C { typedef typename A<C*>::type type; };
> > >   typedef typename A<C*>::type type;
> > > };
> > > B<int> b;
> > >
> > > ... produces ...
> > >
> > > <stdin>:3:38: error: no type named 'type' in 'A<B<int>::C *>'
> > >   struct C { typedef typename A<C*>::type type; };
> > >                      ~~~~~~~~~~~~~~~~^~~~
> > > <stdin>:1:54: note: in instantiation of member class 'B<int>::C'
> requested here
> > > template<typename T> struct A { typedef decltype(T() + 0) type; };
> > >                                                      ^
> > > <stdin>:4:20: note: in instantiation of template class 'A<B<int>::C
> *>' requested here
> > >   typedef typename A<C*>::type type;
> > >                    ^
> > > <stdin>:6:8: note: in instantiation of template class 'B<int>'
> requested here
> > > B<int> b;
> > >        ^
> > >
> > > I think it would be worth filing this as a diagnostic QoR issue. We
> should be able to say something like
> > >
> > > <stdin>:3:38: error: member 'type' of 'A<B<int>::C *>' required
> recursively within the instantiation of 'A<B<int>::C *>', but it has not
> been instantiated yet
> >
> > Hi Richard,
> >
> > Is your position that tot clang/libc++ is in error for not producing a
> diagnostic?
> >
> > No, my position is the opposite. Trunk clang + libc++ still reject this:
> >
> > #include <type_traits>
> > template<typename T> struct S { static_assert(sizeof(T) == 1, ""); };
> > bool b = std::is_assignable<S<int>*&, S<int>*>::value;
> >
> > ... because is_assignable triggers the instantiation of S<int>.
> >
> > I believe Clang is behaving correctly here. I don't know whether
> libc++'s implementation is valid (I don't know under what circumstances the
> library can use constructs which could depend on the completeness of
> user-provided types) but I suspect it is not, and even if it were, we'd
> want to allow this as a QoI issue. Here's a fix (first change: avoid
> overloaded 'operator,', second change: avoid ADL for __is_assignable_test):
> >
> >
> > --- type_traits (revision 164877)
> > +++ type_traits (working copy)
> > @@ -1187,10 +1187,15 @@
> >
> >  #endif  // _LIBCPP_HAS_NO_VARIADICS
> >
> > +template <class _Fst, class _Snd>
> > +struct __select_2nd {
> > +  typedef _Snd type;
> > +};
> > +
> >  // is_assignable
> >
> >  template <class _Tp, class _Arg>
> > -decltype((_VSTD::declval<_Tp>() = _VSTD::declval<_Arg>(), true_type()))
> > +typename __select_2nd<decltype(_VSTD::declval<_Tp>() =
> _VSTD::declval<_Arg>()), true_type>::type
> >  #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
> >  __is_assignable_test(_Tp&&, _Arg&&);
> >  #else
> > @@ -1209,7 +1214,7 @@
> >  struct __is_assignable_imp
> >      : public common_type
> >          <
> > -            decltype(__is_assignable_test(declval<_Tp>(),
> declval<_Arg>()))
> > +            decltype(_VSTD::__is_assignable_test(declval<_Tp>(),
> declval<_Arg>()))
> >          >::type {};
> >
> >  template <class _Tp, class _Arg>
> >
> >
> > I don't know whether there are other similar cases elsewhere in libc++,
> but that's enough to get my is_assignable test to work.
>
> I'm a little confused, and admittedly skimming (it's late for me and I
> can't even believe you're still awake!).  But we seem to have a
> miscommunication somewhere.
>
> For me, all with tot clang, your example is giving a diagnostic:
>
> #include <type_traits>
> template<typename T> struct S { static_assert(sizeof(T) == 1, ""); };
> bool b = std::is_assignable<S<int>*&, S<int>*>::value;
>

And likewise for me. This code triggers the instantiation of S<int>. My
claim was that this code need not give a diagnostic (although it's on
*very*thin ice) because we don't actually need to instantiate S<int>.
And it's
not clear to me that the standard *allows* us to give a diagnostic here,
because the expression declval<S<int>*&>() = declval<S<int>*>() is probably
well-formed (this is somewhat debatable, because the pointer conversion
rules for built-in pointer comparisons in 5.10/1 are handwaved over and
14.7.1/1 is a little vague, but g++, clang and EDG all accept it).


> But Adam's code:
>
>    #include <map>
>    using std::map;
>
>    template<typename K>
>    struct Templ8 {
>        struct Member {
>            typename map<K,Member*>::iterator it;
>        };
>        typedef typename map<K,Member*>::iterator iterator_type;
>    };
>
>    int main() {
>        Templ8<int> test;
>    }
>
> is not giving a diagnostic (-stdlib=libc++ -std=c++11).
>
> You seem to be saying that Adam's code with tot clang++ -stdlib=libc++
> -std=c++11 is giving a diagnostic.  So there's a minor mystery here.


Apologies for the confusion. I didn't mean to imply that Adam's code is
currently giving a diagnostic for me. The original report was that this
code did give a diagnostic, and then "got better", and my observation was
that the component of libc++ which was causing the spurious diagnostic
(is_assignable) still has the same behavior. FWIW, it looks like it was the
delayed instantiation of exception specifications which fixed this.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20120928/604055ca/attachment.html>


More information about the llvm-dev mailing list