On Fri, Sep 28, 2012 at 6:45 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="im">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>
</div>Hi Richard,<br>
<br>
Is your position that tot clang/libc++ is in error for not producing a diagnostic?<span class="HOEnZb"><font color="#888888"><br>
</font></span></blockquote></div><br><div>No, my position is the opposite. Trunk clang + libc++ still reject this:</div><div><br></div><div>#include <type_traits></div><div>template<typename T> struct S { static_assert(sizeof(T) == 1, ""); };</div>
<div>bool b = std::is_assignable<S<int>*&, S<int>*>::value;</div><div><br></div><div>... because is_assignable triggers the instantiation of S<int>.</div><div><br></div><div>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):</div>
<div><br></div><div><br></div><div><div>--- type_traits (revision 164877)</div><div>+++ type_traits (working copy)</div><div>@@ -1187,10 +1187,15 @@</div><div> </div><div> #endif  // _LIBCPP_HAS_NO_VARIADICS</div><div> </div>
<div>+template <class _Fst, class _Snd></div><div>+struct __select_2nd {</div><div>+  typedef _Snd type;</div><div>+};</div><div>+</div><div> // is_assignable</div><div> </div><div> template <class _Tp, class _Arg></div>
<div>-decltype((_VSTD::declval<_Tp>() = _VSTD::declval<_Arg>(), true_type()))</div><div>+typename __select_2nd<decltype(_VSTD::declval<_Tp>() = _VSTD::declval<_Arg>()), true_type>::type</div>
<div> #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES</div><div> __is_assignable_test(_Tp&&, _Arg&&);</div><div> #else</div><div>@@ -1209,7 +1214,7 @@</div><div> struct __is_assignable_imp</div><div>     : public common_type</div>
<div>         <</div><div>-            decltype(__is_assignable_test(declval<_Tp>(), declval<_Arg>()))</div><div>+            decltype(_VSTD::__is_assignable_test(declval<_Tp>(), declval<_Arg>()))</div>
<div>         >::type {};</div><div> </div><div> template <class _Tp, class _Arg></div></div><div><br></div><div><br></div><div>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.</div>