New issue
Advanced search Search tips

Issue 660828 link

Starred by 1 user

Issue metadata

Status: Untriaged
Owner: ----
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: ----
Pri: 3
Type: Bug



Sign in to add a comment

clang produces a static initializer when initializing an array of a struct that has a volatile field that is initialized via a constrexpr ctor

Project Member Reported by primiano@chromium.org, Oct 31 2016

Issue description

I was about to file this on https://llvm.org/bugs, but it looks like it takes an account and the account requires to send an email to the list admins :/
I'll move it there once I get an account, in the meantime filing here or I'll forget about this.

I just found this very interesting behavior of Clang/LLVM.
if I:
 1. Initialize a global array of class/struct X
 2. X has a constrexpr ctor which initializes a field X.v
 3. X.v is volatile

The constrexpr ctor ends up generating a static initializer anyways.
g++ doesn't seem to be affected by this.
This weird behavior happens only if initializing an array. Everything works fine if I initialize just one instance.
-O{0,2,3} don't seem to make a difference.

Repro (code below):
$ /s/chrome/src/third_party/llvm-build/Release+Asserts/bin/clang++ -o clang_bug clang_bug.cc --std=c++11  && ./clang_bug
a:42   a_arr:0  <------------------- !!!
b:42   b_arr:42
c:42   c_arr:42

$ g++ -o clang_bug clang_bug.cc --std=c++11  && ./clang_bug
a:42   a_arr:42
b:42   b_arr:42
c:42   c_arr:42

Interestingly if I use -Wglobal-constructors, clang warns about both |a| and |a_arr|, but only |a_arr| seems affected:
-----
clang_bug.cc:23:3: warning: declaration requires a global constructor [-Wglobal-constructors]
A a = {};
  ^   ~~
clang_bug.cc:24:3: warning: declaration requires a global constructor [-Wglobal-constructors]
A a_arr[1] = { {} };
-----

$ cat clang_bug.cc

#include <stdio.h>

class A {
 public:
  constexpr A(): v(42) {}
  volatile unsigned char v;
};

class B {
 public:
  constexpr B(): v(42) {}
  unsigned char v;
};

struct C {
  volatile unsigned char v;
};


bool Initializer();

bool init = Initializer();
A a = {};
A a_arr[1] = { {} };
B b = {};
B b_arr[1] = { {} };
C c = {42};
C c_arr[1] = { {42} };


bool Initializer() {
  printf("a:%d   a_arr:%d\n", a.v, a_arr[0].v);
  printf("b:%d   b_arr:%d\n", b.v, b_arr[0].v);
  printf("c:%d   c_arr:%d\n", c.v, c_arr[0].v);
  return true;
}

int main() { return 0; }

 

Comment 1 by h...@chromium.org, Oct 31 2016

Labels: clang
Status: Available (was: Untriaged)
> I was about to file this on https://llvm.org/bugs, but it looks like it takes an account and the account requires to send an email to the list admins :/

Yes, there was spam incident a while back, so now they're moderating account creation until some better mechanism is put in place.



Playing with initialization order in C++ is like playing with fire, and now you're mixing in constexpr and volatile too :-)

Trying to wrap my head around your example, I shortened it a little:

struct S {
  constexpr S() : x(42) {}
  volatile int x;
};

bool init();
bool b = init();

S s;
S arr1[1];
S arr2[1] = { {} };

extern "C" int printf(const char *, ...);
bool init() {
  printf("%d\n", s.x);
  printf("%d\n", arr1[0].x);
  printf("%d\n", arr2[0].x);
  return true;
}

int main() {
  return 0;
}


'b' is declared before the others, so init() will run before any other dynamic initialization, and the question is: do s, arr1, and arr2 require dynamic initialization?

It seems fours things factor in: constexpr, volatile, being an array, having an initializer.

Clang output:
42
0
0

GCC output:
42
0
42

I think we need a language lawyer :-)
> Playing with initialization order in C++ is like playing with fire, and now you're mixing in constexpr and volatile too :-)

Well, what I'm ultimately trying to achieve is to have no initialization at all and have all this being linker initialized. at the end the final result should be ismple, as it involves no code at all and just injecting a bit of zeros and ones in the .rwdata.
In practice, I seem to fail to convince the various compilers about what I want them to do.

> Trying to wrap my head around your example, I shortened it a little:
Right, A, B and C in my example was the sequence of tests that led me to think that the problem is "volatile".

Comment 3 by h...@chromium.org, Oct 31 2016

This is what Richard said on IRC:

> a class with a volatile member is not a literal type
> in c++11, there was a special rule that guaranteed constant initialization in that special case, which is why the "S s;" case gets constant initialization
> in c++14 onwards, that special case is extended to volatile subobjects nested somewhat arbitrarily
> gcc appears to be miscompiling arr2; i would guess that it's incorrectly imagining that a copy is performed for the array element


So.. not sure what GCC is up to, but at least that explains Clang's behaviour, and if you turn on -std=c++14, Clang will also constant-initialize arr1 and arr2.

And yes, "volatile" is making things harder here, I suppose.
Allright I'll not use volatile and volatilize the accessors.
FWIW msvc seems aligned with gcc here.
Thanks for digging further.
Project Member

Comment 5 by sheriffbot@chromium.org, Nov 1 2017

Labels: Hotlist-Recharge-Cold
Status: Untriaged (was: Available)
This issue has been Available for over a year. If it's no longer important or seems unlikely to be fixed, please consider closing it out. If it is important, please re-triage the issue.

Sorry for the inconvenience if the bug really should have been left as Available. If you change it back, also remove the "Hotlist-Recharge-Cold" label.

For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot
Since we're now on C++14 and comment 3 says this is fine with C++14, is this still an issue?

Sign in to add a comment