# Why isn’t UnsignedInteger.Magnitude constrained to equal Self?

The `UnsignedInteger` protocol is declared like this:

``````public protocol UnsignedInteger: BinaryInteger { }
``````

And its documentation says:

``````  /// Every unsigned integer is its own magnitude, so for any value `x`,
/// `x == x.magnitude`.
``````

So, why isn’t the protocol instead declared:

``````public protocol UnsignedInteger: BinaryInteger where Magnitude == Self { }
``````

In order to guarantee that relationship?

• • •

I was just writing some generic code similar to this:

``````func foo<T: UnsignedInteger>(_ x: T) -> T {
let y = x.magnitude
return x + y
// error: Binary operator '+' cannot be applied to operands of type 'T' and 'T.Magnitude'
}
``````

And got the error shown in the comment. Thus my question: why isn’t `UnsignedInteger.Magnitude` constrained to equal `Self`?

5 Likes

Would it be ABI-compatible to add the constraint “`where Magnitude == Self`” to the declaration of the `UnsignedInteger` protocol?

• • •

To clarify my use-case, in a context constrained to `T: FixedWidthInteger & UnsignedInteger`, I am working with the result of a call to `multipliedFullWidth(by:)`. That function returns a tuple of `(high: T, low: T.Magnitude)`.

In order to perform further calculations, I currently need to write `T(low)` (or to avoid extra checks, `T(truncatingIfNeeded: low)`).

But, because `T` conforms to `UnsignedInteger`, we (as programmers) know that `low` is actually already of type `T`, since a correctly-implemented `UnsignedInteger` type must use `Self` as `Magnitude`.

Unfortunately the compiler does not know this, so the redundant type conversion is necessary. Can we fix this?

It's not API-compatible: someone might have implemented BadUInt64 with a Magnitude of UInt64.

(It's also not ABI-compatible; it will break any generic that adds that extra condition itself. But in general, if something's not API-compatible, you can assume it's not ABI-compatible, though there are a few rare cases where you can get away with it.)

My best guess for why this is: it's possible UnsignedInteger predates the ability to have protocol-level `where` clauses, and it didn't get fixed in time for ABI stability. But I didn't go back through the history or anything.

3 Likes

Sure, but going by existing Swift Evolution precedent, since `BadUInt64` violates the documented semantics of the protocol, it is an invalid conformance.

All correct conformances would keep working, so formalizing the existing documented semantics in a constraint should be considered source-compatible.

Dang. Is there any way to patch that up so the runtime still handles the generics you describe the way it does now, but the compiler learns the new constraint as well?

That is, when compiling code, a generic declaration which constrains `UnsignedInteger.Magnitude == Self` would compile exactly as it does today, and the runtime would continue treating it the same way.

But code which does not explicitly include that constraint, would be able to take advantage of the compiler’s newly-gained knowledge that `Magnitude == Self`, so it could emit code that benefits from knowing the types are the same.

Hm.

``````  /// Every unsigned integer is its own magnitude, so for any value `x`,
/// `x == x.magnitude`.
``````

If you interpret this to mean "`x.magnitude` is interchangeable with `x`", then yes, it'd be an invalid conformance. But if you interpret it as "`x.magnitude` has the same value as `x`", then it'd be okay.

Unfortunately, I'm not sure of all the implications of making such a change even if it was considered a bug fix. The function mangling thing was the simplest thing I could think of, but there's backwards- and forwards-compatibility concerns here. Someone more runtime-savvy than me would have to think this through.

1 Like

If `x` is of a class type, the requirement `x == x.magnitude` would be satisfied in all respects if `x.magnitude` is of a subclass. But `Magnitude == Self` would cause such a valid conformance not to compile.

2 Likes