llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.8k stars 11.91k forks source link

Unexpected result for a type counter using templates with function local types in Clang #24422

Closed llvmbot closed 9 years ago

llvmbot commented 9 years ago
Bugzilla Link 24048
Resolution WORKSFORME
Resolved on Jul 11, 2015 03:28
Version 3.6
OS Linux
Reporter LLVM Bugzilla Contributor
CC @majnemer,@DougGregor,@zygoloid

Extended Description

I wrote a class template that increments a static counter integer based on the template parameters. Clang produces differing output based on whether or not the template parameters are global or function local types. The output with function local types seems wrong. GCC and MSVC do not produce this behavior.

The code was compiled with: clang++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

// == Code == //

include

include

include

include

include

include

using uint = unsigned int;

template<typename SK,typename T> struct Component { static uint const index; };

template class ComponentCount { template<typename CSK,typename CT> friend struct Component;

private: template static uint next() { return ComponentCount::get_counter(); }

static uint get_counter()
{
    static uint counter = 0;
    return counter++;
}

};

template<typename SK,typename T> uint const Component<SK,T>::index(ComponentCount::template next());

// global scope struct X {}; struct Y {};

int main() { // function scope struct Z{};

uint x0 = Component<X,int>::index;
uint x1 = Component<X,double>::index;
uint x2 = Component<X,double>::index;
uint x3 = Component<X,std::string>::index;
uint x4 = Component<X,int>::index;
uint x5 = Component<X,int>::index;

std::cout << x0 << ", " << x1 << ", " << x2 << ", "
          << x3 << ", " << x4 << ", " << x5 << std::endl;

uint y0 = Component<Y,int>::index;
uint y1 = Component<Y,double>::index;
uint y2 = Component<Y,double>::index;
uint y3 = Component<Y,std::string>::index;
uint y4 = Component<Y,int>::index;
uint y5 = Component<Y,int>::index;

std::cout << y0 << ", " << y1 << ", " << y2 << ", "
          << y3 << ", " << y4 << ", " << y5 << std::endl;

uint z0 = Component<Z,int>::index;
uint z1 = Component<Z,double>::index;
uint z2 = Component<Z,double>::index;
uint z3 = Component<Z,std::string>::index;
uint z4 = Component<Z,int>::index;
uint z5 = Component<Z,int>::index;

std::cout << z0 << ", " << z1 << ", " << z2 << ", "
          << z3 << ", " << z4 << ", " << z5 << std::endl;

return 0;

}

// == Expected output == //

Each of the three cout statements should print out: 0, 1, 1, 2, 0, 0

When compiled with clang, the last statement (using the function local type 'Z') prints out: 5, 2, 2, 3, 5, 5

A reference example that can be run in-browser can be found here (link may not be permanent)

http://coliru.stacked-crooked.com/a/7fcb989ae6eab476

991901f3-cc14-4404-b340-165691b62a58 commented 9 years ago

This doesn't reproduce on trunk for me, I believe r234675 fix this: Author: Reid Kleckner reid@kleckner.net CommitDate: Sat Apr 11 01:25:36 2015 +0000

Only notify consumers about static data members of class templates once
991901f3-cc14-4404-b340-165691b62a58 commented 9 years ago

I'll take a look.

ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 9 years ago

We presumably don't use a guard variable for dynamic initialization of a non-externally-visible static data member, but somehow we manage to emit the initialization multiple times.

David, can I tempt you with this?

llvmbot commented 9 years ago

assigned to @majnemer