Patchwork C++ PATCH for c++/54532 (constexpr ptrmem)

login
register
mail settings
Submitter Jason Merrill
Date March 21, 2013, 3:18 a.m.
Message ID <514A7C05.4080607@redhat.com>
Download mbox | patch
Permalink /patch/229545/
State New
Headers show

Comments

Jason Merrill - March 21, 2013, 3:18 a.m.
While a class is still being defined, we don't know the offsets of the 
members, so a pointer-to-member expression can't be lowered to an 
integer, it needs to stay as a PTRMEM_CST.  But it's still a constant as 
far as the language is concerned, so we need to allow it and then lower 
it when the class is complete.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch

commit c41c286c3b13d1d1b3831fab38497146bd0c2f8b
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Feb 15 16:25:27 2013 -0500

    	PR c++/54532
    	* expr.c (cplus_expand_constant): Do nothing if the class is
    	incomplete.
    	* semantics.c (reduced_constant_expression_p): Allow PTRMEM_CST.
    	* typeck2.c (store_init_value): Use reduced_constant_expression_p.
    	* decl.c (maybe_register_incomplete_var): Handle PTRMEM_CST.
    	(complete_vars): Likewise.

diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index facaae7..4ccb541 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -14018,7 +14018,10 @@  grokmethod (cp_decl_specifier_seq *declspecs,
 
 
 /* VAR is a VAR_DECL.  If its type is incomplete, remember VAR so that
-   we can lay it out later, when and if its type becomes complete.  */
+   we can lay it out later, when and if its type becomes complete.
+
+   Also handle constexpr pointer to member variables where the initializer
+   is an unlowered PTRMEM_CST because the class isn't complete yet.  */
 
 void
 maybe_register_incomplete_var (tree var)
@@ -14043,6 +14046,15 @@  maybe_register_incomplete_var (tree var)
 	  incomplete_var iv = {var, inner_type};
 	  vec_safe_push (incomplete_vars, iv);
 	}
+      else if (TYPE_PTRMEM_P (inner_type)
+	       && DECL_INITIAL (var)
+	       && TREE_CODE (DECL_INITIAL (var)) == PTRMEM_CST)
+	{
+	  tree context = TYPE_PTRMEM_CLASS_TYPE (inner_type);
+	  gcc_assert (TYPE_BEING_DEFINED (context));
+	  incomplete_var iv = {var, context};
+	  vec_safe_push (incomplete_vars, iv);
+	}
     }
 }
 
@@ -14062,10 +14074,17 @@  complete_vars (tree type)
 	{
 	  tree var = iv->decl;
 	  tree type = TREE_TYPE (var);
-	  /* Complete the type of the variable.  The VAR_DECL itself
-	     will be laid out in expand_expr.  */
-	  complete_type (type);
-	  cp_apply_type_quals_to_decl (cp_type_quals (type), var);
+
+	  if (TYPE_PTRMEM_P (type))
+	    DECL_INITIAL (var) = cplus_expand_constant (DECL_INITIAL (var));
+	  else
+	    {
+	      /* Complete the type of the variable.  The VAR_DECL itself
+		 will be laid out in expand_expr.  */
+	      complete_type (type);
+	      cp_apply_type_quals_to_decl (cp_type_quals (type), var);
+	    }
+
 	  /* Remove this entry from the list.  */
 	  incomplete_vars->unordered_remove (ix);
 	}
diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c
index ffd18ca..f15b049 100644
--- a/gcc/cp/expr.c
+++ b/gcc/cp/expr.c
@@ -43,6 +43,10 @@  cplus_expand_constant (tree cst)
 	/* Find the member.  */
 	member = PTRMEM_CST_MEMBER (cst);
 
+	/* We can't lower this until the class is complete.  */
+	if (!COMPLETE_TYPE_P (DECL_CONTEXT (member)))
+	  return cst;
+
 	if (TREE_CODE (member) == FIELD_DECL)
 	  {
 	    /* Find the offset for the field.  */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 5143e4b..3691d86 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -6838,6 +6838,9 @@  cxx_eval_call_expression (const constexpr_call *old_call, tree t,
 bool
 reduced_constant_expression_p (tree t)
 {
+  if (TREE_CODE (t) == PTRMEM_CST)
+    /* Even if we can't lower this yet, it's constant.  */
+    return true;
   /* FIXME are we calling this too much?  */
   return initializer_constant_valid_p (t, TREE_TYPE (t)) != NULL_TREE;
 }
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 3bac67c..6ef46a1 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -792,7 +792,7 @@  store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
      will perform the dynamic initialization.  */
   if (value != error_mark_node
       && (TREE_SIDE_EFFECTS (value)
-	   || ! initializer_constant_valid_p (value, TREE_TYPE (value))))
+	  || ! reduced_constant_expression_p (value)))
     {
       if (TREE_CODE (type) == ARRAY_TYPE
 	  && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (type)))
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-static11.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-static11.C
new file mode 100644
index 0000000..91cc25a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static11.C
@@ -0,0 +1,14 @@ 
+// PR c++/54532
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X,#X)
+
+struct A {
+  int i;
+  constexpr static int A::*p = &A::i;
+};
+
+constexpr A a = { 42 };
+SA(a.*A::p == 42);
+
+constexpr int A::* A::p;