This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fix PR c++/69694 (non-dependent MODOP_EXPR with NULL type)


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]