struct cp_decomp { tree decl; unsigned int count; };
extern void cp_finish_decl (tree, tree, bool, tree, int, cp_decomp * = nullptr);
extern tree lookup_decomp_type (tree);
-extern void cp_finish_decomp (tree, cp_decomp *);
+extern bool cp_finish_decomp (tree, cp_decomp *, bool = false);
extern int cp_complete_array_type (tree *, tree, bool);
extern int cp_complete_array_type_or_error (tree *, tree, bool, tsubst_flags_t);
extern tree build_ptrmemfunc_type (tree);
static tree push_cp_library_fn (enum tree_code, tree, int);
static tree build_cp_library_fn (tree, enum tree_code, tree, int);
static void store_parm_decls (tree);
-static void initialize_local_var (tree, tree);
+static void initialize_local_var (tree, tree, bool);
static void expand_static_init (tree, tree);
static location_t smallest_type_location (const cp_decl_specifier_seq*);
static bool identify_goto (tree, location_t, const location_t *,
/* Generate code to initialize DECL (a local variable). */
static void
-initialize_local_var (tree decl, tree init)
+initialize_local_var (tree decl, tree init, bool decomp)
{
tree type = TREE_TYPE (decl);
tree cleanup;
int already_used;
- gcc_assert (VAR_P (decl)
- || TREE_CODE (decl) == RESULT_DECL);
+ gcc_assert (VAR_P (decl) || TREE_CODE (decl) == RESULT_DECL);
gcc_assert (!TREE_STATIC (decl));
if (DECL_SIZE (decl) == NULL_TREE)
DECL_READ_P (decl) = 1;
/* Generate a cleanup, if necessary. */
- cleanup = cxx_maybe_build_cleanup (decl, tf_warning_or_error);
+ cleanup = (decomp ? NULL_TREE
+ : cxx_maybe_build_cleanup (decl, tf_warning_or_error));
/* Perform the initialization. */
if (init)
gcc_assert (building_stmt_list_p ());
saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p ();
- current_stmt_tree ()->stmts_are_full_exprs_p = 1;
+ /* Avoid CLEANUP_POINT_EXPR for the structured binding
+ bases, those will have CLEANUP_POINT_EXPR at the end of
+ code emitted by cp_finish_decomp. */
+ if (decomp)
+ current_stmt_tree ()->stmts_are_full_exprs_p = 0;
+ else
+ current_stmt_tree ()->stmts_are_full_exprs_p = 1;
finish_expr_stmt (init);
- current_stmt_tree ()->stmts_are_full_exprs_p =
- saved_stmts_are_full_exprs_p;
+ current_stmt_tree ()->stmts_are_full_exprs_p
+ = saved_stmts_are_full_exprs_p;
}
}
int was_readonly = 0;
bool var_definition_p = false;
tree auto_node;
+ auto_vec<tree> extra_cleanups;
+ struct decomp_cleanup {
+ tree decl;
+ cp_decomp *&decomp;
+ ~decomp_cleanup ()
+ {
+ if (decomp && DECL_DECOMPOSITION_P (decl))
+ cp_finish_decomp (decl, decomp);
+ }
+ } decomp_cl = { decl, decomp };
if (decl == error_mark_node)
return;
add_decl_expr (decl);
}
+ tree decomp_init = NULL_TREE;
/* Let the middle end know about variables and functions -- but not
static data members in uninstantiated class templates. */
if (VAR_OR_FUNCTION_DECL_P (decl))
if (var_definition_p)
abstract_virtuals_error (decl, type);
+ if (decomp && !cp_finish_decomp (decl, decomp, true))
+ decomp = NULL;
+
if (TREE_TYPE (decl) == error_mark_node)
/* No initialization required. */
;
}
/* A variable definition. */
else if (DECL_FUNCTION_SCOPE_P (decl) && !TREE_STATIC (decl))
- /* Initialize the local variable. */
- initialize_local_var (decl, init);
+ {
+ /* Initialize the local variable. */
+ if (!decomp)
+ initialize_local_var (decl, init, false);
+ else
+ {
+ tree cleanup = NULL_TREE;
+ if (DECL_SIZE (decl))
+ cleanup = cxx_maybe_build_cleanup (decl, tf_warning_or_error);
+ /* If cp_finish_decomp needs to emit any code, we need to emit that
+ code after code emitted by initialize_local_var in a single
+ CLEANUP_POINT_EXPR, so that temporaries are destructed only
+ after the cp_finish_decomp emitted code.
+ If there are any cleanups, either extend_ref_init_temps
+ created ones or e.g. array destruction, push those first
+ with the cleanups guarded on a bool temporary, initially
+ set to false and set to true after initialize_local_var
+ emitted code. */
+ tree guard = NULL_TREE;
+ if (cleanups || cleanup)
+ {
+ guard = force_target_expr (boolean_type_node,
+ boolean_false_node, tf_none);
+ add_stmt (guard);
+ guard = TARGET_EXPR_SLOT (guard);
+ }
+ tree sl = push_stmt_list ();
+ initialize_local_var (decl, init, true);
+ if (guard)
+ {
+ add_stmt (build2 (MODIFY_EXPR, boolean_type_node,
+ guard, boolean_true_node));
+ for (tree &t : *cleanups)
+ t = build3 (COND_EXPR, void_type_node,
+ guard, t, void_node);
+ if (cleanup)
+ cleanup = build3 (COND_EXPR, void_type_node,
+ guard, cleanup, void_node);
+ }
+ unsigned before = stmt_list_stack->length ();
+ cp_finish_decomp (decl, decomp);
+ decomp = NULL;
+ unsigned n_extra_cleanups = stmt_list_stack->length () - before;
+ sl = pop_stmt_list (sl);
+ if (n_extra_cleanups)
+ {
+ /* If cp_finish_decomp needs any cleanups, such as for
+ extend_ref_init_temps created vars, pop_stmt_list
+ popped that all, so push those extra cleanups around
+ the whole sequence with a guard variable. */
+ gcc_assert (TREE_CODE (sl) == STATEMENT_LIST);
+ guard = force_target_expr (integer_type_node,
+ integer_zero_node, tf_none);
+ add_stmt (guard);
+ guard = TARGET_EXPR_SLOT (guard);
+ for (unsigned i = 0; i < n_extra_cleanups; ++i)
+ {
+ tree_stmt_iterator tsi = tsi_last (sl);
+ gcc_assert (!tsi_end_p (tsi));
+ tree last = tsi_stmt (tsi);
+ gcc_assert (TREE_CODE (last) == CLEANUP_STMT
+ && !CLEANUP_EH_ONLY (last));
+ tree cst = build_int_cst (integer_type_node, i + 1);
+ tree cl = build3 (COND_EXPR, void_type_node,
+ build2 (GE_EXPR, boolean_type_node,
+ guard, cst),
+ CLEANUP_EXPR (last), void_node);
+ extra_cleanups.safe_push (cl);
+ tsi_link_before (&tsi, build2 (MODIFY_EXPR,
+ integer_type_node,
+ guard, cst),
+ TSI_SAME_STMT);
+ tree sl2 = CLEANUP_BODY (last);
+ gcc_assert (TREE_CODE (sl2) == STATEMENT_LIST);
+ tsi_link_before (&tsi, sl2, TSI_SAME_STMT);
+ tsi_delink (&tsi);
+ }
+ }
+ decomp_init = maybe_cleanup_point_expr_void (sl);
+ if (cleanup)
+ finish_decl_cleanup (decl, cleanup);
+ }
+ }
/* If a variable is defined, and then a subsequent
definition with external linkage is encountered, we will
release_tree_vector (cleanups);
}
+ for (tree t : &extra_cleanups)
+ push_cleanup (NULL_TREE, t, false);
+
+ if (decomp_init)
+ add_stmt (decomp_init);
+
if (was_readonly)
TREE_READONLY (decl) = 1;
/* Finish a decomposition declaration. DECL is the underlying declaration
"e", FIRST is the head of a chain of decls for the individual identifiers
chained through DECL_CHAIN in reverse order and COUNT is the number of
- those decls. */
+ those decls. If TEST_P is true, return true if any code would need to be
+ actually emitted but don't emit it. Return false otherwise. */
-void
-cp_finish_decomp (tree decl, cp_decomp *decomp)
+bool
+cp_finish_decomp (tree decl, cp_decomp *decomp, bool test_p)
{
tree first = decomp->decl;
unsigned count = decomp->count;
}
if (DECL_P (decl) && DECL_NAMESPACE_SCOPE_P (decl))
SET_DECL_ASSEMBLER_NAME (decl, get_identifier ("<decomp>"));
- return;
+ return false;
}
location_t loc = DECL_SOURCE_LOCATION (decl);
fit_decomposition_lang_decl (first, decl);
first = DECL_CHAIN (first);
}
- return;
+ return false;
}
auto_vec<tree, 16> v;
eltscnt = tree_to_uhwi (tsize);
if (count != eltscnt)
goto cnt_mismatch;
+ if (test_p)
+ return true;
if (!processing_template_decl && DECL_DECOMP_BASE (decl))
{
/* For structured bindings used in conditions we need to evaluate
DECL_HAS_VALUE_EXPR_P (v[i]) = 1;
}
}
+ return false;
}
/* Returns a declaration for a VAR_DECL as if:
cp_finish_decl (range_decl, deref_begin,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING, decomp);
- if (DECL_DECOMPOSITION_P (range_decl))
- cp_finish_decomp (range_decl, decomp);
warn_for_range_copy (range_decl, deref_begin);
if (decl != error_mark_node)
{
cp_decomp decomp = { prev, cnt };
- cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
- (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT),
- &decomp);
if (keyword != RID_MAX)
DECL_DECOMP_BASE (decl)
= keyword == RID_SWITCH ? integer_one_node : integer_zero_node;
- cp_finish_decomp (decl, &decomp);
+ cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
+ (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT),
+ &decomp);
}
}
else if (decl != error_mark_node)
NULL_TREE, tf_warning_or_error),
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING, decomp);
- if (DECL_DECOMPOSITION_P (decl))
- cp_finish_decomp (decl, decomp);
}
/* Return true if next tokens contain a standard attribute that contains
{
bool const_init = false;
cp_decomp decomp_d, *decomp = NULL;
- tree ndecl = error_mark_node;
tree asmspec_tree = NULL_TREE;
maybe_push_decl (decl);
&& TREE_TYPE (pattern_decl) != error_mark_node)
{
decomp = &decomp_d;
- ndecl = tsubst_decomp_names (decl, pattern_decl, args,
- complain, in_decl, decomp);
+ if (tsubst_decomp_names (decl, pattern_decl, args,
+ complain, in_decl, decomp)
+ == error_mark_node)
+ decomp = NULL;
}
init = tsubst_init (init, decl, args, complain, in_decl);
cp_finish_decl (decl, init, const_init, asmspec_tree, 0,
decomp);
-
- if (ndecl != error_mark_node)
- cp_finish_decomp (ndecl, decomp);
}
}
}
--- /dev/null
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+int c, d, i;
+
+struct A {
+ A () { assert (c == 3); ++c; }
+ ~A () { assert (c == 12); ++c; }
+ template <int I> int &get () const { assert (c == 5 + I); ++c; return i; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, A> { using type = int; };
+template <> struct std::tuple_size <const A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, const A> { using type = int; };
+
+struct B {
+ B () { assert (c >= 1 && c <= 2); ++c; }
+ ~B () { assert (c >= 9 && c <= 10); ++c; }
+};
+
+struct C {
+ constexpr C () {}
+ constexpr C (const C &) {}
+ template <int I> int &get () const { assert (d == 1 + I); ++d; return i; }
+};
+
+template <> struct std::tuple_size <C> { static const int value = 3; };
+template <int I> struct std::tuple_element <I, C> { using type = int; };
+template <> struct std::tuple_size <const C> { static const int value = 3; };
+template <int I> struct std::tuple_element <I, const C> { using type = int; };
+
+A
+foo (const B &, const B &)
+{
+ A a;
+ assert (c == 4);
+ ++c;
+ return a;
+}
+
+constexpr C
+foo (const C &, const C &)
+{
+ return C {};
+}
+
+int
+foo (const int &, const int &)
+{
+ assert (false);
+}
+
+void
+bar ()
+{
+ c = 1;
+ const auto &[x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 11);
+ ++c;
+ d = 1;
+ const auto &[s, t, u] = foo (C {}, C {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (d == 4);
+}
+
+template <int N>
+void
+baz ()
+{
+ c = 1;
+ const auto &[x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 11);
+ ++c;
+ d = 1;
+ const auto &[s, t, u] = foo (C {}, C {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (d == 4);
+}
+
+template <typename T, typename U>
+void
+qux ()
+{
+ c = 1;
+ const auto &[x, y, z, w] = foo (T {}, T {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 11);
+ ++c;
+ d = 1;
+ const auto &[s, t, u] = foo (U {}, U {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (d == 4);
+}
+
+void
+corge ()
+{
+ c = 1;
+ auto [x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 11);
+ ++c;
+ d = 1;
+ auto [s, t, u] = foo (C {}, C {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (d == 4);
+}
+
+template <int N>
+void
+garply ()
+{
+ c = 1;
+ auto [x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 11);
+ ++c;
+ d = 1;
+ auto [s, t, u] = foo (C {}, C {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (d == 4);
+}
+
+template <typename T, typename U>
+void
+freddy ()
+{
+ c = 1;
+ auto [x, y, z, w] = foo (T {}, T {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 11);
+ ++c;
+ d = 1;
+ auto [s, t, u] = foo (U {}, U {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (d == 4);
+}
+
+int
+main ()
+{
+ bar ();
+ assert (c == 13);
+ baz <0> ();
+ assert (c == 13);
+ qux <B, C> ();
+ assert (c == 13);
+ corge ();
+ assert (c == 13);
+ garply <42> ();
+ assert (c == 13);
+ freddy <B, C> ();
+ assert (c == 13);
+}
--- /dev/null
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+int c;
+
+struct C {
+ C () { assert (c >= 5 && c <= 17 && (c - 5) % 4 == 0); ++c; }
+ ~C () { assert (c >= 8 && c <= 20 && c % 4 == 0); ++c; }
+};
+
+struct D {
+ D () { assert (c >= 7 && c <= 19 && (c - 7) % 4 == 0); ++c; }
+ ~D () { assert (c >= 24 && c <= 27); ++c; }
+};
+
+struct A {
+ A () { assert (c == 3); ++c; }
+ ~A () { assert (c == 28); ++c; }
+ template <int I> D get (const C & = C{}) const { assert (c == 6 + 4 * I); ++c; return D {}; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, A> { using type = D; };
+template <> struct std::tuple_size <const A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, const A> { using type = D; };
+
+struct B {
+ B () { assert (c >= 1 && c <= 2); ++c; }
+ ~B () { assert (c >= 21 && c <= 22); ++c; }
+};
+
+A
+foo (const B &, const B &)
+{
+ A a;
+ assert (c == 4);
+ ++c;
+ return a;
+}
+
+int
+foo (const int &, const int &)
+{
+ assert (false);
+}
+
+void
+bar ()
+{
+ c = 1;
+ // First B::B () is invoked twice, then foo called, which invokes A::A ().
+ // e is reference bound to the A::A () constructed temporary.
+ // Then 4 times (in increasing I):
+ // C::C () is invoked, get is called, D::D () is invoked, C::~C () is
+ // invoked.
+ // After that B::~B () is invoked twice, then the following 2 user
+ // statements.
+ // Then D::~D () is invoked 4 times, then A::~A ().
+ const auto &[x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 23);
+ ++c;
+}
+
+template <int N>
+void
+baz ()
+{
+ c = 1;
+ const auto &[x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 23);
+ ++c;
+}
+
+template <typename T>
+void
+qux ()
+{
+ c = 1;
+ const auto &[x, y, z, w] = foo (T {}, T {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } }
+ assert (c == 23);
+ ++c;
+}
+
+int
+main ()
+{
+ bar ();
+ assert (c == 29);
+ baz <42> ();
+ assert (c == 29);
+ qux <B> ();
+ assert (c == 29);
+}