Clang and GCC 8 can compile the code without any errors. But GCC 9 gives an error. #include <algorithm> #include <array> #include <iostream> namespace math { /** forward declaration **/ template <typename T, std::size_t N> struct array; /** is_array **/ template <typename T> struct is_array: std::false_type {}; template <typename T, std::size_t N> struct is_array<array<T, N>>: std::true_type {}; template <typename T> inline constexpr bool is_array_v = is_array<T>::value; /** array_depth **/ template <typename T> struct array_depth: std::integral_constant<std::size_t, 0> {}; template <typename T, std::size_t N> struct array_depth<array<T, N>>: std::integral_constant<std::size_t, 1 + array_depth<T>::value> {}; template <typename T> inline constexpr bool array_depth_v = array_depth<T>::value; /** array **/ template <typename T, std::size_t N> struct array: std::array<T, N> { constexpr array operator * (const array& other) const { array result {}; std::transform( std::begin(*this), std::end(*this), std::begin(other), std::begin(result), std::multiplies<T>()); return result; } template <typename Other, std::enable_if_t< (array_depth_v<array> > array_depth_v<Other>), std::nullptr_t> = nullptr> constexpr array operator * (const Other& other) const { array result {}; std::transform( std::begin(*this), std::end(*this), std::begin(result), [other](const T& t) { return t * other; }); return result; } friend std::ostream& operator << (std::ostream& out, const array& source) { out << "<array" << N; if constexpr (!is_array_v<T>) out << typeid(T).name(); for (auto it: source) { if constexpr (!is_array_v<T>) out << " "; out << it; } out << ">"; return out; } }; } int main() { math::array<math::array<float, 4>, 1> a0 {{math::array<float, 4>{{1.f, 2.f, 3.f, 4.f}}}}; std::cout << a0 * 4.f << std::endl; return 0; }
<source>: In substitution of 'template<class T, long unsigned int N> template<class Other, typename std::enable_if<(array_depth_v<math::array<T, N> > > array_depth_v<Other>), std::nullptr_t>::type <anonymous> > constexpr math::array<T, N> math::array<T, N>::operator*(const Other&) const [with Other = <missing>; typename std::enable_if<(array_depth_v<math::array<T, N> > > array_depth_v<Other>), std::nullptr_t>::type <anonymous> = <missing>; T = float; long unsigned int N = <missing>]': <source>:56:36: required from 'constexpr math::array<T, N> math::array<T, N>::operator*(const Other&) const [with Other = float; typename std::enable_if<(array_depth_v<math::array<T, N> > > array_depth_v<Other>), std::nullptr_t>::type <anonymous> = nullptr; T = math::array<float, 4>; long unsigned int N = 1]' <source>:79:20: required from here <source>:48:5: internal compiler error: unexpected expression 'N' of kind template_parm_index 48 | (array_depth_v<array> > array_depth_v<Other>), | ^~~~~~~~~~~~~~~~~~~~ Please submit a full bug report, with preprocessed source if appropriate. See <https://gcc.gnu.org/bugs/> for instructions.
template <typename Other, std::enable_if_t< (array_depth_v<array> > array_depth_v<Other>), std::nullptr_t> = nullptr> constexpr array operator * (const Other& other) const { array result {}; std::transform( std::begin(*this), std::end(*this), std::begin(result), [other](const T& t) { return t * other; }); return result; } but change this line and add -fconcepts ------ template <typename Other, std::enable_if_t< (array_depth_v<array> > array_depth_v<Other>), std::nullptr_t> = nullptr> ----- template <typename Other> requires (array_depth_v<array> > array_depth_v<Other>) gcc 9.0 compile.
Confirmed, started with r9-6404-g1ce59b6cad83d5ca6f1efee83f910d8b677a976a.
Reduced test-case: $ cat pr93275.ii template <int __v> struct A { static constexpr int value = __v; }; template <bool, typename> struct B; template <typename _Tp> struct B<true, _Tp> { typedef _Tp type; }; template <bool _Cond, typename _Tp> using enable_if_t = typename B<_Cond, _Tp>::type; template <typename> constexpr bool array_depth_v = A<1>::value; template <typename T, int> struct C { template <typename Other, enable_if_t<array_depth_v<C>, decltype(nullptr)> = nullptr> void operator*(Other other) { [other](T t) { t *other; }; } }; int main() { C<C<float, 4>, 1> a0; a0 * 4.f; return 0; } $ g++ -c pr93275.ii -std=c++17 pr93275.ii: In substitution of ‘template<class T, int <anonymous> > template<class Other, typename B<array_depth_v<C<T, <anonymous> > >, std::nullptr_t>::type <anonymous> > void C<T, <anonymous> >::operator*(Other) [with Other = <missing>; typename B<array_depth_v<C<T, <anonymous> > >, std::nullptr_t>::type <anonymous> = <missing>; T = float; int <anonymous> = <missing>]’: pr93275.ii:11:22: required from ‘void C<T, <anonymous> >::operator*(Other) [with Other = float; typename B<array_depth_v<C<T, <anonymous> > >, std::nullptr_t>::type <anonymous> = nullptr; T = C<float, 4>; int <anonymous> = 1]’ pr93275.ii:18:8: required from here pr93275.ii:9:25: internal compiler error: unexpected expression ‘<anonymous>’ of kind template_parm_index 9 | enable_if_t<array_depth_v<C>, decltype(nullptr)> = nullptr> | ^~~~~~~~~~~~~~~~ 0x961234 cxx_eval_constant_expression /home/marxin/Programming/gcc/gcc/cp/constexpr.c:6125 0x96174e cxx_eval_outermost_constant_expr /home/marxin/Programming/gcc/gcc/cp/constexpr.c:6323 0x9660a4 maybe_constant_value(tree_node*, tree_node*, bool) /home/marxin/Programming/gcc/gcc/cp/constexpr.c:6593 0xa96124 convert_nontype_argument /home/marxin/Programming/gcc/gcc/cp/pt.c:7116 0xa96124 convert_template_argument /home/marxin/Programming/gcc/gcc/cp/pt.c:8350 0xa96124 convert_template_argument /home/marxin/Programming/gcc/gcc/cp/pt.c:8087 0xa9796b coerce_template_parms /home/marxin/Programming/gcc/gcc/cp/pt.c:8829 0xabce39 lookup_template_class_1 /home/marxin/Programming/gcc/gcc/cp/pt.c:9666 0xabce39 lookup_template_class(tree_node*, tree_node*, tree_node*, tree_node*, int, int) /home/marxin/Programming/gcc/gcc/cp/pt.c:10038 0xab88cf tsubst_aggr_type /home/marxin/Programming/gcc/gcc/cp/pt.c:13332 0xab0d3f tsubst(tree_node*, tree_node*, int, tree_node*) /home/marxin/Programming/gcc/gcc/cp/pt.c:15033 0xab856b tsubst_template_args(tree_node*, tree_node*, int, tree_node*) /home/marxin/Programming/gcc/gcc/cp/pt.c:13125 0xaab074 tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) /home/marxin/Programming/gcc/gcc/cp/pt.c:18999 0xa9f3b7 tsubst_expr(tree_node*, tree_node*, int, tree_node*, bool) /home/marxin/Programming/gcc/gcc/cp/pt.c:18590 0xab2c53 tsubst_expr(tree_node*, tree_node*, int, tree_node*, bool) /home/marxin/Programming/gcc/gcc/cp/pt.c:12047 0xab2c53 tsubst_template_arg(tree_node*, tree_node*, int, tree_node*) /home/marxin/Programming/gcc/gcc/cp/pt.c:12058 0xab2c53 tsubst_template_arg(tree_node*, tree_node*, int, tree_node*) /home/marxin/Programming/gcc/gcc/cp/pt.c:12046 0xab856b tsubst_template_args(tree_node*, tree_node*, int, tree_node*) /home/marxin/Programming/gcc/gcc/cp/pt.c:13125 0xab88aa tsubst_aggr_type /home/marxin/Programming/gcc/gcc/cp/pt.c:13326 0xab08f5 tsubst(tree_node*, tree_node*, int, tree_node*) /home/marxin/Programming/gcc/gcc/cp/pt.c:15662 Please submit a full bug report, with preprocessed source if appropriate. Please include the complete backtrace with any bug report. See <https://gcc.gnu.org/bugs/> for instructions.
(In reply to Martin Liška from comment #3) > Confirmed, started with r9-6404-g1ce59b6cad83d5ca6f1efee83f910d8b677a976a. Are you sure? It's only a daily bump.
(In reply to Marek Polacek from comment #5) > (In reply to Martin Liška from comment #3) > > Confirmed, started with r9-6404-g1ce59b6cad83d5ca6f1efee83f910d8b677a976a. > > Are you sure? It's only a daily bump. Sorry, it's the commit right after it: r9-6405-gbddee796d0b4800b5ac3d7e7e9e315c23799424d For named function calls in a template, the result of unqualified lookup is safed in CALL_EXPR_FN. But for operator expressions, no unqualified lookup is performed until we know whether the operands have class type. So when we see in a lambda a use of an operator that might be overloaded, we need to do that lookup then and save it away somewhere. One possibility would be in the expression, but we can't really add extra conditional operands to standard tree codes. I mostly implemented another approach using a new WITH_LOOKUP_EXPR code, but teaching everywhere how to handle a new tree code is always complicated. Then it occurred to me that we could associate the lookups with the function, which is both simpler and smaller. So this patch stores any operator bindings needed by a lambda function in an internal attribute on the lambda call operator. * name-lookup.c (op_unqualified_lookup) (maybe_save_operator_binding, discard_operator_bindings) (push_operator_bindings): New. * typeck.c (build_x_binary_op, build_x_unary_op): Call maybe_save_operator_binding. * decl.c (start_preparsed_function): Call push_operator_bindings. * tree.c (cp_free_lang_data): Call discard_operator_bindings. From-SVN: r269477
Dup. *** This bug has been marked as a duplicate of bug 93279 ***