On 12/1/23 18:37, Marek Polacek wrote:
On Thu, Nov 30, 2023 at 06:34:01PM -0500, Jason Merrill wrote:
On 11/23/23 11:46, Marek Polacek wrote:
v5 greatly simplifies the code.
Indeed, it's much cleaner now.
I still need a new ff_ flag to signal that we can return immediately
after seeing an i-e expr.
That's still not clear to me:
+ /* In turn, maybe promote the function we find ourselves in... */
+ if ((data->flags & ff_find_escalating_expr)
+ && DECL_IMMEDIATE_FUNCTION_P (decl)
+ /* ...but not if the call to DECL was constant; that is the
+ "an immediate invocation that is not a constant expression"
+ case. */
+ && (e = cxx_constant_value (stmt, tf_none), e == error_mark_node))
+ {
+ /* Since we had to set DECL_ESCALATION_CHECKED_P before the walk,
+ we call promote_function_to_consteval directly which doesn't
+ check unchecked_immediate_escalating_function_p. */
+ if (current_function_decl)
+ promote_function_to_consteval (current_function_decl);
+ *walk_subtrees = 0;
+ return stmt;
+ }
This is the one use of ff_find_escalating_expr, and it seems redundant with
the code immediately below, where we use complain (derived from
ff_mce_false) to decide whether to return immediately. Can we remove this
hunk and the flag, and merge find_escalating_expr with cp_fold_immediate?
Ah, that works! Hopefully done now.
I think you want to walk the function body for three-ish reasons:
1) at EOF, to check for escalation
2) at EOF, to check for errors
3) at error time, to explain escalation
It's not clear to me that we need a flag to distinguish between them. When
we encounter an immediate-escalating expression E:
A) if we're in an immediate-escalating function, escalate and return E (#1,
#3).
B) otherwise, if we're diagnosing, error and continue (#2).
C) otherwise, return E (individual expression mce_unknown walk from
constexpr.cc).
@@ -1178,11 +1388,19 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_
)
*walk_subtrees = 0;
/* Don't return yet, still need the cp_fold below. */
}
- cp_fold_immediate_r (stmt_p, walk_subtrees, data);
+ else
+ cp_fold_immediate_r (stmt_p, walk_subtrees, data);
}
*stmt_p = stmt = cp_fold (*stmt_p, data->flags);
+ /* For certain trees, like +foo(), the cp_fold below will remove the +,
s/below/above/?
Fixed.
+/* We've stashed immediate-escalating functions. Now see if they indeed
+ ought to be promoted to consteval. */
+
+void
+process_pending_immediate_escalating_fns ()
+{
+ /* This will be null for -fno-immediate-escalation. */
+ if (!deferred_escalating_exprs)
+ return;
+
+ for (auto e : *deferred_escalating_exprs)
+ if (TREE_CODE (e) == FUNCTION_DECL && !DECL_ESCALATION_CHECKED_P (e))
+ cp_fold_immediate (&DECL_SAVED_TREE (e), mce_false, e);
+}
+
+/* We've escalated every function that could have been promoted to
+ consteval. Check that we are not taking the address of a consteval
+ function. */
+
+void
+check_immediate_escalating_refs ()
+{
+ /* This will be null for -fno-immediate-escalation. */
+ if (!deferred_escalating_exprs)
+ return;
+
+ for (auto ref : *deferred_escalating_exprs)
+ {
+ if (TREE_CODE (ref) == FUNCTION_DECL)
+ continue;
+ tree decl = (TREE_CODE (ref) == PTRMEM_CST
+ ? PTRMEM_CST_MEMBER (ref)
+ : TREE_OPERAND (ref, 0));
+ if (DECL_IMMEDIATE_FUNCTION_P (decl))
+ taking_address_of_imm_fn_error (ref, decl);
+ }
+
+ deferred_escalating_exprs = nullptr;
}
Could these be merged, so you do a single loop of cp_fold_immediate over
function bodies or non-function expressions? I'd expect that to work.
We seem to walk the hash table in a random order so I can't use one loop,
otherwise we could hit &f before escalating f.
Is that a problem, since we recurse if we see a function that is still
unchecked?
@@ -1045,90 +1191,138 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees,
void *data_)
/* 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);
+ const tree_code code = TREE_CODE (stmt);
/* 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)))
+ if (TYPE_P (stmt) || unevaluated_p (code) || cp_unevaluated_operand)
Maybe check in_immediate_context here instead?
+ /* [expr.const]p16 "An expression or conversion is immediate-escalating if
+ it is not initially in an immediate function context and it is either
+ -- an immediate invocation that is not a constant expression and is not
+ a subexpression of an immediate invocation."
+ If we are in an immediate-escalating function, the immediate-escalating
+ expression or conversion makes it an immediate function. So STMT does
+ not need to produce a constant expression. */ > + if
(DECL_IMMEDIATE_FUNCTION_P (decl))
+ {
+ tree e = cxx_constant_value (stmt, tf_none);
+ if (e == error_mark_node)
+ {
+ /* This takes care of
+ template <typename T>
+ constexpr int f(T t)
+ {
+ return id(t);
+ }
+ where id (consteval) causes f<int> to be promoted. */
+ if (maybe_promote_function_to_consteval (current_function_decl))
+ return NULL_TREE;
Can we remove maybe_promote in favor of the promote below?
+ /* If we're not complaining, we're either recursing when escalating,
+ or just looking for the i-e expression. */
+ if (!(complain & tf_error))
+ {
+ /* Since we had to set DECL_ESCALATION_CHECKED_P before the walk,
+ we call promote_function_to_consteval directly which doesn't
+ check unchecked_immediate_escalating_function_p. */
+ if (current_function_decl)
+ promote_function_to_consteval (current_function_decl);
+ }
Shouldn't we still check immediate_escalating_function_p (not
unchecked_), even if only in a gcc_checking_assert?
+ else if (!in_immediate_context ())
If we check in_immediate_context at the top of the function, we don't
need to check it here.
Jason