[LLVMdev] Clang bug?

Howard Hinnant hhinnant at apple.com
Fri Sep 28 19:35:01 PDT 2012


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;


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.

Howard




More information about the llvm-dev mailing list