Skip to content
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

refactor: Remove confusing P1008R1 violation in ATMPArgs #24404

Merged
merged 1 commit into from Mar 10, 2022

Conversation

maflcko
Copy link
Member

@maflcko maflcko commented Feb 21, 2022

The = delete doesn't achieve the stated goal and it is also redundant, since it is not possible to default construct the ATMPArgs type.

This can be tested with:

diff --git a/src/validation.cpp b/src/validation.cpp
index 2813b62462..1c939c0b8a 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -519,6 +519,7 @@ public:
         /** Parameters for child-with-unconfirmed-parents package validation. */
         static ATMPArgs PackageChildWithParents(const CChainParams& chainparams, int64_t accept_time,
                                                 std::vector<COutPoint>& coins_to_uncache) {
+            ATMPArgs{};
             return ATMPArgs{/* m_chainparams */ chainparams,
                             /* m_accept_time */ accept_time,
                             /* m_bypass_limits */ false,

Which fails on current master and this pull with the following error:

validation.cpp:525:22: error: reference member of type 'const CChainParams &' uninitialized
            ATMPArgs{};
                    ~^
validation.cpp:470:29: note: uninitialized reference member is here
        const CChainParams& m_chainparams;
                            ^
1 error generated.

Further reading (optional):

@maflcko
Copy link
Member Author

maflcko commented Feb 21, 2022

I believe an alternative (and larger) fix would be to provide an explicit constructor that takes all members as arguments. Happy to switch to that, but for now I prefer the smaller patch.

@glozow
Copy link
Member

glozow commented Feb 21, 2022

ACK facbbec

image

I believe an alternative (and larger) fix would be to provide an explicit constructor that takes all members as arguments.

I prefer this as well, for other places where there's no uninitialized reference to take the bullet for us.

@glozow
Copy link
Member

glozow commented Feb 21, 2022

CI error is the assert_fee_amount thing, unrelated

@DrahtBot
Copy link
Contributor

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #24169 (build: Add --enable-c++20 option by MarcoFalke)
  • #24152 (policy / validation: CPFP fee bumping within packages by glozow)

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.

@JeremyRubin
Copy link
Contributor

NACK.

The point of a deleted constructor is to make the type stable to future modifications that might e.g. turn m_chainparams into p_chainparams. It's essentially an assertion.

@maflcko
Copy link
Member Author

maflcko commented Feb 22, 2022

It's essentially an assertion.

No, it is not. Even if no member was const, aggregate initialization is possible. On current master and this branch. This patch isn't changing what it possible.

@maflcko
Copy link
Member Author

maflcko commented Feb 22, 2022

If you don't believe me, nor the paper P1008R1. You can try it yourself with a C++11 compiler of you choice. The following compiles fine with or without the delete.

struct ATMPArgs {
  int a;
  bool b;
  ATMPArgs() = delete;
};
int main() { (void)ATMPArgs{}; } // compiles

@glozow
Copy link
Member

glozow commented Feb 22, 2022

The point of a deleted constructor is to make the type stable to future modifications that might e.g. turn m_chainparams into p_chainparams. It's essentially an assertion.

No that was not the point when writing this code. The intention was to prevent callers from specifying their own ATMPArgs, e.g. using ATMPArgs args(); args.m_bypass_limits = true;. I wanted them to only be able to initialize arguments using the static constructors (ATMPArgs::SingleAccept, ATMPArgs::TestPackageAccept).

But as shown in the paper (and any compiler), deleting the default constructor doesn't prevent something like this. What does prevent this is that there's a reference in the struct so you can't create a default version of it. So deletion of the default constructor does nothing. This PR is essentially removing code that doesn't do anything and misleading/confusing documentation.

@hebasto
Copy link
Member

hebasto commented Feb 22, 2022

This example looks scary:

struct ATMPArgs {
  int a = 42;
  bool b;
  ATMPArgs() = delete;
  ATMPArgs(int) = delete;
};

int main()
{
    constexpr ATMPArgs atm{0};
    static_assert(atm.a == 0);
}

@achow101
Copy link
Member

I think it's also important to point out that P1008R1 was accepted into C++20 so keeping the delete would also remove the aggregate initialization that the static functions make use of to construct ATMPArgs. However, I think that it is not immediately obvious that the default constructor is deleted by having a reference as a member. I think it would still be useful to comment out the delete and include some additional comments as to why that is.

@JeremyRubin
Copy link
Contributor

The workaround in P1008R1 is to add an explicit modifier to the deletions? Why is that not a workable option here?

@maflcko
Copy link
Member Author

maflcko commented Feb 23, 2022

Why is that not a workable option here?

It wouldn't compile:

validation.cpp:522:20: error: no matching constructor for initialization of '(anonymous namespace)::MemPoolAccept::ATMPArgs'
            return ATMPArgs{/* m_chainparams */ chainparams,
                   ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
validation.cpp:469:12: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 7 were provided
    struct ATMPArgs {
           ^
validation.cpp:469:12: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 7 were provided
validation.cpp:533:18: note: candidate constructor not viable: requires 0 arguments, but 7 were provided
        explicit ATMPArgs() = delete;
                 ^
3 errors generated.

with diff on master:

diff --git a/src/validation.cpp b/src/validation.cpp
index 035b5783c3..c714bfe575 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -530,7 +530,7 @@ public:
         }
         // No default ctor to avoid exposing details to clients and allowing the possibility of
         // mixing up the order of the arguments. Use static functions above instead.
-        ATMPArgs() = delete;
+        explicit ATMPArgs() = delete;
     };
 
     // Single transaction acceptance

@maflcko
Copy link
Member Author

maflcko commented Feb 23, 2022

I've pushed the alternative fix I mentioned in comment 2 #24404 (comment)

@maflcko
Copy link
Member Author

maflcko commented Feb 23, 2022

Suggested diff for testing:

diff --git a/src/validation.cpp b/src/validation.cpp
index 3f64fcc067..900f85fc54 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -707,6 +707,14 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
     if (tx.IsCoinBase())
         return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
 
+            ATMPArgs{       args.m_chainparams,
+                            args.m_accept_time,
+                            args.m_bypass_limits,
+                            args.m_coins_to_uncache,
+                            args.m_test_accept,
+                            args.m_allow_bip125_replacement,
+                            args.m_package_submission
+            };
     // Rather not work on nonstandard transactions (unless -testnet/-regtest)
     std::string reason;
     if (fRequireStandard && !IsStandardTx(tx, reason))

@JeremyRubin
Copy link
Contributor

should you not also retain the explicit delete with a new explicit ctor?

@maflcko
Copy link
Member Author

maflcko commented Feb 23, 2022

Happy to do so if there is any benefit?

@bitcoin bitcoin deleted a comment from Pragmatic2021 Feb 25, 2022
@maflcko
Copy link
Member Author

maflcko commented Mar 2, 2022

Tend toward leaving as-is. explicit isn't used in current master and it isn't used in this pull. If there is any reason to use it, it might be best to do it in a separate pull request (as a follow-up or conflicting pull), with proper motivation.

@maflcko maflcko requested a review from glozow March 10, 2022 12:31
Copy link
Member

@glozow glozow 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 faa1aec

@achow101
Copy link
Member

ACK faa1aec

@fanquake fanquake merged commit 6c37eae into bitcoin:master Mar 10, 2022
@maflcko maflcko deleted the 2202-cpp🥲 branch March 10, 2022 14:24
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Mar 11, 2022
…ATMPArgs

faa1aec Remove confusing P1008R1 violation in ATMPArgs (MarcoFalke)

Pull request description:

  The `= delete` doesn't achieve the stated goal and it is also redundant, since it is not possible to default construct the `ATMPArgs` type.

  This can be tested with:

  ```diff
  diff --git a/src/validation.cpp b/src/validation.cpp
  index 2813b62462..1c939c0b8a 100644
  --- a/src/validation.cpp
  +++ b/src/validation.cpp
  @@ -519,6 +519,7 @@ public:
           /** Parameters for child-with-unconfirmed-parents package validation. */
           static ATMPArgs PackageChildWithParents(const CChainParams& chainparams, int64_t accept_time,
                                                   std::vector<COutPoint>& coins_to_uncache) {
  +            ATMPArgs{};
               return ATMPArgs{/* m_chainparams */ chainparams,
                               /* m_accept_time */ accept_time,
                               /* m_bypass_limits */ false,
  ```

  Which fails on current master *and* this pull with the following error:

  ```
  validation.cpp:525:22: error: reference member of type 'const CChainParams &' uninitialized
              ATMPArgs{};
                      ~^
  validation.cpp:470:29: note: uninitialized reference member is here
          const CChainParams& m_chainparams;
                              ^
  1 error generated.
  ```

  Further reading (optional):
  * http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p1008r1.pdf

ACKs for top commit:
  achow101:
    ACK faa1aec
  glozow:
    code review ACK faa1aec

Tree-SHA512: 16db2c9959a1996eafbfa533dc4d1483761b9d28295aed5a82b86abd7268da37c51c59ddc67c205165ecb415dbe637b12a0e1b3234d50ab0b3b79de66d7bd73e
@bitcoin bitcoin locked and limited conversation to collaborators Mar 10, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants