Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

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

Closed Quuxplusone closed 9 years ago

Quuxplusone commented 9 years ago
Bugzilla Link PR24048
Status RESOLVED WORKSFORME
Importance P normal
Reported by prismatic.project@gmail.com
Reported on 2015-07-06 18:56:18 -0700
Last modified on 2015-07-11 03:28:53 -0700
Version 3.6
Hardware PC Linux
CC david.majnemer@gmail.com, dgregor@apple.com, llvm-bugs@lists.llvm.org, richard-llvm@metafoo.co.uk
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
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 <iostream>
#include <string>
#include <vector>
#include <utility>
#include <cstdlib>
#include <typeinfo>

using uint = unsigned int;

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

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

private:
    template<typename T>
    static uint next() {
        return ComponentCount<SK>::get_counter();
    }

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

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

// 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
Quuxplusone 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?

Quuxplusone commented 9 years ago

I'll take a look.

Quuxplusone 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