itanium-cxx-abi / cxx-abi

C++ ABI Summary
507 stars 95 forks source link

Mangling the name of an externally visible lambda in a static data member of a class #157

Open dwblaikie opened 1 year ago

dwblaikie commented 1 year ago

Clang fails and emits these with local linkage and non-standard mangling (if they are actually internal, that'd be fine) GCC emits these names without using the member name in the mangling and a numbering that's not local to the member, or even local to the type (similar names in subsequent classes get numberings that differ depending on the previous class - despite the class name in the mangling) https://godbolt.org/z/7514cTh5o

struct A {
    static constexpr auto x = [] {
        return 1;
    };
};
template <typename>
struct B {
    static constexpr auto x = [] {
        return 1;
    };
};
template <typename T>
struct C {
    static int x;
};
void side_effect();
template <typename T>
int C<T>::x = (side_effect(), [] { return 1; }());
template int C<int>::x;
void f() {
    A::x();
    B<int>::x();
}

GCC produces these manglings:

A::{lambda()#1}::operator()() const
_ZNK1AUlvE_clEv

B<int>::{lambda()#3}::operator()() const
_ZNK1BIiEUlvE1_clEv

C<int>::x::{lambda()#1}::operator()() const
_ZNK1CIiE1xMUlvE_clEv

Clang produces these manglings:

A::$_0::operator()() const
_ZNK1A3$_0clEv

B<int>::x::{lambda()#1}::operator()() const
_ZNK1BIiE1xMUlvE_clEv

C<int>::x::{lambda()#1}::operator()() const
_ZNK1CIiE1xMUlvE_clEv

I think both GCC and Clang gets C right, Clang gets B right (& GCC gets it wrong) and both Clang and GCC get A wrong for different reasons. (and it should be like Clang's B behavior).

This is discussed in Clang (https://github.com/llvm/llvm-project/issues/58819) and GCC (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107741)

zygoloid commented 1 year ago

The ABI already mostly covers this: we require a lambda to be numbered within the scope of the enclosing variable if the variable is inline or a variable template, but we missed the case where it's a non-inline static data member, eg:

template<typename T> int print_hello = (std::cout << "hello\n", 0);
struct X {
  // Should only print hello once, regardless of how many TUs this appears in.
  static const intptr_t n = (print_hello<decltype([]{})>, 0);
};

I've updated #85 to cover that case too.