Skip to content

Add bitcoin-{node,gui} to release binaries for IPC #31802

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

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Sjors
Copy link
Member

@Sjors Sjors commented Feb 5, 2025

Have depends make libmultiprocess by default. This PR causes the following behavior changes:

  1. bitcoin-node and bitcoin-gui binaries are included in releases, due to ENABLE_IPC option being switched on by default in depends builds
  2. ENABLE_IPC is also switched on by default in non-depends builds
  3. Various changes to CI: switching on ENABLE_IPC on in most configurations and using bitcoin-node binary (bitcoin -m) for functional tests in two of them.
  4. The bitcoin-node and bitcoin-gui are added to Maintenance.cmake (since they're now in the release)
  5. macOS and Linux installation instructions are updated to install capnp by default

This PR doesn't need to do all of 3 things at once. However it's is simpler, avoids code churn (especially in CI), and probably less confusing to make all these changes in the same PR.

Windows is not supported yet, so ENABLE_IPC is off by default for it. It can be enabled after #32387.

The initial main use case for IPC is to enable experimental support for the Mining IPC interface. A working example of a Stratum v2 Template Provider client using this interface can be found here: Sjors#48.

See #31756 for discussion of when this should happen. Supersedes #30975.

Guix hashes:

a33e468f7c89cc00a57d229845f37c44581d83ea3d9a22367c0a7943c2a640f0  guix-build-5a66729744db/output/aarch64-linux-gnu/SHA256SUMS.part
fe9092dfea69183e79ec0010c0a7021de14f2c5fca3be9090944712d3d98e94a  guix-build-5a66729744db/output/aarch64-linux-gnu/bitcoin-5a66729744db-aarch64-linux-gnu-debug.tar.gz
974acac87a4713f0be6f6e6c217ded130c4710b7f2806eaddc6c9fd465b98164  guix-build-5a66729744db/output/aarch64-linux-gnu/bitcoin-5a66729744db-aarch64-linux-gnu.tar.gz
369e26f4bcebb9de816c7762c205fcaa8dfb17640860077cb17a862076f63d08  guix-build-5a66729744db/output/arm-linux-gnueabihf/SHA256SUMS.part
f625a8765462826e87863f0f00193b39abb5e29354821881adac61b58e3c2d8b  guix-build-5a66729744db/output/arm-linux-gnueabihf/bitcoin-5a66729744db-arm-linux-gnueabihf-debug.tar.gz
8a0b50e0d126393145bdeba3b2593945fad8eefd6ca1281f033eafd42e702be6  guix-build-5a66729744db/output/arm-linux-gnueabihf/bitcoin-5a66729744db-arm-linux-gnueabihf.tar.gz
97c1f39a430e1b1246ff750e7f773608241af6cdad2552d50773aecdf056f61e  guix-build-5a66729744db/output/arm64-apple-darwin/SHA256SUMS.part
fb425c62edab2164d5d7163f357f3f83a4caa3cc1d45a22882ff62b9b90ed3ed  guix-build-5a66729744db/output/arm64-apple-darwin/bitcoin-5a66729744db-arm64-apple-darwin-codesigning.tar.gz
115eb7adf41b3ac821f225978eeb2e1574d9151ff3cb3a287705b53ec5d6b6fd  guix-build-5a66729744db/output/arm64-apple-darwin/bitcoin-5a66729744db-arm64-apple-darwin-unsigned.tar.gz
e348c1caa8fcc6dd26a995524c88ddcaed110f7ded8c35197503113e9b4bbce0  guix-build-5a66729744db/output/arm64-apple-darwin/bitcoin-5a66729744db-arm64-apple-darwin-unsigned.zip
feb5585be5745340828a6379c9b2df0d3e0161359c827e8b2f1528d8c8d23653  guix-build-5a66729744db/output/dist-archive/bitcoin-5a66729744db.tar.gz
a5a129621e694a840e866c3b76a619461c005eb2d1b2474989716033e7e0ad63  guix-build-5a66729744db/output/powerpc64-linux-gnu/SHA256SUMS.part
76a02bebc7f6e557c34edde405ca98366a4dd8de528b9521300b710b619d29e2  guix-build-5a66729744db/output/powerpc64-linux-gnu/bitcoin-5a66729744db-powerpc64-linux-gnu-debug.tar.gz
2c8207ad97bf2c794659b58f85235454d9b6c4fe472ea0039cea4c6960bb851a  guix-build-5a66729744db/output/powerpc64-linux-gnu/bitcoin-5a66729744db-powerpc64-linux-gnu.tar.gz
0cc31c02fee219cb3cdd6945928ae9ca18909a64732004f73bca564b70f1ac8d  guix-build-5a66729744db/output/riscv64-linux-gnu/SHA256SUMS.part
8943b074f312e00ede3eeaa6e38e4d203bd85ee48a6a3c2f88b51a130de1aee5  guix-build-5a66729744db/output/riscv64-linux-gnu/bitcoin-5a66729744db-riscv64-linux-gnu-debug.tar.gz
a4d8142fd5a2b39f414d426832bf06d110ac310f340c01f7c141fb22ca1f7019  guix-build-5a66729744db/output/riscv64-linux-gnu/bitcoin-5a66729744db-riscv64-linux-gnu.tar.gz
a566b2e272f2795a8e853c83ceb2f570c544b504fe404f001d9185550a0267a4  guix-build-5a66729744db/output/x86_64-apple-darwin/SHA256SUMS.part
e2efcc3f4d241c62bc87a67b938e9e95aea2d53905911abd544ad2e505d140f7  guix-build-5a66729744db/output/x86_64-apple-darwin/bitcoin-5a66729744db-x86_64-apple-darwin-codesigning.tar.gz
4c7f988105562ccff1f57f1857561c87807a6ee3fa101c058392fe62a3416ee2  guix-build-5a66729744db/output/x86_64-apple-darwin/bitcoin-5a66729744db-x86_64-apple-darwin-unsigned.tar.gz
d4c08e7767f7916370c3e97baef28ef82462a039760391c60202605ff1dd9442  guix-build-5a66729744db/output/x86_64-apple-darwin/bitcoin-5a66729744db-x86_64-apple-darwin-unsigned.zip
f1fcb6cc6396a3992c544d450982988c3cfbadaa28c3e73a4bfe295b64e49a9e  guix-build-5a66729744db/output/x86_64-linux-gnu/SHA256SUMS.part
17614a25c9b6eeeec90451e30fee3d6d1c44b4375d397d038ae0042eca859188  guix-build-5a66729744db/output/x86_64-linux-gnu/bitcoin-5a66729744db-x86_64-linux-gnu-debug.tar.gz
0b1fdcaab302e8179f5f0aa41a6e32ec5a999b30ed22cd941e6900611f5b9cab  guix-build-5a66729744db/output/x86_64-linux-gnu/bitcoin-5a66729744db-x86_64-linux-gnu.tar.gz
d0b09c23c85d9cf285952fff624abbd6fe74412f5ff4cc13e008516ed5845ea2  guix-build-5a66729744db/output/x86_64-w64-mingw32/SHA256SUMS.part
1b158b31a3b66a4e18320afb71717bb1e0ebc2e3df32d377afd3340a60f8f401  guix-build-5a66729744db/output/x86_64-w64-mingw32/bitcoin-5a66729744db-win64-codesigning.tar.gz
f6ab26042033d39bbbc9220f7ca5daf270a30e34db4a515ed2e64d87c4909cf5  guix-build-5a66729744db/output/x86_64-w64-mingw32/bitcoin-5a66729744db-win64-debug.zip
c1f0a9ac8fecc8cc81e20a05fd60bb9c3fe8880e6e0f64f78aca2f77256b9273  guix-build-5a66729744db/output/x86_64-w64-mingw32/bitcoin-5a66729744db-win64-setup-unsigned.exe
fd6677989199ddeb066bb09ac19919ae9509808e1248a314abc85f06060f6621  guix-build-5a66729744db/output/x86_64-w64-mingw32/bitcoin-5a66729744db-win64-unsigned.zip

@DrahtBot
Copy link
Contributor

DrahtBot commented Feb 5, 2025

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/31802.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK ryanofsky, vasild
Concept ACK TheCharlatan

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #32262 (build: Restore cross-compilation for Android by hebasto)
  • #32162 (depends: Switch from multilib to platform-specific toolchains by hebasto)
  • #31349 (ci: detect outbound internet traffic generated while running tests by vasild)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@sipa
Copy link
Member

sipa commented Feb 7, 2025

Some chatter from IRC:

17:21:41 < darosior> It might be confusing to release both a bitcoin-wallet utility and a bitcoin-wallet binary as part of multiprocess?
17:24:08 < darosior> We could rename the utility, but then it would be nice to at least have one deprecation cycle. Given recent momentum i estimate it's possible we might release multiprocess in 
                     30.0, which means if we want to deprecate the bitcoin-wallet utility name we should do it.. now?
17:33:31 < sipa> bitcoin-wallet-util ?
...
23:38:57 < _aj_> darosior: bitcoin-wallet-process, bitcoin-gui-process, etc? it's multi *-process!
03:04:34 < Sjors[m]> darosior: at the moment there is no wallet binary if were to enable multiprocess. That won't happen until #19460.
...
03:05:18 < Sjors[m]> Or maybe already in #10102
...
03:05:50 < Sjors[m]> In any case #31802 only adds bitcoin-node and bitcoin-gui.
...
03:07:19 < Sjors[m]> Though if we want to rename the utility eventually, it's always better to do it early.

@Sjors
Copy link
Member Author

Sjors commented Feb 8, 2025

@sipa I opened #31827

onlinesipahimithu

This comment was marked as spam.

@Sjors
Copy link
Member Author

Sjors commented Jun 16, 2025

Added the commit from #32760 to see if bumping capnp (depends) reveals any issues. Marking draft, will rebase after it lands.

I tested this with OpenBSD (x86, as well as aarch64 with 4f10a57 from #32716).

@Sjors Sjors marked this pull request as draft June 16, 2025 11:55
Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK 7ecf6bb. Since last review rebased, enabled openbsd support by updating capnp, renamed variable and dropped whitespace changes, and avoided c includes to fix tidy modernize-deprecated-headers errors

Sjors and others added 6 commits June 17, 2025 09:30
This causes IPC binaries (bitcoin-node, bitcoin-gui) to be included
in releases.

The effect on CI is that this causes more depends builds to build IPC
binaries, but still the only build running functional tests with them
is the i686_multiprocess one.

Except for Windows.
The bitcoin-node binary is built on all platforms which have
multiprocess enabled, but for functional tests it's only used in
CentOS native (depends) job. The next commit will also add a
non-depends job.
Install capnp on non-depends CI jobs.

Use the bitcoin-node binary in the macOS native non-depends job.

Co-authored-by: Ryan Ofsky <[email protected]>
@Sjors Sjors force-pushed the 2025/02/ipc-yea branch from 7ecf6bb to 5a66729 Compare June 17, 2025 07:31
@Sjors Sjors marked this pull request as ready for review June 17, 2025 07:31
@Sjors
Copy link
Member Author

Sjors commented Jun 17, 2025

Rebased after #32760 (only change is dropping that commit).

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK 5a66729. No change other than rebase and dropping base commit

Copy link
Contributor

@vasild vasild left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 5a66729

Since you are renaming "multiprocess" to "ipc" in some places and you are also renaming 00_setup_env_i686_multiprocess.sh to 00_setup_env_i686_no_multiprocess.sh maybe 00_setup_env_i686_no_ipc.sh would be a better new name of the file?

The last commit refactor: modernize deprecated ipc headers is fine, but I am not sure why it is needed in this PR since those (old) .h includes were not introduced in this PR. Is there not the same problem in master? Or maybe tidy did not look at files in src/ipc/ before? If yes, then maybe that commit should be first, before the one that makes tidy check src/ipc/, so that all intermediate commits are good.

@maflcko
Copy link
Member

maflcko commented Jun 19, 2025

The last commit refactor: modernize deprecated ipc headers is fine, but I am not sure why it is needed in this PR since those (old) .h includes were not introduced in this PR. Is there not the same problem in master? Or maybe tidy did not look at files in src/ipc/ before? If yes, then maybe that commit should be first, before the one that makes tidy check src/ipc/, so that all intermediate commits are good.

If you want it to be bisect-nice, I'd guess you'd have to set -DENABLE_IPC=OFF in the tidy CI in the second to last commit and then enable it in the last commit. However I haven't tested this and I don't think anyone is bisecting the CI on non-merge commits, so anything should be fine here.

@Sjors
Copy link
Member Author

Sjors commented Jun 19, 2025

I don't think anyone is bisecting the CI on non-merge commits

Indeed that's why I didn't move it up initially. With this many CI changes it's hard to be bisect friendly to that level anyway.

@vasild if I need to retouch I'll move that commit up and rename some more multiprocess things to ipc.

@fanquake
Copy link
Member

fanquake commented Jun 19, 2025

This PR needs release notes.

Note that the Tidy job here is passing, even though it's generating output (which also seems like a bug?). That should be addressed, to either fix any issues or suppress any false-positives; otherwise it's going to make the tidy output unusable. i.e (https://cirrus-ci.com/task/6552721135763456):

Details

[02:41:41.544] [706/706][83.5s] clang-tidy-20 -p=/ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu -quiet -load=/tidy-build/libbitcoin-tidy.so /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.proxy-server.c++
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:134:5: warning: Address of stack memory associated with temporary object of type '(lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:134:51)' is still referred to by a temporary object on the stack upon returning to the caller.  This will be a dangling reference [clang-analyzer-core.StackAddressEscape]
[02:41:41.544]   134 |     return ReadDestEmplace{TypeList<LocalType>(), [&](auto&&... args) -> decltype(auto) {
[02:41:41.544]       |     ^
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.proxy-server.c++:41:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::PassCustomParams, mp::test::messages::FooInterface::PassCustomResults>, mp::ServerField<1, mp::Accessor<mp::foo_fields::Arg, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 18>, mp::ServerCall>>>'
[02:41:41.544]    41 |     return serverInvoke(*this, call_context, MakeServerField<1, Accessor<foo_fields::Arg, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT | FIELD_BOXED>>(ServerCall())));
[02:41:41.544]       |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:16: note: Calling 'ReplaceVoid<(lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:28), (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:705:13)>'
[02:41:41.544]   704 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
[02:41:41.544]       |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   705 |             [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:665:19: note: 'is_same_v' is true
[02:41:41.544]   665 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
[02:41:41.544]       |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:665:5: note: Taking true branch
[02:41:41.544]   665 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
[02:41:41.544]       |     ^
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:666:9: note: Calling 'operator()'
[02:41:41.544]   666 |         fn();
[02:41:41.544]       |         ^~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:43: note: Calling 'ServerField::invoke'
[02:41:41.544]   704 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
[02:41:41.544]       |                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:539:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Arg, 17>, mp::test::FooCustom, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::PassCustomParams, mp::test::messages::FooInterface::PassCustomResults>>, const mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 18>, mp::ServerCall> &, mp::TypeList<>>'
[02:41:41.544]   539 |         return PassField<Accessor>(Priority<2>(),
[02:41:41.544]       |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   540 |             typename Split<argc, ArgTypes>::First(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   541 |             server_context,
[02:41:41.544]       |             ~~~~~~~~~~~~~~~
[02:41:41.544]   542 |             this->parent(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~
[02:41:41.544]   543 |             typename Split<argc, ArgTypes>::Second(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   544 |             std::forward<Args>(args)...);
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:297:5: note: Calling 'MaybeReadField<mp::TypeList<mp::test::FooCustom>, mp::InvokeContext &, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 17>, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace<mp::test::FooCustom, (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:298:83)>>'
[02:41:41.544]   297 |     MaybeReadField(std::integral_constant<bool, Accessor::in>(), TypeList<ArgType>(), invoke_context,
[02:41:41.544]       |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   298 |         Make<StructField, Accessor>(params), ReadDestEmplace(TypeList<ArgType>(), [&](auto&&... args) -> auto& {
[02:41:41.544]       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   299 |             param.emplace(std::forward<decltype(args)>(args)...);
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   300 |             return *param;
[02:41:41.544]       |             ~~~~~~~~~~~~~~
[02:41:41.544]   301 |         }));
[02:41:41.544]       |         ~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:269:5: note: Calling 'ReadField<mp::test::FooCustom, mp::InvokeContext &, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 17>, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace<mp::test::FooCustom, (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:298:83)>>'
[02:41:41.544]   269 |     ReadField(std::forward<Args>(args)...);
[02:41:41.544]       |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:172:12: note: Calling 'CustomReadField<mp::StructField<mp::Accessor<mp::foo_fields::Arg, 17>, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace<mp::test::FooCustom, (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:298:83)>>'
[02:41:41.544]   172 |     return CustomReadField(TypeList<RemoveCvRef<LocalTypes>...>(), Priority<2>(), std::forward<Args>(args)...);
[02:41:41.544]       |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/test/mp/test/foo-types.h:35:12: note: Calling 'ReadDestEmplace::update'
[02:41:41.544]    35 |     return read_dest.update([&](FooCustom& value) {
[02:41:41.544]       |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]    36 |         value.v1 = ReadField(TypeList<std::string>(), invoke_context, mp::Make<mp::ValueField>(custom.getV1()), ReadDestTemp<std::string>());
[02:41:41.544]       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]    37 |         value.v2 = custom.getV2();
[02:41:41.544]       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]    38 |     });
[02:41:41.544]       |     ~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:112:23: note: 'is_const_v' is false
[02:41:41.544]   112 |         if constexpr (std::is_const_v<std::remove_reference_t<std::invoke_result_t<EmplaceFn>>>) {
[02:41:41.544]       |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:112:9: note: Taking false branch
[02:41:41.544]   112 |         if constexpr (std::is_const_v<std::remove_reference_t<std::invoke_result_t<EmplaceFn>>>) {
[02:41:41.544]       |         ^
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:122:13: note: Calling 'operator()'
[02:41:41.544]   122 |             update_fn(temp);
[02:41:41.544]       |             ^~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/test/mp/test/foo-types.h:36:113: note: Calling 'ReadDestTemp<std::basic_string<char>>'
[02:41:41.544]    36 |         value.v1 = ReadField(TypeList<std::string>(), invoke_context, mp::Make<mp::ValueField>(custom.getV1()), ReadDestTemp<std::string>());
[02:41:41.544]       |                                                                                                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:134:5: note: Address of stack memory associated with temporary object of type '(lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:134:51)' is still referred to by a temporary object on the stack upon returning to the caller.  This will be a dangling reference
[02:41:41.544]   134 |     return ReadDestEmplace{TypeList<LocalType>(), [&](auto&&... args) -> decltype(auto) {
[02:41:41.544]       |     ^                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   135 |         return LocalType{std::forward<decltype(args)>(args)...};
[02:41:41.544]       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   136 |     }};
[02:41:41.544]       |     ~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/type-number.h:55:32: warning: The value '0' provided to the cast expression is not in the valid range of values for 'FooEnum' [clang-analyzer-optin.core.EnumCastOutOfRange]
[02:41:41.544]    55 |     return read_dest.construct(static_cast<LocalType>(input.get()));
[02:41:41.544]       |                                ^
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/test/mp/test/foo.h:24:12: note: enum declared here
[02:41:41.544]    24 | enum class FooEnum : uint8_t { ONE = 1, TWO = 2, };
[02:41:41.544]       | ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.proxy-server.c++:53:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::PassEnumParams, mp::test::messages::FooInterface::PassEnumResults>, mp::ServerField<1, mp::Accessor<mp::foo_fields::Arg, 1>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>'
[02:41:41.544]    53 |     return serverInvoke(*this, call_context, MakeServerField<1, Accessor<foo_fields::Arg, FIELD_IN>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall())));
[02:41:41.544]       |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:16: note: Calling 'ReplaceVoid<(lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:28), (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:705:13)>'
[02:41:41.544]   704 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
[02:41:41.544]       |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   705 |             [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:665:19: note: 'is_same_v' is true
[02:41:41.544]   665 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
[02:41:41.544]       |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:665:5: note: Taking true branch
[02:41:41.544]   665 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
[02:41:41.544]       |     ^
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:666:9: note: Calling 'operator()'
[02:41:41.544]   666 |         fn();
[02:41:41.544]       |         ^~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:43: note: Calling 'ServerField::invoke'
[02:41:41.544]   704 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
[02:41:41.544]       |                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:539:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Arg, 1>, mp::test::FooEnum, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::PassEnumParams, mp::test::messages::FooInterface::PassEnumResults>>, const mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall> &, mp::TypeList<>>'
[02:41:41.544]   539 |         return PassField<Accessor>(Priority<2>(),
[02:41:41.544]       |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   540 |             typename Split<argc, ArgTypes>::First(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   541 |             server_context,
[02:41:41.544]       |             ~~~~~~~~~~~~~~~
[02:41:41.544]   542 |             this->parent(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~
[02:41:41.544]   543 |             typename Split<argc, ArgTypes>::Second(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   544 |             std::forward<Args>(args)...);
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:297:5: note: Calling 'MaybeReadField<mp::TypeList<mp::test::FooEnum>, mp::InvokeContext &, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 1>, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace<mp::test::FooEnum, (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:298:83)>>'
[02:41:41.544]   297 |     MaybeReadField(std::integral_constant<bool, Accessor::in>(), TypeList<ArgType>(), invoke_context,
[02:41:41.544]       |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   298 |         Make<StructField, Accessor>(params), ReadDestEmplace(TypeList<ArgType>(), [&](auto&&... args) -> auto& {
[02:41:41.544]       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   299 |             param.emplace(std::forward<decltype(args)>(args)...);
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   300 |             return *param;
[02:41:41.544]       |             ~~~~~~~~~~~~~~
[02:41:41.544]   301 |         }));
[02:41:41.544]       |         ~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:269:5: note: Calling 'ReadField<mp::test::FooEnum, mp::InvokeContext &, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 1>, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace<mp::test::FooEnum, (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:298:83)>>'
[02:41:41.544]   269 |     ReadField(std::forward<Args>(args)...);
[02:41:41.544]       |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:172:12: note: Calling 'CustomReadField<mp::test::FooEnum, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 1>, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace<mp::test::FooEnum, (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:298:83)>>'
[02:41:41.544]   172 |     return CustomReadField(TypeList<RemoveCvRef<LocalTypes>...>(), Priority<2>(), std::forward<Args>(args)...);
[02:41:41.544]       |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/type-number.h:55:55: note: Calling 'StructField::get'
[02:41:41.544]    55 |     return read_dest.construct(static_cast<LocalType>(input.get()));
[02:41:41.544]       |                                                       ^~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:40:41: note: Calling 'Arg::get'
[02:41:41.544]    40 |     decltype(auto) get() const { return Accessor::get(this->m_struct); }
[02:41:41.544]       |                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.proxy.h:174:82: note: Calling 'Reader::getArg'
[02:41:41.544]   174 |     template<typename S> static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); }
[02:41:41.544]       |                                                                                  ^~~~~~~~~~
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.h:5998:10: note: Calling 'StructReader::getDataField'
[02:41:41.544]  5998 |   return _reader.getDataField< ::int32_t>(
[02:41:41.544]       |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]  5999 |       ::capnp::bounded<0>() * ::capnp::ELEMENTS);
[02:41:41.544]       |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /usr/include/capnp/layout.h:1099:7: note: Assuming the condition is false
[02:41:41.544]  1099 |   if ((offset + ONE * ELEMENTS) * capnp::bitsPerElement<T>() <= dataSize) {
[02:41:41.544]       |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /usr/include/capnp/layout.h:1099:3: note: Taking false branch
[02:41:41.544]  1099 |   if ((offset + ONE * ELEMENTS) * capnp::bitsPerElement<T>() <= dataSize) {
[02:41:41.544]       |   ^
[02:41:41.544] /usr/include/capnp/layout.h:1102:5: note: Returning zero
[02:41:41.544]  1102 |     return static_cast<T>(0);
[02:41:41.544]       |     ^~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.h:5998:10: note: Returning from 'StructReader::getDataField'
[02:41:41.544]  5998 |   return _reader.getDataField< ::int32_t>(
[02:41:41.544]       |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]  5999 |       ::capnp::bounded<0>() * ::capnp::ELEMENTS);
[02:41:41.544]       |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.h:5998:3: note: Returning zero
[02:41:41.544]  5998 |   return _reader.getDataField< ::int32_t>(
[02:41:41.544]       |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]  5999 |       ::capnp::bounded<0>() * ::capnp::ELEMENTS);
[02:41:41.544]       |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.proxy.h:174:82: note: Returning from 'Reader::getArg'
[02:41:41.544]   174 |     template<typename S> static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); }
[02:41:41.544]       |                                                                                  ^~~~~~~~~~
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.proxy.h:174:75: note: Returning zero
[02:41:41.544]   174 |     template<typename S> static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); }
[02:41:41.544]       |                                                                           ^~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:40:41: note: Returning from 'Arg::get'
[02:41:41.544]    40 |     decltype(auto) get() const { return Accessor::get(this->m_struct); }
[02:41:41.544]       |                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:40:34: note: Returning zero
[02:41:41.544]    40 |     decltype(auto) get() const { return Accessor::get(this->m_struct); }
[02:41:41.544]       |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/type-number.h:55:55: note: Returning from 'StructField::get'
[02:41:41.544]    55 |     return read_dest.construct(static_cast<LocalType>(input.get()));
[02:41:41.544]       |                                                       ^~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/type-number.h:55:32: note: The value '0' provided to the cast expression is not in the valid range of values for 'FooEnum'
[02:41:41.544]    55 |     return read_dest.construct(static_cast<LocalType>(input.get()));
[02:41:41.544]       |                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /usr/include/kj/async-inl.h:609:37: warning: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult]
[02:41:41.544]   609 |       return *(void**)(*(char**)obj + voff);
[02:41:41.544]       |                                     ^
[02:41:41.544] /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mp/test/foo.capnp.proxy-server.c++:68:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::ExtendedCallback>, capnp::CallContext<mp::test::messages::ExtendedCallback::CallExtendedParams, mp::test::messages::ExtendedCallback::CallExtendedResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerField<1, mp::Accessor<mp::foo_fields::Arg, 1>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>>'
[02:41:41.544]    68 |     return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(MakeServerField<1, Accessor<foo_fields::Arg, FIELD_IN>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall()))));
[02:41:41.544]       |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:16: note: Calling 'ReplaceVoid<(lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:28), (lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:705:13)>'
[02:41:41.544]   704 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
[02:41:41.544]       |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   705 |             [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:665:19: note: 'is_same_v' is false
[02:41:41.544]   665 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
[02:41:41.544]       |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:665:5: note: Taking false branch
[02:41:41.544]   665 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
[02:41:41.544]       |     ^
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:669:16: note: Calling 'operator()'
[02:41:41.544]   669 |         return fn();
[02:41:41.544]       |                ^~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:704:43: note: Calling 'ServerField::invoke'
[02:41:41.544]   704 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
[02:41:41.544]       |                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:539:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::ExtendedCallback>, capnp::CallContext<mp::test::messages::ExtendedCallback::CallExtendedParams, mp::test::messages::ExtendedCallback::CallExtendedResults>>, mp::ServerField<1, mp::Accessor<mp::foo_fields::Arg, 1>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>, mp::TypeList<int>>'
[02:41:41.544]   539 |         return PassField<Accessor>(Priority<2>(),
[02:41:41.544]       |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   540 |             typename Split<argc, ArgTypes>::First(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   541 |             server_context,
[02:41:41.544]       |             ~~~~~~~~~~~~~~~
[02:41:41.544]   542 |             this->parent(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~
[02:41:41.544]   543 |             typename Split<argc, ArgTypes>::Second(),
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   544 |             std::forward<Args>(args)...);
[02:41:41.544]       |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /ci_container_base/src/ipc/libmultiprocess/include/mp/type-context.h:152:12: note: Calling 'CapabilityServerSet::getLocalServer'
[02:41:41.544]   152 |     return server.m_context.connection->m_threads.getLocalServer(thread_client)
[02:41:41.544]       |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /usr/include/capnp/capability.h:1274:10: note: Calling 'Promise::then'
[02:41:41.544]  1274 |   return getLocalServerInternal(client)
[02:41:41.544]       |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]  1275 |       .then([](void* server) -> kj::Maybe<typename T::Server&> {
[02:41:41.544]       |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]  1276 |     if (server == nullptr) {
[02:41:41.544]       |     ~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]  1277 |       return nullptr;
[02:41:41.544]       |       ~~~~~~~~~~~~~~~
[02:41:41.544]  1278 |     } else {
[02:41:41.544]       |     ~~~~~~~~
[02:41:41.544]  1279 |       return *reinterpret_cast<typename T::Server*>(server);
[02:41:41.544]       |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]  1280 |     }
[02:41:41.544]       |     ~
[02:41:41.544]  1281 |   });
[02:41:41.544]       |   ~~
[02:41:41.544] /usr/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply'
[02:41:41.544]  1295 |   void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func);
[02:41:41.544]       |                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /usr/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply'
[02:41:41.544]   677 |     return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>(
[02:41:41.544]       |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544]   678 |         &Decay<Func>::operator()).apply(&func);
[02:41:41.544]       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[02:41:41.544] /usr/include/kj/async-inl.h:606:9: note: Assuming the condition is true
[02:41:41.544]   606 |     if (voff & 1) {
[02:41:41.544]       |         ^~~~~~~~
[02:41:41.544] /usr/include/kj/async-inl.h:606:5: note: Taking true branch
[02:41:41.544]   606 |     if (voff & 1) {
[02:41:41.544]       |     ^
[02:41:41.544] /usr/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value
[02:41:41.544]   609 |       return *(void**)(*(char**)obj + voff);
[02:41:41.544]       |                        ~~~~~~~~~~~~ ^
[02:41:41.545] 3 warnings generated.

@ryanofsky
Copy link
Contributor

ryanofsky commented Jun 19, 2025

re: #31802 (comment)

Note that the Tidy job here is passing, even though it's generating output (which also seems like a bug?).

Thanks, will fix. Looks like bitcoin-core/libmultiprocess#172 did not resolve all the warnings.

Just taking a quick look:

  • Need to check if this is a real bug:
    • /usr/include/kj/async-inl.h:609:37: warning: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult]
  • Pretty sure this isn't a bug. Changing [&] to [] should fix it:
    • /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:134:5: warning: Address of stack memory associated with temporary object of type '(lambda at /ci_container_base/src/ipc/libmultiprocess/include/mp/proxy-types.h:134:51)' is still referred to by a temporary object on the stack upon returning to the caller. This will be a dangling reference [clang-analyzer-core.StackAddressEscape]
  • This might be a test bug:
    • /ci_container_base/src/ipc/libmultiprocess/include/mp/type-number.h:55:32: warning: The value '0' provided to the cast expression is not in the valid range of values for 'FooEnum' [clang-analyzer-optin.core.EnumCastOutOfRange]

Update: These are all addressed in bitcoin-core/libmultiprocess#184

@Sjors Sjors marked this pull request as draft June 19, 2025 14:07
ryanofsky added a commit to ryanofsky/libmultiprocess that referenced this pull request Jun 20, 2025
This error should not be a real problem because code is taking an invalid
reference to an empty object that has no state and could never be used. But
taking the reference could technically be undefined behavior.

Reported by fanquake <[email protected]>
bitcoin/bitcoin#31802 (comment)
https://cirrus-ci.com/task/6552721135763456
https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log

include/mp/proxy-types.h:134:5: error: Address of stack memory associated with temporary object of type '(lambda at include/mp/proxy-types.h:134:51)' is still referred to by a temporary object on the stack upon returning to the caller.  This will be a dangling reference [clang-analyzer-core.StackAddressEscape,-warnings-as-errors]
  134 |     return ReadDestEmplace{TypeList<LocalType>(), [&](auto&&... args) -> decltype(auto) {
      |     ^
build/test/mp/test/foo.capnp.proxy-server.c++:51:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::PassCustomParams, mp::test::messages::FooInterface::PassCustomResults>, mp::ServerField<1, mp::Accessor<mp::foo_fields::Arg, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 18>, mp::ServerCall>>>'
   51 |     return serverInvoke(*this, call_context, MakeServerField<1, Accessor<foo_fields::Arg, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT | FIELD_BOXED>>(ServerCall())));
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>'
  739 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  740 |             [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:700:19: note: 'is_same_v' is true
  700 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:700:5: note: Taking true branch
  700 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
      |     ^
include/mp/proxy-types.h:701:9: note: Calling 'operator()'
  701 |         fn();
      |         ^~~~
include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke'
  739 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
      |                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Arg, 17>, mp::test::FooCustom, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::PassCustomParams, mp::test::messages::FooInterface::PassCustomResults>>, const mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 18>, mp::ServerCall> &, mp::TypeList<>>'
  563 |         return PassField<Accessor>(Priority<2>(),
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  564 |             typename Split<argc, ArgTypes>::First(),
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  565 |             server_context,
      |             ~~~~~~~~~~~~~~~
  566 |             this->parent(),
      |             ~~~~~~~~~~~~~~~
  567 |             typename Split<argc, ArgTypes>::Second(),
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  568 |             std::forward<Args>(args)...);
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:304:5: note: Calling 'MaybeReadField<mp::TypeList<mp::test::FooCustom>, mp::InvokeContext &, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 17>, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace<mp::test::FooCustom, (lambda at include/mp/proxy-types.h:305:83)>>'
  304 |     MaybeReadField(std::integral_constant<bool, Accessor::in>(), TypeList<ArgType>(), invoke_context,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  305 |         Make<StructField, Accessor>(params), ReadDestEmplace(TypeList<ArgType>(), [&](auto&&... args) -> auto& {
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  306 |             param.emplace(std::forward<decltype(args)>(args)...);
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  307 |             return *param;
      |             ~~~~~~~~~~~~~~
  308 |         }));
      |         ~~~
include/mp/proxy-types.h:276:5: note: Calling 'ReadField<mp::test::FooCustom, mp::InvokeContext &, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 17>, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace<mp::test::FooCustom, (lambda at include/mp/proxy-types.h:305:83)>>'
  276 |     ReadField(std::forward<Args>(args)...);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:175:12: note: Calling 'CustomReadField<mp::StructField<mp::Accessor<mp::foo_fields::Arg, 17>, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace<mp::test::FooCustom, (lambda at include/mp/proxy-types.h:305:83)>>'
  175 |     return CustomReadField(TypeList<RemoveCvRef<LocalTypes>...>(), Priority<2>(), std::forward<Args>(args)...);
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test/mp/test/foo-types.h:51:12: note: Calling 'ReadDestEmplace::update'
   51 |     return read_dest.update([&](FooCustom& value) {
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   52 |         value.v1 = ReadField(TypeList<std::string>(), invoke_context, mp::Make<mp::ValueField>(custom.getV1()), ReadDestTemp<std::string>());
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   53 |         value.v2 = custom.getV2();
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~
   54 |     });
      |     ~~
include/mp/proxy-types.h:112:23: note: 'is_const_v' is false
  112 |         if constexpr (std::is_const_v<std::remove_reference_t<std::invoke_result_t<EmplaceFn>>>) {
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:112:9: note: Taking false branch
  112 |         if constexpr (std::is_const_v<std::remove_reference_t<std::invoke_result_t<EmplaceFn>>>) {
      |         ^
include/mp/proxy-types.h:122:13: note: Calling 'operator()'
  122 |             update_fn(temp);
      |             ^~~~~~~~~~~~~~~
test/mp/test/foo-types.h:52:113: note: Calling 'ReadDestTemp<std::basic_string<char>>'
   52 |         value.v1 = ReadField(TypeList<std::string>(), invoke_context, mp::Make<mp::ValueField>(custom.getV1()), ReadDestTemp<std::string>());
      |                                                                                                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:134:5: note: Address of stack memory associated with temporary object of type '(lambda at include/mp/proxy-types.h:134:51)' is still referred to by a temporary object on the stack upon returning to the caller.  This will be a dangling reference
  134 |     return ReadDestEmplace{TypeList<LocalType>(), [&](auto&&... args) -> decltype(auto) {
      |     ^                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  135 |         return LocalType{std::forward<decltype(args)>(args)...};
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  136 |     }};
      |     ~
ryanofsky added a commit to ryanofsky/libmultiprocess that referenced this pull request Jun 20, 2025
Reported by fanquake <[email protected]>
bitcoin/bitcoin#31802 (comment)
https://cirrus-ci.com/task/6552721135763456
https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log

include/mp/type-number.h:55:32: error: The value '0' provided to the cast expression is not in the valid range of values for 'FooEnum' [clang-analyzer-optin.core.EnumCastOutOfRange,-warnings-as-errors]
   55 |     return read_dest.construct(static_cast<LocalType>(input.get()));
      |                                ^
test/mp/test/foo.h:26:12: note: enum declared here
   26 | enum class FooEnum : uint8_t { ONE = 1, TWO = 2, };
      | ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
build/test/mp/test/foo.capnp.proxy-server.c++:63:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::PassEnumParams, mp::test::messages::FooInterface::PassEnumResults>, mp::ServerField<1, mp::Accessor<mp::foo_fields::Arg, 1>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>'
   63 |     return serverInvoke(*this, call_context, MakeServerField<1, Accessor<foo_fields::Arg, FIELD_IN>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall())));
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>'
  739 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  740 |             [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:700:19: note: 'is_same_v' is true
  700 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:700:5: note: Taking true branch
  700 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
      |     ^
include/mp/proxy-types.h:701:9: note: Calling 'operator()'
  701 |         fn();
      |         ^~~~
include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke'
  739 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
      |                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Arg, 1>, mp::test::FooEnum, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::PassEnumParams, mp::test::messages::FooInterface::PassEnumResults>>, const mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall> &, mp::TypeList<>>'
  563 |         return PassField<Accessor>(Priority<2>(),
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  564 |             typename Split<argc, ArgTypes>::First(),
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  565 |             server_context,
      |             ~~~~~~~~~~~~~~~
  566 |             this->parent(),
      |             ~~~~~~~~~~~~~~~
  567 |             typename Split<argc, ArgTypes>::Second(),
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  568 |             std::forward<Args>(args)...);
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:304:5: note: Calling 'MaybeReadField<mp::TypeList<mp::test::FooEnum>, mp::InvokeContext &, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 1>, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace<mp::test::FooEnum, (lambda at include/mp/proxy-types.h:305:83)>>'
  304 |     MaybeReadField(std::integral_constant<bool, Accessor::in>(), TypeList<ArgType>(), invoke_context,
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  305 |         Make<StructField, Accessor>(params), ReadDestEmplace(TypeList<ArgType>(), [&](auto&&... args) -> auto& {
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  306 |             param.emplace(std::forward<decltype(args)>(args)...);
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  307 |             return *param;
      |             ~~~~~~~~~~~~~~
  308 |         }));
      |         ~~~
include/mp/proxy-types.h:276:5: note: Calling 'ReadField<mp::test::FooEnum, mp::InvokeContext &, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 1>, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace<mp::test::FooEnum, (lambda at include/mp/proxy-types.h:305:83)>>'
  276 |     ReadField(std::forward<Args>(args)...);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:175:12: note: Calling 'CustomReadField<mp::test::FooEnum, mp::StructField<mp::Accessor<mp::foo_fields::Arg, 1>, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace<mp::test::FooEnum, (lambda at include/mp/proxy-types.h:305:83)>>'
  175 |     return CustomReadField(TypeList<RemoveCvRef<LocalTypes>...>(), Priority<2>(), std::forward<Args>(args)...);
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/type-number.h:55:55: note: Calling 'StructField::get'
   55 |     return read_dest.construct(static_cast<LocalType>(input.get()));
      |                                                       ^~~~~~~~~~~
include/mp/proxy-types.h:40:41: note: Calling 'Arg::get'
   40 |     decltype(auto) get() const { return Accessor::get(this->m_struct); }
      |                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
build/test/mp/test/foo.capnp.proxy.h:198:82: note: Calling 'Reader::getArg'
  198 |     template<typename S> static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); }
      |                                                                                  ^~~~~~~~~~
build/test/mp/test/foo.capnp.h:7042:10: note: Calling 'StructReader::getDataField'
 7042 |   return _reader.getDataField< ::int32_t>(
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 7043 |       ::capnp::bounded<0>() * ::capnp::ELEMENTS);
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/layout.h:1099:7: note: Assuming the condition is false
 1099 |   if ((offset + ONE * ELEMENTS) * capnp::bitsPerElement<T>() <= dataSize) {
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/layout.h:1099:3: note: Taking false branch
 1099 |   if ((offset + ONE * ELEMENTS) * capnp::bitsPerElement<T>() <= dataSize) {
      |   ^
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/layout.h:1102:5: note: Returning zero
 1102 |     return static_cast<T>(0);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~
build/test/mp/test/foo.capnp.h:7042:10: note: Returning from 'StructReader::getDataField'
 7042 |   return _reader.getDataField< ::int32_t>(
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 7043 |       ::capnp::bounded<0>() * ::capnp::ELEMENTS);
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
build/test/mp/test/foo.capnp.h:7042:3: note: Returning zero
 7042 |   return _reader.getDataField< ::int32_t>(
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 7043 |       ::capnp::bounded<0>() * ::capnp::ELEMENTS);
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
build/test/mp/test/foo.capnp.proxy.h:198:82: note: Returning from 'Reader::getArg'
  198 |     template<typename S> static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); }
      |                                                                                  ^~~~~~~~~~
build/test/mp/test/foo.capnp.proxy.h:198:75: note: Returning zero
  198 |     template<typename S> static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); }
      |                                                                           ^~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:40:41: note: Returning from 'Arg::get'
   40 |     decltype(auto) get() const { return Accessor::get(this->m_struct); }
      |                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:40:34: note: Returning zero
   40 |     decltype(auto) get() const { return Accessor::get(this->m_struct); }
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/type-number.h:55:55: note: Returning from 'StructField::get'
   55 |     return read_dest.construct(static_cast<LocalType>(input.get()));
      |                                                       ^~~~~~~~~~~
include/mp/type-number.h:55:32: note: The value '0' provided to the cast expression is not in the valid range of values for 'FooEnum'
   55 |     return read_dest.construct(static_cast<LocalType>(input.get()));
      |                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ryanofsky added a commit to ryanofsky/libmultiprocess that referenced this pull request Jun 20, 2025
Reported by fanquake <[email protected]>
bitcoin/bitcoin#31802 (comment)
https://cirrus-ci.com/task/6552721135763456
https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log

Error is spurious and comes from kj/async-inl.h and it seems to be shown
because clang-tidy considers this error to come from "main file" of the
translation unit (see https://clang.llvm.org/extra/clang-tidy/,
https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418) even
though this is not the case.

Even though the header is included via -isystem and clang-tidy
--dump-config shows "SystemHeaders: false" the error is still shown.

I also tried to suppress the error by editing .clang-tidy to include:

  HeaderFilterRegex: '.*'
  ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$'

and many variations but the error is always shown, I assume because
ExcludeHeaderFilterRegex does not ignore the isInMainFile condition
(https://github.com/llvm/llvm-project/pull/91400/files).

The actual warning is a false positive that comes from ABI-specific code in
Cap'n Proto that gets the starting function address (that can be passed to
addr2line) from a lambda or function object. This code calls a helper to get
the starting function address from a pointer-to-member-function, which in this
case is the the operator() member function. That code handles pointers to
virtual member functions, so it checks if the pointer is virtual by testing its
low-order bit, and if set, assumes the first bytes of the object are a vtable
pointer, and does pointer arithmetic with the vtable address. Clang-tidy
complains about this because it does not know the vtable address is valid,
assuming incorrectly it is a "garbage value".

A possible way to address this might be to submit a patch to Cap'n Proto that
skips this code when __clang_analyzer__ is set. Maybe it could also be reported
as an analyzer bug https://clang-analyzer.llvm.org/
https://clang.llvm.org/docs/analyzer/user-docs/FAQ.html#excluding-code-from-analysis

Error output is:

/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors]
  609 |       return *(void**)(*(char**)obj + voff);
      |                                     ^
build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>'
   93 |     return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall())));
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>'
  739 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  740 |             [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:700:19: note: 'is_same_v' is false
  700 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:700:5: note: Taking false branch
  700 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
      |     ^
include/mp/proxy-types.h:704:16: note: Calling 'operator()'
  704 |         return fn();
      |                ^~~~
include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke'
  739 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
      |                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>'
  563 |         return PassField<Accessor>(Priority<2>(),
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  564 |             typename Split<argc, ArgTypes>::First(),
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  565 |             server_context,
      |             ~~~~~~~~~~~~~~~
  566 |             this->parent(),
      |             ~~~~~~~~~~~~~~~
  567 |             typename Split<argc, ArgTypes>::Second(),
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  568 |             std::forward<Args>(args)...);
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer'
  151 |     return server.m_context.connection->m_threads.getLocalServer(thread_client)
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then'
 1274 |   return getLocalServerInternal(client)
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1275 |       .then([](void* server) -> kj::Maybe<typename T::Server&> {
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1276 |     if (server == nullptr) {
      |     ~~~~~~~~~~~~~~~~~~~~~~~~
 1277 |       return nullptr;
      |       ~~~~~~~~~~~~~~~
 1278 |     } else {
      |     ~~~~~~~~
 1279 |       return *reinterpret_cast<typename T::Server*>(server);
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1280 |     }
      |     ~
 1281 |   });
      |   ~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply'
 1295 |   void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func);
      |                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply'
  677 |     return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>(
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  678 |         &Decay<Func>::operator()).apply(&func);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true
  606 |     if (voff & 1) {
      |         ^~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch
  606 |     if (voff & 1) {
      |     ^
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value
  609 |       return *(void**)(*(char**)obj + voff);
      |                        ~~~~~~~~~~~~ ^
ryanofsky added a commit to ryanofsky/libmultiprocess that referenced this pull request Jun 20, 2025
Reported by fanquake <[email protected]>
bitcoin/bitcoin#31802 (comment)
https://cirrus-ci.com/task/6552721135763456
https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log

Error is spurious and comes from kj/async-inl.h and it seems to be shown
because clang-tidy considers this error to come from "main file" of the
translation unit (see https://clang.llvm.org/extra/clang-tidy/,
https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418) even
though this is not the case.

Even though the header is included via -isystem and clang-tidy
--dump-config shows "SystemHeaders: false" the error is still shown.

I also tried to suppress the error by editing .clang-tidy to include:

  HeaderFilterRegex: '.*'
  ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$'

and many variations but the error is always shown, I assume because
ExcludeHeaderFilterRegex does not ignore the isInMainFile condition
(https://github.com/llvm/llvm-project/pull/91400/files).

Adding NOLINT to the getLocalServer() line in types-context.h at the boundary
between libmultiprocess and Cap'n Proto code also does not suppress the error.
It does suppress clang-tidy "note:" lines below the NOLINT point in the call
stack, making the error messages shorter, but the only way of suppressing the
error completely seems to be either adding NOLINT inside the Cap'n Proto
header, which requires a patch, or adding it to all top-level callers of the
getLocalServer() function in .cpp files, which seems impractical and overbroad,
and I didn't attempt.

The actual warning is a false positive that comes from ABI-specific code in
Cap'n Proto that gets the starting function address (that can be passed to
addr2line) from a lambda or function object. This code calls a helper to get
the starting function address from a pointer-to-member-function, which in this
case is the the operator() member function. That code handles pointers to
virtual member functions, so it checks if the pointer is virtual by testing its
low-order bit, and if set, assumes the first bytes of the object are a vtable
pointer, and does pointer arithmetic with the vtable address. Clang-tidy
complains about this because it does not know the vtable address is valid,
assuming incorrectly it is a "garbage value".

I wrote patch adding NOLINT to Cap'n Proto in
capnproto/capnproto#2334 which could be good long-term
fix for this issue if clang-tidy does not provide a better way of suppressing
the error.

Complete error output is:

/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors]
  609 |       return *(void**)(*(char**)obj + voff);
      |                                     ^
build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>'
   93 |     return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall())));
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>'
  739 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  740 |             [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:700:19: note: 'is_same_v' is false
  700 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:700:5: note: Taking false branch
  700 |     if constexpr (std::is_same_v<decltype(fn()), void>) {
      |     ^
include/mp/proxy-types.h:704:16: note: Calling 'operator()'
  704 |         return fn();
      |                ^~~~
include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke'
  739 |         return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
      |                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>'
  563 |         return PassField<Accessor>(Priority<2>(),
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  564 |             typename Split<argc, ArgTypes>::First(),
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  565 |             server_context,
      |             ~~~~~~~~~~~~~~~
  566 |             this->parent(),
      |             ~~~~~~~~~~~~~~~
  567 |             typename Split<argc, ArgTypes>::Second(),
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  568 |             std::forward<Args>(args)...);
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer'
  151 |     return server.m_context.connection->m_threads.getLocalServer(thread_client)
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then'
 1274 |   return getLocalServerInternal(client)
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1275 |       .then([](void* server) -> kj::Maybe<typename T::Server&> {
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1276 |     if (server == nullptr) {
      |     ~~~~~~~~~~~~~~~~~~~~~~~~
 1277 |       return nullptr;
      |       ~~~~~~~~~~~~~~~
 1278 |     } else {
      |     ~~~~~~~~
 1279 |       return *reinterpret_cast<typename T::Server*>(server);
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 1280 |     }
      |     ~
 1281 |   });
      |   ~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply'
 1295 |   void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func);
      |                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply'
  677 |     return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>(
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  678 |         &Decay<Func>::operator()).apply(&func);
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true
  606 |     if (voff & 1) {
      |         ^~~~~~~~
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch
  606 |     if (voff & 1) {
      |     ^
/nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value
  609 |       return *(void**)(*(char**)obj + voff);
      |                        ~~~~~~~~~~~~ ^
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.