Runtime error with static template members

Hi folks - I’ve decided to try building the Tart compiler using clang, and I’ve managed to get everything to compile (whew!) but I’m getting a strange runtime error which doesn’t occur when I compile my code under gcc.

Let me explain what the code is doing. As you know, when you have a template that has a static data member, the compiler generates a separate copy of that data member for each instantiation of the template. I often use this feature to generate static data structures from a set of template parameters. In this particular case, I’m using it to construct function signatures for the intrinsic (built-in) functions of my compiler.

The function signature includes an array of parameter definitions, each having a type, a name, and so on. They are defined using a template that looks like this:

/// -------------------------------------------------------------------

/// Template that generates a static formal parameter literal.

template<int type, int index, int paramFlags = 0>

class StaticParamDefn {

public:

static ParameterDefn value;

};

template<int type, int index, int paramFlags>

ParameterDefn StaticParamDefn<type, index, paramFlags>::value(

NULL,

ParameterName::value,

&StaticType::value, paramFlags);

This creates a static instance of the ParameterDefn structure. The ParameterName::value expression evaluates to a constant string such as “a0”, “a1”, “a2” and so on. Similarly the &StaticType::value expression evaluates to a pointer to a type object. The static type objects are defined in a .cpp file, and they look like this:

template<> Type & StaticType<TypeId_Bool>::value = BoolType::instance;

template<> Type & StaticType<TypeId_Void>::value = VoidType::instance;

template<> Type & StaticType<TypeId_Char>::value = CharType::instance;

template<> Type & StaticType<TypeId_SInt8>::value = Int8Type::instance;

template<> Type & StaticType<TypeId_SInt16>::value = Int16Type::instance;

template<> Type & StaticType<TypeId_SInt32>::value = Int32Type::instance;

template<> Type & StaticType<TypeId_SInt64>::value = Int64Type::instance;

template<> Type & StaticType<TypeId_UInt8>::value = UInt8Type::instance;

template<> Type & StaticType<TypeId_UInt16>::value = UInt16Type::instance;

template<> Type & StaticType<TypeId_UInt32>::value = UInt32Type::instance;

template<> Type & StaticType<TypeId_UInt64>::value = UInt64Type::instance;

template<> Type & StaticType<TypeId_Float>::value = FloatType::instance;

template<> Type & StaticType<TypeId_Double>::value = DoubleType::instance;

template<> Type & StaticType<TypeId_UnsizedInt>::value = UnsizedIntType::instance;

This template would normally be invoked like this:

&StaticParamDefn<TypeId_SInt32, 1, ParameterDefn::Variadic>::value,

OK, so now let me explain the problem I’m seeing. The ParameterDefn class has an assert(type != NULL) in its constructor, to insure that the type argument is non-NULL. It’s this assertion that is failing under clang but not under gcc. I should note that the name parameter is fine, it’s only the type parameter that is problematic.

I suspect that the problem is related to the order in which static constructors are being invoked. I’m guessing that the “Type &” variables are actually pointers, and the values are being initialized after the ParameterDefn constructor runs. I had always assumed that the Type & pointers were filled in at compile time, since I never had a problem with this before.

My question is - is there any way to make this work under clang?

Hi folks - I’ve decided to try building the Tart compiler using clang, and I’ve managed to get everything to compile (whew!) but I’m getting a strange runtime error which doesn’t occur when I compile my code under gcc.

Let me explain what the code is doing. As you know, when you have a template that has a static data member, the compiler generates a separate copy of that data member for each instantiation of the template. I often use this feature to generate static data structures from a set of template parameters. In this particular case, I’m using it to construct function signatures for the intrinsic (built-in) functions of my compiler.

The function signature includes an array of parameter definitions, each having a type, a name, and so on. They are defined using a template that looks like this:

/// -------------------------------------------------------------------

/// Template that generates a static formal parameter literal.

template<int type, int index, int paramFlags = 0>

class StaticParamDefn {

public:

static ParameterDefn value;

};

template<int type, int index, int paramFlags>

ParameterDefn StaticParamDefn<type, index, paramFlags>::value(

NULL,

ParameterName::value,

&StaticType::value, paramFlags);

This creates a static instance of the ParameterDefn structure. The ParameterName::value expression evaluates to a constant string such as “a0”, “a1”, “a2” and so on. Similarly the &StaticType::value expression evaluates to a pointer to a type object. The static type objects are defined in a .cpp file, and they look like this:

template<> Type & StaticType<TypeId_Bool>::value = BoolType::instance;

template<> Type & StaticType<TypeId_Void>::value = VoidType::instance;

template<> Type & StaticType<TypeId_Char>::value = CharType::instance;

template<> Type & StaticType<TypeId_SInt8>::value = Int8Type::instance;

template<> Type & StaticType<TypeId_SInt16>::value = Int16Type::instance;

template<> Type & StaticType<TypeId_SInt32>::value = Int32Type::instance;

template<> Type & StaticType<TypeId_SInt64>::value = Int64Type::instance;

template<> Type & StaticType<TypeId_UInt8>::value = UInt8Type::instance;

template<> Type & StaticType<TypeId_UInt16>::value = UInt16Type::instance;

template<> Type & StaticType<TypeId_UInt32>::value = UInt32Type::instance;

template<> Type & StaticType<TypeId_UInt64>::value = UInt64Type::instance;

template<> Type & StaticType<TypeId_Float>::value = FloatType::instance;

template<> Type & StaticType<TypeId_Double>::value = DoubleType::instance;

template<> Type & StaticType<TypeId_UnsizedInt>::value = UnsizedIntType::instance;

This template would normally be invoked like this:

&StaticParamDefn<TypeId_SInt32, 1, ParameterDefn::Variadic>::value,

OK, so now let me explain the problem I’m seeing. The ParameterDefn class has an assert(type != NULL) in its constructor, to insure that the type argument is non-NULL. It’s this assertion that is failing under clang but not under gcc. I should note that the name parameter is fine, it’s only the type parameter that is problematic.

I assume that the ParameterDefn constructor is being called as part of the static constructors?

I suspect that the problem is related to the order in which static constructors are being invoked. I’m guessing that the “Type &” variables are actually pointers, and the values are being initialized after the ParameterDefn constructor runs. I had always assumed that the Type & pointers were filled in at compile time, since I never had a problem with this before.

That’s very possible. GCC is more aggressive about optimizing away static constructors than Clang is, and we’ve seen a few cases where people have depended on GCC’s optimizations here to get the right semantics.

My question is - is there any way to make this work under clang?

Well, there’s the init_priority attribute. You could try that.

  • Doug