[cfe-dev] -Wcatch-incomplete-type-extensions

Howard Hinnant hhinnant at apple.com
Thu Jan 19 09:26:28 PST 2012

On Jan 18, 2012, at 11:12 PM, John McCall wrote:

> On Jan 18, 2012, at 6:38 PM, Howard Hinnant wrote:
>> Thanks Eli.  That is the context I was looking for.  This looks more like a template-2-phase-lookup issue to me (where clang is doing a better job than gcc during template parsing).
> The weasel wording about incomplete types within templates is actually separate, but tt's that general category of problem, yeah.
>> In order to match the thrown object to a catch handler the personality function needs to read the std::type_info* out of the lsda table at the end of a procedure fragment.  So the question becomes:  what does the compiler encode for a std::type_info* for an incomplete class type?  I haven't found a way to entice the compiler to reveal that except in the case of clang and in the case of the incomplete type in the catch clause.  For example typeid(A) enforces a complete type A.
> The Itanium ABI tells us to generate a weak (in both the "dynamically replaceable" and "coalesced by the linker" senses) object of type __class_type_info.  It has to be linker-coalesced because there's no way for translation units to agree on who should emit it;  it has to be dynamically replaceable so that RTTI emitted by translation units with the class definition available will replace it.  Of course, there's no guarantee that the "defining" translation units will emit the RTTI at all, and even if they do, I'm not sure that static+dynamic linkers necessarily support all the crazy combinations of replacement that can happen here.
> I am also willing to believe that Clang have bugs relating to that replacement.
> John.

Thanks for your comments John.

I've just stumbled across a troubling piece of code.  I know why it is happening.  But I'm not sure exactly what we should do about it.

Here's the code:

#include <cassert>

struct A;

void foo();

int main()
    catch (A& a)
    catch (...)

struct B {virtual ~B() {}};

struct A : B {};

void foo()
    A a;
    B* bp = &a;
    assert(dynamic_cast<A*>(bp) != 0);

This asserts.  But if you comment out the catch (A&) frame:

//     catch (A& a)
//     {
//     }

It runs correctly.

The issue:  When clang sees the catch(A&) (and A is incomplete at this point) it lays down the wrong kind of type_info:  a __class_type_info.  And that never gets corrected.  So when it comes time to do the dynamic_cast, later when A is complete, the dynamic_cast malfunctions because the type_info for A should be a __si_class_type_info.  Without the correct type_info, the dynamic_cast malfunctions.

I'm willing to file a bug on this, but I wanted to get guidance here first.

g++-4.2 refuses to compile the code that malfunctions, and thus never gets into this situation.


More information about the cfe-dev mailing list