The problem here is that when processing_template_decl, the non-compound MODOP_EXPRs we build (i.e. a = b and not a += b) are given a NULL TREE_TYPE even if none of its operands are dependent. This causes decltypes such as "decltype (a = b)" (where a and b are not dependent) to fail to get resolved to a concrete type since the MODOP_EXPR within is considered to be dependent according to instantiation_dependent_expression_p. And in the case of decltype65.C this causes partial-specialization selection to malfunction since the template parameter type "void_t<decltype (a = b)>" never gets resolved to "void".
This patch fixes this issue by adjusting build_x_modify_expr to give non-compound non-dependent MODOP_EXPRs an accurate non-NULL TREE_TYPE. To do this we have to first process the assignment at template processing time using cp_build_modify_expr. This means we will now diagnose invalid assignments at template-processing time, necessitating some minor adjustments to the testsuite. The changes to the test suite are trivial except for the change to unary2.C. Here, whereas before we were always failing to diagnose at template processing time the invalid assignment -n = 0 (whose LHS is not an lvalue), after this patch we now fail to diagnose this invalid assignment only with c++98. This is because lvalue_kind treats NON_DEPENDENT_EXPRs differently depending on the cxx_dialect: case NON_DEPENDENT_EXPR: /* We just return clk_ordinary for NON_DEPENDENT_EXPR in C++98, but in C++11 lvalues don't bind to rvalue references, so we need to work harder to avoid bogus errors (c++/44870). */ if (cxx_dialect < cxx11) return clk_ordinary; else return lvalue_kind (TREE_OPERAND (ref, 0)); So in c++98 mode any NON_DEPENDENT_EXPR is considered to be a valid LHS of an assignment even if the underlying expression is not actually an lvalue. Removing this special case is not completely trivial. Bootstrap + regtest in progress on x86_64-pc-linux-gnu, will also test against Boost. Does this look OK if testing passes? gcc/cp/ChangeLog: PR c++/69694 * semantics.c (finish_paranthesized_expr): Set the TREE_NO_WARNING flag on MODOP_EXPRs that are wrapped in an implicit INDIRECT_REF. * typeck.c (build_x_modify_expr): Give the middle operand of the resulting MODOP_EXPR a dummy non-NULL type. When MODIFYCODE is NOP_EXPR and the operands are not dependent, don't exit early and instead process the expression with cp_build_modify_expr. Assert that the return value of build_new_op is non-NULL. gcc/testsuite/ChangeLog: PR c++/69694 * g++.dg/cpp0x/decltype64.C: New test. * g++.dg/cpp0x/decltype65.C: New test. * g++.dg/expr/unary2.C: The XFAILs no longer fail on c++11 or later, only with c++98. * g++.dg/cpp0x/error2.C: Adjust expected error message. * g++.dg/ext/fixed1.C: Likewise. * g++.dg/template/error35.C: Likewise. * g++.dg/template/init7.C: Likewise. --- gcc/cp/semantics.c | 7 +++++++ gcc/cp/typeck.c | 30 +++++++++++++++++++++--------- gcc/testsuite/g++.dg/cpp0x/decltype64.C | 30 ++++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp0x/decltype65.C | 24 ++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp0x/error2.C | 2 +- gcc/testsuite/g++.dg/expr/unary2.C | 4 ++-- gcc/testsuite/g++.dg/ext/fixed1.C | 4 ++-- gcc/testsuite/g++.dg/template/error35.C | 2 +- gcc/testsuite/g++.dg/template/init7.C | 3 ++- 9 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype64.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/decltype65.C diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 4bbc698..20f263f 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -1704,6 +1704,13 @@ finish_parenthesized_expr (cp_expr expr) /* This inhibits warnings in c_common_truthvalue_conversion. */ TREE_NO_WARNING (expr) = 1; + /* This inhibits -Wparentheses warnings for parenthesized non-dependent + MODOP_EXPRs, which are wrapped in an implicit INDIRECT_REF. */ + if (processing_template_decl + && REFERENCE_REF_P (expr) + && TREE_CODE (TREE_OPERAND (expr, 0)) == MODOP_EXPR) + TREE_NO_WARNING (TREE_OPERAND (expr, 0)) = 1; + if (TREE_CODE (expr) == OFFSET_REF || TREE_CODE (expr) == SCOPE_REF) /* [expr.unary.op]/3 The qualified id of a pointer-to-member must not be diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 5145879..3da6ea1 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -7869,26 +7869,39 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, tree orig_lhs = lhs; tree orig_rhs = rhs; tree overload = NULL_TREE; - tree op = build_nt (modifycode, NULL_TREE, NULL_TREE); + /* Give this middle operand of the resulting MODOP_EXPR a dummy non-NULL type + so that instantiation_dependent_expression_p won't later consider an + otherwise non-dependent MODOP_EXPR to be dependent. */ + tree op = build_min (modifycode, void_type_node, NULL_TREE, NULL_TREE); if (processing_template_decl) { - if (modifycode == NOP_EXPR - || type_dependent_expression_p (lhs) + if (type_dependent_expression_p (lhs) || type_dependent_expression_p (rhs)) - return build_min_nt_loc (loc, MODOP_EXPR, lhs, - build_min_nt_loc (loc, modifycode, NULL_TREE, - NULL_TREE), rhs); + return build_min_nt_loc (loc, MODOP_EXPR, lhs, op, rhs); lhs = build_non_dependent_expr (lhs); rhs = build_non_dependent_expr (rhs); } - if (modifycode != NOP_EXPR) + if (modifycode == NOP_EXPR) + { + tree result = cp_build_modify_expr (lhs, modifycode, rhs, complain); + + if (processing_template_decl) + return build_min_non_dep (MODOP_EXPR, result, orig_lhs, op, orig_rhs); + + return result; + } + else { tree rval = build_new_op (loc, MODIFY_EXPR, LOOKUP_NORMAL, lhs, rhs, op, &overload, complain); - if (rval) + + /* This shouldn't happen. */ + if (rval == NULL_TREE) + gcc_unreachable (); + else { if (rval == error_mark_node) return rval; @@ -7905,7 +7918,6 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, return rval; } } - return cp_build_modify_expr (lhs, modifycode, rhs, complain); } /* Helper function for get_delta_difference which assumes FROM is a base diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype64.C b/gcc/testsuite/g++.dg/cpp0x/decltype64.C new file mode 100644 index 0000000..9ddd1c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/decltype64.C @@ -0,0 +1,30 @@ +// PR c++/69694 +// { dg-do compile { target c++11 } } + +// n3911: TransformationTrait Alias `void_t` +template<typename...> struct make_void { using type = void; }; +template<typename... Ts> using void_t = typename make_void<Ts...>::type; + +// std::declval<void*&> +void*& declval_void(); + +template<typename, typename = void> struct Fun; +template<typename R> + struct Fun<R(), void> +{ + void fun(); +}; +template<typename Desc> + struct Fun<Desc, void_t<decltype (declval_void() = Desc::name)>> + : Fun<void()> +{ +}; + +struct Tag { static constexpr void* name = 0; }; + +template<typename> void a() +{ + Fun<Tag>{}.fun(); +} + +void b() { a<int>(); } diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype65.C b/gcc/testsuite/g++.dg/cpp0x/decltype65.C new file mode 100644 index 0000000..95fe0f1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/decltype65.C @@ -0,0 +1,24 @@ +// PR c++/69694 +// This is a reduced version of decltype64.C. +// { dg-do compile { target c++11 } } + +template<typename... Ts> using void_t = void; + +extern void *declval_void; + +template<typename, typename> struct Fun { }; + +template<typename Desc> +struct Fun<Desc, void_t<decltype (declval_void = Desc::name)>> +{ + void fun(); +}; + +struct Tag { static constexpr void* name = 0; }; + +template<typename> void a() +{ + Fun<Tag, void>{}.fun(); +} + +void b() { a<int>(); } diff --git a/gcc/testsuite/g++.dg/cpp0x/error2.C b/gcc/testsuite/g++.dg/cpp0x/error2.C index e6af294..ec58c9c 100644 --- a/gcc/testsuite/g++.dg/cpp0x/error2.C +++ b/gcc/testsuite/g++.dg/cpp0x/error2.C @@ -5,5 +5,5 @@ template<int> int foo(); template<typename F> void bar(F f) { - f((foo<0>()=0)...); // { dg-error "pattern '\\(foo\\<0\\>\\)\\(\\)=0'" } + f((foo<0>()=0)...); // { dg-error "lvalue|pattern '\\(foo\\<0\\>\\)\\(\\)=0'" } } diff --git a/gcc/testsuite/g++.dg/expr/unary2.C b/gcc/testsuite/g++.dg/expr/unary2.C index 8418815..80bfd7d 100644 --- a/gcc/testsuite/g++.dg/expr/unary2.C +++ b/gcc/testsuite/g++.dg/expr/unary2.C @@ -15,6 +15,6 @@ void f(void) template <int> void g(void) { - -n = 0; // { dg-error "lvalue" "" { xfail *-*-* } } - +n = 0; // { dg-error "lvalue" "" { xfail *-*-* } } + -n = 0; // { dg-error "lvalue" "" { xfail c++98_only } } + +n = 0; // { dg-error "lvalue" "" { xfail c++98_only } } } diff --git a/gcc/testsuite/g++.dg/ext/fixed1.C b/gcc/testsuite/g++.dg/ext/fixed1.C index 5a479d6..4fcea0f 100644 --- a/gcc/testsuite/g++.dg/ext/fixed1.C +++ b/gcc/testsuite/g++.dg/ext/fixed1.C @@ -3,6 +3,6 @@ template<int> struct A {}; -template<typename> struct B : A<sizeof(0=0r)> {}; // { dg-error "not supported" } +template<typename> struct B : A<sizeof(0=0r)> {}; // { dg-error "convert|not supported" } -template<typename> struct C : A<sizeof(0=0r)> {}; // { dg-error "not supported" } +template<typename> struct C : A<sizeof(0=0r)> {}; // { dg-error "convert|not supported" } diff --git a/gcc/testsuite/g++.dg/template/error35.C b/gcc/testsuite/g++.dg/template/error35.C index d52e599..3b11fb7 100644 --- a/gcc/testsuite/g++.dg/template/error35.C +++ b/gcc/testsuite/g++.dg/template/error35.C @@ -1,3 +1,3 @@ // PR c++/33494 -template<int> void foo(int(*f=0)()); // { dg-error "declared void|scope|erroneous-expression" } +template<int> void foo(int(*f=0)()); // { dg-error "declared void|scope|expression" } diff --git a/gcc/testsuite/g++.dg/template/init7.C b/gcc/testsuite/g++.dg/template/init7.C index bb26c8f..3410c01 100644 --- a/gcc/testsuite/g++.dg/template/init7.C +++ b/gcc/testsuite/g++.dg/template/init7.C @@ -6,4 +6,5 @@ template<typename> struct A static const int i=0; }; -template<typename T> const int A<T>::i = 0=0; /* { dg-error "duplicate initialization" } */ +template<typename T> +const int A<T>::i = 0=0; // { dg-error "duplicate initialization|lvalue" } -- 2.7.1.348.gb763790