if (posix_fallocate(fd, 0, size)) {
errno is not set.
Well, that is horrible. Something might have fixed POSIX like... making entry points too long to invoke in a conditional and then every example of the API involve "NTSTATUS status = ..." then "if(NT_SUCCESS( status ))"... Though I'm not trying to sing anyone's praises here, I actually find that sort of "forced error handling" much clearer than relying on implicit behavior. Of course it can be made just as bad and unstable. However, neither ERRNO or NTSTATUS are in any way the same as a 3 (or 4) element set.
It is not that POSIX APIs are inconsistent from one to another, it is that they are documented and users don't read the documentation. I'm no saint here either, though this is also why we test. No degree of semantic enforcement is going to spare one from making mistakes like those. Worse still, their implementations may be inconsistent or unstable to the extent of forcing incorrect usage.
Combination is particularly important for generic code: how do you write
operator<=> for std::vector<T> or std::tuple<T...> ? For tuples, it's a
heterogeneous list, so the types may be weakly ordered, strongly ordered or
only partially ordered. So what is the result of the three-way compare?
Straightforwardly apply <=> to each pair of members in succession, like a string. Apply common_type to both before comparing in a tuple (this can apply to any structure). Take the first result that isn't equal. If they were all equal, then the result of the composition is equal. This exact same idea applies to multiple precision integers (or even in string comparison), where despite the data being heterogeneous, the comparison still has a consistent interpretation as "sign of difference". For example, you can envision the individual sequences of bytes in PODs contained in tuples as just plain integers.
If a particular sort is needed, like case-insensitivity, this requires a transformation to be applied. Wherever that transformation is applied, even if it is just setting a flag, is also where the change in the nature of the comparison can be noted. It is possible to change the sorting category of strings at runtime by just changing locales. A statically-compiled, semantically rich 3-way comparison enum isn't going to stop someone from setting a flag. As another example from numerics: Change a rounding mode at runtime. Now going from something like an MP::Rational to a double has a slightly different ordering over the doubles. How is this change in behavior enforced? By a predicate separate from both MP::Rational and double, which is also going to implement some kind of contextual awareness of this fact.
It is possible to keep std::strong_ordering around. Just formally allow <signed-int> operator<=>, and let the compiler do the rest. The alternative is really just saving around 12 lines of code per thing that needs a full suite of comparison operators.