On 04/20/2018 01:44 PM, Jason Merrill wrote:

Any time we need an actual adjustment, there will be a PLUS_EXPR. The issue is somehow distinguishing between a reinterpret_cast and one of the many other sources of NOP_EXPR.

yeah, I see that now. Perhaps VIEW_CONVERT_EXPR is more appropriate for the reinterpret_cast case?

Anyway, such a change would require auditing a lot of NOP_EXPR uses. This less invasive patch instead adds a REINTERPRET_CAST_P flag, which we set on NOP_EXPRs coming out of build_reinterpret_1. Then in cxx_eval_constant_expression we reject any NOP_EXPR that has that flag set. We can get rid of the subsequent special casing of a NOP_EXPR involving a PTRMEM_CST. I have to change convert_ptrmem to always expand the constant (into an OFFSET_TYPE) so that initializer_constant_valid_p (used by reduced_constant_expression_p) doesn't get confused by a zero-adjusting conversion of a ptrmem_cst.

cpp0x/addressof1.C thinks thinks like
'static_assert (reinterpret_cast <T*>(&thing) == &thing.member)'
are constant expressions, but AFAICT they are not
cpp0x/constexpr-pmf1.C is checking an optimization occurs at the genericization level without turning the optimizer on. IMHO we only need to check this is happening at some point when the optimizer is turned on. (The original bug was wrong code, but then perhaps it should be a runtime check?)

WDYT?

nathan
--
Nathan Sidwell
2018-04-20  Nathan Sidwell  <nat...@acm.org>

	PR c++/85437
	PR c++/49171
	* cp-tree.h (REINTERPRETT_CAST_P): New.
	* constexpr.c (cxx_eval_constant_expression) <case NOP_EXPR>:
	Reject REINTERPET_CAST_P conversions.  Remove PTRMEM_CST check.
	* typeck.c (convert_ptrmem): Expand PTRMEM_CST even in the
	zero-addition case.
	(build_nop_reinterpret): New.
	(build_reinterpret_cast_1): Use it.  Set REINTERPRET_CAST_P on
	NOP_EXPRs returned by cp_convert.

2018-04-20  Jakub Jelinek  <ja...@redhat.com>

	PR c++/85437
	PR c++/49171
	* g++.dg/cpp0x/addressof1.C: Comment out reinterpret_cast cases.
	* g++.dg/cpp0x/constexpr-cast.C: Remove xfails
	* g++.dg/cpp0x/constexpr-nullptr-2.C: Likewise.
	* g++.dg/cpp0x/constexpr-pmf1.C: Check when optimized.
	* g++.dg/cpp0x/pr85437-1.C: New.
	* g++.dg/cpp0x/pr85437-2.C: New.
	* g++.dg/cpp0x/pr85437-3.C: New.
	* g++.dg/cpp0x/pr85437-4.C: New.

Index: gcc/cp/constexpr.c
===================================================================
--- gcc/cp/constexpr.c	(revision 259523)
+++ gcc/cp/constexpr.c	(working copy)
@@ -1822,8 +1822,8 @@ reduced_constant_expression_p (tree t)
 }
 
 /* Some expressions may have constant operands but are not constant
-   themselves, such as 1/0.  Call this function (or rather, the macro
-   following it) to check for that condition.
+   themselves, such as 1/0.  Call this function to check for that
+   condition.
 
    We only call this in places that require an arithmetic constant, not in
    places where we might have a non-constant expression that can be a
@@ -4579,9 +4579,18 @@ cxx_eval_constant_expression (const cons
 				       non_constant_p, overflow_p);
       break;
 
+    case NOP_EXPR:
+      if (REINTERPRET_CAST_P (t))
+	{
+	  if (!ctx->quiet)
+	    error_at (EXPR_LOC_OR_LOC (t, input_location),
+		      "a reinterpret_cast is not a constant expression");
+	  *non_constant_p = true;
+	  return t;
+	}
+      /* FALLTHROUGH.  */
     case CONVERT_EXPR:
     case VIEW_CONVERT_EXPR:
-    case NOP_EXPR:
     case UNARY_PLUS_EXPR:
       {
 	tree oldop = TREE_OPERAND (t, 0);
@@ -4595,21 +4604,6 @@ cxx_eval_constant_expression (const cons
 	if (TREE_CODE (op) == PTRMEM_CST
 	    && !TYPE_PTRMEM_P (type))
 	  op = cplus_expand_constant (op);
-	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
-	  {
-	    if (same_type_ignoring_top_level_qualifiers_p (type,
-							   TREE_TYPE (op))
-		|| can_convert_qual (type, op))
-	      return cp_fold_convert (type, op);
-	    else
-	      {
-		if (!ctx->quiet)
-		  error_at (EXPR_LOC_OR_LOC (t, input_location),
-			    "a reinterpret_cast is not a constant expression");
-		*non_constant_p = true;
-		return t;
-	      }
-	  }
 
 	if (POINTER_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST)
 	  {
@@ -4653,14 +4647,17 @@ cxx_eval_constant_expression (const cons
 		return t;
 	      }
 	  }
+
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
 	     conversion.  */
 	  return fold (t);
+
 	if (tcode == UNARY_PLUS_EXPR)
 	  r = fold_convert (TREE_TYPE (t), op);
 	else
 	  r = fold_build1 (tcode, type, op);
+
 	/* Conversion of an out-of-range value has implementation-defined
 	   behavior; the language considers it different from arithmetic
 	   overflow, which is undefined.  */
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 259523)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -372,6 +372,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       TEMPLATE_TYPE_PARM_FOR_CLASS (TEMPLATE_TYPE_PARM)
       DECL_NAMESPACE_INLINE_P (in NAMESPACE_DECL)
       SWITCH_STMT_ALL_CASES_P (in SWITCH_STMT)
+      REINTERPRET_CAST_P (in NOP_EXPR)
    1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -631,6 +632,11 @@ typedef struct ptrmem_cst * ptrmem_cst_t
 #define COND_EXPR_IS_VEC_DELETE(NODE) \
   TREE_LANG_FLAG_0 (COND_EXPR_CHECK (NODE))
 
+/* Nonzero if this NOP_EXPR is a reinterpret_cast.  Such conversions
+   are not constexprs.  Other NOP_EXPRs are.  */
+#define REINTERPRET_CAST_P(NODE)		\
+  TREE_LANG_FLAG_0 (NOP_EXPR_CHECK (NODE))
+
 /* Returns nonzero iff TYPE1 and TYPE2 are the same type, in the usual
    sense of `same'.  */
 #define same_type_p(TYPE1, TYPE2) \
Index: gcc/cp/typeck.c
===================================================================
--- gcc/cp/typeck.c	(revision 259523)
+++ gcc/cp/typeck.c	(working copy)
@@ -6837,25 +6837,23 @@ convert_ptrmem (tree type, tree expr, bo
       if (delta == error_mark_node)
 	return error_mark_node;
 
+      if (TREE_CODE (expr) == PTRMEM_CST)
+	expr = cplus_expand_constant (expr);
+
       if (!integer_zerop (delta))
 	{
-	  tree cond, op1, op2;
-
-	  if (TREE_CODE (expr) == PTRMEM_CST)
-	    expr = cplus_expand_constant (expr);
-	  cond = cp_build_binary_op (input_location,
-				     EQ_EXPR,
-				     expr,
-				     build_int_cst (TREE_TYPE (expr), -1),
-				     complain);
-	  op1 = build_nop (ptrdiff_type_node, expr);
-	  op2 = cp_build_binary_op (input_location,
-				    PLUS_EXPR, op1, delta,
-				    complain);
+	  tree cond = cp_build_binary_op (input_location,
+					  EQ_EXPR,
+					  expr,
+					  build_int_cst (TREE_TYPE (expr), -1),
+					  complain);
+	  tree op1 = build_nop (ptrdiff_type_node, expr);
+	  tree op2 = cp_build_binary_op (input_location,
+					 PLUS_EXPR, op1, delta,
+					 complain);
 
 	  expr = fold_build3_loc (input_location,
-			      COND_EXPR, ptrdiff_type_node, cond, op1, op2);
-			 
+				  COND_EXPR, ptrdiff_type_node, cond, op1, op2);
 	}
 
       return build_nop (type, expr);
@@ -7275,6 +7273,17 @@ convert_member_func_to_ptr (tree type, t
   return build_nop (type, expr);
 }
 
+/* Build a NOP_EXPR to TYPE, but mark it as a reinterpret_cast so that
+   constexpr evaluation knows to reject it.  */
+
+static tree
+build_nop_reinterpret (tree type, tree expr)
+{
+  expr = build_nop (type, expr);
+  REINTERPRET_CAST_P (expr) = true;
+  return expr;
+}
+
 /* Return a representation for a reinterpret_cast from EXPR to TYPE.
    If C_CAST_P is true, this reinterpret cast is being done as part of
    a C-style cast.  If VALID_P is non-NULL, *VALID_P is set to
@@ -7409,7 +7418,7 @@ build_reinterpret_cast_1 (tree type, tre
 	warning (OPT_Wcast_function_type,
 		 "cast between incompatible function types"
 		 " from %qH to %qI", intype, type);
-      return build_nop (type, expr);
+      return build_nop_reinterpret (type, expr);
     }
   else if (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype))
     {
@@ -7420,7 +7429,7 @@ build_reinterpret_cast_1 (tree type, tre
 	warning (OPT_Wcast_function_type,
 		 "cast between incompatible pointer to member types"
 		 " from %qH to %qI", intype, type);
-      return build_nop (type, expr);
+      return build_nop_reinterpret (type, expr);
     }
   else if ((TYPE_PTRDATAMEM_P (type) && TYPE_PTRDATAMEM_P (intype))
 	   || (TYPE_PTROBV_P (type) && TYPE_PTROBV_P (intype)))
@@ -7446,7 +7455,7 @@ build_reinterpret_cast_1 (tree type, tre
 	/* strict_aliasing_warning STRIP_NOPs its expr.  */
 	strict_aliasing_warning (EXPR_LOCATION (expr), type, expr);
 
-      return build_nop (type, expr);
+      return build_nop_reinterpret (type, expr);
     }
   else if ((TYPE_PTRFN_P (type) && TYPE_PTROBV_P (intype))
 	   || (TYPE_PTRFN_P (intype) && TYPE_PTROBV_P (type)))
@@ -7457,7 +7466,7 @@ build_reinterpret_cast_1 (tree type, tre
 	warning (OPT_Wconditionally_supported,
 		 "casting between pointer-to-function and pointer-to-object "
 		 "is conditionally-supported");
-      return build_nop (type, expr);
+      return build_nop_reinterpret (type, expr);
     }
   else if (VECTOR_TYPE_P (type))
     return convert_to_vector (type, expr);
@@ -7473,7 +7482,11 @@ build_reinterpret_cast_1 (tree type, tre
       return error_mark_node;
     }
 
-  return cp_convert (type, expr, complain);
+  expr = cp_convert (type, expr, complain);
+  if (TREE_CODE (expr) == NOP_EXPR)
+    /* Mark any nop_expr that created as a reintepret_cast.  */
+    REINTERPRET_CAST_P (expr) = true;
+  return expr;
 }
 
 tree
Index: gcc/testsuite/g++.dg/cpp0x/addressof1.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/addressof1.C	(revision 259523)
+++ gcc/testsuite/g++.dg/cpp0x/addressof1.C	(working copy)
@@ -18,9 +18,9 @@ static_assert (addressof (j) == &i, "");
 
 struct S { int s; } s;
 static_assert (__builtin_addressof (s) == &s, "");
-static_assert ((int *) __builtin_addressof (s) == &s.s, "");
+//static_assert ((int *) __builtin_addressof (s) == &s.s, "");
 static_assert (addressof (s) == &s, "");
-static_assert ((int *) addressof (s) == &s.s, "");
+//static_assert ((int *) addressof (s) == &s.s, "");
 
 struct T
 {
@@ -31,9 +31,9 @@ struct T
 };
 constexpr T t;
 T T::tt;
-static_assert (__builtin_addressof (t) == (const T *) &t.p, "");
+//static_assert (__builtin_addressof (t) == (const T *) &t.p, "");
 static_assert (&t == __builtin_addressof (T::tt), "");
-static_assert (addressof (t) == (const T *) &t.p, "");
+//static_assert (addressof (t) == (const T *) &t.p, "");
 static_assert (&t == addressof (T::tt), "");
 
 struct S x, y;
@@ -76,8 +76,8 @@ constexpr int a = 1;
 static_assert (__builtin_addressof (a) == &a, "");
 static_assert (addressof (a) == &a, "");
 constexpr int c[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
-static_assert ((const int *) __builtin_addressof (c) == &c[0], "");
-static_assert ((const int *) addressof (c) == &c[0], "");
+//static_assert ((const int *) __builtin_addressof (c) == &c[0], "");
+//static_assert ((const int *) addressof (c) == &c[0], "");
 
 void
 baz ()
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C	(revision 259523)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C	(working copy)
@@ -4,8 +4,8 @@
 
 int i;
 
-// The following is accepted due to bug 49171.
-constexpr void *q = reinterpret_cast<void*>(&i);    // { dg-error "" "bug c++/49171" { xfail *-*-* } }
+// The following was accepted due to bug 49171.
+constexpr void *q = reinterpret_cast<void*>(&i);    // { dg-error "not a constant expression" }
 
 constexpr void *r0 = reinterpret_cast<void*>(1);    // { dg-error "not a constant expression|reinterpret_cast from integer to pointer" }
 constexpr void *r1 = reinterpret_cast<void*>(sizeof 'x');  // { dg-error ".reinterpret_cast<void\\*>\\(1\[ul\]\*\\). is not a constant expression" }
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C	(revision 259523)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C	(working copy)
@@ -99,9 +99,8 @@ constexpr const volatile void* pv3 = p0;
 constexpr void* pv4 = static_cast<void*>(p0);
 constexpr const void* pv5 = static_cast<const void*>(p0);
 
-// The following should be rejected but isn't because of bug c++/49171
-// - [C++0x][constexpr] Constant expressions support reinterpret_cast
-constexpr void* pv6 = reinterpret_cast<void*>(p0);   // { dg-error "" "bug c++/49171" { xfail *-*-* } }
+// The following was accepted due to bug c++/49171
+constexpr void* pv6 = reinterpret_cast<void*>(p0);   // { dg-error "not a constant expression" }
 
 // Adding or subtracting zero from a null pointer is valid in C++.
 constexpr int* p1 = p0 + 0;
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-pmf1.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-pmf1.C	(revision 259523)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-pmf1.C	(working copy)
@@ -1,6 +1,6 @@
 // PR c++/77775
-// { dg-options -fdump-tree-gimple }
-// { dg-final { scan-tree-dump "== viewAdded" "gimple" { target c++11 } } }
+// { dg-options "-fdump-tree-fre1 -O2" }
+// { dg-final { scan-tree-dump "== viewAdded" "fre1" { target c++11 } } }
 
 namespace Sublime {
 struct View;
Index: gcc/testsuite/g++.dg/cpp0x/pr85437-1.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/pr85437-1.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-1.C	(working copy)
@@ -0,0 +1,17 @@
+// PR c++/85437
+// { dg-do compile { target c++11 } }
+
+struct A { int a; constexpr A() : a(0) {} };
+struct B : A { int x; constexpr B() : x(0) {} };
+struct X { int z; constexpr X() : z(0) {} };
+struct C : X, B {};
+constexpr int C::*cbx = &B::x;
+constexpr int B::*bx = &B::x;
+constexpr int A::*abx = static_cast<int(A::*)>(&B::x);	// { dg-bogus "not a constant expression" }
+
+constexpr const C y;
+constexpr const B& yb = y;
+constexpr const A& ya = y;
+constexpr int const *pcbx = &(y.*cbx);
+constexpr int const *pbx = &(y.*bx);
+constexpr int const *pabx = &(ya.*abx);
Index: gcc/testsuite/g++.dg/cpp0x/pr85437-2.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/pr85437-2.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-2.C	(working copy)
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { dg-do compile { target c++11 } }
+
+struct A { };
+struct B : A { int x; };
+
+constexpr int A::*abx
+= reinterpret_cast<int(A::*)>(&B::x); // { dg-error "reinterpret.*constant" }
Index: gcc/testsuite/g++.dg/cpp0x/pr85437-3.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/pr85437-3.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-3.C	(working copy)
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { dg-do compile { target c++11 } }
+
+struct A { int y; };
+struct B { int x; };
+struct C : A, B {};
+constexpr int C::*pci = &B::x;
+constexpr int A::*pai = static_cast<int A::*>(static_cast<int C::*>(&B::x)); // { dg-bogus "not a constant expression" }
Index: gcc/testsuite/g++.dg/cpp0x/pr85437-4.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/pr85437-4.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-4.C	(working copy)
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { dg-do compile { target c++11 } }
+
+struct A { };
+struct B { int x; };
+struct C : A, B {};
+constexpr int C::*pci = &B::x;
+constexpr int A::*pai = static_cast<int A::*>(pci);	// { dg-bogus "not a constant expression" }

Reply via email to