-
Notifications
You must be signed in to change notification settings - Fork 977
PtmfHelper: Use NOLINT to disable UndefinedBinaryOperatorResult error #2334
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
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Disable clang-analyzer-core.UndefinedBinaryOperatorResult error in PtmfHelper to appease clang-tidy. This project does not use clang-tidy, but downstream projects may. Normally errors from Cap'n Proto headers will not show up in downstream projects as long as they are included via -isystem, but in some cases it is impossible to hide errors even from system headers and other excluded headers if clang-tidy decides the errors are associated with the "main file" of the translation unit. This seems to be a longstanding issue that's discussed in https://reviews.llvm.org/D26418. Adding NOLINT here is helpful to downstream projects that don't currently have a way of suppressing this error without turning it off more globally, and could be helpful to Cap'n Proto if it uses clang-tidy or clang-analyzer in the future. This particular tidy error is a false positive from clang-analzyer (https://clang-analyzer.llvm.org/) warning that the vtable pointer `*(char**)obj` is a garbage value rather than a valid memory address. The analyzer has no way of knowing it could be a valid address since it's only valid due to assumptions about the ABI. The complete error message from a downstream project looks like: /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 <fanquake@gmail.com> 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); | ~~~~~~~~~~~~ ^
Thanks! |
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 23, 2025
Reported by fanquake <fanquake@gmail.com> 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 should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer 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". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly 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). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override 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. 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); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 25, 2025
Reported by fanquake <fanquake@gmail.com> 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 should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer 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". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly 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). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override 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. 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); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 25, 2025
Reported by fanquake <fanquake@gmail.com> 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 should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer 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". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly 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). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override 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. 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); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 25, 2025
Reported by fanquake <fanquake@gmail.com> 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 should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer 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". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly 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). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override 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. 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); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 25, 2025
Reported by fanquake <fanquake@gmail.com> 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 should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer 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". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly 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). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override 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. 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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Disable
clang-analyzer-core.UndefinedBinaryOperatorResult
error inPtmfHelper
using a NOLINT comment to appease clang-tidy.A complete description of the error and rationale for the change is in the commit message.