1

Is it well-specified (for unsigned types in general), that:

static_assert(-std::size_t{1} == ~std::size_t{0}, "!");

I just looked into libstdc++'s std::align implementation and note using std::size_t negation:

inline void*
align(size_t __align, size_t __size, void*& __ptr, size_t& __space) noexcept
{
  const auto __intptr = reinterpret_cast<uintptr_t>(__ptr);
  const auto __aligned = (__intptr - 1u + __align) & -__align;
  const auto __diff = __aligned - __intptr;
  if ((__size + __diff) > __space)
    return nullptr;
  else
    {
      __space -= __diff;
      return __ptr = reinterpret_cast<void*>(__aligned);
    }
}
Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
  • 3
    *"It is improbably, that libstdc++ contain UB."* When implementing the standard itself, you need not worry about UB because you can just define that instance of UB to do the right thing. This discussion comes up every time someone looks at the implementation of `offsetof`. – Baum mit Augen Jan 28 '18 at 20:08
  • 1
    C != C++. Tag only with the language that you're using, unless both are actually relevant. Your code currently is C++, *not* C. – tambre Jan 28 '18 at 20:09
  • @BaummitAugen I stated that. What wrong? – Tomilov Anatoliy Jan 28 '18 at 20:11
  • @Orient "Wrong" is a strong word, just saying that the issue of UB does not apply to the code that implements the language as it does to the code we mere users write. – Baum mit Augen Jan 28 '18 at 20:13
  • @tambre I started discussion of thing relevant to both C and C++. You are wrong, If you think, that code for all the tags should be presented into the question. It is superfluous. Especially in case of C, C++ and discussed topic. – Tomilov Anatoliy Jan 28 '18 at 20:14
  • @BaummitAugen Your note about legitimacy of using UB in implementation of the language itself is highly unobvious. Never think about this in that way. Non-trivial statement at my mind. – Tomilov Anatoliy Jan 28 '18 at 20:18
  • @BaummitAugen If headers of Standard Library would contain much of UB, then such tools as [UB sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) may be of no use without hard tuning in advance (if possible at all, because not all errors can be recovered). – Tomilov Anatoliy Jan 28 '18 at 20:22
  • @Orient, that's a bit strong. Vendors are allowed to do anything they know works with their own implementation inside their own implementation. There's doesn't even need to be an actual text file corresponding to the standard header names, or it could be written in a different language. UB according to spec does not mean *does not work* on all platforms. – Johan Lundberg Jan 28 '18 at 20:24
  • @JohanLundberg Here I talk about header (i.e. *not-yet-compiled* source code in concrete language) from *libstdc++*, where implementation of mentioned function is not selected by enclosing in target-specific macros. Seems that negative unsigned is not relevant to platform-specific things. – Tomilov Anatoliy Jan 28 '18 at 20:32
  • libstdc++ supports several compilers, but from a standards point of view it's up to them how they do it. All standard library implementations use compiler intrinsics, other legally UB, or other non-C++ constructs. Ie, implementation of std vector. It would for example not be an unexpected limitation for a standard library implementation to only support two complements representation, or specific ieee floating point representation. Sure, there could be static asserts or ifdefs, but it could as well be documented. – Johan Lundberg Jan 28 '18 at 20:35
  • @Orient - You don't have to enclose the code in platform specific macros if the supported platforms are limited to those were the code works. Also, it is *impossible* to write a complete standard library implementation using only portable and standard conforming code. You *have* to use some compiler specific features. Baum mit Augen hints at `offsetof` which is in the standard library *specifically* because it cannot be written in portable user code. – Bo Persson Jan 28 '18 at 23:19

2 Answers2

3

Unsigned integer types are defined to wrap around, and the highest possible value representable in an unsigned integer type is the number with all bits set to one - so yes.

As cpp-reference states it (arithmetic operators / overflow):

Unsigned integer arithmetic is always performed modulo 2n where n is the number of bits in that particular integer. E.g. for unsigned int, adding one to UINT_MAX gives ​0​, and subtracting one from 0​ gives UINT_MAX.

Related: Is it safe to use negative integers with size_t?

Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • What does it mean? How simple negation connected with wraping around? Should I substitute `-std::size_t{x}` -> `0 - std::size_t{x}` to derive result? – Tomilov Anatoliy Jan 28 '18 at 20:03
  • @Orient Simply put, it implements math in Z/64 (for a 64 bit integer) by selecting the representative of each equivalence class that lies in [0, 2^64 - 1]. And thus -1 ~ 2^64 - 1 – Baum mit Augen Jan 28 '18 at 20:06
1

Is it well-specified (for unsigned types in general), that:

static_assert(-std::size_t{1} == ~std::size_t{0}, "!");

No, it is not.

For calculations using unsigned types, the assertion must hold. However, this assertion is not guaranteed to use unsigned types. Unsigned types narrower than int would be promoted to signed int or unsigned int (depending on the types' ranges) before - or ~ is applied. If it is promoted to signed int, and signed int does not use two's complement for representing negative values, the assertion can fail.

libstdc++'s code, as shown, does not perform any arithmetic in any unsigned type narrower than int though. The 1u in __aligned ensures each of the calculations use unsigned int or size_t, whichever is larger. This applies even to the subtraction in __space -= __diff.

Unsigned types at least as wide as unsigned int do not undergo integer promotions, so arithmetic and logical operations on them is applied in their own type, for which Johan Lundberg's answer applies: that's specified to be performed modulo 2N.

Community
  • 1
  • 1