Message ID | 1456006974-7430-1-git-send-email-patrick@parcs.ath.cx |
---|---|
State | New |
Headers | show |
On Sat, Feb 20, 2016 at 5:22 PM, Patrick Palka <patrick@parcs.ath.cx> wrote: > 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. Just noticed that this patch would cause auto deduction to malfunction when a non-dependent plain assignment resolves to an operator overload. In that case we should be building the assignment using build_min_non_dep_op_overload as well. Test case: struct Foo { int operator=(const Foo &) { return 5; } }; template <typename> void foo (Foo &x, Foo &y) { auto&& a = (x = y); const int &b = a; } void bar () { Foo a; Foo b; foo<int> (a, b); } operator=.cc: In instantiation of ‘void foo(Foo&, Foo&) [with <template-parameter-1-1> = int]’: operator=.cc:23:17: required from here operator=.cc:13:17: error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’ auto&& a = (x = y); ~~~^~~~
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" }