The code that collects base initializers in a constructor declared
constexpr was checking potential_constant_expression and then discarding
an initializer for an empty base if true. But
potential_constant_expression only looks at whether a function is
declared constexpr, not whether the call actually turns out to be
constant; to determine that we need to keep the call around until we
actually try to expand it.
Tested x86_64-pc-linux-gnu, applied to 4.7 and trunk.
commit 2e6c5d723ff7407704f2cbd343383991b498fb5b
Author: jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Mon Jun 25 15:17:59 2012 +0000
PR c++/53202
* semantics.c (build_data_member_initialization): Always keep
initializer for empty base.
(cxx_eval_bare_aggregate): Discard it here.
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index ee41861..2d64a66 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5838,12 +5838,9 @@ build_data_member_initialization (tree t, VEC(constructor_elt,gc) **vec)
member = op;
else
{
- /* We don't put out anything for an empty base. */
+ /* This is an initializer for an empty base; keep it for now so
+ we can check it in cxx_eval_bare_aggregate. */
gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (member))));
- /* But if the initializer isn't constexpr, leave it in so we
- complain later. */
- if (potential_constant_expression (init))
- return true;
}
}
if (TREE_CODE (member) == ADDR_EXPR)
@@ -7064,6 +7061,12 @@ cxx_eval_bare_aggregate (const constexpr_call *call, tree t,
constructor_elt *inner = base_field_constructor_elt (n, ce->index);
inner->value = elt;
}
+ else if (TREE_CODE (ce->index) == NOP_EXPR)
+ {
+ /* This is an initializer for an empty base; now that we've
+ checked that it's constant, we can ignore it. */
+ gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (ce->index))));
+ }
else
CONSTRUCTOR_APPEND_ELT (n, ce->index, elt);
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C
new file mode 100644
index 0000000..f59cd84
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C
@@ -0,0 +1,106 @@
+// PR c++/53202
+// { dg-do run { target c++11 } }
+
+#include <tuple>
+
+template<typename Callable>
+ struct Bind_simple
+ {
+ explicit
+ Bind_simple(const Callable& callable)
+ : _M_bound(callable)
+ { }
+
+ Bind_simple(const Bind_simple&) = default;
+ Bind_simple(Bind_simple&&) = default;
+
+ auto operator()() -> decltype((*(Callable*)0)())
+ {
+ return std::get<0>(_M_bound)();
+ }
+
+ private:
+
+ std::tuple<Callable> _M_bound;
+ };
+
+template<typename Callable>
+ Bind_simple<Callable>
+ bind_simple(Callable& callable)
+ {
+ return Bind_simple<Callable>(callable);
+ }
+
+struct thread
+{
+ struct ImplBase { };
+
+ template<typename T>
+ struct Impl : ImplBase {
+ T t;
+ Impl(T&& t) : t(std::move(t)) { }
+ };
+
+ template<typename T>
+ thread(T& t)
+ {
+ auto p = make_routine(bind_simple(t));
+
+ p->t();
+
+ delete p;
+ }
+
+ template<typename Callable>
+ Impl<Callable>*
+ make_routine(Callable&& f)
+ {
+ return new Impl<Callable>(std::forward<Callable>(f));
+ }
+};
+
+
+int c;
+class background_hello
+{
+public:
+ background_hello()
+ {
+ __builtin_printf("default ctor called, this=%p\n", this);
+ ++c;
+ }
+
+ background_hello(const background_hello &)
+ {
+ __builtin_printf("copy ctor called\n");
+ ++c;
+ }
+
+ background_hello(background_hello &&)
+ {
+ __builtin_printf("move ctor called\n");
+ ++c;
+ }
+
+ void operator ()() const
+ {
+ __builtin_printf("void background_hello::operator()() called, this=%p\n", this);
+ }
+
+ ~background_hello()
+ {
+ __builtin_printf("destructor called, this=%p\n", this);
+ --c;
+ }
+
+};
+
+int main()
+{
+ {
+ background_hello bh;
+ thread t(bh);
+ }
+ if (c != 0)
+ __builtin_abort ();
+}