12

Clang/LLVM 7 and 8 on Windows initialize an inline static data member once per TU. As far as I understand C++17 this is not correct.

Although an inline variable may be defined in multiple TUs the compiler and/or linker must ensure that it exists only once in a program and hence is initialized exactly once.

The following little program shows what happens with Clang/LLVM (tested in Visual Studio 2017 and 2019 RC with LLVM Compiler Toolchain extension):

// header.h

#include <iostream>

struct A
{
  A()      { std::cout << "ctor " << this << std::endl; }
  ~A()     { std::cout << "dtor " << this << std::endl; }
  void f() { std::cout << "f " << this << std::endl;  }
};

struct S
{
  inline static A a; // C++17 inline variable, thus also a definition 
};

// TU1.cpp

#include "header.h"

int main()
{
  S::a.f();
}

// TU2.cpp

#include "header.h"

// TU3.cpp

#include "header.h"

// TU4.cpp

#include "header.h"

This program prints:

ctor 010D4020
ctor 010D4020
ctor 010D4020
ctor 010D4020
f 010D4020
dtor 010D4020
dtor 010D4020
dtor 010D4020
dtor 010D4020

That's four initializations for the one and only object of A (in fact one per TU) instead of exactly one (as C++17 demands).

The program should print:

ctor 010D4020
f 010D4020
dtor 010D4020

This is what MSVC does, by the way.

This is a bug in clang/LLVM, right?

x y
  • 557
  • 3
  • 10
  • What compile options are you using for Clang? – Serge Ballesta Jun 22 '18 at 07:33
  • 4
    What's the difference from [the question](https://stackoverflow.com/q/50946270/5376789) you previously asked? – xskxzr Jun 22 '18 at 07:35
  • @xskxzr The other question refers to lld-link.exe, this one to link.exe. – x y Jun 22 '18 at 07:53
  • @SergeBallesta -Xclang -std=c++17 -Xclang -flto -Xclang -O3 – x y Jun 22 '18 at 07:57
  • Does it depend on the optimization options? And what if you remove `-flto` and what is the meaning of `-Xclang`? As you use `-std=c++17`, I really think that this is a Clang problem but I think that you need to only use relevant option in order to fill a bug report. – Serge Ballesta Jun 22 '18 at 08:32
  • Removing both -flto and -O3 does not change anything. So it does not depend on these two options. -Xclang is necessary for clang's cl.exe (I'm using Visual Studio 2017) because these options must be passed directly to clang. Otherwise they are unused (in case of -O3) or confuse link.exe (in case of -flto). – x y Jun 22 '18 at 09:00
  • I can't reproduce with Clang 7.0.1 (x86_64-w64-windows-gnu) that comes with MSYS2, but I'm using `clang++` rather than `clang-cl`, as I don't have a VS installed. – HolyBlackCat Mar 20 '19 at 18:46
  • Yes, I think it's very specific only to clang-cl. For example, there is no error on Wandbox the last time I tested it. – x y Mar 20 '19 at 18:56

2 Answers2

7

The primary feature of the inline keyword is that it amends the ODR rule in two ways:

  1. Multiple definitions (with some restrictions) are allowed

  2. The resulting objects are "folded" into a single instance:

    An inline function or variable with external linkage shall have the same address in all translation units.

The only addition in C++17 is that it also allows a static data member declaration to be a definition. That's it.

A static data member still has the same linkage (external in your case), storage duration and lifetime, and for all practical purposes works just like a globally defined variable. See [class.static.data]/6:

Static data members are initialized and destroyed exactly like non-local variables

That means essentially that it should work the same as this:

struct A
{
  A()      { std::cout << "ctor "; }
  ~A()     { std::cout << "dtor "; }
};

A a; // in one of the TU's

extern A a; // in all other TU's

Conclusion:

It's a bug in Clang. The static S::a must be initialized and destroyed once.

Community
  • 1
  • 1
rustyx
  • 80,671
  • 25
  • 200
  • 267
  • Thanks, that's what I suspected. I'll file a bug report on llvm.org. – x y Jun 22 '18 at 09:02
  • Well, I reported this as a bug in June 2018 for 6.0.0. But even in the brand new 8.0.0 it is still there. Does anyone know why this is not fixed? – x y Mar 20 '19 at 18:50
  • Is the bug still present in Clang 13? I think so. – MKFein Dec 23 '21 at 11:05
1

This bug is fixed in the current snapshot build based on SVN r361807.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
x y
  • 557
  • 3
  • 10