aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2023-08-31 20:11:50 -0400
committerMarek Polacek <polacek@redhat.com>2023-09-19 09:24:44 -0400
commit6851e3423c2b5ec6516efa0677745d25c905e079 (patch)
tree72c6c8bc7a321e6f9f6090b9db82fea35931e707 /gcc/cp
parentf25960b03834712f312d46fed9a021df36f89f22 (diff)
downloadgcc-6851e3423c2b5ec6516efa0677745d25c905e079.zip
gcc-6851e3423c2b5ec6516efa0677745d25c905e079.tar.gz
gcc-6851e3423c2b5ec6516efa0677745d25c905e079.tar.bz2
c++: Move consteval folding to cp_fold_r
In the review of P2564: <https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html> it turned out that in order to correctly handle an example in the paper, we should stop doing immediate evaluation in build_over_call and bot_replace, and instead do it in cp_fold_r. This patch does that. Another benefit is that this is a pretty significant simplification, at least in my opinion. Also, this fixes the c++/110997 ICE (but the test doesn't compile yet). The main drawback seems to be that cp_fold_r doesn't process uninstantiated templates. We still have to handle things like "false ? foo () : 1". To that end, I've added cp_fold_immediate, called on dead branches in cxx_eval_conditional_expression. You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here. This is to detect *(&foo)) () (s.*&S::foo) () which were deemed ill-formed. gcc/cp/ChangeLog: * call.cc (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Don't handle immediate_invocation_p here. * constexpr.cc (cxx_eval_call_expression): Use mce_true for DECL_IMMEDIATE_FUNCTION_P. (cxx_eval_conditional_expression): Call cp_fold_immediate. * cp-gimplify.cc (enum fold_flags): Add ff_fold_immediate. (maybe_replace_decl): Make static. (cp_fold_r): Expand immediate invocations. (cp_fold_immediate_r): New. (cp_fold_immediate): New. * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define. (cp_fold_immediate): Declare. * tree.cc (bot_replace): Don't handle immediate invocations here. libstdc++-v3/ChangeLog: * testsuite/20_util/allocator/105975.cc: Add dg-error. gcc/testsuite/ChangeLog: * g++.dg/cpp23/consteval-if2.C: Add xfail. * g++.dg/cpp2a/consteval-memfn1.C: Adjust. * g++.dg/cpp2a/consteval11.C: Remove dg-message. * g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error. * g++.dg/cpp2a/consteval9.C: Remove dg-message. * g++.dg/cpp2a/consteval32.C: New test. * g++.dg/cpp2a/consteval33.C: New test. * g++.dg/cpp2a/consteval34.C: New test. * g++.dg/cpp2a/consteval35.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/call.cc40
-rw-r--r--gcc/cp/constexpr.cc23
-rw-r--r--gcc/cp/cp-gimplify.cc145
-rw-r--r--gcc/cp/cp-tree.h42
-rw-r--r--gcc/cp/tree.cc23
5 files changed, 168 insertions, 105 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 2bbaeee..e8dafbd 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -10434,6 +10434,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
fn = build_addr_func (fn, complain);
if (fn == error_mark_node)
return error_mark_node;
+
+ /* We're actually invoking the function. (Immediate functions get an
+ & when invoking it even though the user didn't use &.) */
+ ADDR_EXPR_DENOTES_CALL_P (fn) = true;
}
tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag);
@@ -10451,41 +10455,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
if (TREE_CODE (c) == CALL_EXPR)
suppress_warning (c /* Suppress all warnings. */);
}
- if (TREE_CODE (fn) == ADDR_EXPR)
- {
- tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0));
- if (immediate_invocation_p (fndecl))
- {
- tree obj_arg = NULL_TREE;
- /* Undo convert_from_reference called by build_cxx_call. */
- if (REFERENCE_REF_P (call))
- call = TREE_OPERAND (call, 0);
- if (DECL_CONSTRUCTOR_P (fndecl))
- obj_arg = cand->first_arg ? cand->first_arg : (*args)[0];
- if (obj_arg && is_dummy_object (obj_arg))
- {
- call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain);
- obj_arg = NULL_TREE;
- }
- /* Look through *(const T *)&obj. */
- else if (obj_arg && INDIRECT_REF_P (obj_arg))
- {
- tree addr = TREE_OPERAND (obj_arg, 0);
- STRIP_NOPS (addr);
- if (TREE_CODE (addr) == ADDR_EXPR)
- {
- tree typeo = TREE_TYPE (obj_arg);
- tree typei = TREE_TYPE (TREE_OPERAND (addr, 0));
- if (same_type_ignoring_top_level_qualifiers_p (typeo, typei))
- obj_arg = TREE_OPERAND (addr, 0);
- }
- }
- call = cxx_constant_value (call, obj_arg, complain);
- if (obj_arg && !error_operand_p (call))
- call = cp_build_init_expr (obj_arg, call);
- call = convert_from_reference (call);
- }
- }
+
return call;
}
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 0ca4370..a673a60 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
unsigned save_heap_alloc_count = ctx->global->heap_vars.length ();
unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count;
+ /* Make sure we fold std::is_constant_evaluated to true in an
+ immediate function. */
+ if (DECL_IMMEDIATE_FUNCTION_P (fun))
+ call_ctx.manifestly_const_eval = mce_true;
+
/* If this is a constexpr destructor, the object's const and volatile
semantics are no longer in effect; see [class.dtor]p5. */
if (new_obj && DECL_DESTRUCTOR_P (fun))
@@ -3807,8 +3812,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
}
/* Subroutine of cxx_eval_constant_expression.
- Attempt to evaluate condition expressions. Dead branches are not
- looked into. */
+ Attempt to evaluate condition expressions. */
static tree
cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
@@ -3837,12 +3841,25 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
boolean_type_node);
}
/* Don't VERIFY_CONSTANT the other operands. */
- if (integer_zerop (val))
+ const bool zero_p = integer_zerop (val);
+ if (zero_p)
val = TREE_OPERAND (t, 2);
else
val = TREE_OPERAND (t, 1);
if (TREE_CODE (t) == IF_STMT && !val)
val = void_node;
+
+ /* P2564: a subexpression of a manifestly constant-evaluated expression
+ or conversion is an immediate function context. */
+ if (ctx->manifestly_const_eval != mce_true
+ && !in_immediate_context ()
+ && cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2),
+ ctx->manifestly_const_eval))
+ {
+ *non_constant_p = true;
+ return t;
+ }
+
/* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still
serve as the initializer for the same object as the outer TARGET_EXPR,
as in
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 206e791..bdf6e5f 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -53,6 +53,8 @@ enum fold_flags {
definitely not in a manifestly constant-evaluated
context. */
ff_mce_false = 1 << 1,
+ /* Whether we're being called from cp_fold_immediate. */
+ ff_fold_immediate = 1 << 2,
};
using fold_flags_t = int;
@@ -1000,7 +1002,7 @@ cp_genericize_target_expr (tree *stmt_p)
replacement when cp_folding TARGET_EXPR to preserve the invariant that
AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */
-bool
+static bool
maybe_replace_decl (tree *tp, tree decl, tree replacement)
{
if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp)))
@@ -1029,44 +1031,95 @@ struct cp_genericize_data
bool handle_invisiref_parm_p;
};
-/* Perform any pre-gimplification folding of C++ front end trees to
- GENERIC.
- Note: The folding of non-omp cases is something to move into
- the middle-end. As for now we have most foldings only on GENERIC
- in fold-const, we need to perform this before transformation to
- GIMPLE-form. */
+/* A subroutine of cp_fold_r to handle immediate functions. */
static tree
-cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
+cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
{
- cp_fold_data *data = (cp_fold_data*)data_;
+ auto data = static_cast<cp_fold_data *>(data_);
tree stmt = *stmt_p;
- enum tree_code code = TREE_CODE (stmt);
+ /* The purpose of this is not to emit errors for mce_unknown. */
+ const tsubst_flags_t complain = (data->flags & ff_mce_false
+ ? tf_error : tf_none);
- switch (code)
+ /* No need to look into types or unevaluated operands.
+ NB: This affects cp_fold_r as well. */
+ if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt)))
+ {
+ *walk_subtrees = 0;
+ return NULL_TREE;
+ }
+
+ switch (TREE_CODE (stmt))
{
+ /* Unfortunately we must handle code like
+ false ? bar () : 42
+ where we have to check bar too. The cp_fold call in cp_fold_r could
+ fold the ?: into a constant before we see it here. */
+ case COND_EXPR:
+ /* If we are called from cp_fold_immediate, we don't need to worry about
+ cp_fold folding away the COND_EXPR. */
+ if (data->flags & ff_fold_immediate)
+ break;
+ if (TREE_OPERAND (stmt, 1)
+ && cp_walk_tree (&TREE_OPERAND (stmt, 1), cp_fold_immediate_r, data,
+ nullptr))
+ return error_mark_node;
+ if (TREE_OPERAND (stmt, 2)
+ && cp_walk_tree (&TREE_OPERAND (stmt, 2), cp_fold_immediate_r, data,
+ nullptr))
+ return error_mark_node;
+ /* We're done here. Don't clear *walk_subtrees here though: we're called
+ from cp_fold_r and we must let it recurse on the expression with
+ cp_fold. */
+ break;
case PTRMEM_CST:
if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
&& DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
{
- if (!data->pset.add (stmt))
- error_at (PTRMEM_CST_LOCATION (stmt),
- "taking address of an immediate function %qD",
- PTRMEM_CST_MEMBER (stmt));
- stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
- break;
+ if (!data->pset.add (stmt) && (complain & tf_error))
+ {
+ error_at (PTRMEM_CST_LOCATION (stmt),
+ "taking address of an immediate function %qD",
+ PTRMEM_CST_MEMBER (stmt));
+ *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+ }
+ return error_mark_node;
}
break;
+ /* Expand immediate invocations. */
+ case CALL_EXPR:
+ case AGGR_INIT_EXPR:
+ if (tree fn = cp_get_callee (stmt))
+ if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn))
+ if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false))
+ if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
+ {
+ stmt = cxx_constant_value (stmt, complain);
+ if (stmt == error_mark_node)
+ {
+ if (complain & tf_error)
+ *stmt_p = error_mark_node;
+ return error_mark_node;
+ }
+ *stmt_p = stmt;
+ }
+ break;
+
case ADDR_EXPR:
if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL
- && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)))
+ && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))
+ && !ADDR_EXPR_DENOTES_CALL_P (stmt))
{
- error_at (EXPR_LOCATION (stmt),
- "taking address of an immediate function %qD",
- TREE_OPERAND (stmt, 0));
- stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
- break;
+ if (complain & tf_error)
+ {
+ error_at (EXPR_LOCATION (stmt),
+ "taking address of an immediate function %qD",
+ TREE_OPERAND (stmt, 0));
+ *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+ }
+ return error_mark_node;
}
break;
@@ -1074,6 +1127,44 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
break;
}
+ return NULL_TREE;
+}
+
+/* A wrapper around cp_fold_immediate_r. Return true if we found
+ a non-constant immediate function, or taking the address of an
+ immediate function. */
+
+bool
+cp_fold_immediate (tree *tp, mce_value manifestly_const_eval)
+{
+ if (cxx_dialect <= cxx17)
+ return false;
+
+ fold_flags_t flags = ff_fold_immediate;
+ if (manifestly_const_eval == mce_false)
+ flags |= ff_mce_false;
+
+ cp_fold_data data (flags);
+ return !!cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data);
+}
+
+/* Perform any pre-gimplification folding of C++ front end trees to
+ GENERIC.
+ Note: The folding of non-omp cases is something to move into
+ the middle-end. As for now we have most foldings only on GENERIC
+ in fold-const, we need to perform this before transformation to
+ GIMPLE-form. */
+
+static tree
+cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
+{
+ cp_fold_data *data = (cp_fold_data*)data_;
+ tree stmt = *stmt_p;
+ enum tree_code code = TREE_CODE (stmt);
+
+ if (cxx_dialect > cxx17)
+ cp_fold_immediate_r (stmt_p, walk_subtrees, data);
+
*stmt_p = stmt = cp_fold (*stmt_p, data->flags);
if (data->pset.add (stmt))
@@ -1084,7 +1175,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
always the same tree, which the first time cp_fold_r has been
called on it had the subtrees walked. */
*walk_subtrees = 0;
- return NULL;
+ return NULL_TREE;
}
code = TREE_CODE (stmt);
@@ -1136,7 +1227,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
}
cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL);
*walk_subtrees = 0;
- return NULL;
+ return NULL_TREE;
case IF_STMT:
if (IF_STMT_CONSTEVAL_P (stmt))
@@ -1146,7 +1237,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL);
cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL);
*walk_subtrees = 0;
- return NULL;
+ return NULL_TREE;
}
break;
@@ -1183,7 +1274,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
break;
}
- return NULL;
+ return NULL_TREE;
}
/* Fold ALL the trees! FIXME we should be able to remove this, but
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3ca011c..5084932 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t)
#define PTRMEM_OK_P(NODE) \
TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF))
+/* True if this ADDR_EXPR denotes a function call; that is, it's
+ fn() rather than &fn. */
+#define ADDR_EXPR_DENOTES_CALL_P(NODE) \
+ (ADDR_EXPR_CHECK(NODE)->base.protected_flag)
+
/* Get the POINTER_TYPE to the METHOD_TYPE associated with this
pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true,
before using this macro. */
@@ -6580,6 +6585,24 @@ extern int class_dump_id;
extern int module_dump_id;
extern int raw_dump_id;
+/* Whether the current context is manifestly constant-evaluated.
+ Used by the constexpr machinery to control folding of
+ __builtin_is_constant_evaluated. */
+
+enum class mce_value
+{
+ /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */
+ mce_unknown = 0,
+ /* Fold it to true. */
+ mce_true = 1,
+ /* Fold it to false. Primarily used during cp_fold_function and
+ cp_fully_fold_init. */
+ mce_false = -1,
+};
+constexpr mce_value mce_unknown = mce_value::mce_unknown;
+constexpr mce_value mce_true = mce_value::mce_true;
+constexpr mce_value mce_false = mce_value::mce_false;
+
/* in call.cc */
extern bool check_dtor_name (tree, tree);
int magic_varargs_p (tree);
@@ -8354,6 +8377,7 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t);
extern bool simple_empty_class_p (tree, tree, tree_code);
extern tree fold_builtin_source_location (const_tree);
extern tree get_source_location_impl_type ();
+extern bool cp_fold_immediate (tree *, mce_value);
/* in name-lookup.cc */
extern tree strip_using_decl (tree);
@@ -8515,24 +8539,6 @@ struct GTY((for_user)) constexpr_fundef {
tree result;
};
-/* Whether the current context is manifestly constant-evaluated.
- Used by the constexpr machinery to control folding of
- __builtin_is_constant_evaluated. */
-
-enum class mce_value
-{
- /* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */
- mce_unknown = 0,
- /* Fold it to true. */
- mce_true = 1,
- /* Fold it to false. Primarily used during cp_fold_function and
- cp_fully_fold_init. */
- mce_false = -1,
-};
-constexpr mce_value mce_unknown = mce_value::mce_unknown;
-constexpr mce_value mce_true = mce_value::mce_true;
-constexpr mce_value mce_false = mce_value::mce_false;
-
extern void fini_constexpr (void);
extern bool literal_type_p (tree);
extern void maybe_save_constexpr_fundef (tree);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 799183d..eaf882f 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_)
variables. */
static tree
-bot_replace (tree* t, int* walk_subtrees, void* data_)
+bot_replace (tree* t, int */*walk_subtrees*/, void* data_)
{
bot_data &data = *(bot_data*)data_;
splay_tree target_remap = data.target_remap;
@@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_)
/*check_access=*/false, /*nonnull=*/true,
tf_warning_or_error);
}
- else if (cxx_dialect >= cxx20
- && (TREE_CODE (*t) == CALL_EXPR
- || TREE_CODE (*t) == AGGR_INIT_EXPR)
- && !in_immediate_context ())
- {
- /* Expand immediate invocations. */
- if (tree fndecl = cp_get_callee_fndecl_nofold (*t))
- if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
- {
- /* Make in_immediate_context true within the args. */
- in_consteval_if_p_temp_override ito;
- in_consteval_if_p = true;
- int nargs = call_expr_nargs (*t);
- for (int i = 0; i < nargs; ++i)
- cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL);
- *t = cxx_constant_value (*t);
- if (*t == error_mark_node)
- return error_mark_node;
- *walk_subtrees = 0;
- }
- }
return NULL_TREE;
}