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. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20120928/49b92df2/attachment.html>
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
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>