Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler segfault during explicit template instantiation on clang 11

The following explicit template instantiation results in compiler frontend segfault under LLVM clang++ 11.0 on x86_64-pc-windows-msvc, using the clang-cl interface with -std=c++17, regardless of optimization level.

A.h

template <typename T>
class A
{
public:

    T value;

    static constexpr auto address = &A<T>::value;
};

extern template class A<float>;

A.cpp

#include "A.h"

template class A<float>;

Note that since C++17 A::address is an inline variable so ODR-using cannot be a problem here.

The compiler behavior is obviously wrong, I already filed a report at the LLVM bug tracker. Nevertheless, I am still curious about the actual correctness of the code.

Is it an undefined behavior which is handled incorrectly by the compiler, or the code itself is free of any problems and it's only about the compiler. Personally I don't find anything in the standard at the specification of explicit template instantiations which would suggest that the above code is wrong.

I don't think that the above is ill-formed, am I missing something?

like image 660
plasmacel Avatar asked Jan 07 '21 13:01

plasmacel


1 Answers

Your example class A

// A.h
#pragma once

template <typename T>
class A {
public:
    T value;
    static constexpr auto address = &A<T>::value;
};

extern template class A<float>;

// A.cpp
#include "A.h"

template class A<float>;

used e.g. as in the following example (full demo here)

#include <iostream>
#include "A.h"

int main() {
    A<float> af{42.F};  // uses explicit instantiation: no implicit instantiation occurs.
    A<int> ai{43};      // implicit instantiation.
    std::cout << af.*A<float>::address   // 42
        << "\n" << ai.*A<int>::address;  // 43
}

is well-formed, and it is naturally a (ICE; internal compiler error) compiler bug that the compiler crashes. Unless your own program also contains uncarefully placed explicit specializations (see below) or is otherwise very different from the minimal example above, your own program should likewise be well-formed.


Details

[temp.explicit]/14 requires, for an explicit instantiation declaration [emphasis mine]:

If an entity is the subject of both an explicit instantiation declaration and an explicit instantiation definition in the same translation unit, the definition shall follow the declaration. An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiation in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required. [...] An explicit instantiation declaration shall not name a specialization of a template with internal linkage.

These requirements are all fulfilled the example above, as are the requirements on the definition of A to precede the explicit definition of a specialization of it as per [temp.explicit]/5.

Finally, [temp.spec]/5 contains the additional requirements that [emphasis mine]:

For a given template and a given set of template-arguments,

  • (5.1) an explicit instantiation definition shall appear at most once in a program,
  • (5.2) an explicit specialization shall be defined at most once in a program, as specified in [basic.def.odr], and
  • (5.3) both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit instantiation follows a declaration of the explicit specialization. An implementation is not required to diagnose a violation of this rule.

(5.1) is fulfilled as the explicit instantiation definition of A<float> is located within a single translation unit (a common ill-formed NDR error is to uncarefully place explicit instantiation definitions in headers, where the header is included in more than a single source file). (5.2) does not apply as there is no explicit specialization of A present (for any specialization), and (5.3) subsequently does not apply as there is no explicit specialization of the A<float> specialization that can conflict with the explicit instantiation definition of the latter.


Finally note that, as per [temp.local]/2, you may use the injected-class-name A when initializing the static data member address of the class template A:

template <typename T>
class A {
public:
    T value;
    static constexpr auto address = &A::value;
};
like image 109
dfrib Avatar answered Nov 13 '22 14:11

dfrib