Search code examples
c++language-lawyermemcpysignedreinterpret-cast

Signedness aliasing using reinterpret_cast


Take the following code

#include <iostream>

void func() {
    int i = 2147483640;
    while (i < i + 1)
    {
        std::cerr << i << '\n';
        ++i;
    }

    return;
}

int main() {
    func(); 
}

This code is clearly wrong, as the while loop can only terminate if the signed int i overflowed, which is UB, and hence the compiler may for instance optimize this into an infinite loop (which Clang does on -O3), or do other sorts of funky things. My question now is: from my reading of the C++ standard, types that are equivalent up to signedness may alias (i.e. pointers int* and unsigned* may alias). In order to do some funky signed "wrapping", does the following have undefined behavior or not?

#include <iostream>

static int safe_inc(int a)
{
    ++reinterpret_cast<unsigned&>(a);
    return a;
}

void func() {
    int i = 2147483640;
    while (i < safe_inc(i))
    {
        std::cerr << i << '\n';
        ++i;
    }

    return;
}

int main() {
    func(); 
}

I have tried the above code with both Clang 8 and GCC 9 on -O3 with -Wall -Wextra -Wpedantic -O3 -fsanitize=address,undefined arguments and get no errors or warnings and the loop terminates after wrapping to INT_MIN.

cppreference.com tells me that

Type aliasing

Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:

  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.

which from my reading means that for purposes of type aliasing, signedness is not considered, and the code using reinterpret_cast has well-defined semantics (albeit being somewhat cheesy anyhow).


Solution

  • Your code is perfectly legal, cpp reference is a very good source. You can find the same information in the standard [basic.lval]/11

    If a program attempts to access the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:

    • the dynamic type of the object,

    • a type that is the signed or unsigned type corresponding to the dynamic type of the object,[...]