diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc index f5cc7c8371c160864bed2098ab4fa2d7cb1fc85b..72b38317090861f71ae2d6d94d3cd11dafcdc418 100644 --- a/gcc/cp/typeck2.cc +++ b/gcc/cp/typeck2.cc @@ -776,6 +776,27 @@ split_nonconstant_init (tree dest, tree init) return code; } +/* T is the initializer of a constexpr variable. Set CONSTRUCTOR_MUTABLE_POISON + for any CONSTRUCTOR within T that contains (directly or indirectly) a mutable + member, thereby poisoning it so it can't be copied to another a constexpr + variable or read during constexpr evaluation. */ + +static void +poison_mutable_constructors (tree t) +{ + if (TREE_CODE (t) != CONSTRUCTOR) + return; + + if (cp_has_mutable_p (TREE_TYPE (t))) + { + CONSTRUCTOR_MUTABLE_POISON (t) = true; + + if (vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (t)) + for (const constructor_elt &ce : *elts) + poison_mutable_constructors (ce.value); + } +} + /* Perform appropriate conversions on the initial value of a variable, store it in the declaration DECL, and print any error messages that are appropriate. @@ -886,10 +907,7 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags) else value = fold_non_dependent_init (value, tf_warning_or_error, /*manifestly_const_eval=*/true, 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; + poison_mutable_constructors (value); 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-mutable4.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-mutable4.C new file mode 100644 index 0000000000000000000000000000000000000000..fd982bcd0705d5c25506ca4de62655018ed65549 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-mutable4.C @@ -0,0 +1,15 @@ +// PR c++/109745 +// Similar to constexpr-mutable1.C, but with nested 'mutable' accesses. +// { dg-do compile { target c++11 } } + +struct A { mutable int m = 0; }; + +struct B { A a; }; + +struct C { B b; }; + +constexpr B b; +constexpr int bam = b.a.m; // { dg-error "mutable" } + +constexpr C c; +constexpr int cbam = c.b.a.m; // { dg-error "mutable" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-mutable5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-mutable5.C new file mode 100644 index 0000000000000000000000000000000000000000..6a530e2abe6b40dcb2c1e2ff4ba3f0f431c91db4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-mutable5.C @@ -0,0 +1,39 @@ +// PR c++/109745 +// { dg-do run { target c++11 } } +// { dg-additional-options "-O" } + +struct A { + mutable int m = 0; + void f() const { ++m; }; + constexpr int get_m() const { return m; } +}; + +struct B { A a; }; + +struct C { B b; }; + +int main() { + constexpr A a; + a.m++; + if (a.get_m() != 1 || a.m != 1) + __builtin_abort(); + a.m++; + if (a.get_m() != 2 || a.m != 2) + __builtin_abort(); + + constexpr B b; + b.a.m++; + if (b.a.get_m() != 1 || b.a.m != 1) + __builtin_abort(); + b.a.m++; + if (b.a.get_m() != 2 || b.a.m != 2) + __builtin_abort(); + + constexpr C c; + c.b.a.m++; + if (c.b.a.get_m() != 1 || c.b.a.m != 1) + __builtin_abort(); + c.b.a.m++; + if (c.b.a.get_m() != 2 || c.b.a.m != 2) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-mutable2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-mutable2.C new file mode 100644 index 0000000000000000000000000000000000000000..4898aa09a08d716ad71f05049e8db8849977179e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-mutable2.C @@ -0,0 +1,20 @@ +// PR c++/109745 +// { dg-do run { target c++14 } } +// { dg-additional-options "-O" } + +template<class T> +struct Foo { T val; }; + +struct Bar { + constexpr Bar() = default; + constexpr Bar(Bar const& other) { other.val_ = 42; } + constexpr int val() const { return val_; } + mutable int val_{}; +}; + +int main() { + constexpr Foo<Bar> x{}; + Foo<Bar> y{x}; + if (x.val.val() != 42 || x.val.val_ != 42) + __builtin_abort(); +}