Anonymous records with typedef names for linkage

Rafael Espíndola rafael.espindola at gmail.com
Wed Jan 29 07:54:33 PST 2014


On 29 January 2014 04:20, John McCall <rjmccall at apple.com> wrote:
> Our current approach to computing linkage is pretty badly broken in the presence of anonymous types that gain linkage through the timely use of a typedef, e.g.:
>   typedef struct {
>     int foo() { … }
>   } A;
>
> For the entirety of the definition of this struct, we will consider it to not be externally visible because it’s anonymous; but as soon as we process the declarator, it gains external linkage.  This is problematic.  r200380 changes two pieces of code that only really apply to file-scope declarations to actually filter for that before querying linkage, and that’s a win on multiple axes, but it’s not a workable approach to avoiding all such crashes because of the ‘this’ problem.
>
> The ‘this’ problem is quite simple.  For the most part, within the definition of an anonymous struct (including an anonymous-so-far struct like this one), we are protected from the struct type escaping into truly arbitrary parts of the compiler by the fact that you can’t actually refer to it directly.  This almost makes it possible to just carefully control linkage queries during the definition of a struct, like r200380 does, and thereby avoid any semantic troubles.  But it’s not actually possible, because you can define a non-static member function in such a type, and such a function can name its containing type by using ‘this’: in C++11, you can just use decltype(*this), and in C++03, you can use template argument deduction to bind a template argument to the type (*).  Either feature allows the type to escape into the broader environment, and it becomes impossible for us to code around the problem.

I see. You mean

template <typename T>
void f(T* x) {}
typedef struct {
  void foo() { f(this); }
} A;

I see your point. I guess this can reach pretty much every code path
that can compute linkage. gcc errors on this in c++03, but I agree
that it should probably be valid.

> (*) This is, of course, ill-formed prior to C++11 because the type doesn’t have external linkage, except that it isn’t ill-formed if the anonymous struct acquires a typedef name for linkage purposes.
>
> As far as I can tell, this is not technically a language problem, because there is nothing in the language which requires us to compute linkage at a specific point during the processing of a file.  For example, C++03 requires us to check whether a template argument has external linkage, but there’s nothing forcing us to check that while processing the template argument; we could delay the check indefinitely, even to the end of the translation unit, as long as we eventually diagnose it.  But I’m reluctant to actually propose that we pursue an approach that delays all linkage computations that might involve a still-being-defined anonymous declaration until that declaration is complete; that seems like a very brittle solution.  And yet I don’t have a better proposal that’s totally correct.

Why would it be brittle? In c++ there are other contexts where some
computations (lots of it in microsoft mode) need to be delayed. Other
than proposing a change of the standard to restrict what can be in
between the typedef and the type name I also cannot think of another
reasonable solution.  We could force recomputation, but *that* is the
one I think would be brittle (and possibly non linear complexity).

> Unfortunately, it’s not possible to prove one way or the other whether a given anonymous declaration will later be given a typedef name for the purposes of linkage.  While nobody actually codes this way, the ‘typedef’ keyword is allowed to appear after the class-specifier, even if the class-specifier contains a definition; and for the inverse, a ‘typedef’ declaration does not create a typedef name for linkage purposes if none of the declarators is just a simple name (e.g. if it’s "typedef struct { … } *x;”).
>
> I think the simplest solution for now is to just check for cached linkage before setting a typedef name for linkage purposes and then diagnosing it as an unsupported feature.

Basically replacing the current assert with a proper error? I could
definitely live with that.

> John.

Thanks a lot for looking at this,
Rafael




More information about the cfe-commits mailing list