_HashTable+Constants.swift:79: error: undefined reference to 'round'

i’m really struggling with this error when building a SwiftPM Snippet in a package that has a dependency on swift-collections

the project is swift-bson at 0.1.0, and i’m trying to add the following Snippet:

import BSON

do
{
    let full:[UInt8] = [
        0x09, 0x00, 0x00, 0x00, //  Document header
        0x08, 0x62, 0x00, 0x01, //  Document body
        0x00                    //  Trailing null byte
    ]
    let bson:BSON.Document = .init(bytes: full[4 ..< 8])

    print(bson)
}

when i do a fresh build of the package, i get

$ swift build --explicit-target-dependency-import-check=error 
Building for debugging...
error: link command failed with exit code 1 (use -v to see invocation)
/swift/swift-bson/.build/checkouts/swift-collections/Sources/OrderedCollections/HashTable/_HashTable+Constants.swift:79: error: undefined reference to 'round'
/swift/swift-bson/.build/checkouts/swift-collections/Sources/OrderedCollections/HashTable/_HashTable+Constants.swift:79: error: undefined reference to 'rint'
/swift/swift-bson/.build/checkouts/swift-collections/Sources/OrderedCollections/HashTable/_HashTable+Constants.swift:79: error: undefined reference to 'trunc'
/swift/swift-bson/.build/checkouts/swift-collections/Sources/OrderedCollections/HashTable/_HashTable+Constants.swift:79: error: undefined reference to 'ceil'
/swift/swift-bson/.build/checkouts/swift-collections/Sources/OrderedCollections/HashTable/_HashTable+Constants.swift:79: error: undefined reference to 'floor'
/swift/swift-bson/.build/checkouts/swift-collections/Sources/OrderedCollections/HashTable/_HashTable+Constants.swift:79: error: undefined reference to 'ceil'
/swift/swift-bson/.build/checkouts/swift-collections/Sources/OrderedCollections/HashTable/_HashTable+Constants.swift:79: error: undefined reference to 'floor'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
[336/337] Linking GettingStarted

what is really strange is that the BSON target has no actual dependency on swift-collections, that package is only used by a different target called BSON_OrderedCollections.

what is going on here?

Looks like somebody forgot to link libm?

i’m not certain who the “somebody” is here, but i suspect it’s SwiftPM, because the package compiles successfully when the Snippet is removed, or when the BSON_OrderedCollections product is commented out, even though they are not related to each other except through a common dependency on the BSON target.

the Snippet should not be linking libm at all.

Huh. swift-collections just calls Double.rounded(.up) from the Standard Library; it does not directly reference those routines.

    let minimumEntries = Swift.max(
      Int((Double(capacity) / maximumLoadFactor).rounded(.up)),
      capacity + 1)

It is admittedly somewhat silly to use floating point arithmetics to implement the minimum/maximum load factors -- the code in that file can also be implemented with integers.

  internal static var maximumLoadRatio: (num: Int, denom: Int) { (3, 4) }
  internal static var minimumLoadRatio: (num: Int, denom: Int) { (1, 4) }
  ...
   let mlr = maximumLoadRatio
   let minimumEntries = Swift.max(
     (capacity + mlr.num - 1) * mlr.denom / mlr.num,
     capacity + 1)

Doing something like this is an option if the SwiftPM issue proves difficult to fix, e.g., if the code needs to work with shipping toolchains, and there is no easy client-level workaround. (Let me know if you need this.)

@lorentey making this change would be really helpful for embedded where we don't want to assume an fpu is available. If I recall correctly @kubamracek had a PR to do this (or something very similar)

Avoid using Double in HashTable implementation by kubamracek · Pull Request #73583 · swiftlang/swift · GitHub -- but it's a separate instance of this (in the stdlib), which wouldn't magically fix swift-collections's usage of floating points.