aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/backend/rust-constexpr.cc
diff options
context:
space:
mode:
authorFaisal Abbas <90.abbasfaisal@gmail.com>2022-07-16 17:01:47 +0100
committerPhilip Herron <philip.herron@embecosm.com>2022-08-25 12:40:24 +0100
commit7c13a2e5873f5919c00b5f37b6db52af8d0f7300 (patch)
treeb62ac124ec3048924b4bcb50fa81278955be3426 /gcc/rust/backend/rust-constexpr.cc
parenta5b583d021a4cbbfafecc28445709afe32cdd1c8 (diff)
downloadgcc-7c13a2e5873f5919c00b5f37b6db52af8d0f7300.zip
gcc-7c13a2e5873f5919c00b5f37b6db52af8d0f7300.tar.gz
gcc-7c13a2e5873f5919c00b5f37b6db52af8d0f7300.tar.bz2
gccrs const folding port: start porting cxx_eval_array_reference()
Following functions are ported in this changeset: - array_index_cmp - unshare_constructor - find_array_ctor_elt - reduced_constant_expression_p - verify_constant - diag_array_subscript - get_array_or_vector_nelts - eval_and_check_array_index - extract_string_elt - free_constructor - cv_unqualified - make_tree_vector - release_tree_vector - vec_safe_push Following structs, classes and enums are ported in this changeset: - releasing_vec Signed-off-by: Faisal Abbas <90.abbasfaisal@gmail.com>
Diffstat (limited to 'gcc/rust/backend/rust-constexpr.cc')
-rw-r--r--gcc/rust/backend/rust-constexpr.cc549
1 files changed, 532 insertions, 17 deletions
diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc
index 53c6ef6..8fb378f 100644
--- a/gcc/rust/backend/rust-constexpr.cc
+++ b/gcc/rust/backend/rust-constexpr.cc
@@ -25,10 +25,27 @@
#include "print-tree.h"
#include "gimplify.h"
#include "tree-iterator.h"
+#include "varasm.h"
+
+#define VERIFY_CONSTANT(X) \
+ do \
+ { \
+ if (verify_constant ((X), ctx->quiet, non_constant_p, overflow_p)) \
+ return t; \
+ } \
+ while (0)
namespace Rust {
namespace Compile {
+static bool
+verify_constant (tree, bool, bool *, bool *);
+
+static HOST_WIDE_INT
+find_array_ctor_elt (tree ary, tree dindex, bool insert = false);
+static int
+array_index_cmp (tree key, tree index);
+
struct constexpr_global_ctx
{
HOST_WIDE_INT constexpr_ops_count;
@@ -38,7 +55,19 @@ struct constexpr_global_ctx
struct constexpr_ctx
{
+ /* The part of the context that needs to be unique to the whole
+ cxx_eval_outermost_constant_expr invocation. */
constexpr_global_ctx *global;
+
+ /* Whether we should error on a non-constant expression or fail quietly.
+ This flag needs to be here, but some of the others could move to global
+ if they get larger than a word. */
+ bool quiet;
+ /* The object we're building the CONSTRUCTOR for. */
+ tree object;
+ /* The CONSTRUCTOR we're currently building up for an aggregate
+ initializer. */
+ tree ctor;
};
static tree
@@ -51,19 +80,20 @@ static void
non_const_var_error (location_t loc, tree r);
static tree
-constexpr_expression (const constexpr_ctx *ctx, tree);
+constexpr_expression (const constexpr_ctx *ctx, tree, bool, bool *, bool *,
+ tree * = NULL);
static tree
constexpr_fn_retval (const constexpr_ctx *ctx, tree r);
static tree
-eval_store_expression (const constexpr_ctx *ctx, tree r);
+eval_store_expression (const constexpr_ctx *ctx, tree r, bool, bool *, bool *);
static tree
eval_call_expression (const constexpr_ctx *ctx, tree r);
static tree
-eval_binary_expression (const constexpr_ctx *ctx, tree r);
+eval_binary_expression (const constexpr_ctx *ctx, tree r, bool, bool *, bool *);
static tree
get_function_named_in_call (tree t);
@@ -72,15 +102,20 @@ tree
fold_expr (tree expr)
{
constexpr_global_ctx global_ctx;
- constexpr_ctx ctx = {&global_ctx};
+ constexpr_ctx ctx = {&global_ctx, false};
+ bool non_constant_p = false;
+ bool overflow_p = false;
- tree folded = constexpr_expression (&ctx, expr);
+ tree folded
+ = constexpr_expression (&ctx, expr, false, &non_constant_p, &overflow_p);
rust_assert (folded != NULL_TREE);
return folded;
}
static tree
-constexpr_expression (const constexpr_ctx *ctx, tree t)
+constexpr_expression (const constexpr_ctx *ctx, tree t, bool lval,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target /* = NULL */)
{
location_t loc = EXPR_LOCATION (t);
@@ -165,7 +200,7 @@ constexpr_expression (const constexpr_ctx *ctx, tree t)
case LTGT_EXPR:
case RANGE_EXPR:
case COMPLEX_EXPR:
- r = eval_binary_expression (ctx, t);
+ r = eval_binary_expression (ctx, t, false, non_constant_p, overflow_p);
break;
case CALL_EXPR:
@@ -174,11 +209,12 @@ constexpr_expression (const constexpr_ctx *ctx, tree t)
case RETURN_EXPR:
rust_assert (TREE_OPERAND (t, 0) != NULL_TREE);
- r = constexpr_expression (ctx, TREE_OPERAND (t, 0));
+ r = constexpr_expression (ctx, TREE_OPERAND (t, 0), false, non_constant_p,
+ overflow_p);
break;
case MODIFY_EXPR:
- r = eval_store_expression (ctx, t);
+ r = eval_store_expression (ctx, t, false, non_constant_p, overflow_p);
break;
default:
@@ -189,7 +225,8 @@ constexpr_expression (const constexpr_ctx *ctx, tree t)
}
static tree
-eval_store_expression (const constexpr_ctx *ctx, tree t)
+eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval,
+ bool *non_constant_p, bool *overflow_p)
{
tree init = TREE_OPERAND (t, 1);
if (TREE_CLOBBER_P (init))
@@ -219,7 +256,8 @@ eval_store_expression (const constexpr_ctx *ctx, tree t)
object = probe;
else
{
- probe = constexpr_expression (ctx, probe);
+ probe = constexpr_expression (ctx, probe, lval, non_constant_p,
+ overflow_p);
evaluated = true;
}
break;
@@ -232,14 +270,15 @@ eval_store_expression (const constexpr_ctx *ctx, tree t)
/* Subroutine of cxx_eval_constant_expression.
Like cxx_eval_unary_expression, except for binary expressions. */
static tree
-eval_binary_expression (const constexpr_ctx *ctx, tree t)
+eval_binary_expression (const constexpr_ctx *ctx, tree t, bool lval,
+ bool *non_constant_p, bool *overflow_p)
{
tree orig_lhs = TREE_OPERAND (t, 0);
tree orig_rhs = TREE_OPERAND (t, 1);
tree lhs, rhs;
- lhs = constexpr_expression (ctx, orig_lhs);
- rhs = constexpr_expression (ctx, orig_rhs);
+ lhs = constexpr_expression (ctx, orig_lhs, lval, non_constant_p, overflow_p);
+ rhs = constexpr_expression (ctx, orig_rhs, lval, non_constant_p, overflow_p);
location_t loc = EXPR_LOCATION (t);
enum tree_code code = TREE_CODE (t);
@@ -285,9 +324,12 @@ constexpr_fn_retval (const constexpr_ctx *ctx, tree body)
return expr;
}
- case RETURN_EXPR:
- return constexpr_expression (ctx, body);
-
+ case RETURN_EXPR: {
+ bool non_constant_p = false;
+ bool overflow_p = false;
+ return constexpr_expression (ctx, body, false, &non_constant_p,
+ &overflow_p);
+ }
case DECL_EXPR: {
tree decl = DECL_EXPR_DECL (body);
if (TREE_CODE (decl) == USING_DECL
@@ -437,5 +479,478 @@ var_in_maybe_constexpr_fn (tree t)
return (DECL_FUNCTION_SCOPE_P (t) && maybe_constexpr_fn (DECL_CONTEXT (t)));
}
+// forked from gcc/cp/constexpr.cc array_index_cmp
+
+/* Returns less than, equal to, or greater than zero if KEY is found to be
+ less than, to match, or to be greater than the constructor_elt's INDEX. */
+
+static int
+array_index_cmp (tree key, tree index)
+{
+ gcc_assert (TREE_CODE (key) == INTEGER_CST);
+
+ switch (TREE_CODE (index))
+ {
+ case INTEGER_CST:
+ return tree_int_cst_compare (key, index);
+ case RANGE_EXPR: {
+ tree lo = TREE_OPERAND (index, 0);
+ tree hi = TREE_OPERAND (index, 1);
+ if (tree_int_cst_lt (key, lo))
+ return -1;
+ else if (tree_int_cst_lt (hi, key))
+ return 1;
+ else
+ return 0;
+ }
+ default:
+ gcc_unreachable ();
+ }
+}
+
+// forked from gcc/cp/constexpr.cc unshare_constructor
+
+/* If T is a CONSTRUCTOR, return an unshared copy of T and any
+ sub-CONSTRUCTORs. Otherwise return T.
+
+ We use this whenever we initialize an object as a whole, whether it's a
+ parameter, a local variable, or a subobject, so that subsequent
+ modifications don't affect other places where it was used. */
+
+tree
+unshare_constructor (tree t MEM_STAT_DECL)
+{
+ if (!t || TREE_CODE (t) != CONSTRUCTOR)
+ return t;
+ auto_vec<tree *, 4> ptrs;
+ ptrs.safe_push (&t);
+ while (!ptrs.is_empty ())
+ {
+ tree *p = ptrs.pop ();
+ tree n = copy_node (*p PASS_MEM_STAT);
+ CONSTRUCTOR_ELTS (n)
+ = vec_safe_copy (CONSTRUCTOR_ELTS (*p) PASS_MEM_STAT);
+ *p = n;
+ vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (n);
+ constructor_elt *ce;
+ for (HOST_WIDE_INT i = 0; vec_safe_iterate (v, i, &ce); ++i)
+ if (ce->value && TREE_CODE (ce->value) == CONSTRUCTOR)
+ ptrs.safe_push (&ce->value);
+ }
+ return t;
+}
+
+// forked from gcc/cp/constexpr.cc find_array_ctor_elt
+
+/* Returns the index of the constructor_elt of ARY which matches DINDEX, or -1
+ if none. If INSERT is true, insert a matching element rather than fail. */
+
+static HOST_WIDE_INT
+find_array_ctor_elt (tree ary, tree dindex, bool insert)
+{
+ if (tree_int_cst_sgn (dindex) < 0)
+ return -1;
+
+ unsigned HOST_WIDE_INT i = tree_to_uhwi (dindex);
+ vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (ary);
+ unsigned HOST_WIDE_INT len = vec_safe_length (elts);
+
+ unsigned HOST_WIDE_INT end = len;
+ unsigned HOST_WIDE_INT begin = 0;
+
+ /* If the last element of the CONSTRUCTOR has its own index, we can assume
+ that the same is true of the other elements and index directly. */
+ if (end > 0)
+ {
+ tree cindex = (*elts)[end - 1].index;
+ if (cindex == NULL_TREE)
+ {
+ /* Verify that if the last index is missing, all indexes
+ are missing. */
+ if (flag_checking)
+ for (unsigned int j = 0; j < len - 1; ++j)
+ gcc_assert ((*elts)[j].index == NULL_TREE);
+ if (i < end)
+ return i;
+ else
+ {
+ begin = end;
+ if (i == end)
+ /* If the element is to be added right at the end,
+ make sure it is added with cleared index too. */
+ dindex = NULL_TREE;
+ else if (insert)
+ /* Otherwise, in order not to break the assumption
+ that CONSTRUCTOR either has all indexes or none,
+ we need to add indexes to all elements. */
+ for (unsigned int j = 0; j < len; ++j)
+ (*elts)[j].index = build_int_cst (TREE_TYPE (dindex), j);
+ }
+ }
+ else if (TREE_CODE (cindex) == INTEGER_CST
+ && compare_tree_int (cindex, end - 1) == 0)
+ {
+ if (i < end)
+ return i;
+ else
+ begin = end;
+ }
+ }
+
+ /* Otherwise, find a matching index by means of a binary search. */
+ while (begin != end)
+ {
+ unsigned HOST_WIDE_INT middle = (begin + end) / 2;
+ constructor_elt &elt = (*elts)[middle];
+ tree idx = elt.index;
+
+ int cmp = array_index_cmp (dindex, idx);
+ if (cmp < 0)
+ end = middle;
+ else if (cmp > 0)
+ begin = middle + 1;
+ else
+ {
+ if (insert && TREE_CODE (idx) == RANGE_EXPR)
+ {
+ /* We need to split the range. */
+ constructor_elt e;
+ tree lo = TREE_OPERAND (idx, 0);
+ tree hi = TREE_OPERAND (idx, 1);
+ tree value = elt.value;
+ dindex = fold_convert (sizetype, dindex);
+ if (tree_int_cst_lt (lo, dindex))
+ {
+ /* There are still some lower elts; shorten the range. */
+ tree new_hi
+ = int_const_binop (MINUS_EXPR, dindex, size_one_node);
+ if (tree_int_cst_equal (lo, new_hi))
+ /* Only one element left, no longer a range. */
+ elt.index = lo;
+ else
+ TREE_OPERAND (idx, 1) = new_hi;
+ /* Append the element we want to insert. */
+ ++middle;
+ e.index = dindex;
+ e.value = unshare_constructor (value);
+ vec_safe_insert (CONSTRUCTOR_ELTS (ary), middle, e);
+ }
+ else
+ /* No lower elts, the range elt is now ours. */
+ elt.index = dindex;
+
+ if (tree_int_cst_lt (dindex, hi))
+ {
+ /* There are still some higher elts; append a range. */
+ tree new_lo
+ = int_const_binop (PLUS_EXPR, dindex, size_one_node);
+ if (tree_int_cst_equal (new_lo, hi))
+ e.index = hi;
+ else
+ e.index = build2 (RANGE_EXPR, sizetype, new_lo, hi);
+ e.value = unshare_constructor (value);
+ vec_safe_insert (CONSTRUCTOR_ELTS (ary), middle + 1, e);
+ }
+ }
+ return middle;
+ }
+ }
+
+ if (insert)
+ {
+ constructor_elt e = {dindex, NULL_TREE};
+ vec_safe_insert (CONSTRUCTOR_ELTS (ary), end, e);
+ return end;
+ }
+
+ return -1;
+}
+
+// forked from gcc/cp/constexpr.cc reduced_constant_expression_p
+
+/* Return true if T is a valid constant initializer. If a CONSTRUCTOR
+ initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
+ cleared.
+ FIXME speed this up, it's taking 16% of compile time on sieve testcase. */
+
+bool
+reduced_constant_expression_p (tree t)
+{
+ if (t == NULL_TREE)
+ return false;
+
+ switch (TREE_CODE (t))
+ {
+ case PTRMEM_CST:
+ /* Even if we can't lower this yet, it's constant. */
+ return true;
+
+ case CONSTRUCTOR:
+ /* And we need to handle PTRMEM_CST wrapped in a CONSTRUCTOR. */
+ tree field;
+ if (CONSTRUCTOR_NO_CLEARING (t))
+ {
+ if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
+ /* An initialized vector would have a VECTOR_CST. */
+ return false;
+ else if (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+ {
+ /* There must be a valid constant initializer at every array
+ index. */
+ tree min = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (t)));
+ tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (t)));
+ tree cursor = min;
+ for (auto &e : CONSTRUCTOR_ELTS (t))
+ {
+ if (!reduced_constant_expression_p (e.value))
+ return false;
+ if (array_index_cmp (cursor, e.index) != 0)
+ return false;
+ if (TREE_CODE (e.index) == RANGE_EXPR)
+ cursor = TREE_OPERAND (e.index, 1);
+ cursor = int_const_binop (PLUS_EXPR, cursor, size_one_node);
+ }
+ if (find_array_ctor_elt (t, max) == -1)
+ return false;
+ goto ok;
+ }
+ else if (TREE_CODE (TREE_TYPE (t)) == UNION_TYPE)
+ {
+ if (CONSTRUCTOR_NELTS (t) == 0)
+ /* An initialized union has a constructor element. */
+ return false;
+ /* And it only initializes one member. */
+ field = NULL_TREE;
+ }
+ else
+ field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
+ }
+ else
+ field = NULL_TREE;
+ for (auto &e : CONSTRUCTOR_ELTS (t))
+ {
+ /* If VAL is null, we're in the middle of initializing this
+ element. */
+ if (!reduced_constant_expression_p (e.value))
+ return false;
+ /* Empty class field may or may not have an initializer. */
+ for (; field && e.index != field;
+ field = next_initializable_field (DECL_CHAIN (field)))
+ if (!is_really_empty_class (TREE_TYPE (field),
+ /*ignore_vptr*/ false))
+ return false;
+ if (field)
+ field = next_initializable_field (DECL_CHAIN (field));
+ }
+ /* There could be a non-empty field at the end. */
+ for (; field; field = next_initializable_field (DECL_CHAIN (field)))
+ if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/ false))
+ return false;
+ ok:
+ if (CONSTRUCTOR_NO_CLEARING (t))
+ /* All the fields are initialized. */
+ CONSTRUCTOR_NO_CLEARING (t) = false;
+ return true;
+
+ default:
+ /* FIXME are we calling this too much? */
+ return initializer_constant_valid_p (t, TREE_TYPE (t)) != NULL_TREE;
+ }
+}
+
+// forked from gcc/cp/constexpr.cc verify_constant
+
+/* Some expressions may have constant operands but are not constant
+ themselves, such as 1/0. Call this function to check for that
+ condition.
+
+ We only call this in places that require an arithmetic constant, not in
+ places where we might have a non-constant expression that can be a
+ component of a constant expression, such as the address of a constexpr
+ variable that might be dereferenced later. */
+
+static bool
+verify_constant (tree t, bool allow_non_constant, bool *non_constant_p,
+ bool *overflow_p)
+{
+ if (!*non_constant_p && !reduced_constant_expression_p (t) && t != void_node)
+ {
+ if (!allow_non_constant)
+ error ("%q+E is not a constant expression", t);
+ *non_constant_p = true;
+ }
+ if (TREE_OVERFLOW_P (t))
+ {
+ if (!allow_non_constant)
+ {
+ permerror (input_location, "overflow in constant expression");
+ /* If we're being permissive (and are in an enforcing
+ context), ignore the overflow. */
+ if (flag_permissive)
+ return *non_constant_p;
+ }
+ *overflow_p = true;
+ }
+ return *non_constant_p;
+}
+
+// forked in gcc/cp/constexpr.cc diag_array_subscript
+
+/* Under the control of CTX, issue a detailed diagnostic for
+ an out-of-bounds subscript INDEX into the expression ARRAY. */
+
+static void
+diag_array_subscript (location_t loc, const constexpr_ctx *ctx, tree array,
+ tree index)
+{
+ if (!ctx->quiet)
+ {
+ tree arraytype = TREE_TYPE (array);
+
+ /* Convert the unsigned array subscript to a signed integer to avoid
+ printing huge numbers for small negative values. */
+ tree sidx = fold_convert (ssizetype, index);
+ STRIP_ANY_LOCATION_WRAPPER (array);
+ if (DECL_P (array))
+ {
+ if (TYPE_DOMAIN (arraytype))
+ error_at (loc,
+ "array subscript value %qE is outside the bounds "
+ "of array %qD of type %qT",
+ sidx, array, arraytype);
+ else
+ error_at (loc,
+ "nonzero array subscript %qE is used with array %qD of "
+ "type %qT with unknown bounds",
+ sidx, array, arraytype);
+ inform (DECL_SOURCE_LOCATION (array), "declared here");
+ }
+ else if (TYPE_DOMAIN (arraytype))
+ error_at (loc,
+ "array subscript value %qE is outside the bounds "
+ "of array type %qT",
+ sidx, arraytype);
+ else
+ error_at (loc,
+ "nonzero array subscript %qE is used with array of type %qT "
+ "with unknown bounds",
+ sidx, arraytype);
+ }
+}
+
+// forked from gcc/cp/constexpr.cc get_array_or_vector_nelts
+
+/* Return the number of elements for TYPE (which is an ARRAY_TYPE or
+ a VECTOR_TYPE). */
+
+static tree
+get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type,
+ bool *non_constant_p, bool *overflow_p)
+{
+ tree nelts;
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ if (TYPE_DOMAIN (type))
+ nelts = array_type_nelts_top (type);
+ else
+ nelts = size_zero_node;
+ }
+ else if (VECTOR_TYPE_P (type))
+ nelts = size_int (TYPE_VECTOR_SUBPARTS (type));
+ else
+ gcc_unreachable ();
+
+ /* For VLAs, the number of elements won't be an integer constant. */
+ nelts = constexpr_expression (ctx, nelts, false, non_constant_p, overflow_p);
+ return nelts;
+}
+
+// forked from gcc/cp/constexpr.cc eval_and_check_array_index
+
+/* Subroutine of cxx_eval_array_reference. T is an ARRAY_REF; evaluate the
+ subscript, diagnose any problems with it, and return the result. */
+
+static tree
+eval_and_check_array_index (const constexpr_ctx *ctx, tree t,
+ bool allow_one_past, bool *non_constant_p,
+ bool *overflow_p)
+{
+ location_t loc = rs_expr_loc_or_input_loc (t);
+ tree ary = TREE_OPERAND (t, 0);
+ t = TREE_OPERAND (t, 1);
+ tree index
+ = constexpr_expression (ctx, t, allow_one_past, non_constant_p, overflow_p);
+ VERIFY_CONSTANT (index);
+
+ if (!tree_fits_shwi_p (index) || tree_int_cst_sgn (index) < 0)
+ {
+ diag_array_subscript (loc, ctx, ary, index);
+ *non_constant_p = true;
+ return t;
+ }
+
+ tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p,
+ overflow_p);
+ VERIFY_CONSTANT (nelts);
+ if (allow_one_past ? !tree_int_cst_le (index, nelts)
+ : !tree_int_cst_lt (index, nelts))
+ {
+ diag_array_subscript (loc, ctx, ary, index);
+ *non_constant_p = true;
+ return t;
+ }
+
+ return index;
+}
+
+// forked from gcc/cp/constexpr.cc extract_string_elt
+
+/* Extract element INDEX consisting of CHARS_PER_ELT chars from
+ STRING_CST STRING. */
+
+static tree
+extract_string_elt (tree string, unsigned chars_per_elt, unsigned index)
+{
+ tree type = cv_unqualified (TREE_TYPE (TREE_TYPE (string)));
+ tree r;
+
+ if (chars_per_elt == 1)
+ r = build_int_cst (type, TREE_STRING_POINTER (string)[index]);
+ else
+ {
+ const unsigned char *ptr
+ = ((const unsigned char *) TREE_STRING_POINTER (string)
+ + index * chars_per_elt);
+ r = native_interpret_expr (type, ptr, chars_per_elt);
+ }
+ return r;
+}
+
+// forked from gcc/cp/constexpr.cc free_constructor
+
+/* If T is a CONSTRUCTOR, ggc_free T and any sub-CONSTRUCTORs. */
+
+static void
+free_constructor (tree t)
+{
+ if (!t || TREE_CODE (t) != CONSTRUCTOR)
+ return;
+ releasing_vec ctors;
+ vec_safe_push (ctors, t);
+ while (!ctors->is_empty ())
+ {
+ tree c = ctors->pop ();
+ if (vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (c))
+ {
+ constructor_elt *ce;
+ for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
+ if (TREE_CODE (ce->value) == CONSTRUCTOR)
+ vec_safe_push (ctors, ce->value);
+ ggc_free (elts);
+ }
+ ggc_free (c);
+ }
+}
+
} // namespace Compile
} // namespace Rust