Open Erlkoenig90 opened 4 years ago
Slightly more cleaned up test case:
// 1) use_vtable() uses vtable for HasVtable<int>
template<typename T> struct HasVtable {
virtual void please_define_me() { T::error; }
};
template<typename T = int> void use_vtable() { HasVtable<T> q; }
// 2) somehow trigger instantiation of use_vtable without
// triggering instantiation of the vtable (???)
auto F = [](auto...) {
[](auto...) -> decltype(use_vtable()) { use_vtable(); }();
};
template<typename T> auto call(T f) -> decltype(f());
using U = decltype(call(F));
// 3) emit a reference to use_vtable to observe the badness
int main() { use_vtable(); }
I'm not sure exactly what's going wrong in step 2, but ... something about this doubly-nested generic lambda instantiation happening within a SFINAE context seems to mess up the instantiation of use_vtable
.
Looks like the problem is triggered by the use of two_arg_handler_test
from tcp::resolver::initiate_async_resolve::operator()
's static_assert
. Reduced to:
template <typename T> struct Q {
virtual void please_define_me() {}
};
template <typename T> void onResolve(T) {
Q<T> q;
}
template <typename Handler>
auto two_arg_handler_test(Handler h) -> decltype(sizeof(h(), 0));
int main() {
auto F = [](auto... args) {
[](auto... fwdLambdaArgs) -> decltype(onResolve(fwdLambdaArgs...)) {
return onResolve(fwdLambdaArgs...);
}(42, args...);
};
static_assert(sizeof(two_arg_handler_test(F)) != 0);
F();
}
I added the preprocessed source. My source code is already quite reduced, that's why it's so weird... Indeed, changing small details makes it work. I think it has something to do with the combination of the lambda and its return type deduction, the "wrap" function and the fact that "onResolve" is a template (it works if it's not templated).
When I compile with -c
and run llvm-nm
on the generated .o
file, I can see that the symbol is actually undefined ("U"). Other instantiations of the same functions DO exist however ("W").
Please can you attach preprocessed source code (https://llvm.org/docs/HowToSubmitABug.html#front-end-bugs)? (A reduced testcase would be great if you're interested in crafting one, but for a bug of this kind, correct reduction can be ... subtle.)
This looks likely to be a bug; it seems unlikely that there's an explicit instantiation of that function or key function or similar that would justify our not emitting this function template instantiation in every translation unit that has a use.
Extended Description
For the attached C++17 code involving Boost.Beast, clang++ gives an "undefined symbol" error (full message below). It appears that the code for the function
boost::beast::stable_async_base<...>::before_invoke_hook()
is not generated, leading to the error. It works with g++.The error occurs with the LLVM 10.0.0 prebuilt binary for Ubuntu x86_64 and using the Boost library version 1.73.0, on Linux Mint 19.3. It happens with both LD and LLD. The Boost libraries were compiled with clang as well, but I think this is not really relevant as the missing function is a template and should be generated when compiling the application.
The code is compiled with
clang++ test.cpp -fuse-ld=lld -lboost_system -pthread -lssl -lcrypto
.This is the missing function: https://github.com/boostorg/beast/blob/boost-1.73.0/include/boost/beast/core/async_base.hpp#L193
The attached code of course makes zero sense, but appears to be the minimal amount that reproduces the problem. It is unnecessarily contrived and I have since been pointed to a better (and working) solution, but there may still be a bug in Clang/LLVM that could be worth fixing.
The full linker error is: