build: fix clang -Wshorten-64-to-32 warnings #1801

pull csjones wants to merge 4 commits into bitcoin-core:master from 21-DOT-DEV:CLANG-WARN-WSHORTEN-64-TO-32 changing 3 files +12 −10
  1. csjones commented at 12:58 AM on January 15, 2026: none

    Addressing most of the remaining build warnings from issue thread #1617.

    <details> <summary>I used master branch @ 4721e077b4ac9a9ab80db3fd9a05d6dda207f5c1 with command:</summary>

    cmake -B build \
      -DCMAKE_C_COMPILER=clang \
      -DSECP256K1_ENABLE_MODULE_ECDH=ON \
      -DSECP256K1_ENABLE_MODULE_RECOVERY=ON \
      -DSECP256K1_ENABLE_MODULE_EXTRAKEYS=ON \
      -DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON \
      -DSECP256K1_ENABLE_MODULE_MUSIG=ON \
      -DSECP256K1_ENABLE_MODULE_ELLSWIFT=ON \
      -DSECP256K1_BUILD_TESTS=OFF \
      -DSECP256K1_BUILD_EXHAUSTIVE_TESTS=OFF \
      -DSECP256K1_BUILD_CTIME_TESTS=OFF \
      -DSECP256K1_BUILD_BENCHMARK=OFF \
      -DSECP256K1_BUILD_EXAMPLES=OFF \
      -DCMAKE_C_FLAGS="-Wall -Wextra -Wshorten-64-to-32 -Werror=shorten-64-to-32"
      
    cmake --build build --verbose
    

    </details>

    <details> <summary>which produced these 14 errors:</summary>

    /Users/csjones/Developer/secp256k1/src/modinv64_impl.h:279:39: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      279 |             w = (f * g * (f * f - 2)) & m;
          |               ~ ~~~~~~~~~~~~~~~~~~~~~~^~~
    /Users/csjones/Developer/secp256k1/src/modinv64_impl.h:289:19: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      289 |             w = f + (((f + 1) & 4) << 1);
          |               ~ ~~^~~~~~~~~~~~~~~~~~~~~~
    /Users/csjones/Developer/secp256k1/src/modinv64_impl.h:290:26: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      290 |             w = (-w * g) & m;
          |               ~ ~~~~~~~~~^~~
    /Users/csjones/Developer/secp256k1/src/modinv64_impl.h:370:39: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      370 |             w = (f * g * (f * f - 2)) & m;
          |               ~ ~~~~~~~~~~~~~~~~~~~~~~^~~
    /Users/csjones/Developer/secp256k1/src/modinv64_impl.h:380:19: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      380 |             w = f + (((f + 1) & 4) << 1);
          |               ~ ~~^~~~~~~~~~~~~~~~~~~~~~
    /Users/csjones/Developer/secp256k1/src/modinv64_impl.h:381:26: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      381 |             w = (-w * g) & m;
          |               ~ ~~~~~~~~~^~~
    In file included from /Users/csjones/Developer/secp256k1/src/secp256k1.c:28:
    In file included from /Users/csjones/Developer/secp256k1/src/scalar_impl.h:20:
    /Users/csjones/Developer/secp256k1/src/scalar_4x64_impl.h:112:42: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'int' [-Werror,-Wshorten-64-to-32]
      112 |     overflow = secp256k1_u128_to_u64(&t) + secp256k1_scalar_check_overflow(r);
          |              ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /Users/csjones/Developer/secp256k1/src/scalar_4x64_impl.h:637:10: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      637 |     m6 = c0;
          |        ~ ^~
    /Users/csjones/Developer/secp256k1/src/scalar_4x64_impl.h:657:13: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      657 |     p4 = c0 + m6;
          |        ~ ~~~^~~~
    /Users/csjones/Developer/secp256k1/src/scalar_4x64_impl.h:677:34: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'unsigned int' [-Werror,-Wshorten-64-to-32]
      677 |     secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r));
          |     ~~~~~~~~~~~~~~~~~~~~~~~    ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    In file included from /Users/csjones/Developer/secp256k1/src/secp256k1.c:30:
    /Users/csjones/Developer/secp256k1/src/ecmult_impl.h:536:21: error: implicit conversion loses integer precision: 'size_t' (aka 'unsigned long') to 'int' [-Werror,-Wshorten-64-to-32]
      536 |     for (i = n_wnaf - 1; i >= 0; i--) {
          |            ~ ~~~~~~~^~~
    /Users/csjones/Developer/secp256k1/src/ecmult_impl.h:580:52: error: implicit conversion loses integer precision: 'long' to 'int' [-Werror,-Wshorten-64-to-32]
      580 |         for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j > 0; j--) {
          |               ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
    In file included from /Users/csjones/Developer/secp256k1/src/secp256k1.c:32:
    In file included from /Users/csjones/Developer/secp256k1/src/ecmult_gen_impl.h:14:
    /Users/csjones/Developer/secp256k1/src/hash_impl.h:151:52: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      151 |     secp256k1_write_be32(&sizedesc[0], hash->bytes >> 29);
          |     ~~~~~~~~~~~~~~~~~~~~               ~~~~~~~~~~~~^~~~~
    /Users/csjones/Developer/secp256k1/src/hash_impl.h:152:52: error: implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long long') to 'uint32_t' (aka 'unsigned int') [-Werror,-Wshorten-64-to-32]
      152 |     secp256k1_write_be32(&sizedesc[4], hash->bytes << 3);
          |     ~~~~~~~~~~~~~~~~~~~~               ~~~~~~~~~~~~^~~~
    14 errors generated.
    

    </details>

    I applied most of the suggested fixes from #1617 (comment) and used an explicit cast of (uint32_t) in scalar_4x64_impl.h for the two warnings not mentioned in the original issue thread. The warnings in ecmult_impl.h were skipped.

    <details> <summary>Only two warnings remain after applying the changes from this PR.</summary>

    /Users/csjones/Developer/secp256k1/src/ecmult_impl.h:536:21: error: implicit conversion loses integer precision: 'size_t' (aka 'unsigned long') to 'int' [-Werror,-Wshorten-64-to-32]
      536 |     for (i = n_wnaf - 1; i >= 0; i--) {
          |            ~ ~~~~~~~^~~
    /Users/csjones/Developer/secp256k1/src/ecmult_impl.h:580:52: error: implicit conversion loses integer precision: 'long' to 'int' [-Werror,-Wshorten-64-to-32]
      580 |         for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j > 0; j--) {
          |               ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
    2 errors generated.
    

    </details>

    EDIT: Investigating failing CI Fixed.

  2. fix: change w variable type from uint32_t to uint64_t in modinv64 implementation 66452d4825
  3. fix: use explicit casts for 'implicit conversion loses integer precision' warnings in scalar_4x64_impl.h 3ddc5bedef
  4. real-or-random added the label tweak/refactor on Jan 21, 2026
  5. real-or-random commented at 4:12 PM on January 21, 2026: contributor

    Can you squash the last two commits?

  6. fix: use explicit casts for 'implicit conversion loses integer precision' warnings in hash_impl.h
    fix: correct cast placement in hash_impl.h to preserve full 64-bit shift operations
    4a84fb7e43
  7. csjones force-pushed on Jan 21, 2026
  8. csjones commented at 4:55 PM on January 21, 2026: none

    @real-or-random just pushed the squashed commit.

  9. in src/scalar_4x64_impl.h:112 in 3ddc5bedef
     108 | @@ -109,7 +109,7 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a,
     109 |      secp256k1_u128_accum_u64(&t, a->d[3]);
     110 |      secp256k1_u128_accum_u64(&t, b->d[3]);
     111 |      r->d[3] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64);
     112 | -    overflow = secp256k1_u128_to_u64(&t) + secp256k1_scalar_check_overflow(r);
     113 | +    overflow = (int)secp256k1_u128_to_u64(&t) + secp256k1_scalar_check_overflow(r);
    


    real-or-random commented at 7:28 PM on January 21, 2026:

    This changes the addition from a uint64_t addition to a (signed) int addition. I think I'd rather make overflow an uint64_t and cast to (unsigned int) explicitly when passing it to secp256k1_scalar_reduce and to (int) when returning, both after the VERIFY_CHECK.


    csjones commented at 5:31 AM on January 22, 2026:

    Appreciate the feedback, I pushed the change.

  10. in src/scalar_4x64_impl.h:658 in 4a84fb7e43
     653 | @@ -654,7 +654,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
     654 |      muladd_fast(m6, SECP256K1_N_C_1);
     655 |      sumadd_fast(m5);
     656 |      extract_fast(p3);
     657 | -    p4 = c0 + m6;
     658 | +    p4 = (uint32_t)c0 + m6;
     659 |      VERIFY_CHECK(p4 <= 2);
    


    real-or-random commented at 7:32 PM on January 21, 2026:
        VERIFY_CHECK(c0 <= 1 && m6 <= 1);
        p4 = (uint32_t)c0 + m6;
    

    csjones commented at 5:30 AM on January 22, 2026:

    Change made


    csjones commented at 6:29 AM on January 22, 2026:

    I ended up removing m6 <= 1 because it's already verified at line 637. The CI failed with c0 <= 1 and after running the ci script locally a few times, I changed it to c0 <= 2, which is consistent with the original p4 <= 2 check (since p4 = c0 + m6 and m6 <= 1). I also restored the p4 <= 2 check.


    real-or-random commented at 8:11 AM on January 22, 2026:

    Hm, I see. I have no idea how the algorithm works. Still, this is a bit surprising to me because that means m6 == 0 always (but then it probably wouldn't be there in the first place) or m6 == 0 iff c0 == 2, and I don't see why the latter should hold.

    Perhaps @sipa can provide some insights..

  11. in src/scalar_4x64_impl.h:677 in 4a84fb7e43 outdated
     673 | @@ -674,7 +674,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
     674 |  #endif
     675 |  
     676 |      /* Final reduction of r. */
     677 | -    secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r));
     678 | +    secp256k1_scalar_reduce(r, (unsigned int)c + secp256k1_scalar_check_overflow(r));
    


    real-or-random commented at 7:37 PM on January 21, 2026:
        VERIFY_CHECK(c <= 1);
        secp256k1_scalar_reduce(r, (unsigned int)c + secp256k1_scalar_check_overflow(r));
    

    csjones commented at 5:30 AM on January 22, 2026:

    Added!

  12. real-or-random commented at 7:37 PM on January 21, 2026: contributor

    cc @sipa for the w change... Is this correct even? Even if it's correct, do we want to keep the smaller uint32_t?

  13. refactor: preserve unsigned arithmetic and add VERIFY_CHECKs before casts in scalar_4x64_impl.h da9db68bc9
  14. csjones force-pushed on Jan 22, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin-core/secp256k1. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-18 17:15 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me