public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] rust-constexpr.cc: port over potential_constant_expression_1()
@ 2022-08-29 15:35 Thomas Schwinge
0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-08-29 15:35 UTC (permalink / raw)
To: gcc-cvs
https://gcc.gnu.org/g:debe4aedc76a1f52c3072254b6be4da4f4c4696c
commit debe4aedc76a1f52c3072254b6be4da4f4c4696c
Author: Faisal Abbas <90.abbasfaisal@gmail.com>
Date: Sat Aug 13 19:20:31 2022 +0100
rust-constexpr.cc: port over potential_constant_expression_1()
Diff:
---
gcc/rust/backend/rust-constexpr.cc | 1342 +++++++++++++++++++++++++++++++++++-
gcc/rust/backend/rust-tree.cc | 115 ++-
gcc/rust/backend/rust-tree.h | 205 +++++-
3 files changed, 1646 insertions(+), 16 deletions(-)
diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc
index 190abc96070..5ac12d2ec6a 100644
--- a/gcc/rust/backend/rust-constexpr.cc
+++ b/gcc/rust/backend/rust-constexpr.cc
@@ -33,6 +33,7 @@
#include "rust-target.h"
#include "function.h"
#include "builtins.h"
+#include "diagnostic.h"
#define VERIFY_CONSTANT(X) \
do \
@@ -75,13 +76,17 @@ 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);
inline tree
get_nth_callarg (tree t, int n);
tree
unshare_constructor (tree t MEM_STAT_DECL);
void
-explain_invalid_constexpr_fn (tree fun);
-void
maybe_save_constexpr_fundef (tree fun);
struct constexpr_global_ctx
@@ -334,10 +339,6 @@ uid_sensitive_constexpr_evaluation_checker::evaluation_restricted_p () const
static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
-static tree
-constexp_expression (const constexpr_ctx *, tree, bool, bool *, bool *,
- tree * = NULL);
-
/* Compute a hash value for a constexpr call representation. */
inline hashval_t
@@ -1586,6 +1587,10 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval,
return eval_constant_expression (ctx, r, lval, non_constant_p,
overflow_p);
}
+ else
+ r = DECL_VALUE_EXPR (t);
+ return eval_constant_expression (ctx, r, lval, non_constant_p,
+ overflow_p);
case PARM_DECL:
if (lval && !TYPE_REF_P (TREE_TYPE (t)))
@@ -1680,7 +1685,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval,
error ("temporary of non-literal type %qT in a "
"constant expression",
type);
- // explain_non_literal_class (type);
+ explain_non_literal_class (type);
}
*non_constant_p = true;
break;
@@ -1815,7 +1820,7 @@ eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval,
*non_constant_p = true;
return t;
}
-
+ /* FALLTHROUGH. */
case MODIFY_EXPR:
r = eval_store_expression (ctx, t, false, non_constant_p, overflow_p);
break;
@@ -3267,6 +3272,281 @@ eval_call_expression (const constexpr_ctx *ctx, tree t, bool lval,
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, init;
+ 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. */
+ 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)
+ (*vec)->last ().value = init;
+ else
+ 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
@@ -4655,8 +4935,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
bool
is_static_init_expression (tree t)
{
- // return potential_constant_expression_1 (t, false, false, true, tf_none);
- // faisal: just return false for now to make it compile
+ return potential_constant_expression_1 (t, false, false, true, tf_none);
}
/* Like potential_constant_expression, but don't consider possible constexpr
@@ -4669,8 +4948,7 @@ is_static_init_expression (tree t)
bool
is_constant_expression (tree t)
{
- // return potential_constant_expression_1 (t, false, true, true, tf_none);
- // faisal: just return false for now to make it compile
+ return potential_constant_expression_1 (t, false, true, true, tf_none);
}
/* Returns true if T is a potential static initializer expression that is not
@@ -4810,11 +5088,1047 @@ maybe_constant_value (tree t, tree decl, bool manifestly_const_eval)
bool
potential_constant_expression (tree t)
{
- // return potential_constant_expression_1 (t, false, true, false, tf_none);
- // Faisal: return false until we port above call to make the code compile
+ 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 = get_nth_callarg (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 = get_nth_callarg (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;
+
+ case NOP_EXPR:
+ if (REINTERPRET_CAST_P (t))
+ {
+ if (flags & tf_error)
+ error_at (loc, "%<reinterpret_cast%> is not a constant expression");
+ return false;
+ }
+ /* FALLTHRU */
+ 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);
+}
+
// #include "gt-rust-rust-constexpr.h"
} // namespace Compile
diff --git a/gcc/rust/backend/rust-tree.cc b/gcc/rust/backend/rust-tree.cc
index beb4711493d..fc32c7c1c70 100644
--- a/gcc/rust/backend/rust-tree.cc
+++ b/gcc/rust/backend/rust-tree.cc
@@ -1678,6 +1678,8 @@ fields_linear_search (tree klass, tree name, bool want_type)
if (!want_type || DECL_DECLARES_TYPE_P (decl))
return decl;
}
+
+ return NULL_TREE;
}
// forked from gcc/cp/except.cc canonnothrow_spec_pical_eh_spec
@@ -4587,7 +4589,6 @@ lvalue_kind (const_tree ref)
return clk_none;
default:
- default_:
if (!TREE_TYPE (ref))
return clk_none;
if (CLASS_TYPE_P (TREE_TYPE (ref))
@@ -6033,4 +6034,116 @@ cp_fold_rvalue (tree x)
return cp_fold_maybe_rvalue (x, true);
}
+/* Returns true iff class T has a constexpr destructor or has an
+ implicitly declared destructor that we can't tell if it's constexpr
+ without forcing a lazy declaration (which might cause undesired
+ instantiations). */
+
+static bool
+type_maybe_constexpr_destructor (tree t)
+{
+ /* Until C++20, only trivial destruction is constexpr. */
+ if (TYPE_HAS_TRIVIAL_DESTRUCTOR (t))
+ return true;
+
+ if (CLASS_TYPE_P (t) && CLASSTYPE_LAZY_DESTRUCTOR (t))
+ /* Assume it's constexpr. */
+ return true;
+ tree fn = CLASSTYPE_DESTRUCTOR (t);
+ return (fn && Compile::maybe_constexpr_fn (fn));
+}
+
+/* T is a non-literal type used in a context which requires a constant
+ expression. Explain why it isn't literal. */
+
+void
+explain_non_literal_class (tree t)
+{
+ static hash_set<tree> *diagnosed;
+
+ if (!CLASS_TYPE_P (t))
+ return;
+ t = TYPE_MAIN_VARIANT (t);
+
+ if (diagnosed == NULL)
+ diagnosed = new hash_set<tree>;
+ if (diagnosed->add (t))
+ /* Already explained. */
+ return;
+
+ auto_diagnostic_group d;
+ inform (UNKNOWN_LOCATION, "%q+T is not literal because:", t);
+ if (LAMBDA_TYPE_P (t))
+ inform (UNKNOWN_LOCATION,
+ " %qT is a closure type, which is only literal in "
+ "C++17 and later",
+ t);
+ else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
+ && !type_maybe_constexpr_destructor (t))
+ inform (UNKNOWN_LOCATION, " %q+T does not have %<constexpr%> destructor",
+ t);
+ else if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t))
+ inform (UNKNOWN_LOCATION, " %q+T has a non-trivial destructor", t);
+ else if (CLASSTYPE_NON_AGGREGATE (t) && !TYPE_HAS_TRIVIAL_DFLT (t)
+ && !LAMBDA_TYPE_P (t) && !TYPE_HAS_CONSTEXPR_CTOR (t))
+ {
+ inform (UNKNOWN_LOCATION,
+ " %q+T is not an aggregate, does not have a trivial "
+ "default constructor, and has no %<constexpr%> constructor that "
+ "is not a copy or move constructor",
+ t);
+ if (type_has_non_user_provided_default_constructor (t))
+ /* Note that we can't simply call locate_ctor because when the
+ constructor is deleted it just returns NULL_TREE. */
+ for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter)
+ {
+ tree fn = *iter;
+ tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
+
+ parms = skip_artificial_parms_for (fn, parms);
+
+ if (sufficient_parms_p (parms))
+ {
+ Compile::explain_invalid_constexpr_fn (fn);
+ break;
+ }
+ }
+ }
+ else
+ {
+ tree binfo, base_binfo, field;
+ int i;
+ for (binfo = TYPE_BINFO (t), i = 0;
+ BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+ {
+ tree basetype = TREE_TYPE (base_binfo);
+ if (!CLASSTYPE_LITERAL_P (basetype))
+ {
+ inform (UNKNOWN_LOCATION,
+ " base class %qT of %q+T is non-literal", basetype, t);
+ explain_non_literal_class (basetype);
+ return;
+ }
+ }
+ for (field = TYPE_FIELDS (t); field; field = TREE_CHAIN (field))
+ {
+ tree ftype;
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+ ftype = TREE_TYPE (field);
+ if (!Compile::literal_type_p (ftype))
+ {
+ inform (DECL_SOURCE_LOCATION (field),
+ " non-static data member %qD has non-literal type",
+ field);
+ if (CLASS_TYPE_P (ftype))
+ explain_non_literal_class (ftype);
+ }
+ if (RS_TYPE_VOLATILE_P (ftype))
+ inform (DECL_SOURCE_LOCATION (field),
+ " non-static data member %qD has volatile type", field);
+ }
+ }
+}
+
} // namespace Rust
diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h
index 524569a985e..2466efc3309 100644
--- a/gcc/rust/backend/rust-tree.h
+++ b/gcc/rust/backend/rust-tree.h
@@ -1347,7 +1347,7 @@ extern GTY (()) tree cp_global_trees[CPTI_MAX];
#define ENUM_UNDERLYING_TYPE(TYPE) TREE_TYPE (ENUMERAL_TYPE_CHECK (TYPE))
/* Nonzero if this type is volatile-qualified. */
-#define CP_TYPE_VOLATILE_P(NODE) \
+#define RS_TYPE_VOLATILE_P(NODE) \
((rs_type_quals (NODE) & TYPE_QUAL_VOLATILE) != 0)
/* Nonzero means that this type is either complete or being defined, so we
@@ -1366,6 +1366,169 @@ extern GTY (()) tree cp_global_trees[CPTI_MAX];
(DECL_NAME (NODE) != NULL_TREE && MAIN_NAME_P (DECL_NAME (NODE)) \
&& flag_hosted)
+/* Nonzero if the variable was declared to be thread-local.
+ We need a special C++ version of this test because the middle-end
+ DECL_THREAD_LOCAL_P uses the symtab, so we can't use it for
+ templates. */
+#define RS_DECL_THREAD_LOCAL_P(NODE) (TREE_LANG_FLAG_0 (VAR_DECL_CHECK (NODE)))
+
+#define COND_EXPR_IS_VEC_DELETE(NODE) TREE_LANG_FLAG_0 (COND_EXPR_CHECK (NODE))
+
+/* RANGE_FOR_STMT accessors. These give access to the declarator,
+ expression, body, and scope of the statement, respectively. */
+#define RANGE_FOR_DECL(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 0)
+#define RANGE_FOR_EXPR(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 1)
+#define RANGE_FOR_BODY(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 2)
+#define RANGE_FOR_SCOPE(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 3)
+#define RANGE_FOR_UNROLL(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 4)
+#define RANGE_FOR_INIT_STMT(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 5)
+#define RANGE_FOR_IVDEP(NODE) TREE_LANG_FLAG_6 (RANGE_FOR_STMT_CHECK (NODE))
+
+#define CP_DECL_CONTEXT(NODE) \
+ (!DECL_FILE_SCOPE_P (NODE) ? DECL_CONTEXT (NODE) : global_namespace)
+#define CP_TYPE_CONTEXT(NODE) \
+ (!TYPE_FILE_SCOPE_P (NODE) ? TYPE_CONTEXT (NODE) : global_namespace)
+#define FROB_CONTEXT(NODE) \
+ ((NODE) == global_namespace ? DECL_CONTEXT (NODE) : (NODE))
+
+/* Nonzero if NODE is the std namespace. */
+#define DECL_NAMESPACE_STD_P(NODE) ((NODE) == std_node)
+
+/* Whether the namepace is an inline namespace. */
+#define DECL_NAMESPACE_INLINE_P(NODE) \
+ TREE_LANG_FLAG_0 (NAMESPACE_DECL_CHECK (NODE))
+
+#define CP_DECL_CONTEXT(NODE) \
+ (!DECL_FILE_SCOPE_P (NODE) ? DECL_CONTEXT (NODE) : global_namespace)
+
+/* Based off of TYPE_UNNAMED_P. */
+#define LAMBDA_TYPE_P(NODE) \
+ (TREE_CODE (NODE) == RECORD_TYPE && TYPE_LINKAGE_IDENTIFIER (NODE) \
+ && IDENTIFIER_LAMBDA_P (TYPE_LINKAGE_IDENTIFIER (NODE)))
+
+/* Macros to make error reporting functions' lives easier. */
+#define TYPE_LINKAGE_IDENTIFIER(NODE) \
+ (TYPE_IDENTIFIER (TYPE_MAIN_VARIANT (NODE)))
+
+/* Identifiers used for lambda types are almost anonymous. Use this
+ spare flag to distinguish them (they also have the anonymous flag). */
+#define IDENTIFIER_LAMBDA_P(NODE) \
+ (IDENTIFIER_NODE_CHECK (NODE)->base.protected_flag)
+
+/* If NODE, a FUNCTION_DECL, is a C++11 inheriting constructor, then this
+ is the constructor it inherits from. */
+#define DECL_INHERITED_CTOR(NODE) \
+ (DECL_DECLARES_FUNCTION_P (NODE) && DECL_CONSTRUCTOR_P (NODE) \
+ ? LANG_DECL_FN_CHECK (NODE)->context \
+ : NULL_TREE)
+
+/* True if the class type TYPE is a literal type. */
+#define CLASSTYPE_LITERAL_P(TYPE) (LANG_TYPE_CLASS_CHECK (TYPE)->is_literal)
+
+/* Nonzero if NODE (a FUNCTION_DECL or TEMPLATE_DECL)
+ is a destructor. */
+#define DECL_DESTRUCTOR_P(NODE) DECL_CXX_DESTRUCTOR_P (NODE)
+
+/* Nonzero if TYPE has a trivial destructor. From [class.dtor]:
+
+ A destructor is trivial if it is an implicitly declared
+ destructor and if:
+
+ - all of the direct base classes of its class have trivial
+ destructors,
+
+ - for all of the non-static data members of its class that are
+ of class type (or array thereof), each such class has a
+ trivial destructor. */
+#define TYPE_HAS_TRIVIAL_DESTRUCTOR(NODE) \
+ (!TYPE_HAS_NONTRIVIAL_DESTRUCTOR (NODE))
+
+/* Nonzero means that NODE (a class type) has a destructor -- but that
+ it has not yet been declared. */
+#define CLASSTYPE_LAZY_DESTRUCTOR(NODE) \
+ (LANG_TYPE_CLASS_CHECK (NODE)->lazy_destructor)
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a constructor for a complete
+ object. */
+#define DECL_COMPLETE_CONSTRUCTOR_P(NODE) \
+ (DECL_NAME (NODE) == complete_ctor_identifier)
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a constructor for a base
+ object. */
+#define DECL_BASE_CONSTRUCTOR_P(NODE) (DECL_NAME (NODE) == base_ctor_identifier)
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a constructor, but not either the
+ specialized in-charge constructor or the specialized not-in-charge
+ constructor. */
+#define DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P(NODE) \
+ (DECL_NAME (NODE) == ctor_identifier)
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a copy constructor. */
+#define DECL_COPY_CONSTRUCTOR_P(NODE) \
+ (DECL_CONSTRUCTOR_P (NODE) && copy_fn_p (NODE) > 0)
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a move constructor. */
+#define DECL_MOVE_CONSTRUCTOR_P(NODE) \
+ (DECL_CONSTRUCTOR_P (NODE) && move_fn_p (NODE))
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a destructor, but not the
+ specialized in-charge constructor, in-charge deleting constructor,
+ or the base destructor. */
+#define DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P(NODE) \
+ (DECL_NAME (NODE) == dtor_identifier)
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a destructor for a complete
+ object. */
+#define DECL_COMPLETE_DESTRUCTOR_P(NODE) \
+ (DECL_NAME (NODE) == complete_dtor_identifier)
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a destructor for a base
+ object. */
+#define DECL_BASE_DESTRUCTOR_P(NODE) (DECL_NAME (NODE) == base_dtor_identifier)
+
+/* Nonzero if NODE (a FUNCTION_DECL) is a destructor for a complete
+ object that deletes the object after it has been destroyed. */
+#define DECL_DELETING_DESTRUCTOR_P(NODE) \
+ (DECL_NAME (NODE) == deleting_dtor_identifier)
+
+/* Nonzero if either DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P or
+ DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P is true of NODE. */
+#define DECL_MAYBE_IN_CHARGE_CDTOR_P(NODE) \
+ (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (NODE) \
+ || DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (NODE))
+
+/* Nonzero if NODE (a _DECL) is a cloned constructor or
+ destructor. */
+#define DECL_CLONED_FUNCTION_P(NODE) \
+ (DECL_NAME (NODE) && IDENTIFIER_CDTOR_P (DECL_NAME (NODE)) \
+ && !DECL_MAYBE_IN_CHARGE_CDTOR_P (NODE))
+
+/* If DECL_CLONED_FUNCTION_P holds, this is the function that was
+ cloned. */
+#define DECL_CLONED_FUNCTION(NODE) \
+ (DECL_LANG_SPECIFIC (FUNCTION_DECL_CHECK (NODE))->u.fn.u5.cloned_function)
+
+/* Nonzero means that an object of this type cannot be initialized using
+ an initializer list. */
+#define CLASSTYPE_NON_AGGREGATE(NODE) \
+ (LANG_TYPE_CLASS_CHECK (NODE)->non_aggregate)
+#define TYPE_NON_AGGREGATE_CLASS(NODE) \
+ (CLASS_TYPE_P (NODE) && CLASSTYPE_NON_AGGREGATE (NODE))
+
+/* Nonzero for class type means that the default constructor is trivial. */
+#define TYPE_HAS_TRIVIAL_DFLT(NODE) \
+ (TYPE_HAS_DEFAULT_CONSTRUCTOR (NODE) && !TYPE_HAS_COMPLEX_DFLT (NODE))
+
+/* Nonzero if this class has a constexpr constructor other than a copy/move
+ constructor. Note that a class can have constexpr constructors for
+ static initialization even if it isn't a literal class. */
+#define TYPE_HAS_CONSTEXPR_CTOR(NODE) \
+ (LANG_TYPE_CLASS_CHECK (NODE)->has_constexpr_ctor)
+
+/* Nonzero if there is no trivial default constructor for this class. */
+#define TYPE_HAS_COMPLEX_DFLT(NODE) \
+ (LANG_TYPE_CLASS_CHECK (NODE)->has_complex_dflt)
+
#if defined ENABLE_TREE_CHECKING
#define LANG_DECL_MIN_CHECK(NODE) \
@@ -1532,6 +1695,33 @@ extern GTY (()) tree cp_global_trees[CPTI_MAX];
// Above macros are copied from gcc/cp/name-lookup.cc
+/* The various kinds of special functions. If you add to this list,
+ you should update special_function_p as well. */
+enum special_function_kind
+{
+ sfk_none = 0, /* Not a special function. This enumeral
+ must have value zero; see
+ special_function_p. */
+ /* The following are ordered, for use by member synthesis fns. */
+ sfk_destructor, /* A destructor. */
+ sfk_constructor, /* A constructor. */
+ sfk_inheriting_constructor, /* An inheriting constructor */
+ sfk_copy_constructor, /* A copy constructor. */
+ sfk_move_constructor, /* A move constructor. */
+ sfk_copy_assignment, /* A copy assignment operator. */
+ sfk_move_assignment, /* A move assignment operator. */
+ /* The following are unordered. */
+ sfk_complete_destructor, /* A destructor for complete objects. */
+ sfk_base_destructor, /* A destructor for base subobjects. */
+ sfk_deleting_destructor, /* A destructor for complete objects that
+ deletes the object after it has been
+ destroyed. */
+ sfk_conversion, /* A conversion operator. */
+ sfk_deduction_guide, /* A class template deduction guide. */
+ sfk_comparison, /* A comparison operator (e.g. ==, <, <=>). */
+ sfk_virtual_destructor /* Used by member synthesis fns. */
+};
+
/* Places where an lvalue, or modifiable lvalue, may be required.
Used to select diagnostic messages in lvalue_error and
readonly_error. */
@@ -2922,6 +3112,10 @@ extern void lvalue_error (location_t, enum lvalue_use);
extern tree
cp_fold_maybe_rvalue (tree, bool);
+extern tree get_first_fn (tree) ATTRIBUTE_PURE;
+
+extern void explain_non_literal_class (tree);
+
// forked from gcc/cp/cp-tree.h
enum
@@ -3147,7 +3341,16 @@ namespace Compile {
extern tree
maybe_constant_init (tree, tree = NULL_TREE, bool = false);
+extern void
+explain_invalid_constexpr_fn (tree fun);
+
extern bool potential_constant_expression (tree);
+
+extern bool
+literal_type_p (tree t);
+
+extern bool
+maybe_constexpr_fn (tree t);
}
} // namespace Rust
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2022-08-29 15:35 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-29 15:35 [gcc/devel/rust/master] rust-constexpr.cc: port over potential_constant_expression_1() Thomas Schwinge
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).