C++ Logo


Advanced search

std::variant and pointer aliasing problems

From: Myria <myriachan_at_[hidden]>
Date: Fri, 2 Oct 2020 15:50:03 -0700
Currently, there is a bug in Clang where the following program prints
4321 instead of the expected 1234 in optimized builds. As far as I
know, it has defined behavior; the pointer aliasing here is of
matching types.

// Must use libcxx
// clang++ -std=gnu++17 -O3 -fstrict-aliasing variant.cpp -o variant
#include <variant>
#include <cstdio>

struct s1 {unsigned short x;};
struct s2 {unsigned short x;};
using s1s2 = std::variant<s1, s2>;

static int read_s1x(s1 *p) { return p->x; }
static void write_s2x(s2 *p, int v) { p->x=v; }

int test(s1s2 *p1, s1s2 *p2, s1s2 *p3)
  if (read_s1x(std::get_if<s1>(p1)))
    unsigned short temp;
    temp = std::get_if<s1>(p3)->x;
    temp = std::get_if<s2>(p3)->x;
  return read_s1x(std::get_if<s1>(p1));
int test2(int x)
  s1s2 q[2];
  return test(q,q+x,q+x);
int main(void)

This bug is caused by alias confusion within the union chain
comprising std::variant. It's a conversion to std::variant of some
Standard-compliant union code that was found to break Clang's (and
GCC's) type-based aliasing analysis. (Note that GCC only blows up
with the original version; somehow libstdc++'s std::variant is


In that long thread, LLVM maintainers seem to think that the original
non-variant version can't be solved without pessimizing a large
variety of code. They suggest that writing to a union should always
require a union member access expression to have defined meaning.

But... that breaks std::variant. std::get_if returns a pointer to one
of the members, so accessing members of a substructure won't have a
union member access expression. This is what breaks Clang's TBAA,
just like the original non-variant code.

I've known about this for a while, but don't know what to do with it.


Received on 2020-10-02 17:50:19