Query regarding global value initialization standard

Hi All,
I was going through a gcc TC for C++11. The test case is as follows -

// { dg-options -std=c++0x }
// { dg-do run }

extern “C” void abort ();
extern int ar[2];

int f()
{
int k = 0;
if (ar[0] != 42 || ar[1] != 0)
abort();
return 1;
}

int i = f();

int ar[2] = {42,i};

int main()
{
return 0;
}

During dynamic initialization of i in function f() the value of ar[0] is 0 in case of clang were as in case of gcc it is 42.

As per standard(section 3.6.2) all global values should initially be zero initialized followed by const initialized if possible before dynamic initialization takes place.
Hence as per standard the const initialization of int ar[2] = {42,i}; should fail as i is not a const here( which seems to be happening in clang). Hence ar[0],ar[1] is still zero initialized because of which during dynamic initialization in f() ar[0] is 0 which seems to be the correct behavior.

Can i conclude here that clang is behaving correctly and the tc is wrong? or am i missing something which this gcc tc wanted to capture?

Thanks

As far as I can tell, your analysis is correct.

-Eli

Hi,
But isn't function f a constexpr? It doesn't modify anything and always
returns 1.

enjoy,
Karen

It doesn't matter what the implementation of "f" is; "f()" is still not a
constant expression. Please read [basic.start.init] and [expr.const] in
the C++ standard.

-Eli

Hi Eli,
OK. I agree. f() is not a constexpr because the body of the function is not a
return statement. There are other problems, but that is moot.

Based on my reading of the C++11 standard at 3.6.2 note 3, this g++ test case
appears to be fully compliant with the standard. I am referring to the static
initialization of ar[0] to 42. see notes below.

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe5c8) at constexpr.cpp:385
385 {
(gdb) print i
$1 = 1
(gdb) print ar
$2 = {42, 1}

When i is initialized by f(), ar[0] = 42 and ar[1] = 0. My interpretation is
that g++ is initializing ar[0] and ar[1] as individual variables, with respect to
the fact ar[1] has an initializer that is not a constant expression. Unless the
standard explicitly prohibits the static initialization of ar[0], because of some
all or nothing rule pertaining to the static initialization of the elements of an
array and noting i is not a constant expression, I believe the g++ test case is
valid code.

My interpretation doesn't necessarily mean clang has a bug here. But the standard
must explicitly forbid static initialization of ar[0], because the initializer of
ar[1] is not a constant expression, or at the least define it as implementation
dependent, or it would be a bug in clang. Where is this clarified in the standard?
Specifically, where in the standard does it require nonlocal static initialization
of elements of an array to be an all or nothing operation?

begin static initialization

i = 0
ar[0] = 0
ar[1] = 0
i ............. constant initialization is not done, because f() is not a constant
                expression

ar[0] = 42 .... constant initialization is done, because 42 is a constant literal
                integral type. Even if your interpreation is that ar[0] should be
                dynamically initialized, based on 3.6.2 note 3, ar[0] can be initialized
                either statically or dynamically. Its implementation dependent, unless
                the standard explicitly requires array element initialization to be
                an all or nothing operation.

ar[1] ......... constant initialization is not done, because i is not a constant
                expression

end static initialization
begin dynamic initialization
nonlocal variables with static storage duration have ordered dynamic initialization

i = f() = 1 ... f() does not abort because a[0] == 42 and a[1] == 0.
a[1] = 1

enjoy,
Karen

within a single translation unit shall be initialized in the order of their
definitions in the translation unit.

There isn't really any room to interpret that any way other than what clang
is doing.

-Eli

Also i think standard section 3.6.2 point 2 mentions as -

Constant initialization is performed:
if each full-expression (including implicit conversions) that appears in the initializer of a reference with
static or thread storage duration is a constant expression (5.19) and the reference is bound to an lvalue
designating an object with static storage duration or to a temporary (see 12.2);

since i here is not const expression the point each full-expression that appears in the initializer fails in this tc and hence a[0] need not be const initialized to 42.
Am i right?

Thanks!

--- end quoted text ---

Hi Eli,
OK, so I am clear about your perspective. I believe you are asserting that the variable
here is the array ar. And that means initializing the elements of ar must be an all
or nothing process. And this is the basis for your assertion the test case is valid code,
but g++ has a bug in the implementation. And in that context, it follows that 3.6.2 note 3
does not apply, because you cannot statically initialize ar[1].

Thank you for clarifying. Interesting test case.

enjoy,
Karen

--- end quoted text ---

Hi,
I took a few minutes to look a little further. And the stl library containers all abort()
when used in the test case scheme. It appears g++ makes the distinction for this test case
for c-style arrays only. stl containers all adhere to the all or nothing nonlocal static
initialization constraint, using dynamic initialization consistent with clang.

I haven't had time yet to check if clang is aggressively using 3.6.2 note 3 exception that
permits nonlocal static initialization in specific cases where note 2 prohibits it, but I
am confident clang does. Its important in the context of concurrency, avoiding the per
translation unit nonlocal dynamic initialization ordering issues, available since c++11.

enjoy,
Karen

Hi Karthik,

I believe you cannot use a rule that must be satisfied in 3.6.2 note 2 to invalidate
the use of note 3 exception. If you get to note 3, it is a precondition that you already
failed to satisfy the conditions set forth in note 2. Note 3 presents a different set
of conditions, that if satisfied, give the implementation the option to ignore the failure
in note 2 requirements.

While you point to a statement that is clearly related to the all or nothing initialization
of containers or classes, that all or nothing rule is associated with the objects themselves
outside of the scope of 3.6.2 note 2. I believe that is what is needed to invalidate the use
of 3.6.2 note 3 exception - a general rule associated with the objects outside the scope of
note 2.

I wanted to thank you for sharing the test case. Interesting and informative.

enjoy,
Karen

Thanks Eli, Karen for the clarification.