[gcc/devel/rust/master] rust-constexpr.cc: port over cxx_eval_outermost_constant_expr
Thomas Schwinge
tschwinge@gcc.gnu.org
Mon Aug 29 15:34:40 GMT 2022
https://gcc.gnu.org/g:d780f02a54ff9cb0b5fd181eb21ae9afe2fd7d1c
commit d780f02a54ff9cb0b5fd181eb21ae9afe2fd7d1c
Author: Faisal Abbas <90.abbasfaisal@gmail.com>
Date: Sat Aug 6 19:12:47 2022 +0100
rust-constexpr.cc: port over cxx_eval_outermost_constant_expr
Diff:
---
gcc/rust/backend/rust-constexpr.cc | 318 +++++++++++++++++++++++++++++++++++++
gcc/rust/backend/rust-tree.cc | 43 +++++
gcc/rust/backend/rust-tree.h | 72 ++++++++-
3 files changed, 429 insertions(+), 4 deletions(-)
diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc
index 6f645beba45..276bfe94e6f 100644
--- a/gcc/rust/backend/rust-constexpr.cc
+++ b/gcc/rust/backend/rust-constexpr.cc
@@ -31,6 +31,7 @@
#include "tree-inline.h"
#include "vec.h"
#include "rust-target.h"
+#include "function.h"
#define VERIFY_CONSTANT(X) \
do \
@@ -4030,6 +4031,323 @@ eval_unary_expression (const constexpr_ctx *ctx, tree t, bool /*lval*/,
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);
+ // faisal: just return false for now to make it compile
+}
+
+/* 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);
+}
+
// #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 289b4b95801..ad457a2e4ce 100644
--- a/gcc/rust/backend/rust-tree.cc
+++ b/gcc/rust/backend/rust-tree.cc
@@ -4108,4 +4108,47 @@ is_empty_field (tree decl)
return r;
}
+// forked from gcc/cp/call.cc in_immediate_context
+
+/* Return true if in an immediate function context, or an unevaluated operand,
+ or a subexpression of an immediate invocation. */
+
+bool
+in_immediate_context ()
+{
+ return false;
+}
+
+// forked from gcc/cp/cvt.cc cp_get_fndecl_from_callee
+
+/* FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
+ if we can. */
+
+tree
+rs_get_fndecl_from_callee (tree fn, bool fold /* = true */)
+{
+ if (fn == NULL_TREE)
+ return fn;
+ if (TREE_CODE (fn) == FUNCTION_DECL)
+ return fn;
+ tree type = TREE_TYPE (fn);
+ if (type == NULL_TREE || !INDIRECT_TYPE_P (type))
+ return NULL_TREE;
+ if (fold)
+ fn = Compile::maybe_constant_init (fn);
+ STRIP_NOPS (fn);
+ if (TREE_CODE (fn) == ADDR_EXPR || TREE_CODE (fn) == FDESC_EXPR)
+ fn = TREE_OPERAND (fn, 0);
+ if (TREE_CODE (fn) == FUNCTION_DECL)
+ return fn;
+ return NULL_TREE;
+}
+
+// forked from gcc/cp/cvt.cc cp_get_callee_fndecl_nofold
+tree
+rs_get_callee_fndecl_nofold (tree call)
+{
+ return rs_get_fndecl_from_callee (cp_get_callee (call), false);
+}
+
} // namespace Rust
diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h
index f466a81962e..bd11b20e77f 100644
--- a/gcc/rust/backend/rust-tree.h
+++ b/gcc/rust/backend/rust-tree.h
@@ -1221,6 +1221,9 @@ extern GTY (()) tree cp_global_trees[CPTI_MAX];
#define current_class_type scope_chain->class_type
+#define in_discarded_stmt scope_chain->discarded_stmt
+#define in_consteval_if_p scope_chain->consteval_if_p
+
/* Nonzero means that this type is being defined. I.e., the left brace
starting the definition of this type has been seen. */
#define TYPE_BEING_DEFINED(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->being_defined)
@@ -1418,8 +1421,62 @@ extern GTY (()) tree cp_global_trees[CPTI_MAX];
#define STAT_TYPE_HIDDEN_P(N) OVL_HIDDEN_P (N)
#define STAT_DECL_HIDDEN_P(N) OVL_DEDUP_P (N)
+/* The binding level currently in effect. */
+
+#define current_binding_level \
+ (*(cfun && cp_function_chain && cp_function_chain->bindings \
+ ? &cp_function_chain->bindings \
+ : &scope_chain->bindings))
+
// Above macros are copied from gcc/cp/name-lookup.cc
+// forked from gcc/cp/name_lookup.h scope_kind
+
+/* The kinds of scopes we recognize. */
+enum scope_kind
+{
+ sk_block = 0, /* An ordinary block scope. This enumerator must
+ have the value zero because "cp_binding_level"
+ is initialized by using "memset" to set the
+ contents to zero, and the default scope kind
+ is "sk_block". */
+ sk_cleanup, /* A scope for (pseudo-)scope for cleanup. It is
+ pseudo in that it is transparent to name lookup
+ activities. */
+ sk_try, /* A try-block. */
+ sk_catch, /* A catch-block. */
+ sk_for, /* The scope of the variable declared in a
+ init-statement. */
+ sk_cond, /* The scope of the variable declared in the condition
+ of an if or switch statement. */
+ sk_function_parms, /* The scope containing function parameters. */
+ sk_class, /* The scope containing the members of a class. */
+ sk_scoped_enum, /* The scope containing the enumerators of a C++11
+ scoped enumeration. */
+ sk_namespace, /* The scope containing the members of a
+ namespace, including the global scope. */
+ sk_template_parms, /* A scope for template parameters. */
+ sk_template_spec, /* Like sk_template_parms, but for an explicit
+ specialization. Since, by definition, an
+ explicit specialization is introduced by
+ "template <>", this scope is always empty. */
+ sk_transaction, /* A synchronized or atomic statement. */
+ sk_omp /* An OpenMP structured block. */
+};
+
+// forked from gcc/cp/cp-tree.h cp_built_in_function
+
+/* BUILT_IN_FRONTEND function codes. */
+enum cp_built_in_function
+{
+ CP_BUILT_IN_IS_CONSTANT_EVALUATED,
+ CP_BUILT_IN_INTEGER_PACK,
+ CP_BUILT_IN_IS_CORRESPONDING_MEMBER,
+ CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
+ CP_BUILT_IN_SOURCE_LOCATION,
+ CP_BUILT_IN_LAST
+};
+
// forked from gcc/cp/cp-tree.h warning_sentinel
/* RAII sentinel to disable certain warnings during template substitution
@@ -2539,13 +2596,10 @@ extern bool reduced_constant_expression_p (tree);
extern tree cv_unqualified (tree);
extern tree cp_get_callee (tree);
-extern tree cp_get_callee_fndecl_nofold (tree);
+extern tree rs_get_callee_fndecl_nofold (tree);
extern bool is_nondependent_static_init_expression (tree);
-extern tree
-maybe_constant_init (tree, tree = NULL_TREE, bool = false);
-
extern tree build_nop (tree, tree);
extern bool scalarish_type_p (const_tree);
@@ -2609,6 +2663,11 @@ extern tree build_new_constexpr_heap_type (tree, tree, tree);
extern bool is_empty_field (tree);
+extern bool
+in_immediate_context ();
+
+extern tree cp_get_callee_fndecl_nofold (tree);
+
// forked from gcc/cp/cp-tree.h
enum
@@ -2811,6 +2870,11 @@ cxx_incomplete_type_error (const_tree value, const_tree type)
extern location_t
location_of (tree t);
+namespace Compile {
+extern tree
+maybe_constant_init (tree, tree = NULL_TREE, bool = false);
+}
+
} // namespace Rust
#endif // RUST_TREE
More information about the Gcc-cvs
mailing list