blob: 4e581a3f2cfd8696821af6b857d6e17188881d56 [file] [log] [blame]
// 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
// <http://www.gnu.org/licenses/>.
#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"
#include "tree-inline.h"
#include "vec.h"
#include "function.h"
#include "diagnostic.h"
#include "target.h"
#include "builtins.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 {
/* Returns true iff FUN is an instantiation of a constexpr function
template or a defaulted constexpr function. */
bool
is_instantiation_of_constexpr (tree fun)
{
return DECL_DECLARED_CONSTEXPR_P (fun);
}
/* Return true if T is a literal type. */
bool
literal_type_p (tree t)
{
if (SCALAR_TYPE_P (t) || VECTOR_TYPE_P (t) || TYPE_REF_P (t)
|| (VOID_TYPE_P (t)))
return true;
if (TREE_CODE (t) == ARRAY_TYPE)
return literal_type_p (strip_array_types (t));
return false;
}
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);
static bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
tsubst_flags_t flags, tree *jump_target);
bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
tsubst_flags_t flags);
tree
unshare_constructor (tree t MEM_STAT_DECL);
void
maybe_save_constexpr_fundef (tree fun);
static bool
returns (tree *jump_target);
static bool
breaks (tree *jump_target);
static bool
continues (tree *jump_target);
static bool
switches (tree *jump_target);
struct constexpr_global_ctx
{
/* Values for any temporaries or local variables within the
constant-expression. */
hash_map<tree, tree> values;
/* Number of cxx_eval_constant_expression calls (except skipped ones,
on simple constants or location wrappers) encountered during current
cxx_eval_outermost_constant_expr call. */
HOST_WIDE_INT constexpr_ops_count;
/* Heap VAR_DECLs created during the evaluation of the outermost constant
expression. */
auto_vec<tree, 16> heap_vars;
/* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */
vec<tree> *cleanups;
/* Number of heap VAR_DECL deallocations. */
unsigned heap_dealloc_count;
/* Constructor. */
constexpr_global_ctx ()
: constexpr_ops_count (0), cleanups (NULL), heap_dealloc_count (0)
{}
};
/* In constexpr.cc */
/* Representation of entries in the constexpr function definition table. */
struct GTY ((for_user)) constexpr_fundef
{
tree decl;
tree body;
tree parms;
tree result;
};
/* Objects of this type represent calls to constexpr functions
along with the bindings of parameters to their arguments, for
the purpose of compile time evaluation. */
struct GTY ((for_user)) constexpr_call
{
/* Description of the constexpr function definition. */
constexpr_fundef *fundef;
/* Parameter bindings environment. A TREE_VEC of arguments. */
tree bindings;
/* Result of the call.
NULL means the call is being evaluated.
error_mark_node means that the evaluation was erroneous;
otherwise, the actuall value of the call. */
tree result;
/* The hash of this call; we remember it here to avoid having to
recalculate it when expanding the hash table. */
hashval_t hash;
/* Whether __builtin_is_constant_evaluated() should evaluate to true. */
bool manifestly_const_eval;
};
struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call>
{
static hashval_t hash (constexpr_call *);
static bool equal (constexpr_call *, constexpr_call *);
};
enum constexpr_switch_state
{
/* Used when processing a switch for the first time by cxx_eval_switch_expr
and default: label for that switch has not been seen yet. */
css_default_not_seen,
/* Used when processing a switch for the first time by cxx_eval_switch_expr
and default: label for that switch has been seen already. */
css_default_seen,
/* Used when processing a switch for the second time by
cxx_eval_switch_expr, where default: label should match. */
css_default_processing
};
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;
/* The innermost call we're evaluating. */
constexpr_call *call;
/* SAVE_EXPRs and TARGET_EXPR_SLOT vars of TARGET_EXPRs that we've seen
within the current LOOP_EXPR. NULL if we aren't inside a loop. */
vec<tree> *save_exprs;
/* The CONSTRUCTOR we're currently building up for an aggregate
initializer. */
tree ctor;
/* The object we're building the CONSTRUCTOR for. */
tree object;
/* If inside SWITCH_EXPR. */
constexpr_switch_state *css_state;
/* The aggregate initialization context inside which this one is nested. This
is used by lookup_placeholder to resolve PLACEHOLDER_EXPRs. */
const constexpr_ctx *parent;
/* 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;
/* Whether we are strictly conforming to constant expression rules or
trying harder to get a constant value. */
bool strict;
/* Whether __builtin_is_constant_evaluated () should be true. */
bool manifestly_const_eval;
};
struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
{
static hashval_t hash (const constexpr_fundef *);
static bool equal (const constexpr_fundef *, const constexpr_fundef *);
};
/* This table holds all constexpr function definitions seen in
the current translation unit. */
static GTY (()) hash_table<constexpr_fundef_hasher> *constexpr_fundef_table;
/* Utility function used for managing the constexpr function table.
Return true if the entries pointed to by P and Q are for the
same constexpr function. */
inline bool
constexpr_fundef_hasher::equal (const constexpr_fundef *lhs,
const constexpr_fundef *rhs)
{
return lhs->decl == rhs->decl;
}
/* Utility function used for managing the constexpr function table.
Return a hash value for the entry pointed to by Q. */
inline hashval_t
constexpr_fundef_hasher::hash (const constexpr_fundef *fundef)
{
return DECL_UID (fundef->decl);
}
/* Return a previously saved definition of function FUN. */
constexpr_fundef *
retrieve_constexpr_fundef (tree fun)
{
if (constexpr_fundef_table == NULL)
return NULL;
constexpr_fundef fundef = {fun, NULL_TREE, NULL_TREE, NULL_TREE};
return constexpr_fundef_table->find (&fundef);
}
/* This internal flag controls whether we should avoid doing anything during
constexpr evaluation that would cause extra DECL_UID generation, such as
template instantiation and function body copying. */
static bool uid_sensitive_constexpr_evaluation_value;
/* An internal counter that keeps track of the number of times
uid_sensitive_constexpr_evaluation_p returned true. */
static unsigned uid_sensitive_constexpr_evaluation_true_counter;
/* The accessor for uid_sensitive_constexpr_evaluation_value which also
increments the corresponding counter. */
static bool
uid_sensitive_constexpr_evaluation_p ()
{
if (uid_sensitive_constexpr_evaluation_value)
{
++uid_sensitive_constexpr_evaluation_true_counter;
return true;
}
else
return false;
}
/* RAII sentinel that saves the value of a variable, optionally
overrides it right away, and restores its value when the sentinel
id destructed. */
template <typename T> class temp_override
{
T &overridden_variable;
T saved_value;
public:
temp_override (T &var) : overridden_variable (var), saved_value (var) {}
temp_override (T &var, T overrider)
: overridden_variable (var), saved_value (var)
{
overridden_variable = overrider;
}
~temp_override () { overridden_variable = saved_value; }
};
/* An RAII sentinel used to restrict constexpr evaluation so that it
doesn't do anything that causes extra DECL_UID generation. */
struct uid_sensitive_constexpr_evaluation_sentinel
{
temp_override<bool> ovr;
uid_sensitive_constexpr_evaluation_sentinel ();
};
/* Used to determine whether uid_sensitive_constexpr_evaluation_p was
called and returned true, indicating that we've restricted constexpr
evaluation in order to avoid UID generation. We use this to control
updates to the fold_cache and cv_cache. */
struct uid_sensitive_constexpr_evaluation_checker
{
const unsigned saved_counter;
uid_sensitive_constexpr_evaluation_checker ();
bool evaluation_restricted_p () const;
};
/* The default constructor for uid_sensitive_constexpr_evaluation_sentinel
enables the internal flag for uid_sensitive_constexpr_evaluation_p
during the lifetime of the sentinel object. Upon its destruction, the
previous value of uid_sensitive_constexpr_evaluation_p is restored. */
uid_sensitive_constexpr_evaluation_sentinel ::
uid_sensitive_constexpr_evaluation_sentinel ()
: ovr (uid_sensitive_constexpr_evaluation_value, true)
{}
/* The default constructor for uid_sensitive_constexpr_evaluation_checker
records the current number of times that uid_sensitive_constexpr_evaluation_p
has been called and returned true. */
uid_sensitive_constexpr_evaluation_checker ::
uid_sensitive_constexpr_evaluation_checker ()
: saved_counter (uid_sensitive_constexpr_evaluation_true_counter)
{}
/* Returns true iff uid_sensitive_constexpr_evaluation_p is true, and
some constexpr evaluation was restricted due to u_s_c_e_p being called
and returning true during the lifetime of this checker object. */
bool
uid_sensitive_constexpr_evaluation_checker::evaluation_restricted_p () const
{
return (uid_sensitive_constexpr_evaluation_value
&& saved_counter != uid_sensitive_constexpr_evaluation_true_counter);
}
/* A table of all constexpr calls that have been evaluated by the
compiler in this translation unit. */
static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
/* Compute a hash value for a constexpr call representation. */
inline hashval_t
constexpr_call_hasher::hash (constexpr_call *info)
{
return info->hash;
}
/* Return true if the objects pointed to by P and Q represent calls
to the same constexpr function with the same arguments.
Otherwise, return false. */
bool
constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs)
{
if (lhs == rhs)
return true;
if (lhs->hash != rhs->hash)
return false;
if (lhs->manifestly_const_eval != rhs->manifestly_const_eval)
return false;
if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef))
return false;
return rs_tree_equal (lhs->bindings, rhs->bindings);
}
/* Initialize the constexpr call table, if needed. */
static void
maybe_initialize_constexpr_call_table (void)
{
if (constexpr_call_table == NULL)
constexpr_call_table = hash_table<constexpr_call_hasher>::create_ggc (101);
}
/* During constexpr CALL_EXPR evaluation, to avoid issues with sharing when
a function happens to get called recursively, we unshare the callee
function's body and evaluate this unshared copy instead of evaluating the
original body.
FUNDEF_COPIES_TABLE is a per-function freelist of these unshared function
copies. The underlying data structure of FUNDEF_COPIES_TABLE is a hash_map
that's keyed off of the original FUNCTION_DECL and whose value is a
TREE_LIST of this function's unused copies awaiting reuse.
This is not GC-deletable to avoid GC affecting UID generation. */
static GTY (()) decl_tree_map *fundef_copies_table;
/* Reuse a copy or create a new unshared copy of the function FUN.
Return this copy. We use a TREE_LIST whose PURPOSE is body, VALUE
is parms, TYPE is result. */
static tree
get_fundef_copy (constexpr_fundef *fundef)
{
tree copy;
bool existed;
tree *slot
= &(hash_map_safe_get_or_insert<hm_ggc> (fundef_copies_table, fundef->decl,
&existed, 127));
if (!existed)
{
/* There is no cached function available, or in use. We can use
the function directly. That the slot is now created records
that this function is now in use. */
copy = build_tree_list (fundef->body, fundef->parms);
TREE_TYPE (copy) = fundef->result;
}
else if (*slot == NULL_TREE)
{
if (uid_sensitive_constexpr_evaluation_p ())
return NULL_TREE;
/* We've already used the function itself, so make a copy. */
copy = build_tree_list (NULL, NULL);
tree saved_body = DECL_SAVED_TREE (fundef->decl);
tree saved_parms = DECL_ARGUMENTS (fundef->decl);
tree saved_result = DECL_RESULT (fundef->decl);
tree saved_fn = current_function_decl;
DECL_SAVED_TREE (fundef->decl) = fundef->body;
DECL_ARGUMENTS (fundef->decl) = fundef->parms;
DECL_RESULT (fundef->decl) = fundef->result;
current_function_decl = fundef->decl;
TREE_PURPOSE (copy)
= copy_fn (fundef->decl, TREE_VALUE (copy), TREE_TYPE (copy));
current_function_decl = saved_fn;
DECL_RESULT (fundef->decl) = saved_result;
DECL_ARGUMENTS (fundef->decl) = saved_parms;
DECL_SAVED_TREE (fundef->decl) = saved_body;
}
else
{
/* We have a cached function available. */
copy = *slot;
*slot = TREE_CHAIN (copy);
}
return copy;
}
/* Save the copy COPY of function FUN for later reuse by
get_fundef_copy(). By construction, there will always be an entry
to find. */
static void
save_fundef_copy (tree fun, tree copy)
{
tree *slot = fundef_copies_table->get (fun);
TREE_CHAIN (copy) = *slot;
*slot = copy;
}
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
eval_constant_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, bool, bool *, bool *);
static tree
eval_binary_expression (const constexpr_ctx *ctx, tree r, bool, bool *, bool *);
static tree
get_function_named_in_call (tree t);
static tree
eval_statement_list (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
bool *overflow_p, tree *jump_target);
static tree
extract_string_elt (tree string, unsigned chars_per_elt, unsigned index);
static tree
eval_conditional_expression (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p,
tree *jump_target);
static tree
eval_bit_field_ref (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p);
static tree
eval_loop_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
bool *overflow_p, tree *jump_target);
static tree
eval_switch_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
bool *overflow_p, tree *jump_target);
static tree
eval_unary_expression (const constexpr_ctx *ctx, tree t, bool /*lval*/,
bool *non_constant_p, bool *overflow_p);
/* Variables and functions to manage constexpr call expansion context.
These do not need to be marked for PCH or GC. */
/* FIXME remember and print actual constant arguments. */
static vec<tree> call_stack;
static int call_stack_tick;
static int last_cx_error_tick;
static int
push_cx_call_context (tree call)
{
++call_stack_tick;
if (!EXPR_HAS_LOCATION (call))
SET_EXPR_LOCATION (call, input_location);
call_stack.safe_push (call);
int len = call_stack.length ();
if (len > max_constexpr_depth)
return false;
return len;
}
static void
pop_cx_call_context (void)
{
++call_stack_tick;
call_stack.pop ();
}
vec<tree>
cx_error_context (void)
{
vec<tree> r = vNULL;
if (call_stack_tick != last_cx_error_tick && !call_stack.is_empty ())
r = call_stack;
last_cx_error_tick = call_stack_tick;
return r;
}
// this is ported from cxx_eval_outermost_constant_expr
tree
fold_expr (tree expr)
{
bool allow_non_constant = false;
bool strict = true;
bool manifestly_const_eval = false;
constexpr_global_ctx global_ctx;
constexpr_ctx ctx
= {&global_ctx, NULL,
NULL, NULL,
NULL, NULL,
NULL, allow_non_constant,
strict, manifestly_const_eval || !allow_non_constant};
auto_vec<tree, 16> cleanups;
global_ctx.cleanups = &cleanups;
bool non_constant_p = false;
bool overflow_p = false;
tree folded = eval_constant_expression (&ctx, expr, false, &non_constant_p,
&overflow_p);
rust_assert (folded != NULL_TREE);
// more logic here to possibly port
return folded;
}
static bool
same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2)
{
while (TREE_CODE (type1) == ARRAY_TYPE && TREE_CODE (type2) == ARRAY_TYPE
&& (!TYPE_DOMAIN (type1) || !TYPE_DOMAIN (type2)))
{
type1 = TREE_TYPE (type1);
type2 = TREE_TYPE (type2);
}
return same_type_ignoring_top_level_qualifiers_p (type1, type2);
}
// forked from gcc/cp/constexpr.cc cxx_union_active_member
/* Try to determine the currently active union member for an expression
with UNION_TYPE. If it can be determined, return the FIELD_DECL,
otherwise return NULL_TREE. */
static tree
union_active_member (const constexpr_ctx *ctx, tree t)
{
constexpr_ctx new_ctx = *ctx;
new_ctx.quiet = true;
bool non_constant_p = false, overflow_p = false;
tree ctor = eval_constant_expression (&new_ctx, t, false, &non_constant_p,
&overflow_p);
if (TREE_CODE (ctor) == CONSTRUCTOR && CONSTRUCTOR_NELTS (ctor) == 1
&& CONSTRUCTOR_ELT (ctor, 0)->index
&& TREE_CODE (CONSTRUCTOR_ELT (ctor, 0)->index) == FIELD_DECL)
return CONSTRUCTOR_ELT (ctor, 0)->index;
return NULL_TREE;
}
// forked from gcc/cp/constexpr.cc cxx_fold_indirect_ref_1
static tree
fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type,
tree op, unsigned HOST_WIDE_INT off, bool *empty_base)
{
tree optype = TREE_TYPE (op);
unsigned HOST_WIDE_INT const_nunits;
if (off == 0 && similar_type_p (optype, type))
return op;
else if (TREE_CODE (optype) == COMPLEX_TYPE
&& similar_type_p (type, TREE_TYPE (optype)))
{
/* *(foo *)&complexfoo => __real__ complexfoo */
if (off == 0)
return build1_loc (loc, REALPART_EXPR, type, op);
/* ((foo*)&complexfoo)[1] => __imag__ complexfoo */
else if (tree_to_uhwi (TYPE_SIZE_UNIT (type)) == off)
return build1_loc (loc, IMAGPART_EXPR, type, op);
}
/* ((foo*)&vectorfoo)[x] => BIT_FIELD_REF<vectorfoo,...> */
else if (VECTOR_TYPE_P (optype) && similar_type_p (type, TREE_TYPE (optype))
&& TYPE_VECTOR_SUBPARTS (optype).is_constant (&const_nunits))
{
unsigned HOST_WIDE_INT part_width = tree_to_uhwi (TYPE_SIZE_UNIT (type));
unsigned HOST_WIDE_INT max_offset = part_width * const_nunits;
if (off < max_offset && off % part_width == 0)
{
tree index = bitsize_int (off * BITS_PER_UNIT);
return build3_loc (loc, BIT_FIELD_REF, type, op, TYPE_SIZE (type),
index);
}
}
/* ((foo *)&fooarray)[x] => fooarray[x] */
else if (TREE_CODE (optype) == ARRAY_TYPE
&& tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (optype)))
&& !integer_zerop (TYPE_SIZE_UNIT (TREE_TYPE (optype))))
{
tree type_domain = TYPE_DOMAIN (optype);
tree min_val = size_zero_node;
if (type_domain && TYPE_MIN_VALUE (type_domain))
min_val = TYPE_MIN_VALUE (type_domain);
unsigned HOST_WIDE_INT el_sz
= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (optype)));
unsigned HOST_WIDE_INT idx = off / el_sz;
unsigned HOST_WIDE_INT rem = off % el_sz;
if (tree_fits_uhwi_p (min_val))
{
tree index = size_int (idx + tree_to_uhwi (min_val));
op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index,
NULL_TREE, NULL_TREE);
return fold_indirect_ref_1 (ctx, loc, type, op, rem, empty_base);
}
}
/* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */
else if (TREE_CODE (optype) == RECORD_TYPE
|| TREE_CODE (optype) == UNION_TYPE)
{
if (TREE_CODE (optype) == UNION_TYPE)
/* For unions prefer the currently active member. */
if (tree field = union_active_member (ctx, op))
{
unsigned HOST_WIDE_INT el_sz
= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
if (off < el_sz)
{
tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), op, field,
NULL_TREE);
if (tree ret = fold_indirect_ref_1 (ctx, loc, type, cop, off,
empty_base))
return ret;
}
}
for (tree field = TYPE_FIELDS (optype); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& TREE_TYPE (field) != error_mark_node
&& tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (field))))
{
tree pos = byte_position (field);
if (!tree_fits_uhwi_p (pos))
continue;
unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos);
unsigned HOST_WIDE_INT el_sz
= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field)));
if (upos <= off && off < upos + el_sz)
{
tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), op, field,
NULL_TREE);
if (tree ret = fold_indirect_ref_1 (ctx, loc, type, cop,
off - upos, empty_base))
return ret;
}
}
/* Also handle conversion to an empty base class, which
is represented with a NOP_EXPR. */
if (is_empty_class (type) && CLASS_TYPE_P (optype))
{
*empty_base = true;
return op;
}
}
return NULL_TREE;
}
// forked from gcc/cp/constexpr.cc cxx_fold_indirect_ref
/* A less strict version of fold_indirect_ref_1, which requires cv-quals to
match. We want to be less strict for simple *& folding; if we have a
non-const temporary that we access through a const pointer, that should
work. We handle this here rather than change fold_indirect_ref_1
because we're dealing with things like ADDR_EXPR of INTEGER_CST which
don't really make sense outside of constant expression evaluation. Also
we want to allow folding to COMPONENT_REF, which could cause trouble
with TBAA in fold_indirect_ref_1. */
static tree
rs_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
tree op0, bool *empty_base)
{
tree sub = op0;
tree subtype;
poly_uint64 const_op01;
/* STRIP_NOPS, but stop if REINTERPRET_CAST_P. */
while (CONVERT_EXPR_P (sub) || TREE_CODE (sub) == NON_LVALUE_EXPR
|| TREE_CODE (sub) == VIEW_CONVERT_EXPR)
{
if (TREE_CODE (sub) == NOP_EXPR && REINTERPRET_CAST_P (sub))
return NULL_TREE;
sub = TREE_OPERAND (sub, 0);
}
subtype = TREE_TYPE (sub);
if (!INDIRECT_TYPE_P (subtype))
return NULL_TREE;
/* Canonicalizes the given OBJ/OFF pair by iteratively absorbing
the innermost component into the offset until it would make the
offset positive, so that cxx_fold_indirect_ref_1 can identify
more folding opportunities. */
auto canonicalize_obj_off = [] (tree &obj, tree &off) {
while (TREE_CODE (obj) == COMPONENT_REF
&& (tree_int_cst_sign_bit (off) || integer_zerop (off)))
{
tree field = TREE_OPERAND (obj, 1);
tree pos = byte_position (field);
if (integer_zerop (off) && integer_nonzerop (pos))
/* If the offset is already 0, keep going as long as the
component is at position 0. */
break;
off = int_const_binop (PLUS_EXPR, off, pos);
obj = TREE_OPERAND (obj, 0);
}
};
if (TREE_CODE (sub) == ADDR_EXPR)
{
tree op = TREE_OPERAND (sub, 0);
tree optype = TREE_TYPE (op);
/* *&CONST_DECL -> to the value of the const decl. */
if (TREE_CODE (op) == CONST_DECL)
return DECL_INITIAL (op);
/* *&p => p; make sure to handle *&"str"[cst] here. */
if (similar_type_p (optype, type))
{
tree fop = fold_read_from_constant_string (op);
if (fop)
return fop;
else
return op;
}
else
{
tree off = integer_zero_node;
canonicalize_obj_off (op, off);
gcc_assert (integer_zerop (off));
return fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base);
}
}
else if (TREE_CODE (sub) == POINTER_PLUS_EXPR
&& tree_fits_uhwi_p (TREE_OPERAND (sub, 1)))
{
tree op00 = TREE_OPERAND (sub, 0);
tree off = TREE_OPERAND (sub, 1);
STRIP_NOPS (op00);
if (TREE_CODE (op00) == ADDR_EXPR)
{
tree obj = TREE_OPERAND (op00, 0);
canonicalize_obj_off (obj, off);
return fold_indirect_ref_1 (ctx, loc, type, obj, tree_to_uhwi (off),
empty_base);
}
}
/* *(foo *)fooarrptr => (*fooarrptr)[0] */
else if (TREE_CODE (TREE_TYPE (subtype)) == ARRAY_TYPE
&& similar_type_p (type, TREE_TYPE (TREE_TYPE (subtype))))
{
tree type_domain;
tree min_val = size_zero_node;
tree newsub
= rs_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL);
if (newsub)
sub = newsub;
else
sub = build1_loc (loc, INDIRECT_REF, TREE_TYPE (subtype), sub);
type_domain = TYPE_DOMAIN (TREE_TYPE (sub));
if (type_domain && TYPE_MIN_VALUE (type_domain))
min_val = TYPE_MIN_VALUE (type_domain);
return build4_loc (loc, ARRAY_REF, type, sub, min_val, NULL_TREE,
NULL_TREE);
}
return NULL_TREE;
}
// forked from gcc/cp/constexpr.cc cxx_eval_indirect_ref
static tree
rs_eval_indirect_ref (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p)
{
tree orig_op0 = TREE_OPERAND (t, 0);
bool empty_base = false;
/* We can handle a MEM_REF like an INDIRECT_REF, if MEM_REF's second
operand is an integer-zero. Otherwise reject the MEM_REF for now. */
if (TREE_CODE (t) == MEM_REF
&& (!TREE_OPERAND (t, 1) || !integer_zerop (TREE_OPERAND (t, 1))))
{
gcc_assert (ctx->quiet);
*non_constant_p = true;
return t;
}
/* First try to simplify it directly. */
tree r = rs_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t),
orig_op0, &empty_base);
if (!r)
{
/* If that didn't work, evaluate the operand first. */
tree op0
= eval_constant_expression (ctx, orig_op0,
/*lval*/ false, non_constant_p, overflow_p);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p)
return t;
if (!lval && integer_zerop (op0))
{
if (!ctx->quiet)
error ("dereferencing a null pointer");
*non_constant_p = true;
return t;
}
r = rs_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), op0,
&empty_base);
if (r == NULL_TREE)
{
/* We couldn't fold to a constant value. Make sure it's not
something we should have been able to fold. */
tree sub = op0;
STRIP_NOPS (sub);
if (TREE_CODE (sub) == ADDR_EXPR)
{
gcc_assert (
!similar_type_p (TREE_TYPE (TREE_TYPE (sub)), TREE_TYPE (t)));
/* DR 1188 says we don't have to deal with this. */
if (!ctx->quiet)
error_at (rs_expr_loc_or_input_loc (t),
"accessing value of %qE through a %qT glvalue in a "
"constant expression",
build_fold_indirect_ref (sub), TREE_TYPE (t));
*non_constant_p = true;
return t;
}
if (lval && op0 != orig_op0)
return build1 (INDIRECT_REF, TREE_TYPE (t), op0);
if (!lval)
VERIFY_CONSTANT (t);
return t;
}
}
r = eval_constant_expression (ctx, r, lval, non_constant_p, overflow_p);
if (*non_constant_p)
return t;
/* If we're pulling out the value of an empty base, just return an empty
CONSTRUCTOR. */
if (empty_base && !lval)
{
r = build_constructor (TREE_TYPE (t), NULL);
TREE_CONSTANT (r) = true;
}
return r;
}
// forked from gcc/cp/constexpr.cc cxx_eval_logical_expression
/* Subroutine of cxx_eval_constant_expression.
Evaluate a short-circuited logical expression T in the context
of a given constexpr CALL. BAILOUT_VALUE is the value for
early return. CONTINUE_VALUE is used here purely for
sanity check purposes. */
static tree
eval_logical_expression (const constexpr_ctx *ctx, tree t, tree bailout_value,
tree continue_value, bool lval, bool *non_constant_p,
bool *overflow_p)
{
tree r;
tree lhs = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p);
VERIFY_CONSTANT (lhs);
if (tree_int_cst_equal (lhs, bailout_value))
return lhs;
gcc_assert (tree_int_cst_equal (lhs, continue_value));
r = eval_constant_expression (ctx, TREE_OPERAND (t, 1), lval, non_constant_p,
overflow_p);
VERIFY_CONSTANT (r);
return r;
}
// forked from gcc/cp/constexp.rcc lookup_placeholder
/* Find the object of TYPE under initialization in CTX. */
static tree
lookup_placeholder (const constexpr_ctx *ctx, bool lval, tree type)
{
if (!ctx)
return NULL_TREE;
/* Prefer the outermost matching object, but don't cross
CONSTRUCTOR_PLACEHOLDER_BOUNDARY constructors. */
if (ctx->ctor && !CONSTRUCTOR_PLACEHOLDER_BOUNDARY (ctx->ctor))
if (tree outer_ob = lookup_placeholder (ctx->parent, lval, type))
return outer_ob;
/* We could use ctx->object unconditionally, but using ctx->ctor when we
can is a minor optimization. */
if (!lval && ctx->ctor && same_type_p (TREE_TYPE (ctx->ctor), type))
return ctx->ctor;
if (!ctx->object)
return NULL_TREE;
/* Since an object cannot have a field of its own type, we can search outward
from ctx->object to find the unique containing object of TYPE. */
tree ob = ctx->object;
while (ob)
{
if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (ob), type))
break;
if (handled_component_p (ob))
ob = TREE_OPERAND (ob, 0);
else
ob = NULL_TREE;
}
return ob;
}
// forked from gcc/cp/constexp.rcc inline_asm_in_constexpr_error
/* Complain about an attempt to evaluate inline assembly. */
static void
inline_asm_in_constexpr_error (location_t loc)
{
auto_diagnostic_group d;
error_at (loc, "inline assembly is not a constant expression");
inform (loc, "only unevaluated inline assembly is allowed in a "
"%<constexpr%> function in C++20");
}
// forked from gcc/cp/constexpr.cc verify_ctor_sanity
/* We're about to process an initializer for a class or array TYPE. Make
sure that CTX is set up appropriately. */
static void
verify_ctor_sanity (const constexpr_ctx *ctx, tree type)
{
/* We don't bother building a ctor for an empty base subobject. */
if (is_empty_class (type))
return;
/* We're in the middle of an initializer that might involve placeholders;
our caller should have created a CONSTRUCTOR for us to put the
initializer into. We will either return that constructor or T. */
gcc_assert (ctx->ctor);
gcc_assert (
same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (ctx->ctor)));
/* We used to check that ctx->ctor was empty, but that isn't the case when
the object is zero-initialized before calling the constructor. */
if (ctx->object)
{
tree otype = TREE_TYPE (ctx->object);
gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, otype)
/* Handle flexible array members. */
|| (TREE_CODE (otype) == ARRAY_TYPE
&& TYPE_DOMAIN (otype) == NULL_TREE
&& TREE_CODE (type) == ARRAY_TYPE
&& (same_type_ignoring_top_level_qualifiers_p (
TREE_TYPE (type), TREE_TYPE (otype)))));
}
gcc_assert (!ctx->object || !DECL_P (ctx->object)
|| *(ctx->global->values.get (ctx->object)) == ctx->ctor);
}
// 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);
}
// forked from gcc/cp/constexpr.cc init_subob_ctx
/* We're about to initialize element INDEX of an array or class from VALUE.
Set up NEW_CTX appropriately by adjusting .object to refer to the
subobject and creating a new CONSTRUCTOR if the element is itself
a class or array. */
static void
init_subob_ctx (const constexpr_ctx *ctx, constexpr_ctx &new_ctx, tree index,
tree &value)
{
new_ctx = *ctx;
if (index && TREE_CODE (index) != INTEGER_CST
&& TREE_CODE (index) != FIELD_DECL && TREE_CODE (index) != RANGE_EXPR)
/* This won't have an element in the new CONSTRUCTOR. */
return;
tree type = initialized_type (value);
if (!AGGREGATE_TYPE_P (type) && !VECTOR_TYPE_P (type))
/* A non-aggregate member doesn't get its own CONSTRUCTOR. */
return;
/* The sub-aggregate initializer might contain a placeholder;
update object to refer to the subobject and ctor to refer to
the (newly created) sub-initializer. */
if (ctx->object)
{
if (index == NULL_TREE || TREE_CODE (index) == RANGE_EXPR)
/* There's no well-defined subobject for this index. */
new_ctx.object = NULL_TREE;
else
{
// Faisal: commenting this out as not sure if it's needed and it's
// huge new_ctx.object = build_ctor_subob_ref (index, type,
// ctx->object);
}
}
tree elt = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (elt) = true;
new_ctx.ctor = elt;
if (TREE_CODE (value) == TARGET_EXPR)
/* Avoid creating another CONSTRUCTOR when we expand the TARGET_EXPR. */
value = TARGET_EXPR_INITIAL (value);
}
// forked from gcc/cp/constexpr.cc base_field_constructor_elt
/* REF is a COMPONENT_REF designating a particular field. V is a vector of
CONSTRUCTOR elements to initialize (part of) an object containing that
field. Return a pointer to the constructor_elt corresponding to the
initialization of the field. */
static constructor_elt *
base_field_constructor_elt (vec<constructor_elt, va_gc> *v, tree ref)
{
tree aggr = TREE_OPERAND (ref, 0);
tree field = TREE_OPERAND (ref, 1);
HOST_WIDE_INT i;
constructor_elt *ce;
gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
if (TREE_CODE (aggr) == COMPONENT_REF)
{
constructor_elt *base_ce = base_field_constructor_elt (v, aggr);
v = CONSTRUCTOR_ELTS (base_ce->value);
}
for (i = 0; vec_safe_iterate (v, i, &ce); ++i)
if (ce->index == field)
return ce;
gcc_unreachable ();
return NULL;
}
/* Return a pointer to the constructor_elt of CTOR which matches INDEX. If no
matching constructor_elt exists, then add one to CTOR.
As an optimization, if POS_HINT is non-negative then it is used as a guess
for the (integer) index of the matching constructor_elt within CTOR. */
static constructor_elt *
get_or_insert_ctor_field (tree ctor, tree index, int pos_hint = -1)
{
/* Check the hint first. */
if (pos_hint >= 0 && (unsigned) pos_hint < CONSTRUCTOR_NELTS (ctor)
&& CONSTRUCTOR_ELT (ctor, pos_hint)->index == index)
return CONSTRUCTOR_ELT (ctor, pos_hint);
tree type = TREE_TYPE (ctor);
if (TREE_CODE (type) == VECTOR_TYPE && index == NULL_TREE)
{
CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (ctor), index, NULL_TREE);
return &CONSTRUCTOR_ELTS (ctor)->last ();
}
else if (TREE_CODE (type) == ARRAY_TYPE || TREE_CODE (type) == VECTOR_TYPE)
{
if (TREE_CODE (index) == RANGE_EXPR)
{
/* Support for RANGE_EXPR index lookups is currently limited to
accessing an existing element via POS_HINT, or appending a new
element to the end of CTOR. ??? Support for other access
patterns may also be needed. */
vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (ctor);
if (vec_safe_length (elts))
{
tree lo = TREE_OPERAND (index, 0);
gcc_assert (array_index_cmp (elts->last ().index, lo) < 0);
}
CONSTRUCTOR_APPEND_ELT (elts, index, NULL_TREE);
return &elts->last ();
}
HOST_WIDE_INT i = find_array_ctor_elt (ctor, index, /*insert*/ true);
gcc_assert (i >= 0);
constructor_elt *cep = CONSTRUCTOR_ELT (ctor, i);
gcc_assert (cep->index == NULL_TREE
|| TREE_CODE (cep->index) != RANGE_EXPR);
return cep;
}
else
{
gcc_assert (
TREE_CODE (index) == FIELD_DECL
&& (same_type_ignoring_top_level_qualifiers_p (DECL_CONTEXT (index),
TREE_TYPE (ctor))));
/* We must keep the CONSTRUCTOR's ELTS in FIELD order.
Usually we meet initializers in that order, but it is
possible for base types to be placed not in program
order. */
tree fields = TYPE_FIELDS (DECL_CONTEXT (index));
unsigned HOST_WIDE_INT idx = 0;
constructor_elt *cep = NULL;
/* Check if we're changing the active member of a union. */
if (TREE_CODE (type) == UNION_TYPE && CONSTRUCTOR_NELTS (ctor)
&& CONSTRUCTOR_ELT (ctor, 0)->index != index)
vec_safe_truncate (CONSTRUCTOR_ELTS (ctor), 0);
/* If the bit offset of INDEX is larger than that of the last
constructor_elt, then we can just immediately append a new
constructor_elt to the end of CTOR. */
else if (CONSTRUCTOR_NELTS (ctor)
&& tree_int_cst_compare (
bit_position (index),
bit_position (CONSTRUCTOR_ELTS (ctor)->last ().index))
> 0)
{
idx = CONSTRUCTOR_NELTS (ctor);
goto insert;
}
/* Otherwise, we need to iterate over CTOR to find or insert INDEX
appropriately. */
for (; vec_safe_iterate (CONSTRUCTOR_ELTS (ctor), idx, &cep);
idx++, fields = DECL_CHAIN (fields))
{
if (index == cep->index)
goto found;
/* The field we're initializing must be on the field
list. Look to see if it is present before the
field the current ELT initializes. */
for (; fields != cep->index; fields = DECL_CHAIN (fields))
if (index == fields)
goto insert;
}
/* We fell off the end of the CONSTRUCTOR, so insert a new
entry at the end. */
insert : {
constructor_elt ce = {index, NULL_TREE};
vec_safe_insert (CONSTRUCTOR_ELTS (ctor), idx, ce);
cep = CONSTRUCTOR_ELT (ctor, idx);
}
found:;
return cep;
}
}
// forked from gcc/cp/constexpr.cc cxx_eval_vector_conditional_expression
/* Subroutine of cxx_eval_constant_expression.
Attempt to evaluate vector condition expressions. Unlike
cxx_eval_conditional_expression, VEC_COND_EXPR acts like a normal
ternary arithmetics operation, where all 3 arguments have to be
evaluated as constants and then folding computes the result from
them. */
static tree
eval_vector_conditional_expression (const constexpr_ctx *ctx, tree t,
bool *non_constant_p, bool *overflow_p)
{
tree arg1
= eval_constant_expression (ctx, TREE_OPERAND (t, 0),
/*lval*/ false, non_constant_p, overflow_p);
VERIFY_CONSTANT (arg1);
tree arg2
= eval_constant_expression (ctx, TREE_OPERAND (t, 1),
/*lval*/ false, non_constant_p, overflow_p);
VERIFY_CONSTANT (arg2);
tree arg3
= eval_constant_expression (ctx, TREE_OPERAND (t, 2),
/*lval*/ false, non_constant_p, overflow_p);
VERIFY_CONSTANT (arg3);
location_t loc = EXPR_LOCATION (t);
tree type = TREE_TYPE (t);
tree r = fold_ternary_loc (loc, VEC_COND_EXPR, type, arg1, arg2, arg3);
if (r == NULL_TREE)
{
if (arg1 == TREE_OPERAND (t, 0) && arg2 == TREE_OPERAND (t, 1)
&& arg3 == TREE_OPERAND (t, 2))
r = t;
else
r = build3_loc (loc, VEC_COND_EXPR, type, arg1, arg2, arg3);
}
VERIFY_CONSTANT (r);
return r;
}
// forked from gcc/cp/constexpr.cc cxx_eval_bare_aggregate
/* Subroutine of cxx_eval_constant_expression.
The expression tree T denotes a C-style array or a C-style
aggregate. Reduce it to a constant expression. */
static tree
eval_bare_aggregate (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p)
{
vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t);
bool changed = false;
gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (t));
tree type = TREE_TYPE (t);
constexpr_ctx new_ctx;
if (TYPE_PTRMEMFUNC_P (type) || VECTOR_TYPE_P (type))
{
/* We don't really need the ctx->ctor business for a PMF or
vector, but it's simpler to use the same code. */
new_ctx = *ctx;
new_ctx.ctor = build_constructor (type, NULL);
new_ctx.object = NULL_TREE;
ctx = &new_ctx;
};
verify_ctor_sanity (ctx, type);
vec<constructor_elt, va_gc> **p = &CONSTRUCTOR_ELTS (ctx->ctor);
vec_alloc (*p, vec_safe_length (v));
if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (t))
CONSTRUCTOR_PLACEHOLDER_BOUNDARY (ctx->ctor) = 1;
unsigned i;
tree index, value;
bool constant_p = true;
bool side_effects_p = false;
FOR_EACH_CONSTRUCTOR_ELT (v, i, index, value)
{
tree orig_value = value;
/* Like in cxx_eval_store_expression, omit entries for empty fields. */
bool no_slot = TREE_CODE (type) == RECORD_TYPE && is_empty_field (index);
if (no_slot)
new_ctx = *ctx;
else
init_subob_ctx (ctx, new_ctx, index, value);
int pos_hint = -1;
if (new_ctx.ctor != ctx->ctor)
{
/* If we built a new CONSTRUCTOR, attach it now so that other
initializers can refer to it. */
constructor_elt *cep = get_or_insert_ctor_field (ctx->ctor, index);
cep->value = new_ctx.ctor;
pos_hint = cep - (*p)->begin ();
}
else if (TREE_CODE (type) == UNION_TYPE)
/* Otherwise if we're constructing a non-aggregate union member, set
the active union member now so that we can later detect and diagnose
if its initializer attempts to activate another member. */
get_or_insert_ctor_field (ctx->ctor, index);
tree elt = eval_constant_expression (&new_ctx, value, lval,
non_constant_p, overflow_p);
/* Don't VERIFY_CONSTANT here. */
if (ctx->quiet && *non_constant_p)
break;
if (elt != orig_value)
changed = true;
if (!TREE_CONSTANT (elt))
constant_p = false;
if (TREE_SIDE_EFFECTS (elt))
side_effects_p = true;
if (index && TREE_CODE (index) == COMPONENT_REF)
{
/* This is an initialization of a vfield inside a base
subaggregate that we already initialized; push this
initialization into the previous initialization. */
constructor_elt *inner = base_field_constructor_elt (*p, index);
inner->value = elt;
changed = true;
}
else if (index
&& (TREE_CODE (index) == NOP_EXPR
|| TREE_CODE (index) == POINTER_PLUS_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 (index))));
changed = true;
}
else if (no_slot)
changed = true;
else
{
if (TREE_CODE (type) == UNION_TYPE && (*p)->last ().index != index)
/* The initializer erroneously changed the active union member that
we're initializing. */
gcc_assert (*non_constant_p);
else
{
/* The initializer might have mutated the underlying CONSTRUCTOR,
so recompute the location of the target constructer_elt. */
constructor_elt *cep
= get_or_insert_ctor_field (ctx->ctor, index, pos_hint);
cep->value = elt;
}
/* Adding or replacing an element might change the ctor's flags. */
TREE_CONSTANT (ctx->ctor) = constant_p;
TREE_SIDE_EFFECTS (ctx->ctor) = side_effects_p;
}
}
if (*non_constant_p || !changed)
return t;
t = ctx->ctor;
/* We're done building this CONSTRUCTOR, so now we can interpret an
element without an explicit initializer as value-initialized. */
CONSTRUCTOR_NO_CLEARING (t) = false;
TREE_CONSTANT (t) = constant_p;
TREE_SIDE_EFFECTS (t) = side_effects_p;
if (VECTOR_TYPE_P (type))
t = fold (t);
return t;
}
/* Subroutine of cxx_eval_constant_expression.
Like cxx_eval_unary_expression, except for trinary expressions. */
static tree
cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p)
{
int i;
tree args[3];
tree val;
for (i = 0; i < 3; i++)
{
args[i] = eval_constant_expression (ctx, TREE_OPERAND (t, i), lval,
non_constant_p, overflow_p);
VERIFY_CONSTANT (args[i]);
}
val = fold_ternary_loc (EXPR_LOCATION (t), TREE_CODE (t), TREE_TYPE (t),
args[0], args[1], args[2]);
if (val == NULL_TREE)
return t;
VERIFY_CONSTANT (val);
return val;
}
/* 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;
}
}
/* TEMP is the constant value of a temporary object of type TYPE. Adjust
the type of the value to match. */
static tree
adjust_temp_type (tree type, tree temp)
{
if (same_type_p (TREE_TYPE (temp), type))
return temp;
gcc_assert (scalarish_type_p (type));
/* Now we know we're dealing with a scalar, and a prvalue of non-class
type is cv-unqualified. */
return fold_convert (cv_unqualified (type), temp);
}
// 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);
}
}
static tree
eval_and_check_array_index (const constexpr_ctx *ctx, tree t,
bool allow_one_past, bool *non_constant_p,
bool *overflow_p);
// forked from gcc/cp/constexpr.cc cxx_eval_array_reference
/* Subroutine of cxx_eval_constant_expression.
Attempt to reduce a reference to an array slot. */
static tree
eval_array_reference (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p)
{
tree oldary = TREE_OPERAND (t, 0);
tree ary
= eval_constant_expression (ctx, oldary, lval, non_constant_p, overflow_p);
if (*non_constant_p)
return t;
if (!lval && TREE_CODE (ary) == VIEW_CONVERT_EXPR
&& VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0)))
&& TREE_TYPE (t) == TREE_TYPE (TREE_TYPE (TREE_OPERAND (ary, 0))))
ary = TREE_OPERAND (ary, 0);
tree oldidx = TREE_OPERAND (t, 1);
tree index
= eval_and_check_array_index (ctx, t, lval, non_constant_p, overflow_p);
if (*non_constant_p)
return t;
if (lval && ary == oldary && index == oldidx)
return t;
else if (lval)
return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL);
unsigned len = 0, elem_nchars = 1;
tree elem_type = TREE_TYPE (TREE_TYPE (ary));
if (TREE_CODE (ary) == CONSTRUCTOR)
len = CONSTRUCTOR_NELTS (ary);
else if (TREE_CODE (ary) == STRING_CST)
{
elem_nchars
= (TYPE_PRECISION (elem_type) / TYPE_PRECISION (char_type_node));
len = (unsigned) TREE_STRING_LENGTH (ary) / elem_nchars;
}
else if (TREE_CODE (ary) == VECTOR_CST)
/* We don't create variable-length VECTOR_CSTs. */
len = VECTOR_CST_NELTS (ary).to_constant ();
else
{
/* We can't do anything with other tree codes, so use
VERIFY_CONSTANT to complain and fail. */
VERIFY_CONSTANT (ary);
gcc_unreachable ();
}
bool found;
HOST_WIDE_INT i = 0;
if (TREE_CODE (ary) == CONSTRUCTOR)
{
HOST_WIDE_INT ix = find_array_ctor_elt (ary, index);
found = (ix >= 0);
if (found)
i = ix;
}
else
{
i = tree_to_shwi (index);
found = (i < len);
}
if (found)
{
tree r;
if (TREE_CODE (ary) == CONSTRUCTOR)
r = (*CONSTRUCTOR_ELTS (ary))[i].value;
else if (TREE_CODE (ary) == VECTOR_CST)
r = VECTOR_CST_ELT (ary, i);
else
r = extract_string_elt (ary, elem_nchars, i);
if (r)
/* Don't VERIFY_CONSTANT here. */
return r;
/* Otherwise the element doesn't have a value yet. */
}
/* Not found. */
if (TREE_CODE (ary) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (ary))
{
/* 'ary' is part of the aggregate initializer we're currently
building; if there's no initializer for this element yet,
that's an error. */
if (!ctx->quiet)
error ("accessing uninitialized array element");
*non_constant_p = true;
return t;
}
/* If it's within the array bounds but doesn't have an explicit
initializer, it's initialized from {}. But use build_value_init
directly for non-aggregates to avoid creating a garbage CONSTRUCTOR. */
tree val = NULL_TREE;
sorry ("array size expression is not supported yet");
constexpr_ctx new_ctx;
if (is_really_empty_class (elem_type, /*ignore_vptr*/ false))
return build_constructor (elem_type, NULL);
// else if (CP_AGGREGATE_TYPE_P (elem_type))
// {
// tree empty_ctor = build_constructor (init_list_type_node, NULL);
// //val = digest_init (elem_type, empty_ctor, tf_warning_or_error);
// }
// else
// val = build_value_init (elem_type, tf_warning_or_error);
if (!SCALAR_TYPE_P (elem_type))
{
new_ctx = *ctx;
if (ctx->object)
/* If there was no object, don't add one: it could confuse us
into thinking we're modifying a const object. */
new_ctx.object = t;
new_ctx.ctor = build_constructor (elem_type, NULL);
ctx = &new_ctx;
}
t = eval_constant_expression (ctx, val, lval, non_constant_p, overflow_p);
if (!SCALAR_TYPE_P (elem_type) && t != ctx->ctor)
free_constructor (ctx->ctor);
return t;
}
// forked from gcc/cp/constexpr.cc cxx_eval_component_reference
/* Subroutine of cxx_eval_constant_expression.
Attempt to reduce a field access of a value of class type. */
static tree
eval_component_reference (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p)
{
unsigned HOST_WIDE_INT i;
tree field;
tree value;
tree part = TREE_OPERAND (t, 1);
tree orig_whole = TREE_OPERAND (t, 0);
tree whole = eval_constant_expression (ctx, orig_whole, lval, non_constant_p,
overflow_p);
if (INDIRECT_REF_P (whole) && integer_zerop (TREE_OPERAND (whole, 0)))
{
if (!ctx->quiet)
error ("dereferencing a null pointer in %qE", orig_whole);
*non_constant_p = true;
return t;
}
if (whole == orig_whole)
return t;
if (lval)
return fold_build3 (COMPONENT_REF, TREE_TYPE (t), whole, part, NULL_TREE);
/* Don't VERIFY_CONSTANT here; we only want to check that we got a
CONSTRUCTOR. */
if (!*non_constant_p && TREE_CODE (whole) != CONSTRUCTOR)
{
if (!ctx->quiet)
error ("%qE is not a constant expression", orig_whole);
*non_constant_p = true;
}
if (DECL_MUTABLE_P (part))
{
if (!ctx->quiet)
error ("mutable %qD is not usable in a constant expression", part);
*non_constant_p = true;
}
if (*non_constant_p)
return t;
bool pmf = TYPE_PTRMEMFUNC_P (TREE_TYPE (whole));
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value)
{
/* Use name match for PMF fields, as a variant will have a
different FIELD_DECL with a different type. */
if (pmf ? DECL_NAME (field) == DECL_NAME (part) : field == part)
{
if (value)
{
STRIP_ANY_LOCATION_WRAPPER (value);
return value;
}
else
/* We're in the middle of initializing it. */
break;
}
}
if (TREE_CODE (TREE_TYPE (whole)) == UNION_TYPE
&& CONSTRUCTOR_NELTS (whole) > 0)
{
/* DR 1188 says we don't have to deal with this. */
if (!ctx->quiet)
{
constructor_elt *cep = CONSTRUCTOR_ELT (whole, 0);
if (cep->value == NULL_TREE)
error ("accessing uninitialized member %qD", part);
else
error ("accessing %qD member instead of initialized %qD member in "
"constant expression",
part, cep->index);
}
*non_constant_p = true;
return t;
}
/* We only create a CONSTRUCTOR for a subobject when we modify it, so empty
classes never get represented; throw together a value now. */
if (is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/ false))
return build_constructor (TREE_TYPE (t), NULL);
gcc_assert (DECL_CONTEXT (part) == TYPE_MAIN_VARIANT (TREE_TYPE (whole)));
if (CONSTRUCTOR_NO_CLEARING (whole))
{
/* 'whole' is part of the aggregate initializer we're currently
building; if there's no initializer for this member yet, that's an
error. */
if (!ctx->quiet)
error ("accessing uninitialized member %qD", part);
*non_constant_p = true;
return t;
}
value = NULL_TREE;
sorry ("constant folding not supported for this tree code");
/* If there's no explicit init for this field, it's value-initialized. */
// Faisal: commenting this out as not sure if we need this but we need to come
// back to handle this to assign suitable value to value before sending it in
// eval_constant_expression below
// value = build_value_init (TREE_TYPE (t), tf_warning_or_error);
return eval_constant_expression (ctx, value, lval, non_constant_p,
overflow_p);
}
/* Subroutine of cxx_eval_statement_list. Determine whether the statement
STMT matches *jump_target. If we're looking for a case label and we see
the default label, note it in ctx->css_state. */
static bool
label_matches (const constexpr_ctx *ctx, tree *jump_target, tree stmt)
{
switch (TREE_CODE (*jump_target))
{
case LABEL_DECL:
if (TREE_CODE (stmt) == LABEL_EXPR
&& LABEL_EXPR_LABEL (stmt) == *jump_target)
return true;
break;
case INTEGER_CST:
if (TREE_CODE (stmt) == CASE_LABEL_EXPR)
{
gcc_assert (ctx->css_state != NULL);
if (!CASE_LOW (stmt))
{
/* default: should appear just once in a SWITCH_EXPR
body (excluding nested SWITCH_EXPR). */
gcc_assert (*ctx->css_state != css_default_seen);
/* When evaluating SWITCH_EXPR body for the second time,
return true for the default: label. */
if (*ctx->css_state == css_default_processing)
return true;
*ctx->css_state = css_default_seen;
}
else if (CASE_HIGH (stmt))
{
if (tree_int_cst_le (CASE_LOW (stmt), *jump_target)
&& tree_int_cst_le (*jump_target, CASE_HIGH (stmt)))
return true;
}
else if (tree_int_cst_equal (*jump_target, CASE_LOW (stmt)))
return true;
}
break;
case BREAK_STMT:
case CONTINUE_STMT:
/* These two are handled directly in cxx_eval_loop_expr by testing
breaks (jump_target) or continues (jump_target). */
break;
default:
gcc_unreachable ();
}
return false;
}
static tree
eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p,
tree *jump_target /* = NULL */)
{
if (jump_target && *jump_target)
{
/* If we are jumping, ignore all statements/expressions except those
that could have LABEL_EXPR or CASE_LABEL_EXPR in their bodies. */
switch (TREE_CODE (t))
{
case BIND_EXPR:
case STATEMENT_LIST:
case LOOP_EXPR:
case COND_EXPR:
case IF_STMT:
case DO_STMT:
case WHILE_STMT:
case FOR_STMT:
break;
case LABEL_EXPR:
case CASE_LABEL_EXPR:
if (label_matches (ctx, jump_target, t))
/* Found it. */
*jump_target = NULL_TREE;
return NULL_TREE;
default:
return NULL_TREE;
}
}
if (error_operand_p (t))
{
*non_constant_p = true;
return t;
}
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),
"%<constexpr%> evaluation operation count exceeds limit of "
"%wd (use %<-fconstexpr-ops-limit=%> to increase the limit)",
constexpr_ops_limit);
return t;
}
constexpr_ctx new_ctx;
tree r = t;
tree_code tcode = TREE_CODE (t);
switch (tcode)
{
case VAR_DECL:
if (DECL_HAS_VALUE_EXPR_P (t))
{
r = DECL_VALUE_EXPR (t);
return eval_constant_expression (ctx, r, lval, non_constant_p,
overflow_p);
}
/* fall through */
case CONST_DECL: {
/* We used to not check lval for CONST_DECL, but darwin.cc uses
CONST_DECL for aggregate constants. */
if (lval)
return t;
else if (t == ctx->object)
return ctx->ctor;
if (VAR_P (t))
if (tree *p = ctx->global->values.get (t))
if (*p != NULL_TREE)
{
r = *p;
break;
}
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 PARM_DECL:
if (lval && !TYPE_REF_P (TREE_TYPE (t)))
/* glvalue use. */;
else if (tree *p = ctx->global->values.get (r))
r = *p;
else if (lval)
/* Defer in case this is only used for its type. */;
else if (COMPLETE_TYPE_P (TREE_TYPE (t))
&& is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/ false))
{
/* If the class is empty, we aren't actually loading anything. */
r = build_constructor (TREE_TYPE (t), NULL);
TREE_CONSTANT (r) = true;
}
else
{
if (!ctx->quiet)
error ("%qE is not a constant expression", t);
*non_constant_p = true;
}
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, lval, non_constant_p, overflow_p);
break;
/* fold can introduce non-IF versions of these; still treat them as
short-circuiting. */
case TRUTH_AND_EXPR:
case TRUTH_ANDIF_EXPR:
r = eval_logical_expression (ctx, t, boolean_false_node,
boolean_true_node, lval, non_constant_p,
overflow_p);
break;
case TRUTH_OR_EXPR:
case TRUTH_ORIF_EXPR:
r = eval_logical_expression (ctx, t, boolean_true_node,
boolean_false_node, lval, non_constant_p,
overflow_p);
break;
case TARGET_EXPR: {
tree type = TREE_TYPE (t);
if (!literal_type_p (type))
{
if (!ctx->quiet)
{
auto_diagnostic_group d;
error ("temporary of non-literal type %qT in a "
"constant expression",
type);
explain_non_literal_class (type);
}
*non_constant_p = true;
break;
}
gcc_checking_assert (!TARGET_EXPR_DIRECT_INIT_P (t));
/* Avoid evaluating a TARGET_EXPR more than once. */
tree slot = TARGET_EXPR_SLOT (t);
if (tree *p = ctx->global->values.get (slot))
{
if (lval)
return slot;
r = *p;
break;
}
if ((AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)))
{
/* We're being expanded without an explicit target, so start
initializing a new object; expansion with an explicit target
strips the TARGET_EXPR before we get here. */
new_ctx = *ctx;
/* Link CTX to NEW_CTX so that lookup_placeholder can resolve
any PLACEHOLDER_EXPR within the initializer that refers to the
former object under construction. */
new_ctx.parent = ctx;
new_ctx.ctor = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
new_ctx.object = slot;
ctx->global->values.put (new_ctx.object, new_ctx.ctor);
ctx = &new_ctx;
}
/* Pass false for 'lval' because this indicates
initialization of a temporary. */
r = eval_constant_expression (ctx, TREE_OPERAND (t, 1), false,
non_constant_p, overflow_p);
if (*non_constant_p)
break;
/* Adjust the type of the result to the type of the temporary. */
r = adjust_temp_type (type, r);
if (TARGET_EXPR_CLEANUP (t) && !CLEANUP_EH_ONLY (t))
ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t));
r = unshare_constructor (r);
ctx->global->values.put (slot, r);
if (ctx->save_exprs)
ctx->save_exprs->safe_push (slot);
if (lval)
return slot;
}
break;
case CALL_EXPR:
r = eval_call_expression (ctx, t, lval, non_constant_p, overflow_p);
break;
case RETURN_EXPR:
if (TREE_OPERAND (t, 0) != NULL_TREE)
r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p);
/* FALLTHRU */
case BREAK_STMT:
case CONTINUE_STMT:
if (jump_target)
*jump_target = t;
else
{
/* Can happen with ({ return true; }) && false; passed to
maybe_constant_value. There is nothing to jump over in this
case, and the bug will be diagnosed later. */
gcc_assert (ctx->quiet);
*non_constant_p = true;
}
break;
case DECL_EXPR: {
r = DECL_EXPR_DECL (t);
if (AGGREGATE_TYPE_P (TREE_TYPE (r)) || VECTOR_TYPE_P (TREE_TYPE (r)))
{
new_ctx = *ctx;
new_ctx.object = r;
new_ctx.ctor = build_constructor (TREE_TYPE (r), NULL);
CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
ctx->global->values.put (r, new_ctx.ctor);
ctx = &new_ctx;
}
if (tree init = DECL_INITIAL (r))
{
init = eval_constant_expression (ctx, init, false, non_constant_p,
overflow_p);
/* Don't share a CONSTRUCTOR that might be changed. */
init = unshare_constructor (init);
/* Remember that a constant object's constructor has already
run. */
if (CLASS_TYPE_P (TREE_TYPE (r)) && RS_TYPE_CONST_P (TREE_TYPE (r)))
TREE_READONLY (init) = true;
ctx->global->values.put (r, init);
}
else if (ctx == &new_ctx)
/* We gave it a CONSTRUCTOR above. */;
else
ctx->global->values.put (r, NULL_TREE);
}
break;
/* These differ from cxx_eval_unary_expression in that this doesn't
check for a constant operand or result; an address can be
constant without its operand being, and vice versa. */
case MEM_REF:
case INDIRECT_REF:
r = rs_eval_indirect_ref (ctx, t, lval, non_constant_p, overflow_p);
break;
case VEC_PERM_EXPR:
r = cxx_eval_trinary_expression (ctx, t, lval, non_constant_p,
overflow_p);
break;
case PAREN_EXPR:
gcc_assert (!REF_PARENTHESIZED_P (t));
/* A PAREN_EXPR resulting from __builtin_assoc_barrier has no effect in
constant expressions since it's unaffected by -fassociative-math. */
r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p);
break;
case MODIFY_EXPR:
r = eval_store_expression (ctx, t, false, non_constant_p, overflow_p);
break;
case STATEMENT_LIST:
new_ctx = *ctx;
new_ctx.ctor = new_ctx.object = NULL_TREE;
return eval_statement_list (&new_ctx, t, non_constant_p, overflow_p,
jump_target);
case BIND_EXPR:
return eval_constant_expression (ctx, BIND_EXPR_BODY (t), lval,
non_constant_p, overflow_p, jump_target);
case OBJ_TYPE_REF:
/* Virtual function lookup. We don't need to do anything fancy. */
return eval_constant_expression (ctx, OBJ_TYPE_REF_EXPR (t), lval,
non_constant_p, overflow_p);
case EXIT_EXPR: {
tree cond = TREE_OPERAND (t, 0);
cond = eval_constant_expression (ctx, cond, /*lval*/ false,
non_constant_p, overflow_p);
VERIFY_CONSTANT (cond);
if (integer_nonzerop (cond))
*jump_target = t;
}
break;
case RESULT_DECL:
if (lval)
return t;
/* We ask for an rvalue for the RESULT_DECL when indirecting
through an invisible reference, or in named return value
optimization. */
if (tree *p = ctx->global->values.get (t))
return *p;
else
{
if (!ctx->quiet)
error ("%qE is not a constant expression", t);
*non_constant_p = true;
}
break;
case SAVE_EXPR:
/* Avoid evaluating a SAVE_EXPR more than once. */
if (tree *p = ctx->global->values.get (t))
r = *p;
else
{
r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
non_constant_p, overflow_p);
if (*non_constant_p)
break;
ctx->global->values.put (t, r);
if (ctx->save_exprs)
ctx->save_exprs->safe_push (t);
}
break;
case ADDR_EXPR: {
tree oldop = TREE_OPERAND (t, 0);
tree op = eval_constant_expression (ctx, oldop,
/*lval*/ true, non_constant_p,
overflow_p);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p)
return t;
gcc_checking_assert (TREE_CODE (op) != CONSTRUCTOR);
/* This function does more aggressive folding than fold itself. */
r = build_fold_addr_expr_with_type (op, TREE_TYPE (t));
if (TREE_CODE (r) == ADDR_EXPR && TREE_OPERAND (r, 0) == oldop)
{
ggc_free (r);
return t;
}
break;
}
case COMPOUND_EXPR: {
/* check_return_expr sometimes wraps a TARGET_EXPR in a
COMPOUND_EXPR; don't get confused. Also handle EMPTY_CLASS_EXPR
introduced by build_call_a. */
tree op0 = TREE_OPERAND (t, 0);
tree op1 = TREE_OPERAND (t, 1);
STRIP_NOPS (op1);
if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0))
|| TREE_CODE (op1) == EMPTY_CLASS_EXPR)
r = eval_constant_expression (ctx, op0, lval, non_constant_p,
overflow_p, jump_target);
else
{
/* Check that the LHS is constant and then discard it. */
eval_constant_expression (ctx, op0, true, non_constant_p,
overflow_p, jump_target);
if (*non_constant_p)
return t;
op1 = TREE_OPERAND (t, 1);
r = eval_constant_expression (ctx, op1, lval, non_constant_p,
overflow_p, jump_target);
}
}
break;
case REALPART_EXPR:
case IMAGPART_EXPR:
if (lval)
{
r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p);
if (r == error_mark_node)
;
else if (r == TREE_OPERAND (t, 0))
r = t;
else
r = fold_build1 (TREE_CODE (t), TREE_TYPE (t), r);
break;
}
/* FALLTHRU */
case CONJ_EXPR:
case FIX_TRUNC_EXPR:
case FLOAT_EXPR:
case NEGATE_EXPR:
case ABS_EXPR:
case ABSU_EXPR:
case BIT_NOT_EXPR:
case TRUTH_NOT_EXPR:
case FIXED_CONVERT_EXPR:
r = eval_unary_expression (ctx, t, lval, non_constant_p, overflow_p);
break;
case LOOP_EXPR:
case WHILE_STMT:
case FOR_STMT:
eval_loop_expr (ctx, t, non_constant_p, overflow_p, jump_target);
break;
case SWITCH_EXPR:
case SWITCH_STMT:
eval_switch_expr (ctx, t, non_constant_p, overflow_p, jump_target);
break;
case ARRAY_REF:
r = eval_array_reference (ctx, t, lval, non_constant_p, overflow_p);
break;
case COMPONENT_REF:
if (is_overloaded_fn (t))
{
/* We can only get here in checking mode via
build_non_dependent_expr, because any expression that
calls or takes the address of the function will have
pulled a FUNCTION_DECL out of the COMPONENT_REF. */
gcc_checking_assert (ctx->quiet || errorcount);
*non_constant_p = true;
return t;
}
r = eval_component_reference (ctx, t, lval, non_constant_p, overflow_p);
break;
case BIT_FIELD_REF:
r = eval_bit_field_ref (ctx, t, lval, non_constant_p, overflow_p);
break;
case COND_EXPR:
case IF_STMT: // comes from cp-tree.def
if (jump_target && *jump_target)
{
tree orig_jump = *jump_target;
tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
? TREE_OPERAND (t, 1)
: void_node);
/* When jumping to a label, the label might be either in the
then or else blocks, so process then block first in skipping
mode first, and if we are still in the skipping mode at its end,
process the else block too. */
r = eval_constant_expression (ctx, arg, lval, non_constant_p,
overflow_p, jump_target);
/* It's possible that we found the label in the then block. But
it could have been followed by another jumping statement, e.g.
say we're looking for case 1:
if (cond)
{
// skipped statements
case 1:; // clears up *jump_target
return 1; // and sets it to a RETURN_EXPR
}
else { ... }
in which case we need not go looking to the else block.
(goto is not allowed in a constexpr function.) */
if (*jump_target == orig_jump)
{
arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 2))
? TREE_OPERAND (t, 2)
: void_node);
r = eval_constant_expression (ctx, arg, lval, non_constant_p,
overflow_p, jump_target);
}
break;
}
r = eval_conditional_expression (ctx, t, lval, non_constant_p, overflow_p,
jump_target);
break;
case VEC_COND_EXPR:
r = eval_vector_conditional_expression (ctx, t, non_constant_p,
overflow_p);
break;
case TRY_CATCH_EXPR:
if (TREE_OPERAND (t, 0) == NULL_TREE)
{
r = void_node;
break;
}
r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p, jump_target);
break;
case CLEANUP_POINT_EXPR: {
auto_vec<tree, 2> cleanups;
vec<tree> *prev_cleanups = ctx->global->cleanups;
ctx->global->cleanups = &cleanups;
r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p, jump_target);
ctx->global->cleanups = prev_cleanups;
unsigned int i;
tree cleanup;
/* Evaluate the cleanups. */
FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
eval_constant_expression (ctx, cleanup, false, non_constant_p,
overflow_p);
}
break;
case TRY_FINALLY_EXPR:
r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p, jump_target);
if (!*non_constant_p)
/* Also evaluate the cleanup. */
eval_constant_expression (ctx, TREE_OPERAND (t, 1), true,
non_constant_p, overflow_p);
break;
case CONSTRUCTOR:
if (TREE_CONSTANT (t) && reduced_constant_expression_p (t))
{
/* Don't re-process a constant CONSTRUCTOR, but do fold it to
VECTOR_CST if applicable. */
verify_constructor_flags (t);
if (TREE_CONSTANT (t))
return fold (t);
}
r = eval_bare_aggregate (ctx, t, lval, non_constant_p, overflow_p);
break;
/* FALLTHROUGH. */
case NOP_EXPR:
case CONVERT_EXPR:
case VIEW_CONVERT_EXPR: {
tree oldop = TREE_OPERAND (t, 0);
tree op = eval_constant_expression (ctx, oldop, lval, non_constant_p,
overflow_p);
if (*non_constant_p)
return t;
tree type = TREE_TYPE (t);
if (VOID_TYPE_P (type))
return void_node;
if (TREE_CODE (t) == CONVERT_EXPR && ARITHMETIC_TYPE_P (type)
&& INDIRECT_TYPE_P (TREE_TYPE (op)) && ctx->manifestly_const_eval)
{
if (!ctx->quiet)
error_at (loc,
"conversion from pointer type %qT to arithmetic type "
"%qT in a constant expression",
TREE_TYPE (op), type);
*non_constant_p = true;
return t;
}
if (TYPE_PTROB_P (type) && TYPE_PTR_P (TREE_TYPE (op))
&& VOID_TYPE_P (TREE_TYPE (TREE_TYPE (op))))
{
/* Likewise, don't error when casting from void* when OP is
&heap uninit and similar. */
tree sop = tree_strip_nop_conversions (op);
if (TREE_CODE (sop) == ADDR_EXPR && VAR_P (TREE_OPERAND (sop, 0))
&& DECL_ARTIFICIAL (TREE_OPERAND (sop, 0)))
/* OK */;
else
{
if (!ctx->quiet)
error_at (loc, "cast from %qT is not allowed",
TREE_TYPE (op));
*non_constant_p = true;
return t;
}
}
if (INDIRECT_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST)
{
if (integer_zerop (op))
{
if (TYPE_REF_P (type))
{
if (!ctx->quiet)
error_at (loc, "dereferencing a null pointer");
*non_constant_p = true;
return t;
}
}
else
{
/* This detects for example:
reinterpret_cast<void*>(sizeof 0)
*/
if (!ctx->quiet)
error_at (loc,
"%<reinterpret_cast<%T>(%E)%> is not "
"a constant expression",
type, op);
*non_constant_p = true;
return t;
}
}
if (INDIRECT_TYPE_P (type) && TREE_CODE (op) == NOP_EXPR
&& TREE_TYPE (op) == ptr_type_node
&& TREE_CODE (TREE_OPERAND (op, 0)) == ADDR_EXPR
&& VAR_P (TREE_OPERAND (TREE_OPERAND (op, 0), 0))
&& (DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0), 0))
== heap_uninit_identifier
|| DECL_NAME (TREE_OPERAND (TREE_OPERAND (op, 0), 0))
== heap_vec_uninit_identifier))
{
tree var = TREE_OPERAND (TREE_OPERAND (op, 0), 0);
tree var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
tree elt_type = TREE_TYPE (type);
tree cookie_size = NULL_TREE;
if (TREE_CODE (elt_type) == RECORD_TYPE
&& TYPE_NAME (elt_type) == heap_identifier)
{
tree fld1 = TYPE_FIELDS (elt_type);
tree fld2 = DECL_CHAIN (fld1);
elt_type = TREE_TYPE (TREE_TYPE (fld2));
cookie_size = TYPE_SIZE_UNIT (TREE_TYPE (fld1));
}
DECL_NAME (var) = (DECL_NAME (var) == heap_uninit_identifier
? heap_identifier
: heap_vec_identifier);
TREE_TYPE (var)
= build_new_constexpr_heap_type (elt_type, cookie_size, var_size);
TREE_TYPE (TREE_OPERAND (op, 0))
= build_pointer_type (TREE_TYPE (var));
}
if (op == oldop)
/* We didn't fold at the top so we could check for ptr-int
conversion. */
return fold (t);
tree sop;
/* Handle an array's bounds having been deduced after we built
the wrapping expression. */
if (same_type_ignoring_tlq_and_bounds_p (type, TREE_TYPE (op)))
r = op;
else if (sop = tree_strip_nop_conversions (op),
sop != op
&& (same_type_ignoring_tlq_and_bounds_p (type,
TREE_TYPE (sop))))
r = sop;
else
r = fold_build1 (tcode, type, op);
/* Conversion of an out-of-range value has implementation-defined
behavior; the language considers it different from arithmetic
overflow, which is undefined. */
if (TREE_OVERFLOW_P (r) && !TREE_OVERFLOW_P (op))
TREE_OVERFLOW (r) = false;
}
break;
case PLACEHOLDER_EXPR:
/* Use of the value or address of the current object. */
if (tree ctor = lookup_placeholder (ctx, lval, TREE_TYPE (t)))
{
if (TREE_CODE (ctor) == CONSTRUCTOR)
return ctor;
else
return eval_constant_expression (ctx, ctor, lval, non_constant_p,
overflow_p);
}
/* A placeholder without a referent. We can get here when
checking whether NSDMIs are noexcept, or in massage_init_elt;
just say it's non-constant for now. */
gcc_assert (ctx->quiet);
*non_constant_p = true;
break;
case ANNOTATE_EXPR:
r = eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p, jump_target);
break;
case ASM_EXPR:
if (!ctx->quiet)
inline_asm_in_constexpr_error (loc);
*non_constant_p = true;
return t;
default:
break;
}
return r;
}
/* Complain about a const object OBJ being modified in a constant expression.
EXPR is the MODIFY_EXPR expression performing the modification. */
static void
modifying_const_object_error (tree expr, tree obj)
{
location_t loc = EXPR_LOCATION (expr);
auto_diagnostic_group d;
error_at (loc,
"modifying a const object %qE is not allowed in "
"a constant expression",
TREE_OPERAND (expr, 0));
inform (location_of (obj), "originally declared %<const%> here");
}
/* Return true iff DECL is an empty field, either for an empty base or a
[[no_unique_address]] data member. */
bool
is_empty_field (tree decl)
{
if (!decl || TREE_CODE (decl) != FIELD_DECL)
return false;
bool r = is_empty_class (TREE_TYPE (decl));
/* Empty fields should have size zero. */
gcc_checking_assert (!r || integer_zerop (DECL_SIZE (decl)));
return r;
}
static tree
eval_store_expression (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p)
{
constexpr_ctx new_ctx = *ctx;
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. */
if (!SCALAR_TYPE_P (type))
new_ctx.ctor = new_ctx.object = NULL_TREE;
init = eval_constant_expression (&new_ctx, init, false, non_constant_p,
overflow_p);
if (*non_constant_p)
return t;
}
bool evaluated = false;
if (lval)
{
/* If we want to return a reference to the target, we need to evaluate it
as a whole; otherwise, only evaluate the innermost piece to avoid
building up unnecessary *_REFs. */
target = eval_constant_expression (ctx, target, true, non_constant_p,
overflow_p);
evaluated = true;
if (*non_constant_p)
return t;
}
/* Find the underlying variable. */
releasing_vec refs;
tree object = NULL_TREE;
/* If we're modifying a const object, save it. */
tree const_object_being_modified = NULL_TREE;
// bool mutable_p = false;
for (tree probe = target; object == NULL_TREE;)
{
switch (TREE_CODE (probe))
{
case BIT_FIELD_REF:
case COMPONENT_REF:
case ARRAY_REF: {
tree ob = TREE_OPERAND (probe, 0);
tree elt = TREE_OPERAND (probe, 1);
if (TREE_CODE (elt) == FIELD_DECL /*&& DECL_MUTABLE_P (elt)*/)
{
// mutable_p = true;
}
if (TREE_CODE (probe) == ARRAY_REF)
{
// TODO
gcc_unreachable ();
// elt = eval_and_check_array_index (ctx, probe, false,
// non_constant_p, overflow_p);
if (*non_constant_p)
return t;
}
/* We don't check modifying_const_object_p for ARRAY_REFs. Given
"int a[10]", an ARRAY_REF "a[2]" can be "const int", even though
the array isn't const. Instead, check "a" in the next iteration;
that will detect modifying "const int a[10]". */
// else if (evaluated
// && modifying_const_object_p (TREE_CODE (t), probe,
// mutable_p)
// && const_object_being_modified == NULL_TREE)
// const_object_being_modified = probe;
vec_safe_push (refs, elt);
vec_safe_push (refs, TREE_TYPE (probe));
probe = ob;
}
break;
default:
if (evaluated)
object = probe;
else
{
probe = eval_constant_expression (ctx, probe, true,
non_constant_p, overflow_p);
evaluated = true;
if (*non_constant_p)
return t;
}
break;
}
}
// if (modifying_const_object_p (TREE_CODE (t), object, mutable_p)
// && const_object_being_modified == NULL_TREE)
// const_object_being_modified = object;
/* And then find/build up our initializer for the path to the subobject
we're initializing. */
tree *valp;
if (DECL_P (object))
valp = ctx->global->values.get (object);
else
valp = NULL;
if (!valp)
{
/* A constant-expression cannot modify objects from outside the
constant-expression. */
if (!ctx->quiet)
error ("modification of %qE is not a constant expression", object);
*non_constant_p = true;
return t;
}
type = TREE_TYPE (object);
bool no_zero_init = true;
releasing_vec ctors, indexes;
auto_vec<int> index_pos_hints;
bool activated_union_member_p = false;
while (!refs->is_empty ())
{
if (*valp == NULL_TREE)
{
*valp = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (*valp) = no_zero_init;
}
else if (TREE_CODE (*valp) == STRING_CST)
{
/* An array was initialized with a string constant, and now
we're writing into one of its elements. Explode the
single initialization into a set of element
initializations. */
gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
tree string = *valp;
tree elt_type = TREE_TYPE (type);
unsigned chars_per_elt
= (TYPE_PRECISION (elt_type) / TYPE_PRECISION (char_type_node));
unsigned num_elts = TREE_STRING_LENGTH (string) / chars_per_elt;
tree ary_ctor = build_constructor (type, NULL);
vec_safe_reserve (CONSTRUCTOR_ELTS (ary_ctor), num_elts);
for (unsigned ix = 0; ix != num_elts; ix++)
{
constructor_elt elt
= {build_int_cst (size_type_node, ix),
extract_string_elt (string, chars_per_elt, ix)};
CONSTRUCTOR_ELTS (ary_ctor)->quick_push (elt);
}
*valp = ary_ctor;
}
/* If the value of object is already zero-initialized, any new ctors for
subobjects will also be zero-initialized. */
no_zero_init = CONSTRUCTOR_NO_CLEARING (*valp);
enum tree_code code = TREE_CODE (type);
type = refs->pop ();
tree index = refs->pop ();
if (code == RECORD_TYPE && is_empty_field (index))
/* Don't build a sub-CONSTRUCTOR for an empty base or field, as they
have no data and might have an offset lower than previously declared
fields, which confuses the middle-end. The code below will notice
that we don't have a CONSTRUCTOR for our inner target and just
return init. */
break;
if (code == UNION_TYPE && CONSTRUCTOR_NELTS (*valp)
&& CONSTRUCTOR_ELT (*valp, 0)->index != index)
{
if (TREE_CODE (t) == MODIFY_EXPR && CONSTRUCTOR_NO_CLEARING (*valp))
{
/* Diagnose changing the active union member while the union
is in the process of being initialized. */
if (!ctx->quiet)
error_at (EXPR_LOCATION (t),
"change of the active member of a union "
"from %qD to %qD during initialization",
CONSTRUCTOR_ELT (*valp, 0)->index, index);
*non_constant_p = true;
}
no_zero_init = true;
}
vec_safe_push (ctors, *valp);
vec_safe_push (indexes, index);
constructor_elt *cep = get_or_insert_ctor_field (*valp, index);
index_pos_hints.safe_push (cep - CONSTRUCTOR_ELTS (*valp)->begin ());
if (code == UNION_TYPE)
activated_union_member_p = true;
valp = &cep->value;
}
/* Detect modifying a constant object in constexpr evaluation.
We have found a const object that is being modified. Figure out
if we need to issue an error. Consider
struct A {
int n;
constexpr A() : n(1) { n = 2; } // #1
};
struct B {
const A a;
constexpr B() { a.n = 3; } // #2
};
constexpr B b{};
#1 is OK, since we're modifying an object under construction, but
#2 is wrong, since "a" is const and has been fully constructed.
To track it, we use the TREE_READONLY bit in the object's CONSTRUCTOR
which means that the object is read-only. For the example above, the
*ctors stack at the point of #2 will look like:
ctors[0] = {.a={.n=2}} TREE_READONLY = 0
ctors[1] = {.n=2} TREE_READONLY = 1
and we're modifying "b.a", so we search the stack and see if the
constructor for "b.a" has already run. */
if (const_object_being_modified)
{
bool fail = false;
tree const_objtype
= strip_array_types (TREE_TYPE (const_object_being_modified));
if (!CLASS_TYPE_P (const_objtype))
fail = true;
else
{
/* [class.ctor]p5 "A constructor can be invoked for a const,
volatile, or const volatile object. const and volatile
semantics are not applied on an object under construction.
They come into effect when the constructor for the most
derived object ends." */
for (tree elt : *ctors)
if (same_type_ignoring_top_level_qualifiers_p (
TREE_TYPE (const_object_being_modified), TREE_TYPE (elt)))
{
fail = TREE_READONLY (elt);
break;
}
}
if (fail)
{
if (!ctx->quiet)
modifying_const_object_error (t, const_object_being_modified);
*non_constant_p = true;
return t;
}
}
if (!preeval)
{
/* We're handling an INIT_EXPR of class type, so the value of the
initializer can depend on the object it's initializing. */
/* Create a new CONSTRUCTOR in case evaluation of the initializer
wants to modify it. */
if (*valp == NULL_TREE)
{
*valp = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (*valp) = no_zero_init;
}
new_ctx.ctor = *valp;
new_ctx.object = target;
/* Avoid temporary materialization when initializing from a TARGET_EXPR.
We don't need to mess with AGGR_EXPR_SLOT/VEC_INIT_EXPR_SLOT because
expansion of those trees uses ctx instead. */
if (TREE_CODE (init) == TARGET_EXPR)
if (tree tinit = TARGET_EXPR_INITIAL (init))
init = tinit;
init = eval_constant_expression (&new_ctx, init, false, non_constant_p,
overflow_p);
/* The hash table might have moved since the get earlier, and the
initializer might have mutated the underlying CONSTRUCTORs, so we must
recompute VALP. */
valp = ctx->global->values.get (object);
for (unsigned i = 0; i < vec_safe_length (indexes); i++)
{
constructor_elt *cep
= get_or_insert_ctor_field (*valp, indexes[i], index_pos_hints[i]);
valp = &cep->value;
}
}
/* Don't share a CONSTRUCTOR that might be changed later. */
init = unshare_constructor (init);
if (*valp && TREE_CODE (*valp) == CONSTRUCTOR
&& TREE_CODE (init) == CONSTRUCTOR)
{
/* An outer ctx->ctor might be pointing to *valp, so replace
its contents. */
if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init),
TREE_TYPE (*valp)))
{
/* For initialization of an empty base, the original target will be
*(base*)this, evaluation of which resolves to the object
argument, which has the derived type rather than the base type. In
this situation, just evaluate the initializer and return, since
there's no actual data to store. */
gcc_assert (is_empty_class (TREE_TYPE (init)));
return lval ? target : init;
}
CONSTRUCTOR_ELTS (*valp) = CONSTRUCTOR_ELTS (init);
TREE_CONSTANT (*valp) = TREE_CONSTANT (init);
TREE_SIDE_EFFECTS (*valp) = TREE_SIDE_EFFECTS (init);
CONSTRUCTOR_NO_CLEARING (*valp) = CONSTRUCTOR_NO_CLEARING (init);
}
// else if (TREE_CODE (init) == CONSTRUCTOR
// && !same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init),
// type))
// {
// /* See above on initialization of empty bases. */
// // gcc_assert (is_empty_class (TREE_TYPE (init)) && !lval);
// return init;
// }
else
*valp = init;
/* After initialization, 'const' semantics apply to the value of the
object. Make a note of this fact by marking the CONSTRUCTOR
TREE_READONLY. */
if (TREE_CODE (t) == INIT_EXPR && TREE_CODE (*valp) == CONSTRUCTOR
&& TYPE_READONLY (type))
{
// this vs self? can rust's self be anything other than self or &self in
// constexpr mode? if (INDIRECT_REF_P (target)
// && (is_this_parameter (
// tree_strip_nop_conversions (TREE_OPERAND (target, 0)))))
/* We've just initialized '*this' (perhaps via the target
constructor of a delegating constructor). Leave it up to the
caller that set 'this' to set TREE_READONLY appropriately. */
// gcc_checking_assert (
// same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (target),
// type));
// else
// TREE_READONLY (*valp) = true;
}
/* Update TREE_CONSTANT and TREE_SIDE_EFFECTS on enclosing
CONSTRUCTORs, if any. */
bool c = TREE_CONSTANT (init);
bool s = TREE_SIDE_EFFECTS (init);
if (!c || s || activated_union_member_p)
for (tree elt : *ctors)
{
if (!c)
TREE_CONSTANT (elt) = false;
if (s)
TREE_SIDE_EFFECTS (elt) = true;
/* Clear CONSTRUCTOR_NO_CLEARING since we've activated a member of
this union. */
if (TREE_CODE (TREE_TYPE (elt)) == UNION_TYPE)
CONSTRUCTOR_NO_CLEARING (elt) = false;
}
if (*non_constant_p)
return t;
else if (lval)
return target;
else
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 = eval_constant_expression (ctx, orig_lhs, lval, non_constant_p,
overflow_p);
rhs = eval_constant_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);
}
/* Helper function of cxx_bind_parameters_in_call. Return non-NULL
if *TP is address of a static variable (or part of it) currently being
constructed or of a heap artificial variable. */
static tree
addr_of_non_const_var (tree *tp, int *walk_subtrees, void *data)
{
if (TREE_CODE (*tp) == ADDR_EXPR)
if (tree var = get_base_address (TREE_OPERAND (*tp, 0)))
if (VAR_P (var) && TREE_STATIC (var))
{
if (DECL_NAME (var) == heap_uninit_identifier
|| DECL_NAME (var) == heap_identifier
|| DECL_NAME (var) == heap_vec_uninit_identifier
|| DECL_NAME (var) == heap_vec_identifier)
return var;
constexpr_global_ctx *global = (constexpr_global_ctx *) data;
if (global->values.get (var))
return var;
}
if (TYPE_P (*tp))
*walk_subtrees = false;
return NULL_TREE;
}
/* Subroutine of cxx_eval_call_expression.
We are processing a call expression (either CALL_EXPR or
AGGR_INIT_EXPR) in the context of CTX. Evaluate
all arguments and bind their values to correspondings
parameters, making up the NEW_CALL context. */
static tree
rs_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
bool *non_constant_p, bool *overflow_p,
bool *non_constant_args)
{
const int nargs = call_expr_nargs (t);
tree parms = DECL_ARGUMENTS (fun);
int i;
/* We don't record ellipsis args below. */
int nparms = list_length (parms);
int nbinds = nargs < nparms ? nargs : nparms;
tree binds = make_tree_vec (nbinds);
for (i = 0; i < nargs; ++i)
{
tree x, arg;
tree type = parms ? TREE_TYPE (parms) : void_type_node;
if (parms && DECL_BY_REFERENCE (parms))
type = TREE_TYPE (type);
x = CALL_EXPR_ARG (t, i);
if (TREE_ADDRESSABLE (type))
/* Undo convert_for_arg_passing work here. */
x = convert_from_reference (x);
/* Normally we would strip a TARGET_EXPR in an initialization context
such as this, but here we do the elision differently: we keep the
TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm. */
arg = eval_constant_expression (ctx, x, /*lval=*/false, non_constant_p,
overflow_p);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p && ctx->quiet)
break;
/* Just discard ellipsis args after checking their constantitude. */
if (!parms)
continue;
if (!*non_constant_p)
{
/* Make sure the binding has the same type as the parm. But
only for constant args. */
if (!TYPE_REF_P (type))
arg = adjust_temp_type (type, arg);
if (!TREE_CONSTANT (arg))
*non_constant_args = true;
else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
/* The destructor needs to see any modifications the callee makes
to the argument. */
*non_constant_args = true;
/* If arg is or contains address of a heap artificial variable or
of a static variable being constructed, avoid caching the
function call, as those variables might be modified by the
function, or might be modified by the callers in between
the cached function and just read by the function. */
else if (!*non_constant_args
&& rs_walk_tree (&arg, addr_of_non_const_var, ctx->global,
NULL))
*non_constant_args = true;
// /* For virtual calls, adjust the this argument, so that it is
// the object on which the method is called, rather than
// one of its bases. */
// if (i == 0 && DECL_VIRTUAL_P (fun))
// {
// tree addr = arg;
// STRIP_NOPS (addr);
// if (TREE_CODE (addr) == ADDR_EXPR)
// {
// tree obj = TREE_OPERAND (addr, 0);
// while (TREE_CODE (obj) == COMPONENT_REF
// && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1))
// && !same_type_ignoring_top_level_qualifiers_p (
// TREE_TYPE (obj), DECL_CONTEXT (fun)))
// obj = TREE_OPERAND (obj, 0);
// if (obj != TREE_OPERAND (addr, 0))
// arg = build_fold_addr_expr_with_type (obj, TREE_TYPE
// (arg));
// }
// }
TREE_VEC_ELT (binds, i) = arg;
}
parms = TREE_CHAIN (parms);
}
return binds;
}
// forked from gcc/cp/constexpr.cc cxx_eval_builtin_function_call
/* Attempt to evaluate T which represents a call to a builtin function.
We assume here that all builtin functions evaluate to scalar types
represented by _CST nodes. */
static tree
eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
bool lval, bool *non_constant_p, bool *overflow_p)
{
const int nargs = call_expr_nargs (t);
tree *args = (tree *) alloca (nargs * sizeof (tree));
tree new_call;
int i;
/* Don't fold __builtin_constant_p within a constexpr function. */
bool bi_const_p = DECL_IS_BUILTIN_CONSTANT_P (fun);
/* If we aren't requiring a constant expression, defer __builtin_constant_p
in a constexpr function until we have values for the parameters. */
if (bi_const_p && !ctx->manifestly_const_eval && current_function_decl
&& DECL_DECLARED_CONSTEXPR_P (current_function_decl))
{
*non_constant_p = true;
return t;
}
/* For __builtin_is_constant_evaluated, defer it if not
ctx->manifestly_const_eval (as sometimes we try to constant evaluate
without manifestly_const_eval even expressions or parts thereof which
will later be manifestly const_eval evaluated), otherwise fold it to
true. */
if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
BUILT_IN_FRONTEND))
{
if (!ctx->manifestly_const_eval)
{
*non_constant_p = true;
return t;
}
return boolean_true_node;
}
if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND))
{
temp_override<tree> ovr (current_function_decl);
if (ctx->call && ctx->call->fundef)
current_function_decl = ctx->call->fundef->decl;
return fold_builtin_source_location (EXPR_LOCATION (t));
}
int strops = 0;
int strret = 0;
if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
switch (DECL_FUNCTION_CODE (fun))
{
case BUILT_IN_STRLEN:
case BUILT_IN_STRNLEN:
strops = 1;
break;
case BUILT_IN_MEMCHR:
case BUILT_IN_STRCHR:
case BUILT_IN_STRRCHR:
strops = 1;
strret = 1;
break;
case BUILT_IN_MEMCMP:
case BUILT_IN_STRCMP:
strops = 2;
break;
case BUILT_IN_STRSTR:
strops = 2;
strret = 1;
break;
case BUILT_IN_ASAN_POINTER_COMPARE:
case BUILT_IN_ASAN_POINTER_SUBTRACT:
/* These builtins shall be ignored during constant expression
evaluation. */
return void_node;
default:
break;
}
/* Be permissive for arguments to built-ins; __builtin_constant_p should
return constant false for a non-constant argument. */
constexpr_ctx new_ctx = *ctx;
new_ctx.quiet = true;
for (i = 0; i < nargs; ++i)
{
tree arg = CALL_EXPR_ARG (t, i);
tree oarg = arg;
/* To handle string built-ins we need to pass ADDR_EXPR<STRING_CST> since
expand_builtin doesn't know how to look in the values table. */
bool strop = i < strops;
if (strop)
{
STRIP_NOPS (arg);
if (TREE_CODE (arg) == ADDR_EXPR)
arg = TREE_OPERAND (arg, 0);
else
strop = false;
}
/* If builtin_valid_in_constant_expr_p is true,
potential_constant_expression_1 has not recursed into the arguments
of the builtin, verify it here. */
if (!builtin_valid_in_constant_expr_p (fun)
|| potential_constant_expression (arg))
{
bool dummy1 = false, dummy2 = false;
arg
= eval_constant_expression (&new_ctx, arg, false, &dummy1, &dummy2);
}
if (bi_const_p)
/* For __builtin_constant_p, fold all expressions with constant values
even if they aren't C++ constant-expressions. */
arg = cp_fold_rvalue (arg);
else if (strop)
{
if (TREE_CODE (arg) == CONSTRUCTOR)
arg = braced_lists_to_strings (TREE_TYPE (arg), arg);
if (TREE_CODE (arg) == STRING_CST)
arg = build_address (arg);
else
arg = oarg;
}
args[i] = arg;
}
bool save_ffbcp = force_folding_builtin_constant_p;
force_folding_builtin_constant_p |= ctx->manifestly_const_eval;
tree save_cur_fn = current_function_decl;
/* Return name of ctx->call->fundef->decl for __builtin_FUNCTION (). */
if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION) && ctx->call
&& ctx->call->fundef)
current_function_decl = ctx->call->fundef->decl;
if (fndecl_built_in_p (fun,
CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
BUILT_IN_FRONTEND))
{
location_t loc = EXPR_LOCATION (t);
if (nargs >= 1)
VERIFY_CONSTANT (args[0]);
new_call
= fold_builtin_is_pointer_inverconvertible_with_class (loc, nargs,
args);
}
else if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
BUILT_IN_FRONTEND))
{
location_t loc = EXPR_LOCATION (t);
if (nargs >= 2)
{
VERIFY_CONSTANT (args[0]);
VERIFY_CONSTANT (args[1]);
}
new_call = fold_builtin_is_corresponding_member (loc, nargs, args);
}
else
new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
CALL_EXPR_FN (t), nargs, args);
current_function_decl = save_cur_fn;
force_folding_builtin_constant_p = save_ffbcp;
if (new_call == NULL)
{
if (!*non_constant_p && !ctx->quiet)
{
/* Do not allow__builtin_unreachable in constexpr function.
The __builtin_unreachable call with BUILTINS_LOCATION
comes from cp_maybe_instrument_return. */
if (fndecl_built_in_p (fun, BUILT_IN_UNREACHABLE)
&& EXPR_LOCATION (t) == BUILTINS_LOCATION)
error ("%<constexpr%> call flows off the end of the function");
else
{
new_call = build_call_array_loc (EXPR_LOCATION (t), TREE_TYPE (t),
CALL_EXPR_FN (t), nargs, args);
error ("%q+E is not a constant expression", new_call);
}
}
*non_constant_p = true;
return t;
}
if (!potential_constant_expression (new_call))
{
if (!*non_constant_p && !ctx->quiet)
error ("%q+E is not a constant expression", new_call);
*non_constant_p = true;
return t;
}
if (strret)
{
/* memchr returns a pointer into the first argument, but we replaced the
argument above with a STRING_CST; put it back it now. */
tree op = CALL_EXPR_ARG (t, strret - 1);
STRIP_NOPS (new_call);
if (TREE_CODE (new_call) == POINTER_PLUS_EXPR)
TREE_OPERAND (new_call, 0) = op;
else if (TREE_CODE (new_call) == ADDR_EXPR)
new_call = op;
}
return eval_constant_expression (&new_ctx, new_call, lval, non_constant_p,
overflow_p);
}
// 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, bool lval,
bool *non_constant_p, bool *overflow_p)
{
location_t loc = EXPR_LOCATION (t);
tree fun = get_function_named_in_call (t);
constexpr_call new_call = {NULL, NULL, NULL, 0, ctx->manifestly_const_eval};
int depth_ok;
if (fun == NULL_TREE)
{
// return cxx_eval_internal_function (ctx, t, lval,
// non_constant_p, overflow_p);
gcc_unreachable ();
return error_mark_node;
}
if (TREE_CODE (fun) != FUNCTION_DECL)
{
if (!ctx->quiet && !*non_constant_p)
error_at (loc,
"expression %qE does not designate a %<constexpr%> "
"function",
fun);
*non_constant_p = true;
return t;
}
if (fndecl_built_in_p (fun))
return eval_builtin_function_call (ctx, t, fun, lval, non_constant_p,
overflow_p);
bool non_constant_args = false;
new_call.bindings
= rs_bind_parameters_in_call (ctx, t, fun, non_constant_p, overflow_p,
&non_constant_args);
/* We build up the bindings list before we know whether we already have this
call cached. If we don't end up saving these bindings, ggc_free them when
this function exits. */
class free_bindings
{
tree *bindings;
public:
free_bindings (tree &b) : bindings (&b) {}
~free_bindings ()
{
if (bindings)
ggc_free (*bindings);
}
void preserve () { bindings = NULL; }
} fb (new_call.bindings);
if (*non_constant_p)
return t;
/* If in direct recursive call, optimize definition search. */
if (ctx && ctx->call && ctx->call->fundef && ctx->call->fundef->decl == fun)
new_call.fundef = ctx->call->fundef;
else
{
new_call.fundef = retrieve_constexpr_fundef (fun);
if (new_call.fundef == NULL || new_call.fundef->body == NULL
|| new_call.fundef->result == error_mark_node
|| fun == current_function_decl)
{
if (!ctx->quiet)
{
/* We need to check for current_function_decl here in case we're
being called during cp_fold_function, because at that point
DECL_INITIAL is set properly and we have a fundef but we
haven't lowered invisirefs yet (c++/70344). */
if (DECL_INITIAL (fun) == error_mark_node
|| fun == current_function_decl)
error_at (loc,
"%qD called in a constant expression before its "
"definition is complete",
fun);
else if (DECL_INITIAL (fun))
{
// /* The definition of fun was somehow unsuitable. But
// pretend
// that lambda static thunks don't exist. */
// if (!lambda_static_thunk_p (fun))
// error_at (loc, "%qD called in a constant expression",
// fun);
explain_invalid_constexpr_fn (fun);
}
else
error_at (loc, "%qD used before its definition", fun);
}
*non_constant_p = true;
return t;
}
}
depth_ok = push_cx_call_context (t);
tree result = NULL_TREE;
constexpr_call *entry = NULL;
if (depth_ok && !non_constant_args && ctx->strict)
{
new_call.hash = constexpr_fundef_hasher::hash (new_call.fundef);
new_call.hash = iterative_hash_object (new_call.bindings, new_call.hash);
new_call.hash
= iterative_hash_object (ctx->manifestly_const_eval, new_call.hash);
/* If we have seen this call before, we are done. */
maybe_initialize_constexpr_call_table ();
constexpr_call **slot
= constexpr_call_table->find_slot (&new_call, INSERT);
entry = *slot;
if (entry == NULL)
{
/* Only cache up to constexpr_cache_depth to limit memory use. */
if (depth_ok < constexpr_cache_depth)
{
/* We need to keep a pointer to the entry, not just the slot, as
the slot can move during evaluation of the body. */
*slot = entry = ggc_alloc<constexpr_call> ();
*entry = new_call;
fb.preserve ();
}
}
/* Calls that are in progress have their result set to NULL, so that we
can detect circular dependencies. Now that we only cache up to
constexpr_cache_depth this won't catch circular dependencies that
start deeper, but they'll hit the recursion or ops limit. */
else if (entry->result == NULL)
{
if (!ctx->quiet)
error ("call has circular dependency");
*non_constant_p = true;
entry->result = result = error_mark_node;
}
else
result = entry->result;
}
if (!depth_ok)
{
if (!ctx->quiet)
error ("%<constexpr%> evaluation depth exceeds maximum of %d (use "
"%<-fconstexpr-depth=%> to increase the maximum)",
max_constexpr_depth);
*non_constant_p = true;
result = error_mark_node;
}
else
{
bool cacheable = true;
if (result && result != error_mark_node)
/* OK */;
else if (!DECL_SAVED_TREE (fun))
{
/* When at_eof >= 2, cgraph has started throwing away
DECL_SAVED_TREE, so fail quietly. FIXME we get here because of
late code generation for VEC_INIT_EXPR, which needs to be
completely reconsidered. */
// gcc_assert (at_eof >= 2 && ctx->quiet);
*non_constant_p = true;
}
else if (tree copy = get_fundef_copy (new_call.fundef))
{
tree body, parms, res;
releasing_vec ctors;
/* Reuse or create a new unshared copy of this function's body. */
body = TREE_PURPOSE (copy);
parms = TREE_VALUE (copy);
res = TREE_TYPE (copy);
/* Associate the bindings with the remapped parms. */
tree bound = new_call.bindings;
tree remapped = parms;
for (int i = 0; i < TREE_VEC_LENGTH (bound); ++i)
{
tree arg = TREE_VEC_ELT (bound, i);
if (entry)
{
/* Unshare args going into the hash table to separate them
from the caller's context, for better GC and to avoid
problems with verify_gimple. */
arg = unshare_expr_without_location (arg);
TREE_VEC_ELT (bound, i) = arg;
/* And then unshare again so the callee doesn't change the
argument values in the hash table. XXX Could we unshare
lazily in cxx_eval_store_expression? */
arg = unshare_constructor (arg);
if (TREE_CODE (arg) == CONSTRUCTOR)
vec_safe_push (ctors, arg);
}
ctx->global->values.put (remapped, arg);
remapped = DECL_CHAIN (remapped);
}
/* Add the RESULT_DECL to the values map, too. */
gcc_assert (!DECL_BY_REFERENCE (res));
ctx->global->values.put (res, NULL_TREE);
/* Track the callee's evaluated SAVE_EXPRs and TARGET_EXPRs so that
we can forget their values after the call. */
constexpr_ctx ctx_with_save_exprs = *ctx;
auto_vec<tree, 10> save_exprs;
ctx_with_save_exprs.save_exprs = &save_exprs;
ctx_with_save_exprs.call = &new_call;
unsigned save_heap_alloc_count = ctx->global->heap_vars.length ();
unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count;
tree jump_target = NULL_TREE;
eval_constant_expression (&ctx_with_save_exprs, body, lval,
non_constant_p, overflow_p, &jump_target);
if (VOID_TYPE_P (TREE_TYPE (res)))
result = void_node;
else
{
result = *ctx->global->values.get (res);
if (result == NULL_TREE && !*non_constant_p)
{
if (!ctx->quiet)
error ("%<constexpr%> call flows off the end "
"of the function");
*non_constant_p = true;
}
}
/* Forget the saved values of the callee's SAVE_EXPRs and
TARGET_EXPRs. */
for (tree save_expr : save_exprs)
ctx->global->values.remove (save_expr);
/* Remove the parms/result from the values map. Is it worth
bothering to do this when the map itself is only live for
one constexpr evaluation? If so, maybe also clear out
other vars from call, maybe in BIND_EXPR handling? */
ctx->global->values.remove (res);
for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
ctx->global->values.remove (parm);
/* Make the unshared function copy we used available for re-use. */
save_fundef_copy (fun, copy);
/* If the call allocated some heap object that hasn't been
deallocated during the call, or if it deallocated some heap
object it has not allocated, the call isn't really stateless
for the constexpr evaluation and should not be cached.
It is fine if the call allocates something and deallocates it
too. */
if (entry
&& (save_heap_alloc_count != ctx->global->heap_vars.length ()
|| (save_heap_dealloc_count
!= ctx->global->heap_dealloc_count)))
{
tree heap_var;
unsigned int i;
if ((ctx->global->heap_vars.length ()
- ctx->global->heap_dealloc_count)
!= save_heap_alloc_count - save_heap_dealloc_count)
cacheable = false;
else
FOR_EACH_VEC_ELT_FROM (ctx->global->heap_vars, i, heap_var,
save_heap_alloc_count)
if (DECL_NAME (heap_var) != heap_deleted_identifier)
{
cacheable = false;
break;
}
}
}
else
/* Couldn't get a function copy to evaluate. */
*non_constant_p = true;
if (result == error_mark_node)
*non_constant_p = true;
if (*non_constant_p || *overflow_p)
result = error_mark_node;
else if (!result)
result = void_node;
if (entry)
entry->result = cacheable ? result : error_mark_node;
}
pop_cx_call_context ();
return result;
}
/* Subroutine of build_data_member_initialization. MEMBER is a COMPONENT_REF
for a member of an anonymous aggregate, INIT is the initializer for that
member, and VEC_OUTER is the vector of constructor elements for the class
whose constructor we are processing. Add the initializer to the vector
and return true to indicate success. */
// static bool
// build_anon_member_initialization (tree member, tree init,
// vec<constructor_elt, va_gc> **vec_outer)
// {
// /* MEMBER presents the relevant fields from the inside out, but we need
// to build up the initializer from the outside in so that we can reuse
// previously built CONSTRUCTORs if this is, say, the second field in an
// anonymous struct. So we use a vec as a stack. */
// auto_vec<tree, 2> fields;
// do
// {
// fields.safe_push (TREE_OPERAND (member, 1));
// member = TREE_OPERAND (member, 0);
// } while (ANON_AGGR_TYPE_P (TREE_TYPE (member))
// && TREE_CODE (member) == COMPONENT_REF);
//
// /* VEC has the constructor elements vector for the context of FIELD.
// If FIELD is an anonymous aggregate, we will push inside it. */
// vec<constructor_elt, va_gc> **vec = vec_outer;
// tree field;
// while (field = fields.pop (), ANON_AGGR_TYPE_P (TREE_TYPE (field)))
// {
// tree ctor;
// /* If there is already an outer constructor entry for the anonymous
// aggregate FIELD, use it; otherwise, insert one. */
// if (vec_safe_is_empty (*vec) || (*vec)->last ().index != field)
// {
// ctor = build_constructor (TREE_TYPE (field), NULL);
// CONSTRUCTOR_APPEND_ELT (*vec, field, ctor);
// }
// else
// ctor = (*vec)->last ().value;
// vec = &CONSTRUCTOR_ELTS (ctor);
// }
//
// /* Now we're at the innermost field, the one that isn't an anonymous
// aggregate. Add its initializer to the CONSTRUCTOR and we're done. */
// gcc_assert (fields.is_empty ());
// CONSTRUCTOR_APPEND_ELT (*vec, field, init);
//
// return true;
// }
///* V is a vector of constructor elements built up for the base and member
// initializers of a constructor for TYPE. They need to be in increasing
// offset order, which they might not be yet if TYPE has a primary base
// which is not first in the base-clause or a vptr and at least one base
// all of which are non-primary. */
//
// static vec<constructor_elt, va_gc> *
// sort_constexpr_mem_initializers (tree type, vec<constructor_elt, va_gc> *v)
//{
// tree pri = CLASSTYPE_PRIMARY_BINFO (type);
// tree field_type;
// unsigned i;
// constructor_elt *ce;
//
// if (pri)
// field_type = BINFO_TYPE (pri);
// else if (TYPE_CONTAINS_VPTR_P (type))
// field_type = vtbl_ptr_type_node;
// else
// return v;
//
// /* Find the element for the primary base or vptr and move it to the
// beginning of the vec. */
// for (i = 0; vec_safe_iterate (v, i, &ce); ++i)
// if (TREE_TYPE (ce->index) == field_type)
// break;
//
// if (i > 0 && i < vec_safe_length (v))
// {
// vec<constructor_elt, va_gc> &vref = *v;
// constructor_elt elt = vref[i];
// for (; i > 0; --i)
// vref[i] = vref[i - 1];
// vref[0] = elt;
// }
//
// return v;
//}
/* Subroutine of build_constexpr_constructor_member_initializers.
The expression tree T represents a data member initialization
in a (constexpr) constructor definition. Build a pairing of
the data member with its initializer, and prepend that pair
to the existing initialization pair INITS. */
static bool
build_data_member_initialization (tree t, vec<constructor_elt, va_gc> **vec)
{
tree member;
if (TREE_CODE (t) == CLEANUP_POINT_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == EXPR_STMT)
t = TREE_OPERAND (t, 0);
if (t == error_mark_node)
return false;
if (TREE_CODE (t) == STATEMENT_LIST)
{
for (tree stmt : tsi_range (t))
if (!build_data_member_initialization (stmt, vec))
return false;
return true;
}
if (TREE_CODE (t) == CONVERT_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == INIT_EXPR
/* vptr initialization shows up as a MODIFY_EXPR. In C++14 we only
use what this function builds for cx_check_missing_mem_inits, and
assignment in the ctor body doesn't count. */
|| (TREE_CODE (t) == MODIFY_EXPR))
{
member = TREE_OPERAND (t, 0);
// Faisal: not sure if we need to port over break_out_target_exprs
// if not, then not sure how to handle init in this case
// init = break_out_target_exprs (TREE_OPERAND (t, 1));
}
else if (TREE_CODE (t) == CALL_EXPR)
{
tree fn = get_callee_fndecl (t);
if (!fn || !DECL_CONSTRUCTOR_P (fn))
/* We're only interested in calls to subobject constructors. */
return true;
member = CALL_EXPR_ARG (t, 0);
/* We don't use build_cplus_new here because it complains about
abstract bases. Leaving the call unwrapped means that it has the
wrong type, but cxx_eval_constant_expression doesn't care. */
// Faisal: not sure if we need to port over break_out_target_exprs
// if not, then not sure how to handle init in this case
// init = break_out_target_exprs (t);
}
else if (TREE_CODE (t) == BIND_EXPR)
return build_data_member_initialization (BIND_EXPR_BODY (t), vec);
else
/* Don't add anything else to the CONSTRUCTOR. */
return true;
if (INDIRECT_REF_P (member))
member = TREE_OPERAND (member, 0);
if (TREE_CODE (member) == NOP_EXPR)
{
tree op = member;
STRIP_NOPS (op);
if (TREE_CODE (op) == ADDR_EXPR)
{
gcc_assert (same_type_ignoring_top_level_qualifiers_p (
TREE_TYPE (TREE_TYPE (op)), TREE_TYPE (TREE_TYPE (member))));
/* Initializing a cv-qualified member; we need to look through
the const_cast. */
member = op;
}
else if (op == current_class_ptr
&& (same_type_ignoring_top_level_qualifiers_p (
TREE_TYPE (TREE_TYPE (member)), current_class_type)))
/* Delegating constructor. */
member = op;
else
{
/* 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))));
}
}
if (TREE_CODE (member) == ADDR_EXPR)
member = TREE_OPERAND (member, 0);
if (TREE_CODE (member) == COMPONENT_REF)
{
tree aggr = TREE_OPERAND (member, 0);
if (TREE_CODE (aggr) == VAR_DECL)
/* Initializing a local variable, don't add anything. */
return true;
if (TREE_CODE (aggr) != COMPONENT_REF)
/* Normal member initialization. */
member = TREE_OPERAND (member, 1);
else if (ANON_AGGR_TYPE_P (TREE_TYPE (aggr)))
/* Initializing a member of an anonymous union. */
rust_sorry_at (Location (), "cannot handle value initialization yet");
// return build_anon_member_initialization (member, init, vec);
else
/* We're initializing a vtable pointer in a base. Leave it as
COMPONENT_REF so we remember the path to get to the vfield. */
gcc_assert (TREE_TYPE (member) == vtbl_ptr_type_node);
}
/* Value-initialization can produce multiple initializers for the
same field; use the last one. */
if (!vec_safe_is_empty (*vec) && (*vec)->last ().index == member)
rust_sorry_at (Location (), "cannot handle value initialization yet");
// (*vec)->last ().value = init;
else
rust_sorry_at (Location (), "cannot handle value initialization yet");
// CONSTRUCTOR_APPEND_ELT (*vec, member, init);
return true;
}
///* Build compile-time evalable representations of member-initializer list
// for a constexpr constructor. */
//
// static tree
// build_constexpr_constructor_member_initializers (tree type, tree body)
//{
// vec<constructor_elt, va_gc> *vec = NULL;
// bool ok = true;
// while (true)
// switch (TREE_CODE (body))
// {
// case STATEMENT_LIST:
// for (tree stmt : tsi_range (body))
// {
// body = stmt;
// if (TREE_CODE (body) == BIND_EXPR)
// break;
// }
// break;
//
// case BIND_EXPR:
// body = BIND_EXPR_BODY (body);
// goto found;
//
// default:
// gcc_unreachable ();
// }
// found:
//
// if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
// {
// body = TREE_OPERAND (body, 0);
// if (TREE_CODE (body) == EXPR_STMT)
// body = TREE_OPERAND (body, 0);
// if (TREE_CODE (body) == INIT_EXPR
// && (same_type_ignoring_top_level_qualifiers_p (
// TREE_TYPE (TREE_OPERAND (body, 0)), current_class_type)))
// {
// /* Trivial copy. */
// return TREE_OPERAND (body, 1);
// }
// ok = build_data_member_initialization (body, &vec);
// }
// else if (TREE_CODE (body) == STATEMENT_LIST)
// {
// for (tree stmt : tsi_range (body))
// {
// ok = build_data_member_initialization (stmt, &vec);
// if (!ok)
// break;
// }
// }
// else if (EXPR_P (body))
// ok = build_data_member_initialization (body, &vec);
// else
// gcc_assert (errorcount > 0);
// if (ok)
// {
// if (vec_safe_length (vec) > 0)
// {
// /* In a delegating constructor, return the target. */
// constructor_elt *ce = &(*vec)[0];
// if (ce->index == current_class_ptr)
// {
// body = ce->value;
// vec_free (vec);
// return body;
// }
// }
// vec = sort_constexpr_mem_initializers (type, vec);
// return build_constructor (type, vec);
// }
// else
// return error_mark_node;
//}
// 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 eval_constant_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, bool, 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 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)));
}
/* 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<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;
}
/* 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;
}
/* 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
= eval_constant_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 = eval_constant_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;
}
/* Check whether the parameter and return types of FUN are valid for a
constexpr function, and complain if COMPLAIN. */
bool
is_valid_constexpr_fn (tree fun, bool complain)
{
bool ret = true;
for (tree parm = FUNCTION_FIRST_USER_PARM (fun); parm != NULL_TREE;
parm = TREE_CHAIN (parm))
if (!literal_type_p (TREE_TYPE (parm)))
{
ret = false;
if (complain)
{
// auto_diagnostic_group d;
// error ("invalid type for parameter %d of %<constexpr%> "
// "function %q+#D",
// DECL_PARM_INDEX (parm), fun);
Location locus = Location (DECL_SOURCE_LOCATION (fun));
rust_error_at (
locus, "invalid type for parameter %d of %<constexpr%> function",
DECL_PARM_INDEX (parm));
}
}
return ret;
}
void
explain_invalid_constexpr_fn (tree fun)
{
static hash_set<tree> *diagnosed;
// tree body;
if (diagnosed == NULL)
diagnosed = new hash_set<tree>;
if (diagnosed->add (fun))
/* Already explained. */
return;
iloc_sentinel ils = input_location;
// if (!lambda_static_thunk_p (fun))
// {
// /* Diagnostics should completely ignore the static thunk, so leave
// input_location set to our caller's location. */
// input_location = DECL_SOURCE_LOCATION (fun);
// inform (input_location,
// "%qD is not usable as a %<constexpr%> function because:",
// fun);
// }
/* First check the declaration. */
if (is_valid_constexpr_fn (fun, true))
{
// /* Then if it's OK, the body. */
// if (!DECL_DECLARED_CONSTEXPR_P (fun))
// explain_implicit_non_constexpr (fun);
// else
// {
// if (constexpr_fundef *fd = retrieve_constexpr_fundef (fun))
// body = fd->body;
// else
// body = DECL_SAVED_TREE (fun);
// body = massage_constexpr_body (fun, body);
// require_potential_rvalue_constant_expression (body);
// }
}
}
/* BODY is a validated and massaged definition of a constexpr
function. Register it in the hash table. */
void
register_constexpr_fundef (const constexpr_fundef &value)
{
/* Create the constexpr function table if necessary. */
if (constexpr_fundef_table == NULL)
constexpr_fundef_table
= hash_table<constexpr_fundef_hasher>::create_ggc (101);
constexpr_fundef **slot = constexpr_fundef_table->find_slot (
const_cast<constexpr_fundef *> (&value), INSERT);
gcc_assert (*slot == NULL);
*slot = ggc_alloc<constexpr_fundef> ();
**slot = value;
}
/* We are processing the definition of the constexpr function FUN.
Check that its body fulfills the apropriate requirements and
enter it in the constexpr function definition table. */
void
maybe_save_constexpr_fundef (tree fun)
{
// FIXME
constexpr_fundef entry = {fun, NULL_TREE, NULL_TREE, NULL_TREE};
bool clear_ctx = false;
if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
{
clear_ctx = true;
DECL_CONTEXT (DECL_RESULT (fun)) = fun;
}
tree saved_fn = current_function_decl;
current_function_decl = fun;
entry.body = copy_fn (entry.decl, entry.parms, entry.result);
current_function_decl = saved_fn;
if (clear_ctx)
DECL_CONTEXT (DECL_RESULT (entry.decl)) = NULL_TREE;
register_constexpr_fundef (entry);
}
/* Evaluate a STATEMENT_LIST for side-effects. Handles various jump
semantics, for switch, break, continue, and return. */
static tree
eval_statement_list (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
bool *overflow_p, tree *jump_target)
{
tree local_target;
/* In a statement-expression we want to return the last value.
For empty statement expression return void_node. */
tree r = void_node;
if (!jump_target)
{
local_target = NULL_TREE;
jump_target = &local_target;
}
for (tree stmt : tsi_range (t))
{
/* We've found a continue, so skip everything until we reach
the label its jumping to. */
if (continues (jump_target))
{
if (label_matches (ctx, jump_target, stmt))
/* Found it. */
*jump_target = NULL_TREE;
else
continue;
}
if (TREE_CODE (stmt) == DEBUG_BEGIN_STMT)
continue;
r = eval_constant_expression (ctx, stmt, false, non_constant_p,
overflow_p, jump_target);
if (*non_constant_p)
break;
if (returns (jump_target) || breaks (jump_target))
break;
}
if (*jump_target && jump_target == &local_target)
{
/* We aren't communicating the jump to our caller, so give up. We don't
need to support evaluation of jumps out of statement-exprs. */
if (!ctx->quiet)
error_at (EXPR_LOCATION (r), "statement is not a constant expression");
*non_constant_p = true;
}
return r;
}
// forked from gcc/cp/constexpr.cc cxx_eval_conditional_expression
/* Subroutine of cxx_eval_constant_expression.
Attempt to evaluate condition expressions. Dead branches are not
looked into. */
static tree
eval_conditional_expression (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p,
tree *jump_target)
{
tree val
= eval_constant_expression (ctx, TREE_OPERAND (t, 0),
/*lval*/ false, non_constant_p, overflow_p);
VERIFY_CONSTANT (val);
if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
{
/* Evaluate the condition as if it was
if (__builtin_is_constant_evaluated ()), i.e. defer it if not
ctx->manifestly_const_eval (as sometimes we try to constant evaluate
without manifestly_const_eval even expressions or parts thereof which
will later be manifestly const_eval evaluated), otherwise fold it to
true. */
if (ctx->manifestly_const_eval)
val = boolean_true_node;
else
{
*non_constant_p = true;
return t;
}
}
/* Don't VERIFY_CONSTANT the other operands. */
if (integer_zerop (val))
val = TREE_OPERAND (t, 2);
else
val = TREE_OPERAND (t, 1);
if (/*TREE_CODE (t) == IF_STMT && */ !val)
val = void_node;
return eval_constant_expression (ctx, val, lval, non_constant_p, overflow_p,
jump_target);
}
// forked from gcc/cp/constexpr.cc cxx_eval_bit_field_ref
/* Subroutine of cxx_eval_constant_expression.
Attempt to reduce a field access of a value of class type that is
expressed as a BIT_FIELD_REF. */
static tree
eval_bit_field_ref (const constexpr_ctx *ctx, tree t, bool lval,
bool *non_constant_p, bool *overflow_p)
{
tree orig_whole = TREE_OPERAND (t, 0);
tree retval, fldval, utype, mask;
bool fld_seen = false;
HOST_WIDE_INT istart, isize;
tree whole = eval_constant_expression (ctx, orig_whole, lval, non_constant_p,
overflow_p);
tree start, field, value;
unsigned HOST_WIDE_INT i;
if (whole == orig_whole)
return t;
/* Don't VERIFY_CONSTANT here; we only want to check that we got a
CONSTRUCTOR. */
if (!*non_constant_p && TREE_CODE (whole) != VECTOR_CST
&& TREE_CODE (whole) != CONSTRUCTOR)
{
if (!ctx->quiet)
error ("%qE is not a constant expression", orig_whole);
*non_constant_p = true;
}
if (*non_constant_p)
return t;
if (TREE_CODE (whole) == VECTOR_CST)
return fold_ternary (BIT_FIELD_REF, TREE_TYPE (t), whole,
TREE_OPERAND (t, 1), TREE_OPERAND (t, 2));
start = TREE_OPERAND (t, 2);
istart = tree_to_shwi (start);
isize = tree_to_shwi (TREE_OPERAND (t, 1));
utype = TREE_TYPE (t);
if (!TYPE_UNSIGNED (utype))
utype = build_nonstandard_integer_type (TYPE_PRECISION (utype), 1);
retval = build_int_cst (utype, 0);
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value)
{
tree bitpos = bit_position (field);
STRIP_ANY_LOCATION_WRAPPER (value);
if (bitpos == start && DECL_SIZE (field) == TREE_OPERAND (t, 1))
return value;
if (TREE_CODE (TREE_TYPE (field)) == INTEGER_TYPE
&& TREE_CODE (value) == INTEGER_CST && tree_fits_shwi_p (bitpos)
&& tree_fits_shwi_p (DECL_SIZE (field)))
{
HOST_WIDE_INT bit = tree_to_shwi (bitpos);
HOST_WIDE_INT sz = tree_to_shwi (DECL_SIZE (field));
HOST_WIDE_INT shift;
if (bit >= istart && bit + sz <= istart + isize)
{
fldval = fold_convert (utype, value);
mask = build_int_cst_type (utype, -1);
mask = fold_build2 (LSHIFT_EXPR, utype, mask,
size_int (TYPE_PRECISION (utype) - sz));
mask = fold_build2 (RSHIFT_EXPR, utype, mask,
size_int (TYPE_PRECISION (utype) - sz));
fldval = fold_build2 (BIT_AND_EXPR, utype, fldval, mask);
shift = bit - istart;
if (BYTES_BIG_ENDIAN)
shift = TYPE_PRECISION (utype) - shift - sz;
fldval
= fold_build2 (LSHIFT_EXPR, utype, fldval, size_int (shift));
retval = fold_build2 (BIT_IOR_EXPR, utype, retval, fldval);
fld_seen = true;
}
}
}
if (fld_seen)
return fold_convert (TREE_TYPE (t), retval);
gcc_unreachable ();
return error_mark_node;
}
// forked from gcc/cp/constexpr.cc returns
/* Predicates for the meaning of *jump_target. */
static bool
returns (tree *jump_target)
{
return *jump_target
&& (TREE_CODE (*jump_target) == RETURN_EXPR
|| (TREE_CODE (*jump_target) == LABEL_DECL
&& LABEL_DECL_CDTOR (*jump_target)));
}
// forked from gcc/cp/constexpr.cc breaks
static bool
breaks (tree *jump_target)
{
return *jump_target
&& ((TREE_CODE (*jump_target) == LABEL_DECL
&& LABEL_DECL_BREAK (*jump_target))
|| TREE_CODE (*jump_target) == BREAK_STMT
|| TREE_CODE (*jump_target) == EXIT_EXPR);
}
// forked from gcc/cp/constexpr.cc continues
static bool
continues (tree *jump_target)
{
return *jump_target
&& ((TREE_CODE (*jump_target) == LABEL_DECL
&& LABEL_DECL_CONTINUE (*jump_target))
|| TREE_CODE (*jump_target) == CONTINUE_STMT);
}
// forked from gcc/cp/constexpr.cc switches
static bool
switches (tree *jump_target)
{
return *jump_target && TREE_CODE (*jump_target) == INTEGER_CST;
}
// forked from gcc/cp/constexpr.cc cxx_eval_loop_expr
/* Evaluate a LOOP_EXPR for side-effects. Handles break and return
semantics; continue semantics are covered by cxx_eval_statement_list. */
static tree
eval_loop_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
bool *overflow_p, tree *jump_target)
{
constexpr_ctx new_ctx = *ctx;
tree local_target;
if (!jump_target)
{
local_target = NULL_TREE;
jump_target = &local_target;
}
tree body, cond = NULL_TREE, expr = NULL_TREE;
int count = 0;
switch (TREE_CODE (t))
{
case LOOP_EXPR:
body = LOOP_EXPR_BODY (t);
break;
case WHILE_STMT:
body = WHILE_BODY (t);
cond = WHILE_COND (t);
count = -1;
break;
case FOR_STMT:
if (FOR_INIT_STMT (t))
eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/ false,
non_constant_p, overflow_p, jump_target);
if (*non_constant_p)
return NULL_TREE;
body = FOR_BODY (t);
cond = FOR_COND (t);
expr = FOR_EXPR (t);
count = -1;
break;
default:
gcc_unreachable ();
}
auto_vec<tree, 10> save_exprs;
new_ctx.save_exprs = &save_exprs;
do
{
if (count != -1)
{
if (body)
eval_constant_expression (&new_ctx, body, /*lval*/ false,
non_constant_p, overflow_p, jump_target);
if (breaks (jump_target))
{
*jump_target = NULL_TREE;
break;
}
if (TREE_CODE (t) != LOOP_EXPR && continues (jump_target))
*jump_target = NULL_TREE;
if (expr)
eval_constant_expression (&new_ctx, expr, /*lval*/ false,
non_constant_p, overflow_p, jump_target);
}
if (cond)
{
tree res = eval_constant_expression (&new_ctx, cond, /*lval*/ false,
non_constant_p, overflow_p,
jump_target);
if (res)
{
if (verify_constant (res, ctx->quiet, non_constant_p, overflow_p))
break;
if (integer_zerop (res))
break;
}
else
gcc_assert (*jump_target);
}
/* Forget saved values of SAVE_EXPRs and TARGET_EXPRs. */
for (tree save_expr : save_exprs)
ctx->global->values.remove (save_expr);
save_exprs.truncate (0);
if (++count >= constexpr_loop_limit)
{
if (!ctx->quiet)
error_at (rs_expr_loc_or_input_loc (t),
"%<constexpr%> loop iteration count exceeds limit of %d "
"(use %<-fconstexpr-loop-limit=%> to increase the limit)",
constexpr_loop_limit);
*non_constant_p = true;
break;
}
}
while (!returns (jump_target) && !breaks (jump_target)
&& !continues (jump_target) && (!switches (jump_target) || count == 0)
&& !*non_constant_p);
/* Forget saved values of SAVE_EXPRs and TARGET_EXPRs. */
for (tree save_expr : save_exprs)
ctx->global->values.remove (save_expr);
return NULL_TREE;
}
// forked from gcc/cp/constexpr.cc cxx_eval_switch_expr
/* Evaluate a SWITCH_EXPR for side-effects. Handles switch and break jump
semantics. */
static tree
eval_switch_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
bool *overflow_p, tree *jump_target)
{
tree cond
= TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
cond
= eval_constant_expression (ctx, cond, false, non_constant_p, overflow_p);
VERIFY_CONSTANT (cond);
*jump_target = cond;
tree body
= TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_BODY (t) : SWITCH_BODY (t);
constexpr_ctx new_ctx = *ctx;
constexpr_switch_state css = css_default_not_seen;
new_ctx.css_state = &css;
eval_constant_expression (&new_ctx, body, false, non_constant_p, overflow_p,
jump_target);
if (switches (jump_target) && css == css_default_seen)
{
/* If the SWITCH_EXPR body has default: label, process it once again,
this time instructing label_matches to return true for default:
label on switches (jump_target). */
css = css_default_processing;
eval_constant_expression (&new_ctx, body, false, non_constant_p,
overflow_p, jump_target);
}
if (breaks (jump_target) || switches (jump_target))
*jump_target = NULL_TREE;
return NULL_TREE;
}
// forked from gcc/cp/constexpr.cc eval_unary_expression
/* Subroutine of cxx_eval_constant_expression.
Attempt to reduce the unary expression tree T to a compile time value.
If successful, return the value. Otherwise issue a diagnostic
and return error_mark_node. */
static tree
eval_unary_expression (const constexpr_ctx *ctx, tree t, bool /*lval*/,
bool *non_constant_p, bool *overflow_p)
{
tree r;
tree orig_arg = TREE_OPERAND (t, 0);
tree arg = eval_constant_expression (ctx, orig_arg, /*lval*/ false,
non_constant_p, overflow_p);
VERIFY_CONSTANT (arg);
location_t loc = EXPR_LOCATION (t);
enum tree_code code = TREE_CODE (t);
tree type = TREE_TYPE (t);
r = fold_unary_loc (loc, code, type, arg);
if (r == NULL_TREE)
{
if (arg == orig_arg)
r = t;
else
r = build1_loc (loc, code, type, arg);
}
VERIFY_CONSTANT (r);
return r;
}
// forked from gcc/cp/constexpr.cc cxx_eval_outermost_constant_expr
/* ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
STRICT has the same sense as for constant_value_1: true if we only allow
conforming C++ constant expressions, or false if we want a constant value
even if it doesn't conform.
MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as
per P0595 even when ALLOW_NON_CONSTANT is true.
CONSTEXPR_DTOR is true when evaluating the dtor of a constexpr variable.
OBJECT must be non-NULL in that case. */
static tree
cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
bool strict = true,
bool manifestly_const_eval = false,
bool constexpr_dtor = false,
tree object = NULL_TREE)
{
auto_timevar time (TV_CONSTEXPR);
bool non_constant_p = false;
bool overflow_p = false;
if (BRACE_ENCLOSED_INITIALIZER_P (t))
{
gcc_checking_assert (allow_non_constant);
return t;
}
constexpr_global_ctx global_ctx;
constexpr_ctx ctx
= {&global_ctx, NULL,
NULL, NULL,
NULL, NULL,
NULL, allow_non_constant,
strict, manifestly_const_eval || !allow_non_constant};
/* Turn off -frounding-math for manifestly constant evaluation. */
warning_sentinel rm (flag_rounding_math, ctx.manifestly_const_eval);
tree type = initialized_type (t);
tree r = t;
bool is_consteval = false;
if (VOID_TYPE_P (type))
{
if (constexpr_dtor)
/* Used for destructors of array elements. */
type = TREE_TYPE (object);
else
{
if (TREE_CODE (t) != CALL_EXPR)
return t;
/* Calls to immediate functions returning void need to be
evaluated. */
tree fndecl = rs_get_callee_fndecl_nofold (t);
if (fndecl == NULL_TREE || !DECL_IMMEDIATE_FUNCTION_P (fndecl))
return t;
else
is_consteval = true;
}
}
else if ((TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == TARGET_EXPR))
{
/* For non-concept checks, determine if it is consteval. */
tree x = t;
if (TREE_CODE (x) == TARGET_EXPR)
x = TARGET_EXPR_INITIAL (x);
tree fndecl = rs_get_callee_fndecl_nofold (x);
if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl))
is_consteval = true;
}
if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
{
/* In C++14 an NSDMI can participate in aggregate initialization,
and can refer to the address of the object being initialized, so
we need to pass in the relevant VAR_DECL if we want to do the
evaluation in a single pass. The evaluation will dynamically
update ctx.values for the VAR_DECL. We use the same strategy
for C++11 constexpr constructors that refer to the object being
initialized. */
if (constexpr_dtor)
{
gcc_assert (object && VAR_P (object));
gcc_assert (DECL_DECLARED_CONSTEXPR_P (object));
gcc_assert (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object));
if (error_operand_p (DECL_INITIAL (object)))
return t;
ctx.ctor = unshare_expr (DECL_INITIAL (object));
TREE_READONLY (ctx.ctor) = false;
/* Temporarily force decl_really_constant_value to return false
for it, we want to use ctx.ctor for the current value instead. */
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = false;
}
else
{
ctx.ctor = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (ctx.ctor) = true;
}
if (!object)
{
if (TREE_CODE (t) == TARGET_EXPR)
object = TARGET_EXPR_SLOT (t);
}
ctx.object = object;
if (object)
gcc_assert (
same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (object)));
if (object && DECL_P (object))
global_ctx.values.put (object, ctx.ctor);
if (TREE_CODE (r) == TARGET_EXPR)
/* Avoid creating another CONSTRUCTOR when we expand the
TARGET_EXPR. */
r = TARGET_EXPR_INITIAL (r);
}
auto_vec<tree, 16> cleanups;
global_ctx.cleanups = &cleanups;
if (manifestly_const_eval)
instantiate_constexpr_fns (r);
r = eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p);
if (!constexpr_dtor)
verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
else
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
unsigned int i;
tree cleanup;
/* Evaluate the cleanups. */
FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
eval_constant_expression (&ctx, cleanup, false, &non_constant_p,
&overflow_p);
/* Mutable logic is a bit tricky: we want to allow initialization of
constexpr variables with mutable members, but we can't copy those
members to another constexpr variable. */
if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r))
{
if (!allow_non_constant)
error ("%qE is not a constant expression because it refers to "
"mutable subobjects of %qT",
t, type);
non_constant_p = true;
}
if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r))
{
if (!allow_non_constant)
error ("%qE is not a constant expression because it refers to "
"an incompletely initialized variable",
t);
TREE_CONSTANT (r) = false;
non_constant_p = true;
}
if (!global_ctx.heap_vars.is_empty ())
{
tree heap_var
= rs_walk_tree_without_duplicates (&r, find_heap_var_refs, NULL);
unsigned int i;
if (heap_var)
{
if (!allow_non_constant && !non_constant_p)
error_at (DECL_SOURCE_LOCATION (heap_var),
"%qE is not a constant expression because it refers to "
"a result of %<operator new%>",
t);
r = t;
non_constant_p = true;
}
FOR_EACH_VEC_ELT (global_ctx.heap_vars, i, heap_var)
{
if (DECL_NAME (heap_var) != heap_deleted_identifier)
{
if (!allow_non_constant && !non_constant_p)
error_at (DECL_SOURCE_LOCATION (heap_var),
"%qE is not a constant expression because allocated "
"storage has not been deallocated",
t);
r = t;
non_constant_p = true;
}
varpool_node::get (heap_var)->remove ();
}
}
/* Check that immediate invocation does not return an expression referencing
any immediate function decls. */
if (is_consteval || in_immediate_context ())
if (tree immediate_fndecl
= rs_walk_tree_without_duplicates (&r, find_immediate_fndecl, NULL))
{
if (!allow_non_constant && !non_constant_p)
error_at (rs_expr_loc_or_input_loc (t),
"immediate evaluation returns address of immediate "
"function %qD",
immediate_fndecl);
r = t;
non_constant_p = true;
}
if (non_constant_p)
/* If we saw something bad, go back to our argument. The wrapping below is
only for the cases of TREE_CONSTANT argument or overflow. */
r = t;
if (!non_constant_p && overflow_p)
non_constant_p = true;
/* Unshare the result. */
bool should_unshare = true;
if (r == t || (TREE_CODE (t) == TARGET_EXPR && TARGET_EXPR_INITIAL (t) == r))
should_unshare = false;
if (non_constant_p && !allow_non_constant)
return error_mark_node;
else if (constexpr_dtor)
return r;
else if (non_constant_p && TREE_CONSTANT (r))
{
/* This isn't actually constant, so unset TREE_CONSTANT.
Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
it to be set if it is invariant address, even when it is not
a valid C++ constant expression. Wrap it with a NOP_EXPR
instead. */
if (EXPR_P (r) && TREE_CODE (r) != ADDR_EXPR)
r = copy_node (r);
else if (TREE_CODE (r) == CONSTRUCTOR)
r = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (r), r);
else
r = build_nop (TREE_TYPE (r), r);
TREE_CONSTANT (r) = false;
}
else if (non_constant_p)
return t;
if (should_unshare)
r = unshare_expr (r);
if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
{
r = adjust_temp_type (type, r);
if (TREE_CODE (t) == TARGET_EXPR && TARGET_EXPR_INITIAL (t) == r)
return t;
}
/* Remember the original location if that wouldn't need a wrapper. */
if (location_t loc = EXPR_LOCATION (t))
protected_set_expr_location (r, loc);
return r;
}
/* Like is_constant_expression, but allow const variables that are not allowed
under constexpr rules. */
bool
is_static_init_expression (tree t)
{
return potential_constant_expression_1 (t, false, false, true, tf_none);
}
/* Like potential_constant_expression, but don't consider possible constexpr
substitution of the current function. That is, PARM_DECL qualifies under
potential_constant_expression, but not here.
This is basically what you can check when any actual constant values might
be value-dependent. */
bool
is_constant_expression (tree t)
{
return potential_constant_expression_1 (t, false, true, true, tf_none);
}
/* Returns true if T is a potential static initializer expression that is not
instantiation-dependent. */
bool
is_nondependent_static_init_expression (tree t)
{
return (!type_unknown_p (t) && is_static_init_expression (t));
}
/* Like maybe_constant_value, but returns a CONSTRUCTOR directly, rather
than wrapped in a TARGET_EXPR.
ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated as
per P0595 even when ALLOW_NON_CONSTANT is true. */
static tree
maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,
bool manifestly_const_eval)
{
if (!t)
return t;
if (TREE_CODE (t) == EXPR_STMT)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == CONVERT_EXPR && VOID_TYPE_P (TREE_TYPE (t)))
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == INIT_EXPR)
t = TREE_OPERAND (t, 1);
if (TREE_CODE (t) == TARGET_EXPR)
t = TARGET_EXPR_INITIAL (t);
if (!is_nondependent_static_init_expression (t))
/* Don't try to evaluate it. */;
else if (CONSTANT_CLASS_P (t) && allow_non_constant)
/* No evaluation needed. */;
else
t = cxx_eval_outermost_constant_expr (t, allow_non_constant,
/*strict*/ false,
manifestly_const_eval, false, decl);
if (TREE_CODE (t) == TARGET_EXPR)
{
tree init = TARGET_EXPR_INITIAL (t);
if (TREE_CODE (init) == CONSTRUCTOR)
t = init;
}
return t;
}
/* Wrapper for maybe_constant_init_1 which permits non constants. */
tree
maybe_constant_init (tree t, tree decl, bool manifestly_const_eval)
{
return maybe_constant_init_1 (t, decl, true, manifestly_const_eval);
}
/* Returns true if T is a potential constant expression that is not
instantiation-dependent, and therefore a candidate for constant folding even
in a template. */
bool
is_nondependent_constant_expression (tree t)
{
return (!type_unknown_p (t) && is_constant_expression (t)
&& !instantiation_dependent_expression_p (t));
}
// forked from gcc/cp/parser.cc cp_unevaluated_operand
/* Nonzero if we are parsing an unevaluated operand: an operand to
sizeof, typeof, or alignof. */
int cp_unevaluated_operand;
// forked from gcc/cp/constexpr.cc cv_cache
/* If T is a constant expression, returns its reduced value.
Otherwise, if T does not have TREE_CONSTANT set, returns T.
Otherwise, returns a version of T without TREE_CONSTANT.
MANIFESTLY_CONST_EVAL is true if T is manifestly const-evaluated
as per P0595. */
static GTY ((deletable)) hash_map<tree, tree> *cv_cache;
// forked from gcc/cp/constexpr.cc maybe_constant_value
tree
maybe_constant_value (tree t, tree decl, bool manifestly_const_eval)
{
tree r;
if (!is_nondependent_constant_expression (t))
{
if (TREE_OVERFLOW_P (t))
{
t = build_nop (TREE_TYPE (t), t);
TREE_CONSTANT (t) = false;
}
return t;
}
else if (CONSTANT_CLASS_P (t))
/* No caching or evaluation needed. */
return t;
if (manifestly_const_eval)
return cxx_eval_outermost_constant_expr (t, true, true, true, false, decl);
if (cv_cache == NULL)
cv_cache = hash_map<tree, tree>::create_ggc (101);
if (tree *cached = cv_cache->get (t))
{
r = *cached;
if (r != t)
{
// Faisal: commenting this out as not sure if it's needed and it's
// huge r = break_out_target_exprs (r, /*clear_loc*/true);
protected_set_expr_location (r, EXPR_LOCATION (t));
}
return r;
}
/* Don't evaluate an unevaluated operand. */
if (cp_unevaluated_operand)
return t;
uid_sensitive_constexpr_evaluation_checker c;
r = cxx_eval_outermost_constant_expr (t, true, true, false, false, decl);
gcc_checking_assert (
r == t || CONVERT_EXPR_P (t) || TREE_CODE (t) == VIEW_CONVERT_EXPR
|| (TREE_CONSTANT (t) && !TREE_CONSTANT (r)) || !rs_tree_equal (r, t));
if (!c.evaluation_restricted_p ())
cv_cache->put (t, r);
return r;
}
// forked from gcc/cp/constexpr.cc
bool
potential_constant_expression (tree t)
{
return potential_constant_expression_1 (t, false, true, false, tf_none);
}
/* Data structure for passing data from potential_constant_expression_1
to check_for_return_continue via cp_walk_tree. */
struct check_for_return_continue_data
{
hash_set<tree> *pset;
tree continue_stmt;
tree break_stmt;
};
/* Helper function for potential_constant_expression_1 SWITCH_STMT handling,
called through cp_walk_tree. Return the first RETURN_EXPR found, or note
the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found. */
static tree
check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
{
tree t = *tp, s, b;
check_for_return_continue_data *d = (check_for_return_continue_data *) data;
switch (TREE_CODE (t))
{
case RETURN_EXPR:
return t;
case CONTINUE_STMT:
if (d->continue_stmt == NULL_TREE)
d->continue_stmt = t;
break;
case BREAK_STMT:
if (d->break_stmt == NULL_TREE)
d->break_stmt = t;
break;
#define RECUR(x) \
if (tree r = rs_walk_tree (&x, check_for_return_continue, data, d->pset)) \
return r
/* For loops, walk subtrees manually, so that continue stmts found
inside of the bodies of the loops are ignored. */
case WHILE_STMT:
*walk_subtrees = 0;
RECUR (WHILE_COND (t));
s = d->continue_stmt;
b = d->break_stmt;
RECUR (WHILE_BODY (t));
d->continue_stmt = s;
d->break_stmt = b;
break;
case FOR_STMT:
*walk_subtrees = 0;
RECUR (FOR_INIT_STMT (t));
RECUR (FOR_COND (t));
RECUR (FOR_EXPR (t));
s = d->continue_stmt;
b = d->break_stmt;
RECUR (FOR_BODY (t));
d->continue_stmt = s;
d->break_stmt = b;
break;
case RANGE_FOR_STMT:
*walk_subtrees = 0;
RECUR (RANGE_FOR_EXPR (t));
s = d->continue_stmt;
b = d->break_stmt;
RECUR (RANGE_FOR_BODY (t));
d->continue_stmt = s;
d->break_stmt = b;
break;
case SWITCH_STMT:
*walk_subtrees = 0;
RECUR (SWITCH_STMT_COND (t));
b = d->break_stmt;
RECUR (SWITCH_STMT_BODY (t));
d->break_stmt = b;
break;
#undef RECUR
case STATEMENT_LIST:
case CONSTRUCTOR:
break;
default:
if (!EXPR_P (t))
*walk_subtrees = 0;
break;
}
return NULL_TREE;
}
/* Returns the namespace that contains DECL, whether directly or
indirectly. */
tree
decl_namespace_context (tree decl)
{
while (1)
{
if (TREE_CODE (decl) == NAMESPACE_DECL)
return decl;
else if (TYPE_P (decl))
decl = CP_DECL_CONTEXT (TYPE_MAIN_DECL (decl));
else
decl = CP_DECL_CONTEXT (decl);
}
}
/* Returns true if DECL is in the std namespace. */
bool
decl_in_std_namespace_p (tree decl)
{
while (decl)
{
decl = decl_namespace_context (decl);
if (DECL_NAMESPACE_STD_P (decl))
return true;
/* Allow inline namespaces inside of std namespace, e.g. with
--enable-symvers=gnu-versioned-namespace std::forward would be
actually std::_8::forward. */
if (!DECL_NAMESPACE_INLINE_P (decl))
return false;
decl = CP_DECL_CONTEXT (decl);
}
return false;
}
/* Return true if FNDECL is std::construct_at. */
static inline bool
is_std_construct_at (tree fndecl)
{
if (!decl_in_std_namespace_p (fndecl))
return false;
tree name = DECL_NAME (fndecl);
return name && id_equal (name, "construct_at");
}
/* Return true if FNDECL is __dynamic_cast. */
static inline bool
cxx_dynamic_cast_fn_p (tree fndecl)
{
return (id_equal (DECL_NAME (fndecl), "__dynamic_cast")
&& CP_DECL_CONTEXT (fndecl) == global_namespace);
}
/* Return true if FNDECL is std::allocator<T>::{,de}allocate. */
static inline bool
is_std_allocator_allocate (tree fndecl)
{
tree name = DECL_NAME (fndecl);
if (name == NULL_TREE
|| !(id_equal (name, "allocate") || id_equal (name, "deallocate")))
return false;
tree ctx = DECL_CONTEXT (fndecl);
if (ctx == NULL_TREE || !CLASS_TYPE_P (ctx) || !TYPE_MAIN_DECL (ctx))
return false;
tree decl = TYPE_MAIN_DECL (ctx);
name = DECL_NAME (decl);
if (name == NULL_TREE || !id_equal (name, "allocator"))
return false;
return decl_in_std_namespace_p (decl);
}
/* Overload for the above taking constexpr_call*. */
static inline bool
is_std_allocator_allocate (const constexpr_call *call)
{
return (call && call->fundef
&& is_std_allocator_allocate (call->fundef->decl));
}
/* Return true if T denotes a potentially constant expression. Issue
diagnostic as appropriate under control of FLAGS. If WANT_RVAL is true,
an lvalue-rvalue conversion is implied. If NOW is true, we want to
consider the expression in the current context, independent of constexpr
substitution.
C++0x [expr.const] used to say
6 An expression is a potential constant expression if it is
a constant expression where all occurrences of function
parameters are replaced by arbitrary constant expressions
of the appropriate type.
2 A conditional expression is a constant expression unless it
involves one of the following as a potentially evaluated
subexpression (3.2), but subexpressions of logical AND (5.14),
logical OR (5.15), and conditional (5.16) operations that are
not evaluated are not considered. */
static bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
tsubst_flags_t flags, tree *jump_target)
{
#define RECUR(T, RV) \
potential_constant_expression_1 ((T), (RV), strict, now, flags, jump_target)
enum
{
any = false,
rval = true
};
int i;
tree tmp;
if (t == error_mark_node)
return false;
if (t == NULL_TREE)
return true;
location_t loc = rs_expr_loc_or_input_loc (t);
if (*jump_target)
/* If we are jumping, ignore everything. This is simpler than the
cxx_eval_constant_expression handling because we only need to be
conservatively correct, and we don't necessarily have a constant value
available, so we don't bother with switch tracking. */
return true;
if (TREE_THIS_VOLATILE (t) && want_rval)
{
if (flags & tf_error)
error_at (loc,
"lvalue-to-rvalue conversion of a volatile lvalue "
"%qE with type %qT",
t, TREE_TYPE (t));
return false;
}
if (CONSTANT_CLASS_P (t))
return true;
if (CODE_CONTAINS_STRUCT (TREE_CODE (t), TS_TYPED)
&& TREE_TYPE (t) == error_mark_node)
return false;
switch (TREE_CODE (t))
{
case FUNCTION_DECL:
case OVERLOAD:
case LABEL_DECL:
case CASE_LABEL_EXPR:
case PREDICT_EXPR:
case CONST_DECL:
case IDENTIFIER_NODE:
/* We can see a FIELD_DECL in a pointer-to-member expression. */
case FIELD_DECL:
case RESULT_DECL:
case PLACEHOLDER_EXPR:
case STATIC_ASSERT:
return true;
case RETURN_EXPR:
if (!RECUR (TREE_OPERAND (t, 0), any))
return false;
/* FALLTHROUGH */
case BREAK_STMT:
case CONTINUE_STMT:
*jump_target = t;
return true;
case PARM_DECL:
if (now && want_rval)
{
tree type = TREE_TYPE (t);
if (is_really_empty_class (type, /*ignore_vptr*/ false))
/* An empty class has no data to read. */
return true;
if (flags & tf_error)
error ("%qE is not a constant expression", t);
return false;
}
return true;
case CALL_EXPR:
/* -- an invocation of a function other than a constexpr function
or a constexpr constructor. */
{
tree fun = get_function_named_in_call (t);
const int nargs = call_expr_nargs (t);
i = 0;
if (fun == NULL_TREE)
{
/* Reset to allow the function to continue past the end
of the block below. Otherwise return early. */
bool bail = true;
if (TREE_CODE (t) == CALL_EXPR && CALL_EXPR_FN (t) == NULL_TREE)
switch (CALL_EXPR_IFN (t))
{
/* These should be ignored, they are optimized away from
constexpr functions. */
case IFN_UBSAN_NULL:
case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
case IFN_FALLTHROUGH:
return true;
case IFN_ADD_OVERFLOW:
case IFN_SUB_OVERFLOW:
case IFN_MUL_OVERFLOW:
case IFN_LAUNDER:
case IFN_VEC_CONVERT:
bail = false;
break;
default:
break;
}
if (bail)
{
/* fold_call_expr can't do anything with IFN calls. */
if (flags & tf_error)
error_at (loc, "call to internal function %qE", t);
return false;
}
}
if (fun && is_overloaded_fn (fun))
{
if (TREE_CODE (fun) == FUNCTION_DECL)
{
if (builtin_valid_in_constant_expr_p (fun))
return true;
if (!maybe_constexpr_fn (fun)
/* Allow any built-in function; if the expansion
isn't constant, we'll deal with that then. */
&& !fndecl_built_in_p (fun)
/* In C++20, replaceable global allocation functions
are constant expressions. */
&& (/* !cxx_replaceable_global_alloc_fn (fun)
||*/ TREE_CODE (t) != CALL_EXPR
|| (!CALL_FROM_NEW_OR_DELETE_P (t)
&& (current_function_decl == NULL_TREE
/*|| !is_std_allocator_allocate(current_function_decl)*/)))
/* Allow placement new in std::construct_at. */
&& (/*!cxx_placement_new_fn (fun)
||*/ TREE_CODE (t) != CALL_EXPR
|| current_function_decl == NULL_TREE
/*|| !is_std_construct_at (current_function_decl)*/)
/* && !cxx_dynamic_cast_fn_p (fun)*/)
{
if (flags & tf_error)
{
error_at (loc, "call to non-%<constexpr%> function %qD",
fun);
explain_invalid_constexpr_fn (fun);
}
return false;
}
/* A call to a non-static member function takes the address
of the object as the first argument. But in a constant
expression the address will be folded away, so look
through it now. */
if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun)
&& !DECL_CONSTRUCTOR_P (fun))
{
tree x = CALL_EXPR_ARG (t, 0);
/* Don't require an immediately constant value, as
constexpr substitution might not use the value. */
bool sub_now = false;
if (!potential_constant_expression_1 (x, rval, strict,
sub_now, flags,
jump_target))
return false;
i = 1;
}
}
else
{
if (!RECUR (fun, true))
return false;
fun = get_first_fn (fun);
}
fun = DECL_ORIGIN (fun);
}
else if (fun)
{
if (RECUR (fun, rval))
/* Might end up being a constant function pointer. */;
else
return false;
}
for (; i < nargs; ++i)
{
tree x = CALL_EXPR_ARG (t, i);
/* In a template, reference arguments haven't been converted to
REFERENCE_TYPE and we might not even know if the parameter
is a reference, so accept lvalue constants too. */
bool rv = rval;
/* Don't require an immediately constant value, as constexpr
substitution might not use the value of the argument. */
bool sub_now = false;
if (!potential_constant_expression_1 (x, rv, strict, sub_now, flags,
jump_target))
return false;
}
return true;
}
case NON_LVALUE_EXPR:
/* -- an lvalue-to-rvalue conversion (4.1) unless it is applied to
-- an lvalue of integral type that refers to a non-volatile
const variable or static data member initialized with
constant expressions, or
-- an lvalue of literal type that refers to non-volatile
object defined with constexpr, or that refers to a
sub-object of such an object; */
return RECUR (TREE_OPERAND (t, 0), rval);
case VAR_DECL:
if (DECL_HAS_VALUE_EXPR_P (t))
{
return RECUR (DECL_VALUE_EXPR (t), rval);
}
if (want_rval && !var_in_maybe_constexpr_fn (t)
&& !decl_maybe_constant_var_p (t)
&& (strict || !RS_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (t))
|| (DECL_INITIAL (t)
&& !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (t)))
&& COMPLETE_TYPE_P (TREE_TYPE (t))
&& !is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/ false))
{
if (flags & tf_error)
non_const_var_error (loc, t);
return false;
}
return true;
/* FALLTHRU */
case NOP_EXPR:
case CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
/* -- a reinterpret_cast. FIXME not implemented, and this rule
may change to something more specific to type-punning (DR 1312). */
{
tree from = TREE_OPERAND (t, 0);
if (location_wrapper_p (t))
return (RECUR (from, want_rval));
if (INDIRECT_TYPE_P (TREE_TYPE (t)))
{
STRIP_ANY_LOCATION_WRAPPER (from);
if (TREE_CODE (from) == INTEGER_CST && !integer_zerop (from))
{
if (flags & tf_error)
error_at (loc,
"%<reinterpret_cast%> from integer to pointer");
return false;
}
}
return (RECUR (from, TREE_CODE (t) != VIEW_CONVERT_EXPR));
}
case ADDR_EXPR:
/* -- a unary operator & that is applied to an lvalue that
designates an object with thread or automatic storage
duration; */
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == OFFSET_REF && PTRMEM_OK_P (t))
/* A pointer-to-member constant. */
return true;
// handle_addr_expr:
#if 0
/* FIXME adjust when issue 1197 is fully resolved. For now don't do
any checking here, as we might dereference the pointer later. If
we remove this code, also remove check_automatic_or_tls. */
i = check_automatic_or_tls (t);
if (i == ck_ok)
return true;
if (i == ck_bad)
{
if (flags & tf_error)
error ("address-of an object %qE with thread local or "
"automatic storage is not a constant expression", t);
return false;
}
#endif
return RECUR (t, any);
case COMPONENT_REF:
/* -- a class member access unless its postfix-expression is
of literal type or of pointer to literal type. */
/* This test would be redundant, as it follows from the
postfix-expression being a potential constant expression. */
if (type_unknown_p (t))
return true;
if (is_overloaded_fn (t))
/* In a template, a COMPONENT_REF of a function expresses ob.fn(),
which uses ob as an lvalue. */
want_rval = false;
gcc_fallthrough ();
case REALPART_EXPR:
case IMAGPART_EXPR:
case BIT_FIELD_REF:
return RECUR (TREE_OPERAND (t, 0), want_rval);
case INDIRECT_REF: {
tree x = TREE_OPERAND (t, 0);
STRIP_NOPS (x);
return RECUR (x, rval);
}
case STATEMENT_LIST:
for (tree stmt : tsi_range (t))
if (!RECUR (stmt, any))
return false;
return true;
case MODIFY_EXPR:
if (!RECUR (TREE_OPERAND (t, 0), any))
return false;
/* Just ignore clobbers. */
if (TREE_CLOBBER_P (TREE_OPERAND (t, 1)))
return true;
if (!RECUR (TREE_OPERAND (t, 1), rval))
return false;
return true;
case FOR_STMT:
if (!RECUR (FOR_INIT_STMT (t), any))
return false;
tmp = FOR_COND (t);
if (!RECUR (tmp, rval))
return false;
if (tmp)
{
tmp = cxx_eval_outermost_constant_expr (tmp, true);
/* If we couldn't evaluate the condition, it might not ever be
true. */
if (!integer_onep (tmp))
{
/* Before returning true, check if the for body can contain
a return. */
hash_set<tree> pset;
check_for_return_continue_data data
= {&pset, NULL_TREE, NULL_TREE};
if (tree ret_expr
= rs_walk_tree (&FOR_BODY (t), check_for_return_continue,
&data, &pset))
*jump_target = ret_expr;
return true;
}
}
if (!RECUR (FOR_EXPR (t), any))
return false;
if (!RECUR (FOR_BODY (t), any))
return false;
if (breaks (jump_target) || continues (jump_target))
*jump_target = NULL_TREE;
return true;
case WHILE_STMT:
tmp = WHILE_COND (t);
if (!RECUR (tmp, rval))
return false;
tmp = cxx_eval_outermost_constant_expr (tmp, true);
/* If we couldn't evaluate the condition, it might not ever be true. */
if (!integer_onep (tmp))
{
/* Before returning true, check if the while body can contain
a return. */
hash_set<tree> pset;
check_for_return_continue_data data = {&pset, NULL_TREE, NULL_TREE};
if (tree ret_expr
= rs_walk_tree (&WHILE_BODY (t), check_for_return_continue, &data,
&pset))
*jump_target = ret_expr;
return true;
}
if (!RECUR (WHILE_BODY (t), any))
return false;
if (breaks (jump_target) || continues (jump_target))
*jump_target = NULL_TREE;
return true;
case SWITCH_STMT:
if (!RECUR (SWITCH_STMT_COND (t), rval))
return false;
/* FIXME we don't check SWITCH_STMT_BODY currently, because even
unreachable labels would be checked and it is enough if there is
a single switch cond value for which it is a valid constant
expression. We need to check if there are any RETURN_EXPRs
or CONTINUE_STMTs inside of the body though, as in that case
we need to set *jump_target. */
else
{
hash_set<tree> pset;
check_for_return_continue_data data = {&pset, NULL_TREE, NULL_TREE};
if (tree ret_expr
= rs_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue,
&data, &pset))
/* The switch might return. */
*jump_target = ret_expr;
else if (data.continue_stmt)
/* The switch can't return, but might continue. */
*jump_target = data.continue_stmt;
}
return true;
case DYNAMIC_CAST_EXPR:
case PSEUDO_DTOR_EXPR:
case NEW_EXPR:
case VEC_NEW_EXPR:
case DELETE_EXPR:
case VEC_DELETE_EXPR:
case THROW_EXPR:
case OMP_PARALLEL:
case OMP_TASK:
case OMP_FOR:
case OMP_SIMD:
case OMP_DISTRIBUTE:
case OMP_TASKLOOP:
case OMP_LOOP:
case OMP_TEAMS:
case OMP_TARGET_DATA:
case OMP_TARGET:
case OMP_SECTIONS:
case OMP_ORDERED:
case OMP_CRITICAL:
case OMP_SINGLE:
case OMP_SECTION:
case OMP_MASTER:
case OMP_MASKED:
case OMP_TASKGROUP:
case OMP_TARGET_UPDATE:
case OMP_TARGET_ENTER_DATA:
case OMP_TARGET_EXIT_DATA:
case OMP_ATOMIC:
case OMP_ATOMIC_READ:
case OMP_ATOMIC_CAPTURE_OLD:
case OMP_ATOMIC_CAPTURE_NEW:
case OMP_DEPOBJ:
case OACC_PARALLEL:
case OACC_KERNELS:
case OACC_SERIAL:
case OACC_DATA:
case OACC_HOST_DATA:
case OACC_LOOP:
case OACC_CACHE:
case OACC_DECLARE:
case OACC_ENTER_DATA:
case OACC_EXIT_DATA:
case OACC_UPDATE:
/* GCC internal stuff. */
case VA_ARG_EXPR:
case TRANSACTION_EXPR:
case AT_ENCODE_EXPR:
if (flags & tf_error)
error_at (loc, "expression %qE is not a constant expression", t);
return false;
case ASM_EXPR:
if (flags & tf_error)
inline_asm_in_constexpr_error (loc);
return false;
case OBJ_TYPE_REF:
return true;
case POINTER_DIFF_EXPR:
case MINUS_EXPR:
want_rval = true;
goto binary;
case LT_EXPR:
case LE_EXPR:
case GT_EXPR:
case GE_EXPR:
case EQ_EXPR:
case NE_EXPR:
case SPACESHIP_EXPR:
want_rval = true;
goto binary;
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTDECREMENT_EXPR:
goto unary;
case BIT_NOT_EXPR:
/* A destructor. */
if (TYPE_P (TREE_OPERAND (t, 0)))
return true;
/* fall through. */
case CONJ_EXPR:
case SAVE_EXPR:
case FIX_TRUNC_EXPR:
case FLOAT_EXPR:
case NEGATE_EXPR:
case ABS_EXPR:
case ABSU_EXPR:
case TRUTH_NOT_EXPR:
case FIXED_CONVERT_EXPR:
case UNARY_PLUS_EXPR:
case UNARY_LEFT_FOLD_EXPR:
case UNARY_RIGHT_FOLD_EXPR:
unary:
return RECUR (TREE_OPERAND (t, 0), rval);
case BIND_EXPR:
return RECUR (BIND_EXPR_BODY (t), want_rval);
case CLEANUP_POINT_EXPR:
case EXPR_STMT:
case PAREN_EXPR:
case NON_DEPENDENT_EXPR:
/* For convenience. */
case LOOP_EXPR:
case EXIT_EXPR:
return RECUR (TREE_OPERAND (t, 0), want_rval);
case DECL_EXPR:
tmp = DECL_EXPR_DECL (t);
if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp))
{
if (RS_DECL_THREAD_LOCAL_P (tmp))
{
if (flags & tf_error)
error_at (DECL_SOURCE_LOCATION (tmp),
"%qD declared "
"%<thread_local%> in %<constexpr%> context",
tmp);
return false;
}
else if (TREE_STATIC (tmp))
{
if (flags & tf_error)
error_at (DECL_SOURCE_LOCATION (tmp),
"%qD declared "
"%<static%> in %<constexpr%> context",
tmp);
return false;
}
else if (!check_for_uninitialized_const_var (
tmp, /*constexpr_context_p=*/true, flags))
return false;
}
return RECUR (tmp, want_rval);
case TRY_FINALLY_EXPR:
return (RECUR (TREE_OPERAND (t, 0), want_rval)
&& RECUR (TREE_OPERAND (t, 1), any));
case SCOPE_REF:
return RECUR (TREE_OPERAND (t, 1), want_rval);
case TARGET_EXPR:
if (!TARGET_EXPR_DIRECT_INIT_P (t) && !literal_type_p (TREE_TYPE (t)))
{
if (flags & tf_error)
{
auto_diagnostic_group d;
error_at (loc,
"temporary of non-literal type %qT in a "
"constant expression",
TREE_TYPE (t));
explain_non_literal_class (TREE_TYPE (t));
}
return false;
}
/* FALLTHRU */
case INIT_EXPR:
return RECUR (TREE_OPERAND (t, 1), rval);
case CONSTRUCTOR: {
vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t);
constructor_elt *ce;
for (i = 0; vec_safe_iterate (v, i, &ce); ++i)
if (!RECUR (ce->value, want_rval))
return false;
return true;
}
case TREE_LIST: {
gcc_assert (TREE_PURPOSE (t) == NULL_TREE || DECL_P (TREE_PURPOSE (t)));
if (!RECUR (TREE_VALUE (t), want_rval))
return false;
if (TREE_CHAIN (t) == NULL_TREE)
return true;
return RECUR (TREE_CHAIN (t), want_rval);
}
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: {
tree denom = TREE_OPERAND (t, 1);
if (!RECUR (denom, rval))
return false;
/* We can't call cxx_eval_outermost_constant_expr on an expression
that hasn't been through instantiate_non_dependent_expr yet. */
denom = cxx_eval_outermost_constant_expr (denom, true);
if (integer_zerop (denom))
{
if (flags & tf_error)
error ("division by zero is not a constant expression");
return false;
}
else
{
want_rval = true;
return RECUR (TREE_OPERAND (t, 0), want_rval);
}
}
case COMPOUND_EXPR: {
/* check_return_expr sometimes wraps a TARGET_EXPR in a
COMPOUND_EXPR; don't get confused. */
tree op0 = TREE_OPERAND (t, 0);
tree op1 = TREE_OPERAND (t, 1);
STRIP_NOPS (op1);
if (TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0))
return RECUR (op0, want_rval);
else
goto binary;
}
/* If the first operand is the non-short-circuit constant, look at
the second operand; otherwise we only care about the first one for
potentiality. */
case TRUTH_AND_EXPR:
case TRUTH_ANDIF_EXPR:
tmp = boolean_true_node;
goto truth;
case TRUTH_OR_EXPR:
case TRUTH_ORIF_EXPR:
tmp = boolean_false_node;
truth : {
tree op0 = TREE_OPERAND (t, 0);
tree op1 = TREE_OPERAND (t, 1);
if (!RECUR (op0, rval))
return false;
if (!(flags & tf_error) && RECUR (op1, rval))
/* When quiet, try to avoid expensive trial evaluation by first
checking potentiality of the second operand. */
return true;
op0 = cxx_eval_outermost_constant_expr (op0, true);
if (tree_int_cst_equal (op0, tmp))
return (flags & tf_error) ? RECUR (op1, rval) : false;
else
return true;
}
case PLUS_EXPR:
case MULT_EXPR:
case POINTER_PLUS_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 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:
want_rval = true;
/* Fall through. */
case ARRAY_REF:
case ARRAY_RANGE_REF:
case MEMBER_REF:
case DOTSTAR_EXPR:
case MEM_REF:
case BINARY_LEFT_FOLD_EXPR:
case BINARY_RIGHT_FOLD_EXPR:
binary:
for (i = 0; i < 2; ++i)
if (!RECUR (TREE_OPERAND (t, i), want_rval))
return false;
return true;
case VEC_PERM_EXPR:
for (i = 0; i < 3; ++i)
if (!RECUR (TREE_OPERAND (t, i), true))
return false;
return true;
case COND_EXPR:
if (COND_EXPR_IS_VEC_DELETE (t))
{
if (flags & tf_error)
error_at (loc, "%<delete[]%> is not a constant expression");
return false;
}
/* Fall through. */
case IF_STMT:
case VEC_COND_EXPR:
/* If the condition is a known constant, we know which of the legs we
care about; otherwise we only require that the condition and
either of the legs be potentially constant. */
tmp = TREE_OPERAND (t, 0);
if (!RECUR (tmp, rval))
return false;
tmp = cxx_eval_outermost_constant_expr (tmp, true);
/* potential_constant_expression* isn't told if it is called for
manifestly_const_eval or not, so for consteval if always
process both branches as if the condition is not a known
constant. */
if (TREE_CODE (t) != IF_STMT || !IF_STMT_CONSTEVAL_P (t))
{
if (integer_zerop (tmp))
return RECUR (TREE_OPERAND (t, 2), want_rval);
else if (TREE_CODE (tmp) == INTEGER_CST)
return RECUR (TREE_OPERAND (t, 1), want_rval);
}
tmp = *jump_target;
for (i = 1; i < 3; ++i)
{
tree this_jump_target = tmp;
if (potential_constant_expression_1 (TREE_OPERAND (t, i), want_rval,
strict, now, tf_none,
&this_jump_target))
{
if (returns (&this_jump_target))
*jump_target = this_jump_target;
else if (!returns (jump_target))
{
if (breaks (&this_jump_target)
|| continues (&this_jump_target))
*jump_target = this_jump_target;
if (i == 1)
{
/* If the then branch is potentially constant, but
does not return, check if the else branch
couldn't return, break or continue. */
hash_set<tree> pset;
check_for_return_continue_data data
= {&pset, NULL_TREE, NULL_TREE};
if (tree ret_expr
= rs_walk_tree (&TREE_OPERAND (t, 2),
check_for_return_continue, &data,
&pset))
*jump_target = ret_expr;
else if (*jump_target == NULL_TREE)
{
if (data.continue_stmt)
*jump_target = data.continue_stmt;
else if (data.break_stmt)
*jump_target = data.break_stmt;
}
}
}
return true;
}
}
if (flags & tf_error)
error_at (loc, "expression %qE is not a constant expression", t);
return false;
case TYPE_DECL:
/* We can see these in statement-expressions. */
return true;
case LABEL_EXPR:
t = LABEL_EXPR_LABEL (t);
if (DECL_ARTIFICIAL (t))
return true;
else if (flags & tf_error)
error_at (loc, "label definition in %<constexpr%> function only "
"available with %<-std=c++2b%> or %<-std=gnu++2b%>");
return false;
case ANNOTATE_EXPR:
return RECUR (TREE_OPERAND (t, 0), rval);
case BIT_CAST_EXPR:
return RECUR (TREE_OPERAND (t, 0), rval);
default:
sorry ("unexpected AST of kind %s", get_tree_code_name (TREE_CODE (t)));
gcc_unreachable ();
return false;
}
#undef RECUR
}
bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
tsubst_flags_t flags)
{
if (flags & tf_error)
{
/* Check potentiality quietly first, as that could be performed more
efficiently in some cases (currently only for TRUTH_*_EXPR). If
that fails, replay the check noisily to give errors. */
flags &= ~tf_error;
if (potential_constant_expression_1 (t, want_rval, strict, now, flags))
return true;
flags |= tf_error;
}
tree target = NULL_TREE;
return potential_constant_expression_1 (t, want_rval, strict, now, flags,
&target);
}
// forked from gcc/cp/constexpr.cc fold_non_dependent_init
/* Like maybe_constant_init but first fully instantiate the argument. */
tree
fold_non_dependent_init (tree t, tsubst_flags_t /*=tf_warning_or_error*/,
bool manifestly_const_eval /*=false*/,
tree object /* = NULL_TREE */)
{
if (t == NULL_TREE)
return NULL_TREE;
return maybe_constant_init (t, object, manifestly_const_eval);
}
// #include "gt-rust-rust-constexpr.h"
} // namespace Compile
} // namespace Rust