On Fri, Sep 28, 2012 at 7:35 PM, Howard Hinnant <span dir="ltr"><<a href="mailto:hhinnant@apple.com" target="_blank">hhinnant@apple.com</a>></span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="HOEnZb"><div class="h5">On Sep 28, 2012, at 10:18 PM, Richard Smith <<a href="mailto:richard@metafoo.co.uk">richard@metafoo.co.uk</a>> wrote:<br>
<br>
> On Fri, Sep 28, 2012 at 6:45 PM, Howard Hinnant <<a href="mailto:hhinnant@apple.com">hhinnant@apple.com</a>> wrote:<br>
> On Sep 28, 2012, at 5:54 PM, Richard Smith <<a href="mailto:richard@metafoo.co.uk">richard@metafoo.co.uk</a>> wrote:<br>
><br>
> > Reduced testcase:<br>
> ><br>
> > template<typename T> struct A { typedef decltype(T() + 0) type; };<br>
> > template<typename T> struct B {<br>
> >   struct C { typedef typename A<C*>::type type; };<br>
> >   typedef typename A<C*>::type type;<br>
> > };<br>
> > B<int> b;<br>
> ><br>
> > ... produces ...<br>
> ><br>
> > <stdin>:3:38: error: no type named 'type' in 'A<B<int>::C *>'<br>
> >   struct C { typedef typename A<C*>::type type; };<br>
> >                      ~~~~~~~~~~~~~~~~^~~~<br>
> > <stdin>:1:54: note: in instantiation of member class 'B<int>::C' requested here<br>
> > template<typename T> struct A { typedef decltype(T() + 0) type; };<br>
> >                                                      ^<br>
> > <stdin>:4:20: note: in instantiation of template class 'A<B<int>::C *>' requested here<br>
> >   typedef typename A<C*>::type type;<br>
> >                    ^<br>
> > <stdin>:6:8: note: in instantiation of template class 'B<int>' requested here<br>
> > B<int> b;<br>
> >        ^<br>
> ><br>
> > I think it would be worth filing this as a diagnostic QoR issue. We should be able to say something like<br>
> ><br>
> > <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<br>

><br>
> Hi Richard,<br>
><br>
> Is your position that tot clang/libc++ is in error for not producing a diagnostic?<br>
><br>
> No, my position is the opposite. Trunk clang + libc++ still reject this:<br>
><br>
> #include <type_traits><br>
> template<typename T> struct S { static_assert(sizeof(T) == 1, ""); };<br>
> bool b = std::is_assignable<S<int>*&, S<int>*>::value;<br>
><br>
> ... because is_assignable triggers the instantiation of S<int>.<br>
><br>
> 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):<br>

><br>
><br>
> --- type_traits (revision 164877)<br>
> +++ type_traits (working copy)<br>
> @@ -1187,10 +1187,15 @@<br>
><br>
>  #endif  // _LIBCPP_HAS_NO_VARIADICS<br>
><br>
> +template <class _Fst, class _Snd><br>
> +struct __select_2nd {<br>
> +  typedef _Snd type;<br>
> +};<br>
> +<br>
>  // is_assignable<br>
><br>
>  template <class _Tp, class _Arg><br>
> -decltype((_VSTD::declval<_Tp>() = _VSTD::declval<_Arg>(), true_type()))<br>
> +typename __select_2nd<decltype(_VSTD::declval<_Tp>() = _VSTD::declval<_Arg>()), true_type>::type<br>
>  #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES<br>
>  __is_assignable_test(_Tp&&, _Arg&&);<br>
>  #else<br>
> @@ -1209,7 +1214,7 @@<br>
>  struct __is_assignable_imp<br>
>      : public common_type<br>
>          <<br>
> -            decltype(__is_assignable_test(declval<_Tp>(), declval<_Arg>()))<br>
> +            decltype(_VSTD::__is_assignable_test(declval<_Tp>(), declval<_Arg>()))<br>
>          >::type {};<br>
><br>
>  template <class _Tp, class _Arg><br>
><br>
><br>
> 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.<br>
<br>
</div></div>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.<br>
<br>
For me, all with tot clang, your example is giving a diagnostic:<br>
<div class="im"><br>
#include <type_traits><br>
template<typename T> struct S { static_assert(sizeof(T) == 1, ""); };<br>
bool b = std::is_assignable<S<int>*&, S<int>*>::value;<br></div></blockquote><div><br></div><div>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 <i>very</i> thin ice) because we don't actually need to instantiate S<int>. And it's not clear to me that the standard <i>allows</i> 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).</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">
</div>But Adam's code:<br>
<div class="im"><br>
   #include <map><br>
   using std::map;<br>
<br>
   template<typename K><br>
   struct Templ8 {<br>
       struct Member {<br>
           typename map<K,Member*>::iterator it;<br>
       };<br>
       typedef typename map<K,Member*>::iterator iterator_type;<br>
   };<br>
<br>
   int main() {<br>
       Templ8<int> test;<br>
   }<br>
<br>
</div>is not giving a diagnostic (-stdlib=libc++ -std=c++11).<br>
<br>
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.</blockquote><div><br></div><div>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.</div>
</div>