[LLVMdev] Clang bug?

Adam Peterson alpha.eta.pi at gmail.com
Fri Sep 28 18:32:14 PDT 2012

On Fri, Sep 28, 2012 at 7:11 PM, Richard Smith <richard at metafoo.co.uk> wrote:
> 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.]

Right; I was just thrown off by the reduction of the test-case from
valid code to invalid code, and I was calling on G++ as secondary
anecdotal evidence, but in the context of the error message QoI, my
observation was misguided.

> 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.

The ramifications here are a bit beyond me, but certainly the ability
to create standard containers of pointers is fundamental, and it seems
to me like we (that is, someone like me, a programmer writing C++
code) would have ODR violations that are almost impossible to avoid if
the same template were instantiated differently depending on whether
the pointed-at type were complete.

> 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.

Ugh.  That seems like enough to make me wonder if it were ever wise to
allow operator,() to be overloaded.  Its semantics are so different
from the built-in one and I've personally never found it useful.  (I
think boost::lambda uses it to some good effect, though.)

> 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.

I'm curious, though.  Why doesn't the problem manifest if the typedef
is in the Member inner class?  And, for whatever reason that works,
can the same or a similar code-path be used when the typedef is in the
surrounding template scope?

> 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.

I understand now.  Thank you.

More information about the llvm-dev mailing list