diff mbox

C++ PATCH for c++/58102 (constexpr and mutable)

Message ID 546B4B31.6040906@redhat.com
State New
Headers show

Commit Message

Jason Merrill Nov. 18, 2014, 1:35 p.m. UTC
We need to allow copy-list-initialization of constexpr variables with 
mutable members, too.  The thing we need to avoid is not so much an 
full-expression with mutable-containing type as assuming that a mutable 
member of a variable hasn't changed since the variable was initialized.

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

Comments

Paolo Carlini Nov. 18, 2014, 1:51 p.m. UTC | #1
On 11/18/2014 02:35 PM, Jason Merrill wrote:
> We need to allow copy-list-initialization of constexpr variables with 
> mutable members, too.  The thing we need to avoid is not so much an 
> full-expression with mutable-containing type as assuming that a 
> mutable member of a variable hasn't changed since the variable was 
> initialized.
Thanks. You suggest a similar fix a while ago but I failed to pursue it, 
sorry.

Paolo.
diff mbox

Patch

commit 58e6e22ca70f9efe534f9d038f0cf9a2fe1a7504
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Nov 17 23:44:41 2014 -0500

    	PR c++/58102
    	* typeck2.c (store_init_value): Set it.
    	* cp-tree.h (CONSTRUCTOR_MUTABLE_POISON): New.
    	* constexpr.c (cxx_eval_outermost_constant_expr): Check it.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 5b25654..2f0708b 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -3315,15 +3315,15 @@  cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 
   verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
 
-  if (TREE_CODE (t) != CONSTRUCTOR
-      && cp_has_mutable_p (TREE_TYPE (t)))
+  /* Mutable logic is a bit tricky: we want to allow initialization of
+     constexpr variables with mutable members, but we can't copy those
+     members to another constexpr variable.  */
+  if (TREE_CODE (r) == CONSTRUCTOR
+      && CONSTRUCTOR_MUTABLE_POISON (r))
     {
-      /* We allow a mutable type if the original expression was a
-	 CONSTRUCTOR so that we can do aggregate initialization of
-	 constexpr variables.  */
       if (!allow_non_constant)
-	error ("%qT cannot be the type of a complete constant expression "
-	       "because it has mutable sub-objects", type);
+	error ("%qE is not a constant expression because it refers to "
+	       "mutable subobjects of %qT", t, type);
       non_constant_p = true;
     }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d3722d7..3542344 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -109,6 +109,7 @@  c-common.h, not after.
       DECLTYPE_FOR_LAMBDA_PROXY (in DECLTYPE_TYPE)
       REF_PARENTHESIZED_P (in COMPONENT_REF, INDIRECT_REF)
       AGGR_INIT_ZERO_FIRST (in AGGR_INIT_EXPR)
+      CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR)
    3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -3497,6 +3498,11 @@  more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define CONSTRUCTOR_NO_IMPLICIT_ZERO(NODE) \
   (TREE_LANG_FLAG_1 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR should not be used as a variable initializer
+   because it was loaded from a constexpr variable with mutable fields.  */
+#define CONSTRUCTOR_MUTABLE_POISON(NODE) \
+  (TREE_LANG_FLAG_2 (CONSTRUCTOR_CHECK (NODE)))
+
 #define DIRECT_LIST_INIT_P(NODE) \
    (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE))
 
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 01a0671..5748650 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -809,6 +809,10 @@  store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
 	    value = cxx_constant_value (value, decl);
 	}
       value = maybe_constant_init (value, decl);
+      if (TREE_CODE (value) == CONSTRUCTOR && cp_has_mutable_p (type))
+	/* Poison this CONSTRUCTOR so it can't be copied to another
+	   constexpr variable.  */
+	CONSTRUCTOR_MUTABLE_POISON (value) = true;
       const_init = (reduced_constant_expression_p (value)
 		    || error_operand_p (value));
       DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = const_init;
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-mutable2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-mutable2.C
new file mode 100644
index 0000000..c449c3a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-mutable2.C
@@ -0,0 +1,10 @@ 
+// PR c++/58102
+// { dg-do compile { target c++11 } }
+
+struct S {
+  mutable int n;
+  constexpr S() : n() {}
+};
+
+constexpr S s = {};
+constexpr S s2 = s;		// { dg-error "mutable" }