-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[libc++][modules] Remove a dependency of __type_traits/invoke.h on __utility #106795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-libcxx Author: Louis Dionne (ldionne) ChangesWe can use static_cast<T&&> instead of std::forward to break up a cyclic dependency between __type_traits and __utility. While we normally favour using std::forward instead of static_cast, in this case using std::forward kinda breaks the layering of __type_traits being a dependency-free "submodule" of the codebase. Full diff: https://github.com/llvm/llvm-project/pull/106795.diff 1 Files Affected:
diff --git a/libcxx/include/__type_traits/invoke.h b/libcxx/include/__type_traits/invoke.h
index 71db32ae6a3cef..f17da7669c4512 100644
--- a/libcxx/include/__type_traits/invoke.h
+++ b/libcxx/include/__type_traits/invoke.h
@@ -23,7 +23,6 @@
#include <__type_traits/is_void.h>
#include <__type_traits/nat.h>
#include <__utility/declval.h>
-#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -213,7 +212,7 @@ template <class _Ret, bool = is_void<_Ret>::value>
struct __invoke_void_return_wrapper {
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) {
- return std::__invoke(std::forward<_Args>(__args)...);
+ return std::__invoke(static_cast<_Args&&>(__args)...);
}
};
@@ -221,7 +220,7 @@ template <class _Ret>
struct __invoke_void_return_wrapper<_Ret, true> {
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) {
- std::__invoke(std::forward<_Args>(__args)...);
+ std::__invoke(static_cast<_Args&&>(__args)...);
}
};
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly is the benefit here? This feels a lot like making the code worse to make a tool happy.
The benefit is that we no longer include anything from Another option would be to move |
…utility We can use static_cast<T&&> instead of std::forward to break up a cyclic dependency between __type_traits and __utility. While we normally favour using std::forward instead of static_cast, in this case using std::forward kinda breaks the layering of __type_traits being a dependency-free "submodule" of the codebase.
4da62dd
to
ef9a46b
Compare
I think I'd rather go for having an extra module for now. I have a feeling that we can absorb most of the modulemap into a single large module, which would make the cross-module dependencies not a problem. |
I just tried and this actually doesn't solve the problem after all. The problem is that:
That's the cycle, and even if Also note that the moment we make |
What if we put |
|
I mean something like
I think that should work and avoids the dependency problem. The module names could probably be better. |
This is basically equivalent to moving Alternatively, your suggestion can be seen as:
But then that begs the question of defining what the |
I'd rather do that for now and I'm fine with having somewhat arbitrary modules for some time. If this is actually fundamentally necessary we can look into removing the cycle later, but I'm not yet convinced it actually is. Basically, I'd rather sacrifice a bit of modulemap simplicity than code readability at this point, since we don't yet really know where the modulemap journey will get us. |
That suggestion seems to work (functionally). One downside is that we won't catch the reintroduction of cycles between these submodules, allowing us to regress. But if we're careful not to add too many headers into this "core" module, perhaps it's OK. I'll close this for now since we won't be going for this approach. |
As I experiment with this more and more, the approach of having a monolithic top-level module to avoid actually disentangling our code dependencies doesn't really work well. You end up in cycles with things that are outside that monolithic module extremely fast. IMO the only sane way of proceeding is to actually disentangle the dependencies in the code. If you forget about the modulemap itself, what we need to do is basically simplify the include graph to make it acyclic, and then the modulemap will follow naturally from that. I don't think there's any way around that. |
We can use static_cast<T&&> instead of std::forward to break up a cyclic dependency between __type_traits and __utility. While we normally favour using std::forward instead of static_cast, in this case using std::forward kinda breaks the layering of __type_traits being a dependency-free "submodule" of the codebase.