[SERVER-62324] Failure to build mongo::unique_function uses with MSVS 2022 in C++20 mode Created: 30/Dec/21  Updated: 29/Oct/23  Resolved: 05/Jan/22

Status: Closed
Project: Core Server
Component/s: None
Affects Version/s: None
Fix Version/s: 5.3.0

Type: Bug Priority: Critical - P2
Reporter: Andrew Morrow (Inactive) Assignee: Billy Donahue
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
is depended on by SERVER-62234 Add support for MSVS 2022 builds in C... Closed
is depended on by SERVER-62235 Add experimental MSVS builders to the... Closed
Backwards Compatibility: Fully Compatible
Operating System: ALL
Sprint: Service Arch 2022-10-17
Participants:

 Description   

While working on SERVER-62234 and SERVER-62235, I've run into issues building the tree with MSVS 2022 in C++20 mode. The issues all appear to revolve around mongo::unique_function, and probably, more narrowly, its Functor taking constuctor.

One instance happens in exit.hpp, where it appears unable to decide which sort of unique_function is to be created from a lambda, despite the fact that the lambda is clearly a good type match for one and not the other option:

Z:\data\acm\src\mongo\src\mongo/util/exit.h(81,87): error C2665: 'mongo::registerShutdownTask': none of the 2 overloads could convert all the argument types
    registerShutdownTask([task = std::move(task)](const ShutdownTaskArgs&) { task(); });
                                                                                      ^
Z:\data\acm\src\mongo\src\mongo/util/exit.h(80,13): note: could be 'void mongo::registerShutdownTask(mongo::unique_function<void (void)>)'
inline void registerShutdownTask(unique_function<void()> task) {
            ^
Z:\data\acm\src\mongo\src\mongo/util/exit.h(75,6): note: or       'void mongo::registerShutdownTask(mongo::unique_function<void (const mongo::ShutdownTaskArgs &)>)'
void registerShutdownTask(unique_function<void(const ShutdownTaskArgs& shutdownArgs)>);
     ^
Z:\data\acm\src\mongo\src\mongo/util/exit.h(81,87): note: while trying to match the argument list '(mongo::registerShutdownTask::<lambda_1>)'
    registerShutdownTask([task = std::move(task)](const ShutdownTaskArgs&) { task(); });

Another example is in mongo/util/out_of_line_executor.h, where it appears that what looks like a perfectly good match isn't seen as available:

Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(143,20): error C2440: '<function-style-cast>': cannot convert from 'mongo::GuaranteedExecutor::e
nforceRunOnce::<lambda_1>' to 'mongo::OutOfLineExecutor::Task'
        return Task([task = std::move(task), guard = RunOnceGuard()](Status status) mutable {
                   ^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(149,1): note: No constructor could take the source type, or constructor overload resolution was
ambiguous
        });
^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(154,1): error C3313: 'sureFunc': variable cannot have the type 'void'
        auto sureFunc = enforceRunOnce(std::move(func));
^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(155,1): error C3536: 'sureFunc': cannot be used before it is initialized
        _exec->schedule(std::move(sureFunc));
^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(155,1): error C2664: 'void mongo::OutOfLineExecutor::schedule(mongo::OutOfLineExecutor::Task)': cannot convert argument 1 from 'int' to 'mongo::OutOfLineExecutor::Task'
        _exec->schedule(std::move(sureFunc));
^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(155,1): note: No constructor could take the source type, or constructor overload resolution was ambiguous
        _exec->schedule(std::move(sureFunc));
^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(118,18): note: see declaration of 'mongo::OutOfLineExecutor::schedule'
    virtual void schedule(Task func) = 0;
                 ^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(181,19): error C2664: 'void mongo::OutOfLineExecutor::schedule(mongo::OutOfLineExecutor::Task)': cannot convert argument 1 from 'mongo::GuaranteedExecutorWithFallback::schedule::<lambda_1>' to 'mongo::OutOfLineExecutor::Task'
        _preferred->schedule([func = std::move(func), fallback = _fallback](Status status) mutable {
                  ^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(190,9): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
        });
        ^
Z:\data\acm\src\mongo\src\mongo/util/out_of_line_executor.h(118,18): note: see declaration of 'mongo::OutOfLineExecutor::schedule'
    virtual void schedule(Task func) = 0;
                 ^

A fix will be required for these issues in order to stand up a C++20 enabled canary builder with MSVS 2022, which is a critical path step towards moving to the v4 toolchains and on to C++20 enablement.

Note that this code works with GCC 8.3/11, clang 7/12, and Xcode 10/13 in both C++17 and C++20 mode, as well as with VS 2019 in C++17 mode and VS 2022 in C++17 mode, so it is entirely possible that this is a C++20 mode VS 2022 specific issue. Some workaround would still be required though.



 Comments   
Comment by Githook User [ 05/Jan/22 ]

Author:

{'name': 'Andrew Morrow', 'email': 'acm@mongodb.com', 'username': 'acmorrow'}

Message: SERVER-62324 Fix unique_function issues in C++20 mode with VS 2022
Branch: master
https://github.com/mongodb/mongo/commit/35a20b4134d98e63e22a4879330c03f2f6577ece

Comment by Andrew Morrow (Inactive) [ 04/Jan/22 ]

lauren.lewis - As soon as possible please. MSVS is the last platform for which we don't have a C++20 builder up to lock in C++20 compatibility, which is a crucial step before we can migrate.

Comment by Lauren Lewis (Inactive) [ 04/Jan/22 ]

hi acm, is this something we need to address in the next few days, or something we could address in the upcoming sprint?

cc: matthew.saltz blake.oler shameek.ray

Comment by Billy Donahue [ 31/Dec/21 ]

We could try explicitly telling the compiler which unique_function overload we want.
In the exit.h case:

 inline void registerShutdownTask(unique_function<void()> task) {
-    registerShutdownTask([task = std::move(task)](const ShutdownTaskArgs&) { task(); });
+    registerShutdownTask(unique_function<void(const ShutdownTaskArgs&)>{
+        [task = std::move(task)](const ShutdownTaskArgs&) { task(); }});
}

https://github.com/10gen/mongo/compare/billydonahue/SERVER-62324

It may be the case that due to a MSVC bug, a void() is considered invocable with extra arguments, and those arguments are ignored?
Or it might be our stdx::is_invocable_r used in the unique_function SFINAE requirements, that's screwing up here.
Maybe we can switch to regular std::is_invocable_r ( it should be available in C++17) ?

Generated at Thu Feb 08 05:54:48 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.