http://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316

--- Comment #53 from Harald van Dijk <harald at gigawatt dot nl> ---
(In reply to Marc Glisse from comment #52)
> (In reply to Harald van Dijk from comment #51)
> > extern "C" { void f(); }
> > typedef void t();
> > t f, *g = f; // valid redeclaration of f, invalid initialisation of g
> 
> Fun!

What's fun is that in getting this working, I found not only had I accidentally
introduced a bug (already fixed -- the typedef got changed, instead of only the
function's type), but that exact same bug also affects in Sun C++.

> > extern "C" t f; // invalid redeclaration of f
> 
> I am not 100% sure about that one.

It redeclares f as void(), where the previous declaration gave it type void()
extern "C", and the special exception in [dcl.link] to inherit a previous
declaration's linkage (both of the name and of the type) doesn't apply when the
linkage is explicitly specified, right?

> > Linkage conflicts with built-in declarations of functions are also a bit of
> > a problem:
> 
> Yes, as I said in comment #38 and comment #39, giving builtin functions the
> right linkage is a big missing part of the patch.

I've got this part working, and found it much easier to reverse the logic: all
code using build_function_type gets a C function type. It required just a few
more changes of build_function_type to build_function_type2 in the C++
frontend.

> > implementing this as described makes GCC fail to compile,
> 
> At least in the version of the patch that is attached to the PR, gcc tries
> to accept almost anything except in some pedantic mode. The goal is to avoid
> breaking every code on earth, or the patch has no chance of ever being
> accepted.

I've given it a try on a large number of packages (including Firefox, Chromium,
Qt+KDE), and I'm down to five categories of hard errors, four of which IMO
should be warnings, but comments on that would be welcome.

1:

extern "C" typedef void (*fpt1)() __attribute__((noreturn));
void f1();
fpt1 fp1 = f1; // error: invalid conversion from ‘void (*)()’ to ‘fpt1 {aka
void (*)()__attribute__((noreturn)) extern "C"}’ [-fpermissive]

This should work, with a warning. The logic to allow an implicit conversion to
the corresponding function type with different linkage doesn't work when the
attributes differ.

2:

extern "C" typedef void (*fpt2)();
template <typename> void f2();
fpt2 fp2 = f2<int>; // error: no matches converting function ‘f2’ to type ‘fpt2
{aka void (*)() extern "C"}’

This should work, with a warning. This possibly has the same underlying cause
as what I reported as PR60531, namely that f2<int> is considered an overloaded
function even though it isn't.

3:

extern "C" void f3();
template <void()> struct S3 { };
template struct S3<f3>;

This should work, with a warning. I was worried this might cause a conflict
between a C function template argument and a C++ function template argument
with the same name, but that cannot ever happen, the only possible way that
they could get the same mangled name is if the code is already invalid for
other reasons.

4:

extern "C" void f4();
struct S4 {
  S4(void());
private:
  S4(const S4 &);
};
S4 s4(f4);

This should work, with a warning, but only after all standard means of
constructing S4 have failed. In particular, if another constructor is added
that can accept f4's type (say S4(...), or S4(bool)), the other constructor
needs to be picked.

5:

extern "C" void f5(void());
void f5(void());
void g5() { long p = (long)f5; }

This should be considered acceptable breakage. The function f5 in question is
actually atexit, which in current glibc is not an overloaded function, but
would become an overloaded function, as specified in the standard. Taking its
address without any context doesn't give enough information to determine which
overload's address should be taken (even though they both have the same
address).

P.S.: the patch here contains a comment questioning the validity of extern "C"
template aliases. I think that a _lot_ more is valid than is currently
accepted:

7.5p1:
All function types, function names with external linkage, and variable names
with external linkage have a language linkage.

14p4:
A template name has linkage (3.5). A non-member function template can have
internal linkage; any other template name shall have external linkage. [...] A
template, a template explicit specialization (14.7.3), and a class template
partial specialization shall not have C linkage.

A static template function has internal linkage, so doesn't have external
linkage, so doesn't have language linkage, so never has C linkage, even if it
appears in an extern "C" block. Template classes and template aliases aren't
function types, so also never have language linkage, so never have C linkage.

The restriction on class template partial specializations suggests that this
isn't what the standard intends (they never have C linkage, so why specify that
they cannot have C linkage?), but it's useful, and I don't see any other
interpretation based on the actual wording, so that is what I've implemented.
This avoids the need for some of the uses of template aliases in libstdc++. If
desired, the useful templates that were previously rejected could get a pedwarn
rather than a hard error.

Reply via email to