I encountered a strange issue that appears to depend on the initialization syntax I use. The compiler only reports an internal error, and only when I use an initializer list with rvalue elements.
First I made a type to designate a value as an angle.
math.hpp:
// ...
template<class S = float>
struct Angle { S value = 0, cosine = cos(value), sine = sin(value); };
// ...
Next, a quaternion (math object, not really important) with different constructors for regular values and axis-angle form.
quaternion.hpp:
// ...
template<class S = float>
struct Quaternion {
S w, x, y, z;
// ...
Quaternion(S && w = 0, S && x = 0, S && y = 0, S && z = 0):
w(std::move(w)), x(std::move(x)), y(std::move(y)), z(std::move(z)) {}
Quaternion(S const& w, S const& x, S const& y, S const& z):
w(w), x(x), y(y), z(z) {}
Quaternion(Angle<S> const& t = {0}, S const& x = 0, S const& y = 0, S const& z = 0):
w(t.cosine), x(t.sine*x), y(t.sine*y), z(t.sine*z) {}
template<class T> Quaternion(Quaternion<T> const& q):
w(q.w), x(q.x), y(q.y), z(q.z) {}
template<class T> Quaternion(Quaternion<T> && q):
w(std::move(q.w)), x(std::move(q.x)), y(std::move(q.y)), z(std::move(q.z)) {}
virtual ~Quaternion(void) {}
};
// ...
This is what it looks like in use - all of the methods of initializing both angles and quaternions look valid, but like I described earlier, only one combination of methods causes this internal compiler error.
quaternion.cpp:
typedef float T;
T theta = M_PI/2;
Angle<T> a { theta }, b = { theta };
Quaternion<T> q1 = 1, q2 = {2}, q3 = {3, 4, 5, 6},
qval1(Angle<T>{theta}, 1, 0, 0),
// qval2 = {Angle<T>{theta}, 1, 0, 0},
// internal compiler error: in replace_placeholders_r, at cp/tree.c:2804
qref1(a, 1, 0, 0),
qref2 = {a, 1, 0, 0};
I am compiling this as C++14 with gcc version 7.3.0. What is causing the error? Should I report it? Is there a workaround, or should I just avoid that method?
Internal compiler errors are always errors in the compiler. It is best to avoid the areas around these, even if it "should really work in theory".
My experience is that advanced initialization methods are usually a weak field in compilers. Test suites seem to avoid these. I had similar problems 19 years ago with gcc using C and named field initialization, producing internal compiler errors for certain cases.
Try a newer compiler version (like gcc 8).
Workaround if you need your code to be portable: Add a default constructor and put all initialization code into the constructor. Use initialization only for trivial things like plain constant values (not calculated).