// This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #include "rust-constexpr.h" #include "rust-location.h" #include "rust-diagnostics.h" #include "rust-tree.h" #include "fold-const.h" #include "realmpfr.h" #include "convert.h" #include "print-tree.h" #include "gimplify.h" #include "tree-iterator.h" #include "timevar.h" #include "varasm.h" #include "cgraph.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; /* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */ vec *cleanups; /* Heap VAR_DECLs created during the evaluation of the outermost constant expression. */ auto_vec heap_vars; constexpr_global_ctx () : constexpr_ops_count (0) {} }; 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 constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p, bool unshare_p); tree decl_constant_value (tree decl, bool unshare_p); static void non_const_var_error (location_t loc, tree r); static 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, 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, bool, bool *, bool *); static tree get_function_named_in_call (tree t); tree fold_expr (tree expr) { constexpr_global_ctx global_ctx; constexpr_ctx ctx = {&global_ctx, false}; bool non_constant_p = false; bool overflow_p = false; 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, bool lval, bool *non_constant_p, bool *overflow_p, tree *jump_target /* = NULL */) { location_t loc = EXPR_LOCATION (t); if (CONSTANT_CLASS_P (t)) { if (TREE_OVERFLOW (t)) { error_at (loc, "overflow in constant expression"); return t; } return t; } // Avoid excessively long constexpr evaluations if (++ctx->global->constexpr_ops_count >= constexpr_ops_limit) { rust_error_at ( Location (loc), "% evaluation operation count exceeds limit of " "%wd (use %<-fconstexpr-ops-limit=%> to increase the limit)", constexpr_ops_limit); return t; } tree r = t; tree_code tcode = TREE_CODE (t); switch (tcode) { case CONST_DECL: { r = decl_constant_value (t, /*unshare_p=*/false); if (TREE_CODE (r) == TARGET_EXPR && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR) r = TARGET_EXPR_INITIAL (r); if (DECL_P (r)) { non_const_var_error (loc, r); return r; } } break; case POINTER_PLUS_EXPR: case POINTER_DIFF_EXPR: case PLUS_EXPR: case MINUS_EXPR: case MULT_EXPR: case TRUNC_DIV_EXPR: case CEIL_DIV_EXPR: case FLOOR_DIV_EXPR: case ROUND_DIV_EXPR: case TRUNC_MOD_EXPR: case CEIL_MOD_EXPR: case ROUND_MOD_EXPR: case RDIV_EXPR: case EXACT_DIV_EXPR: case MIN_EXPR: case MAX_EXPR: case LSHIFT_EXPR: case RSHIFT_EXPR: case LROTATE_EXPR: case RROTATE_EXPR: case BIT_IOR_EXPR: case BIT_XOR_EXPR: case BIT_AND_EXPR: case TRUTH_XOR_EXPR: case LT_EXPR: case LE_EXPR: case GT_EXPR: case GE_EXPR: case EQ_EXPR: case NE_EXPR: case SPACESHIP_EXPR: case UNORDERED_EXPR: case ORDERED_EXPR: case UNLT_EXPR: case UNLE_EXPR: case UNGT_EXPR: case UNGE_EXPR: case UNEQ_EXPR: case LTGT_EXPR: case RANGE_EXPR: case COMPLEX_EXPR: r = eval_binary_expression (ctx, t, false, non_constant_p, overflow_p); break; case CALL_EXPR: r = eval_call_expression (ctx, t); break; case RETURN_EXPR: rust_assert (TREE_OPERAND (t, 0) != NULL_TREE); r = constexpr_expression (ctx, TREE_OPERAND (t, 0), false, non_constant_p, overflow_p); break; case MODIFY_EXPR: r = eval_store_expression (ctx, t, false, non_constant_p, overflow_p); break; default: break; } return r; } static tree 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)) /* Just ignore clobbers. */ return void_node; /* First we figure out where we're storing to. */ tree target = TREE_OPERAND (t, 0); tree type = TREE_TYPE (target); bool preeval = SCALAR_TYPE_P (type) || TREE_CODE (t) == MODIFY_EXPR; if (preeval) { /* Evaluate the value to be stored without knowing what object it will be stored in, so that any side-effects happen first. */ init = fold_expr (init); } bool evaluated = false; tree object = NULL_TREE; for (tree probe = target; object == NULL_TREE;) { switch (TREE_CODE (probe)) { default: if (evaluated) object = probe; else { probe = constexpr_expression (ctx, probe, lval, non_constant_p, overflow_p); evaluated = true; } break; } } return init; } /* 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, 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, 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); tree type = TREE_TYPE (t); return fold_binary_loc (loc, code, type, lhs, rhs); } // Subroutine of cxx_eval_constant_expression. // Evaluate the call expression tree T in the context of OLD_CALL expression // evaluation. static tree eval_call_expression (const constexpr_ctx *ctx, tree t) { tree fun = get_function_named_in_call (t); return constexpr_fn_retval (ctx, DECL_SAVED_TREE (fun)); } // Subroutine of check_constexpr_fundef. BODY is the body of a function // declared to be constexpr, or a sub-statement thereof. Returns the // return value if suitable, error_mark_node for a statement not allowed in // a constexpr function, or NULL_TREE if no return value was found. static tree constexpr_fn_retval (const constexpr_ctx *ctx, tree body) { switch (TREE_CODE (body)) { case STATEMENT_LIST: { tree expr = NULL_TREE; for (tree stmt : tsi_range (body)) { tree s = constexpr_fn_retval (ctx, stmt); if (s == error_mark_node) return error_mark_node; else if (s == NULL_TREE) /* Keep iterating. */; else if (expr) /* Multiple return statements. */ return error_mark_node; else expr = s; } return expr; } 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 /* Accept __func__, __FUNCTION__, and __PRETTY_FUNCTION__. */ || DECL_ARTIFICIAL (decl)) return NULL_TREE; return error_mark_node; } case CLEANUP_POINT_EXPR: return constexpr_fn_retval (ctx, TREE_OPERAND (body, 0)); case BIND_EXPR: { tree b = BIND_EXPR_BODY (body); return constexpr_fn_retval (ctx, b); } break; default: return error_mark_node; } return error_mark_node; } // Taken from cp/constexpr.cc // // If DECL is a scalar enumeration constant or variable with a // constant initializer, return the initializer (or, its initializers, // recursively); otherwise, return DECL. If STRICT_P, the // initializer is only returned if DECL is a // constant-expression. If RETURN_AGGREGATE_CST_OK_P, it is ok to // return an aggregate constant. If UNSHARE_P, return an unshared // copy of the initializer. static tree constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p, bool unshare_p) { while (TREE_CODE (decl) == CONST_DECL) { tree init; /* If DECL is a static data member in a template specialization, we must instantiate it here. The initializer for the static data member is not processed until needed; we need it now. */ init = DECL_INITIAL (decl); if (init == error_mark_node) { if (TREE_CODE (decl) == CONST_DECL) /* Treat the error as a constant to avoid cascading errors on excessively recursive template instantiation (c++/9335). */ return init; else return decl; } decl = init; } return unshare_p ? unshare_expr (decl) : decl; } // A more relaxed version of decl_really_constant_value, used by the // common C/C++ code. tree decl_constant_value (tree decl, bool unshare_p) { return constant_value_1 (decl, /*strict_p=*/false, /*return_aggregate_cst_ok_p=*/true, /*unshare_p=*/unshare_p); } static void non_const_var_error (location_t loc, tree r) { error_at (loc, "the value of %qD is not usable in a constant " "expression", r); /* Avoid error cascade. */ if (DECL_INITIAL (r) == error_mark_node) return; // more in cp/constexpr.cc } static tree get_callee (tree call) { if (call == NULL_TREE) return call; else if (TREE_CODE (call) == CALL_EXPR) return CALL_EXPR_FN (call); return NULL_TREE; } // We have an expression tree T that represents a call, either CALL_EXPR // or AGGR_INIT_EXPR. If the call is lexically to a named function, // return the _DECL for that function. static tree get_function_named_in_call (tree t) { tree fun = get_callee (t); if (fun && TREE_CODE (fun) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (fun, 0)) == FUNCTION_DECL) fun = TREE_OPERAND (fun, 0); return fun; } // forked from gcc/cp/constexpr.cc maybe_constexpr_fn /* True if a function might be declared constexpr */ bool maybe_constexpr_fn (tree t) { return (DECL_DECLARED_CONSTEXPR_P (t)); } // forked from gcc/cp/constexpr.cc get_nth_callarg /* We have an expression tree T that represents a call, either CALL_EXPR. Return the Nth argument. */ inline tree get_nth_callarg (tree t, int n) { switch (TREE_CODE (t)) { case CALL_EXPR: return CALL_EXPR_ARG (t, n); default: gcc_unreachable (); return NULL; } } // forked from gcc/cp/constexpr.cc var_in_maybe_constexpr_fn /* True if T was declared in a function that might be constexpr: either a function that was declared constexpr. */ bool 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 /* Some of the expressions fed to the constexpr mechanism are calls to constructors, which have type void. In that case, return the type being initialized by the constructor. */ static tree initialized_type (tree t) { if (TYPE_P (t)) return t; tree type = TREE_TYPE (t); if (TREE_CODE (t) == CALL_EXPR) { /* A constructor call has void type, so we need to look deeper. */ tree fn = get_function_named_in_call (t); if (fn && TREE_CODE (fn) == FUNCTION_DECL && DECL_CXX_CONSTRUCTOR_P (fn)) type = DECL_CONTEXT (fn); } else if (TREE_CODE (t) == COMPOUND_EXPR) return initialized_type (TREE_OPERAND (t, 1)); return cv_unqualified (type); } /* P0859: A function is needed for constant evaluation if it is a constexpr function that is named by an expression ([basic.def.odr]) that is potentially constant evaluated. So we need to instantiate any constexpr functions mentioned by the expression even if the definition isn't needed for evaluating the expression. */ static tree instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void * /*data*/) { if (TREE_CODE (*tp) == CALL_EXPR) { if (EXPR_HAS_LOCATION (*tp)) input_location = EXPR_LOCATION (*tp); } if (!EXPR_P (*tp)) *walk_subtrees = 0; return NULL_TREE; } static void instantiate_constexpr_fns (tree t) { location_t loc = input_location; rs_walk_tree_without_duplicates (&t, instantiate_cx_fn_r, NULL); input_location = loc; } /* 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 (); } } /* 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 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 *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; } /* 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 *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; } /* 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; } } /* 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 from gcc/cp/constexpr.cc find_heap_var_refs /* Look for heap variables in the expression *TP. */ static tree find_heap_var_refs (tree *tp, int *walk_subtrees, void * /*data*/) { if (VAR_P (*tp) && (DECL_NAME (*tp) == heap_uninit_identifier || DECL_NAME (*tp) == heap_identifier || DECL_NAME (*tp) == heap_vec_uninit_identifier || DECL_NAME (*tp) == heap_vec_identifier || DECL_NAME (*tp) == heap_deleted_identifier)) return *tp; if (TYPE_P (*tp)) *walk_subtrees = 0; return NULL_TREE; } // forked from gcc/cp/constexpr.cc find_immediate_fndecl /* Find immediate function decls in *TP if any. */ static tree find_immediate_fndecl (tree *tp, int * /*walk_subtrees*/, void * /*data*/) { if (TREE_CODE (*tp) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (*tp)) return *tp; if (TREE_CODE (*tp) == PTRMEM_CST && TREE_CODE (PTRMEM_CST_MEMBER (*tp)) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (*tp))) return PTRMEM_CST_MEMBER (*tp); return NULL_TREE; } // 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 *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