cxx_eval_outermost_constant_expr invocation. VALUES is a map of values of
variables initialized within the expression. */
-struct constexpr_global_ctx {
+class constexpr_global_ctx {
/* Values for any temporaries or local variables within the
constant-expression. */
hash_map<tree,tree> values;
+public:
/* Number of cxx_eval_constant_expression calls (except skipped ones,
on simple constants or location wrappers) encountered during current
cxx_eval_outermost_constant_expr call. */
auto_vec<tree, 16> heap_vars;
/* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */
vec<tree> *cleanups;
+ /* If non-null, only allow modification of existing values of the variables
+ in this set. Set by modifiable_tracker, below. */
+ hash_set<tree> *modifiable;
/* Number of heap VAR_DECL deallocations. */
unsigned heap_dealloc_count;
/* Constructor. */
constexpr_global_ctx ()
- : constexpr_ops_count (0), cleanups (NULL), heap_dealloc_count (0) {}
+ : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr),
+ heap_dealloc_count (0) {}
+
+ tree get_value (tree t)
+ {
+ if (tree *p = values.get (t))
+ return *p;
+ return NULL_TREE;
+ }
+ tree *get_value_ptr (tree t)
+ {
+ if (modifiable && !modifiable->contains (t))
+ return nullptr;
+ return values.get (t);
+ }
+ void put_value (tree t, tree v)
+ {
+ bool already_in_map = values.put (t, v);
+ if (!already_in_map && modifiable)
+ modifiable->add (t);
+ }
+ void remove_value (tree t) { values.remove (t); }
+};
+
+/* Helper class for constexpr_global_ctx. In some cases we want to avoid
+ side-effects from evaluation of a particular subexpression of a
+ constant-expression. In such cases we use modifiable_tracker to prevent
+ modification of variables created outside of that subexpression.
+
+ ??? We could change the hash_set to a hash_map, allow and track external
+ modifications, and roll them back in the destructor. It's not clear to me
+ that this would be worthwhile. */
+
+class modifiable_tracker
+{
+ hash_set<tree> set;
+ constexpr_global_ctx *global;
+public:
+ modifiable_tracker (constexpr_global_ctx *g): global(g)
+ {
+ global->modifiable = &set;
+ }
+ ~modifiable_tracker ()
+ {
+ for (tree t: set)
+ global->remove_value (t);
+ global->modifiable = nullptr;
+ }
};
/* The constexpr expansion context. CALL is the current function
return var;
constexpr_global_ctx *global = (constexpr_global_ctx *) data;
- if (global->values.get (var))
+ if (global->get_value (var))
return var;
}
if (TYPE_P (*tp))
{
/* Nobody wants to see the artificial (bool) cast. */
bad = tree_strip_nop_conversions (bad);
+ if (TREE_CODE (bad) == CLEANUP_POINT_EXPR)
+ bad = TREE_OPERAND (bad, 0);
/* Actually explain the failure if this is a concept check or a
requires-expression. */
return void_node;
case IFN_ASSUME:
- /* For now, restrict constexpr evaluation of [[assume (cond)]]
- only to the cases which don't have side-effects. Evaluating
- it even when it does would mean we'd need to somehow undo
- all the side-effects e.g. in ctx->global->values. */
- if (!TREE_SIDE_EFFECTS (CALL_EXPR_ARG (t, 0))
- /* And it needs to be a potential constant expression. */
- && potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0)))
+ if (potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0)))
{
constexpr_ctx new_ctx = *ctx;
new_ctx.quiet = true;
tree arg = CALL_EXPR_ARG (t, 0);
bool new_non_constant_p = false, new_overflow_p = false;
+ /* Avoid modification of existing values. */
+ modifiable_tracker ms (new_ctx.global);
arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
&new_non_constant_p,
&new_overflow_p);
// See PR98988 and PR99031.
varpool_node::finalize_decl (var);
ctx->global->heap_vars.safe_push (var);
- ctx->global->values.put (var, NULL_TREE);
+ ctx->global->put_value (var, NULL_TREE);
return fold_convert (ptr_type_node, build_address (var));
}
else
return t;
}
DECL_NAME (var) = heap_deleted_identifier;
- ctx->global->values.remove (var);
+ ctx->global->remove_value (var);
ctx->global->heap_dealloc_count++;
return void_node;
}
return t;
}
DECL_NAME (var) = heap_deleted_identifier;
- ctx->global->values.remove (var);
+ ctx->global->remove_value (var);
ctx->global->heap_dealloc_count++;
return void_node;
}
new_ctx.object = AGGR_INIT_EXPR_SLOT (t);
tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL);
CONSTRUCTOR_NO_CLEARING (ctor) = true;
- ctx->global->values.put (new_ctx.object, ctor);
+ ctx->global->put_value (new_ctx.object, ctor);
ctx = &new_ctx;
}
if (TREE_CODE (arg) == CONSTRUCTOR)
vec_safe_push (ctors, arg);
}
- ctx->global->values.put (remapped, arg);
+ ctx->global->put_value (remapped, arg);
remapped = DECL_CHAIN (remapped);
}
/* Add the RESULT_DECL to the values map, too. */
gcc_assert (!DECL_BY_REFERENCE (res));
- ctx->global->values.put (res, NULL_TREE);
+ ctx->global->put_value (res, NULL_TREE);
/* Track the callee's evaluated SAVE_EXPRs and TARGET_EXPRs so that
we can forget their values after the call. */
result = void_node;
else
{
- result = *ctx->global->values.get (res);
+ result = ctx->global->get_value (res);
if (result == NULL_TREE && !*non_constant_p
&& !DECL_DESTRUCTOR_P (fun))
{
/* Forget the saved values of the callee's SAVE_EXPRs and
TARGET_EXPRs. */
for (tree save_expr : save_exprs)
- ctx->global->values.remove (save_expr);
+ ctx->global->remove_value (save_expr);
/* Remove the parms/result from the values map. Is it worth
bothering to do this when the map itself is only live for
one constexpr evaluation? If so, maybe also clear out
other vars from call, maybe in BIND_EXPR handling? */
- ctx->global->values.remove (res);
+ ctx->global->remove_value (res);
for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
- ctx->global->values.remove (parm);
+ ctx->global->remove_value (parm);
/* Free any parameter CONSTRUCTORs we aren't returning directly. */
while (!ctors->is_empty ())
(TREE_TYPE (type), TREE_TYPE (otype)))));
}
gcc_assert (!ctx->object || !DECL_P (ctx->object)
- || *(ctx->global->values.get (ctx->object)) == ctx->ctor);
+ || ctx->global->get_value (ctx->object) == ctx->ctor);
}
/* Subroutine of cxx_eval_constant_expression.
new_ctx.object = VEC_INIT_EXPR_SLOT (t);
tree ctor = new_ctx.ctor = build_constructor (atype, NULL);
CONSTRUCTOR_NO_CLEARING (ctor) = true;
- ctx->global->values.put (new_ctx.object, ctor);
+ ctx->global->put_value (new_ctx.object, ctor);
ctx = &new_ctx;
}
init = expand_vec_init_expr (ctx->object, t, complain);
we're initializing. */
tree *valp;
if (DECL_P (object))
- valp = ctx->global->values.get (object);
+ valp = ctx->global->get_value_ptr (object);
else
valp = NULL;
if (!valp)
/* The hash table might have moved since the get earlier, and the
initializer might have mutated the underlying CONSTRUCTORs, so we must
recompute VALP. */
- valp = ctx->global->values.get (object);
+ valp = ctx->global->get_value_ptr (object);
for (unsigned i = 0; i < vec_safe_length (indexes); i++)
{
ctors[i] = valp;
/* Forget saved values of SAVE_EXPRs and TARGET_EXPRs. */
for (tree save_expr : save_exprs)
- ctx->global->values.remove (save_expr);
+ ctx->global->remove_value (save_expr);
save_exprs.truncate (0);
if (++count >= constexpr_loop_limit)
/* Forget saved values of SAVE_EXPRs and TARGET_EXPRs. */
for (tree save_expr : save_exprs)
- ctx->global->values.remove (save_expr);
+ ctx->global->remove_value (save_expr);
return NULL_TREE;
}
/* We ask for an rvalue for the RESULT_DECL when indirecting
through an invisible reference, or in named return value
optimization. */
- if (tree *p = ctx->global->values.get (t))
- return *p;
+ if (tree v = ctx->global->get_value (t))
+ return v;
else
{
if (!ctx->quiet)
else if (t == ctx->object)
return ctx->ctor;
if (VAR_P (t))
- if (tree *p = ctx->global->values.get (t))
- if (*p != NULL_TREE)
+ if (tree v = ctx->global->get_value (t))
{
- r = *p;
+ r = v;
break;
}
if (ctx->manifestly_const_eval)
case PARM_DECL:
if (lval && !TYPE_REF_P (TREE_TYPE (t)))
/* glvalue use. */;
- else if (tree *p = ctx->global->values.get (r))
- r = *p;
+ else if (tree v = ctx->global->get_value (r))
+ r = v;
else if (lval)
/* Defer in case this is only used for its type. */;
else if (COMPLETE_TYPE_P (TREE_TYPE (t))
new_ctx.object = r;
new_ctx.ctor = build_constructor (TREE_TYPE (r), NULL);
CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
- ctx->global->values.put (r, new_ctx.ctor);
+ ctx->global->put_value (r, new_ctx.ctor);
ctx = &new_ctx;
}
if (CLASS_TYPE_P (TREE_TYPE (r))
&& CP_TYPE_CONST_P (TREE_TYPE (r)))
TREE_READONLY (init) = true;
- ctx->global->values.put (r, init);
+ ctx->global->put_value (r, init);
}
else if (ctx == &new_ctx)
/* We gave it a CONSTRUCTOR above. */;
else
- ctx->global->values.put (r, NULL_TREE);
+ ctx->global->put_value (r, NULL_TREE);
}
break;
gcc_checking_assert (!TARGET_EXPR_DIRECT_INIT_P (t));
/* Avoid evaluating a TARGET_EXPR more than once. */
tree slot = TARGET_EXPR_SLOT (t);
- if (tree *p = ctx->global->values.get (slot))
+ if (tree v = ctx->global->get_value (slot))
{
if (lval)
return slot;
- r = *p;
+ r = v;
break;
}
if ((AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)))
new_ctx.ctor = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
new_ctx.object = slot;
- ctx->global->values.put (new_ctx.object, new_ctx.ctor);
+ ctx->global->put_value (new_ctx.object, new_ctx.ctor);
ctx = &new_ctx;
}
/* Pass vc_prvalue because this indicates
if (TARGET_EXPR_CLEANUP (t) && !CLEANUP_EH_ONLY (t))
ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t));
r = unshare_constructor (r);
- ctx->global->values.put (slot, r);
+ ctx->global->put_value (slot, r);
if (ctx->save_exprs)
ctx->save_exprs->safe_push (slot);
if (lval)
case SAVE_EXPR:
/* Avoid evaluating a SAVE_EXPR more than once. */
- if (tree *p = ctx->global->values.get (t))
- r = *p;
+ if (tree v = ctx->global->get_value (t))
+ r = v;
else
{
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue,
non_constant_p, overflow_p);
if (*non_constant_p)
break;
- ctx->global->values.put (t, r);
+ ctx->global->put_value (t, r);
if (ctx->save_exprs)
ctx->save_exprs->safe_push (t);
}
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);
+ global_ctx.put_value (object, ctx.ctor);
if (TREE_CODE (r) == TARGET_EXPR)
/* Avoid creating another CONSTRUCTOR when we expand the
TARGET_EXPR. */