Created attachment 33455 [details] reproduces decltype bug Attempting to use decltype to name an argument type in a template function declaration results in a spurious error message. For example, // Reproduce decltype bug, condensed from Spirit X3 // gcc 4.9.1 fails to identify f1's declaration template <typename T> struct s1; template <typename T> struct s2 { typedef s1<T> t1; }; template <typename T> struct s1 { template <typename T2> s2<T> operator = (T2 const &) const; }; struct T; s1<T> v1; s2<T> v2; template <typename T> void f1(decltype(v1 = v2)::t1); Compiling this yields: blackice:decltype-bug rabeles$ ~/Development/gcc/gcc-4.9.1-baseline-build/gcc/cc1plus test1.cpp --std=c++1y test1.cpp:19:30: error: variable or field ‘f1’ declared void void f1(decltype(v1 = v2)::t1);
struct X { using t1 = int; }; struct Y { X operator=(const Y&); } y; template <typename T> void f1(decltype(y = y)::t1); $ g++ -std=c++11 -c d.cc d.cc:3:50: error: variable or field ‘f1’ declared void template <typename T> void f1(decltype(y = y)::t1); ^ Using -std=c++1y is not necessary, this doesn't use any C++14 features. The error only happens if f1 is a template, and only seems to happen if the decltype expression uses the assignment operator, not any other expression I tried.
This bug occurs when cp/parser.c:cp_parser_name() while parsing 'decltype(y = y)::t1' is called on 't1'. Earlier, the decltype has been determined to be dependent (it isn't actually in this case, but doing so appears to be a reasonable simplification), so cp_parser_name() defers the look up and returns the result of cp/tree.c:build_qualified_name(). The calling routine, cp/parser.c:cp_parser_class_name() fails to handle this case, returning an error_mark_node.
I tried investigating this and it seems that decltype( v1 = v2 )::t1 should be handled as a dependent type, but isn't. I modified the test case like this template <typename T> void f1(typename decltype(v1 = v2)::t1); and the problem seems to be solved.
I investigated a bit further and I come up to the same conclusion as Bob Abeles. Having an assignment operator inside the decltype makes gcc believe the type is dependent. There are a couple of spots where the assignment operator is handled specifically, most probably it isn't handled correctly inside the decltype.
I think I found the root of the problem. I'm preparing a patch.
The assignment operator is an operator which always has side effects. That is why it is considered dependent during template definitions to prevent early instanciations of code. By adding a typename before the decltype, everything is fine. Trying to add the typename implicitly works well to solve this small test case. However, using the Spirit X3 code, it only moves the problem. The template function overload doesn't get resolved correctly, so there must be some other solution that is more appropriate. Figuring out what exactly is going wrong was above my capacities, so I'll leave this to the pros.
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>: https://gcc.gnu.org/g:6e92a6a2a72d3b7a5e1b29042d8a6a43fe1085aa commit r14-4111-g6e92a6a2a72d3b7a5e1b29042d8a6a43fe1085aa Author: Patrick Palka <ppalka@redhat.com> Date: Mon Sep 18 14:47:52 2023 -0400 c++: non-dependent assignment checking [PR63198, PR18474] This patch makes us recognize and check non-dependent simple assigments ahead of time, like we already do for compound assignments. This means the templated representation of such assignments will now usually have an implicit INDIRECT_REF (due to the reference return type), which the -Wparentheses code needs to handle. As a drive-by improvement, this patch also makes maybe_convert_cond issue -Wparentheses warnings ahead of time, and removes a seemingly unnecessary suppress_warning call in build_x_modify_expr. On the libstdc++ side, some tests were attempting to modify a data member from a uninstantiated const member function, which this patch minimally fixes by making the data member mutable. PR c++/63198 PR c++/18474 gcc/cp/ChangeLog: * semantics.cc (maybe_convert_cond): Look through implicit INDIRECT_REF when deciding whether to issue a -Wparentheses warning, and consider templated assignment expressions as well. (finish_parenthesized_expr): Look through implicit INDIRECT_REF when suppressing -Wparentheses warning. * typeck.cc (build_x_modify_expr): Check simple assignments ahead time too, not just compound assignments. Give the second operand of MODOP_EXPR a non-null type so that it's not considered always instantiation-dependent. Don't call suppress_warning. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/static_assert15.C: Expect diagnostic for non-constant static_assert condition. * g++.dg/expr/unary2.C: Remove xfails. * g++.dg/template/init7.C: Make initializer type-dependent to preserve intent of test. * g++.dg/template/recurse3.C: Likewise for the erroneous statement. * g++.dg/template/non-dependent26.C: New test. * g++.dg/warn/Wparentheses-32.C: New test. libstdc++-v3/ChangeLog: * testsuite/26_numerics/random/discard_block_engine/cons/seed_seq2.cc: Make data member seed_seq::called mutable. * testsuite/26_numerics/random/independent_bits_engine/cons/seed_seq2.cc: Likewise. * testsuite/26_numerics/random/linear_congruential_engine/cons/seed_seq2.cc: Likewise. * testsuite/26_numerics/random/mersenne_twister_engine/cons/seed_seq2.cc: Likewise. * testsuite/26_numerics/random/shuffle_order_engine/cons/seed_seq2.cc: Likewise. * testsuite/26_numerics/random/subtract_with_carry_engine/cons/seed_seq2.cc: Likewise. * testsuite/ext/random/simd_fast_mersenne_twister_engine/cons/seed_seq2.cc: Likewise.
Fixed for GCC 14.