[LLVMdev] Clang bug?

Richard Smith richard at metafoo.co.uk
Fri Sep 28 18:11:13 PDT 2012


On Fri, Sep 28, 2012 at 5:11 PM, Adam Peterson <alpha.eta.pi at gmail.com>wrote:

> This doesn't appear to me to be the same issue (but of course I don't
> know clang as well as you all do).  This code appears invalid (and G++
> rejects it with a similar "no type named ..." error message), whereas
> the original code is valid C++ (both C++98 and C++11).


[The difference in whether we accept the original code isn't clang versus
g++, it's libc++ versus libstdc++. Clang with libstdc++ accepted this code.]

There's two separate problems here. One is that given:

typedef typename map<K,Member*>::iterator iterator_type;

libc++'s std::map implementation was using a construct whose semantics
depended on whether 'Member' is a complete type (thus triggering the
instantiation of that type in the middle of instantiating map<K,Member*>,
creating a dependency cycle between the two instantiations). I'm not at all
clear on whether that is permissible behavior for a standard library
implementation, but it still seems to be a problem in some cases. libc++'s
is_assignable implementation uses SFINAE on this:

  decltype((_VSTD::declval<_Tp>() = _VSTD::declval<_Arg>(), true_type()))

Now, our case was: _Tp = Member*&, _Arg = Member*. In that case, we use the
built-in assignment operator, which produces an lvalue of type Member*.
Then we perform argument-dependent lookup looking for an overloaded
'operator,', because its RHS is of a class type. This lookup triggers the
(undesirable) instantiation of Member, because it is an associated class.
I'm not sure exactly what has stopped this problem from manifesting, but
the root cause superficially appears to still be present.


The other problem (the one my prior message was addressing) is that we have
a diagnostic QoR issue, in that we produced a diagnostic message which is
factually incorrect.


> On Fri, Sep 28, 2012 at 3: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
> >
> > On Fri, Sep 28, 2012 at 7:51 AM, Howard Hinnant <hhinnant at apple.com>
> wrote:
> >>
> >> That is one evil bug!
> >>
> >> I just tested it against tip-of-trunk clang and it appears to be fixed
> >> there (just fyi for everyone).
> >>
> >> Howard
> >>
> >> On Sep 27, 2012, at 7:17 PM, Adam Peterson <alpha.eta.pi at gmail.com>
> wrote:
> >>
> >> > Is this a relevant location to provide information about what I
> >> > believe is a compiler bug in clang?  If not, please forgive me for
> >> > posting it here.  (Perhaps you can redirect me to some place more
> >> > appropriate.)  If so, here are the details:
> >> >
> >> > I have a short 15-line C++ program using only one standard header that
> >> > clang fails to compile properly under C++11 with the new standard
> >> > library (although the code itself doesn't use any C++11-specific
> >> > syntax), but G++ (4.6-Ubuntu and 4.2-Darwin) compiles it fine:
> >> >
> >> >    #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;
> >> >    }
> >> >
> >> >
> >> >
> >> > This is the command-line:
> >> >    clang++ -stdlib=libc++ -std=c++11 main_bug.cpp
> >> > If either of the flags are removed (to compile in C++03 mode, or to
> >> > use the non-clang standard library), the code compiles.  If the
> >> > typedef is removed or commented out, clang++ compiles the code fine.
> >> > If the typedef is moved inside the "Member" inner class, the code
> >> > compiles fine (even if a second typedef pulls the symbol back into the
> >> > main template scope).
> >> >
> >> > My clang version information:
> >> >    Apple clang version 4.1 (tags/Apple/clang-421.11.65) (based on LLVM
> >> > 3.1svn)
> >> >    Target: x86_64-apple-darwin12.2.0
> >> >    Thread model: posix
> >> >
> >> > The error output:
> >> >
> >> > main_bug.cpp:7:34: error: no type named 'iterator' in
> >> > 'std::__1::map<int, Templ8<int>::Member *, std::__1::less<int>,
> >> > std::__1::allocator<std::__1::pair<const int, Templ8<int>::Member
> >> > *>>>'
> >> >        typename map<K,Member*>::iterator it;
> >> >        ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
> >> > /usr/bin/../lib/c++/v1/type_traits:1184:57: note: in instantiation of
> >> > member class 'Templ8<int>::Member' requested here
> >> > decltype((_VSTD::declval<_Tp>() = _VSTD::declval<_Arg>(),
> true_type()))
> >> >                                                        ^
> >> > /usr/bin/../lib/c++/v1/type_traits:1186:1: note: while substituting
> >> > deduced template arguments into function template
> >> > '__is_assignable_test' [with _Tp = Templ8<int>::Member *&, _Arg = <no
> >> > value>]
> >> > __is_assignable_test(_Tp&&, _Arg&&);
> >> > ^
> >> > /usr/bin/../lib/c++/v1/type_traits:1214:14: note: in instantiation of
> >> > template class 'std::__1::__is_assignable_imp<Templ8<int>::Member *&,
> >> > Templ8<int>::Member *&, false>' requested here
> >> >    : public __is_assignable_imp<_Tp, _Arg> {};
> >> >             ^
> >> > /usr/bin/../lib/c++/v1/type_traits:2616:38: note: in instantiation of
> >> > template class 'std::__1::is_assignable<Templ8<int>::Member *&,
> >> > Templ8<int>::Member *&>' requested here
> >> >    : public __is_nothrow_assignable<is_assignable<_Tp, _Arg>::value,
> >> > _Tp, _Arg>
> >> >                                     ^
> >> > /usr/bin/../lib/c++/v1/type_traits:2667:14: note: in instantiation of
> >> > template class 'std::__1::is_nothrow_assignable<Templ8<int>::Member
> >> > *&, Templ8<int>::Member *&>' requested here
> >> >    : public is_nothrow_assignable<typename
> >> > add_lvalue_reference<_Tp>::type,
> >> >             ^
> >> > /usr/bin/../lib/c++/v1/utility:250:20: note: (skipping 9 contexts in
> >> > backtrace; use -ftemplate-backtrace-limit=0 to see all)
> >> >                   is_nothrow_copy_assignable<second_type>::value)
> >> >                   ^
> >> > /usr/bin/../lib/c++/v1/__config:253:34: note: expanded from macro
> >> > '_NOEXCEPT_'
> >> > #  define _NOEXCEPT_(x) noexcept(x)
> >> >                                 ^
> >> > /usr/bin/../lib/c++/v1/memory:2386:15: note: in instantiation of
> >> > template class
> >> >
> 'std::__1::__libcpp_compressed_pair_imp<std::__1::__tree_end_node<std::__1::__tree_node_base<void
> >> > *> *>,
> >> >      std::__1::allocator<std::__1::__tree_node<std::__1::pair<int,
> >> > Templ8<int>::Member *>, void *>>, 2>' requested here
> >> >    : private __libcpp_compressed_pair_imp<_T1, _T2>
> >> >              ^
> >> > /usr/bin/../lib/c++/v1/__tree:813:56: note: in instantiation of
> >> > template class
> >> >
> 'std::__1::__compressed_pair<std::__1::__tree_end_node<std::__1::__tree_node_base<void
> >> > *> *>,
> >> >      std::__1::allocator<std::__1::__tree_node<std::__1::pair<int,
> >> > Templ8<int>::Member *>, void *>>>' requested here
> >> >    __compressed_pair<__end_node_t, __node_allocator>  __pair1_;
> >> >                                                       ^
> >> > /usr/bin/../lib/c++/v1/map:711:22: note: in instantiation of template
> >> > class 'std::__1::__tree<std::__1::pair<int, Templ8<int>::Member *>,
> >> > std::__1::__map_value_compare<int, Templ8<int>::Member *,
> >> >      std::__1::less<int>, true>,
> >> > std::__1::allocator<std::__1::pair<int, Templ8<int>::Member *>>>'
> >> > requested here
> >> >    typedef typename __base::__node_traits
> __node_traits;
> >> >                     ^
> >> > main_bug.cpp:9:22: note: in instantiation of template class
> >> > 'std::__1::map<int, Templ8<int>::Member *, std::__1::less<int>,
> >> > std::__1::allocator<std::__1::pair<const int, Templ8<int>::Member
> >> > *>>>'
> >> >      requested here
> >> >    typedef typename map<K,Member*>::iterator iterator_type;
> >> >                     ^
> >> > main_bug.cpp:13:17: note: in instantiation of template class
> >> > 'Templ8<int>' requested here
> >> >    Templ8<int> test;
> >> >                ^
> >> > 1 error generated.
> >> > _______________________________________________
> >> > LLVM Developers mailing list
> >> > LLVMdev at cs.uiuc.edu         http://llvm.cs.uiuc.edu
> >> > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
> >>
> >> _______________________________________________
> >> LLVM Developers mailing list
> >> LLVMdev at cs.uiuc.edu         http://llvm.cs.uiuc.edu
> >> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
> >
> >
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20120928/f2b00fde/attachment.html>


More information about the llvm-dev mailing list