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.
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;
--- /dev/null
+// 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" }
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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();
+}