+2019-12-03 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/91369
+ * constexpr.c (struct constexpr_global_ctx): Add cleanups member,
+ initialize it in the ctor.
+ (cxx_eval_constant_expression) <case TARGET_EXPR>: If TARGET_EXPR_SLOT
+ is already in the values hash_map, don't evaluate it again. Put
+ TARGET_EXPR_SLOT into hash_map even if not lval, and push it into
+ save_exprs too. If there is TARGET_EXPR_CLEANUP and not
+ CLEANUP_EH_ONLY, push the cleanup to cleanups vector.
+ <case CLEANUP_POINT_EXPR>: Save outer cleanups, set cleanups to
+ local auto_vec, after evaluating the body evaluate cleanups and
+ restore previous cleanups.
+ <case TRY_CATCH_EXPR>: Don't crash if the first operand is NULL_TREE.
+ (cxx_eval_outermost_constant_expr): Set cleanups to local auto_vec,
+ after evaluating the expression evaluate cleanups.
+
2019-12-03 Marek Polacek <polacek@redhat.com>
PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
/* Heap VAR_DECLs created during the evaluation of the outermost constant
expression. */
auto_vec<tree, 16> heap_vars;
+ /* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */
+ vec<tree> *cleanups;
/* Constructor. */
- constexpr_global_ctx () : constexpr_ops_count (0) {}
+ constexpr_global_ctx () : constexpr_ops_count (0), cleanups (NULL) {}
};
/* The constexpr expansion context. CALL is the current function
constexpr_global_ctx *global;
/* The innermost call we're evaluating. */
constexpr_call *call;
- /* SAVE_EXPRs that we've seen within the current LOOP_EXPR. NULL if we
- aren't inside a loop. */
+ /* SAVE_EXPRs and TARGET_EXPR_SLOT vars of TARGET_EXPRs that we've seen
+ within the current LOOP_EXPR. NULL if we aren't inside a loop. */
vec<tree> *save_exprs;
/* The CONSTRUCTOR we're currently building up for an aggregate
initializer. */
else
ctx->global->values.put (res, NULL_TREE);
- /* Track the callee's evaluated SAVE_EXPRs so that we can forget
- their values after the call. */
+ /* Track the callee's evaluated SAVE_EXPRs and TARGET_EXPRs so that
+ we can forget their values after the call. */
constexpr_ctx ctx_with_save_exprs = *ctx;
auto_vec<tree, 10> save_exprs;
ctx_with_save_exprs.save_exprs = &save_exprs;
TREE_READONLY (e) = true;
}
- /* Forget the saved values of the callee's SAVE_EXPRs. */
+ /* Forget the saved values of the callee's SAVE_EXPRs and
+ TARGET_EXPRs. */
unsigned int i;
tree save_expr;
FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
gcc_assert (*jump_target);
}
- /* Forget saved values of SAVE_EXPRs. */
+ /* Forget saved values of SAVE_EXPRs and TARGET_EXPRs. */
unsigned int i;
tree save_expr;
FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
&& (!switches (jump_target) || count == 0)
&& !*non_constant_p);
- /* Forget saved values of SAVE_EXPRs. */
+ /* Forget saved values of SAVE_EXPRs and TARGET_EXPRs. */
unsigned int i;
tree save_expr;
FOR_EACH_VEC_ELT (save_exprs, i, save_expr)
*non_constant_p = true;
break;
}
+ /* Avoid evaluating a TARGET_EXPR more than once. */
+ if (tree *p = ctx->global->values.get (TARGET_EXPR_SLOT (t)))
+ {
+ if (lval)
+ return TARGET_EXPR_SLOT (t);
+ r = *p;
+ break;
+ }
if ((AGGREGATE_TYPE_P (TREE_TYPE (t)) || VECTOR_TYPE_P (TREE_TYPE (t))))
{
/* We're being expanded without an explicit target, so start
if (!*non_constant_p)
/* Adjust the type of the result to the type of the temporary. */
r = adjust_temp_type (TREE_TYPE (t), r);
+ 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 (TARGET_EXPR_SLOT (t), r);
+ if (ctx->save_exprs)
+ ctx->save_exprs->safe_push (TARGET_EXPR_SLOT (t));
if (lval)
- {
- tree slot = TARGET_EXPR_SLOT (t);
- r = unshare_constructor (r);
- ctx->global->values.put (slot, r);
- return slot;
- }
+ return TARGET_EXPR_SLOT (t);
break;
case INIT_EXPR:
}
break;
- case NON_LVALUE_EXPR:
case TRY_CATCH_EXPR:
+ if (TREE_OPERAND (t, 0) == NULL_TREE)
+ {
+ r = void_node;
+ break;
+ }
+ /* FALLTHRU */
+ case NON_LVALUE_EXPR:
case TRY_BLOCK:
- case CLEANUP_POINT_EXPR:
case MUST_NOT_THROW_EXPR:
case EXPR_STMT:
case EH_SPEC_BLOCK:
jump_target);
break;
+ case CLEANUP_POINT_EXPR:
+ {
+ auto_vec<tree, 2> cleanups;
+ vec<tree> *prev_cleanups = ctx->global->cleanups;
+ ctx->global->cleanups = &cleanups;
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+ lval,
+ non_constant_p, overflow_p,
+ jump_target);
+ ctx->global->cleanups = prev_cleanups;
+ unsigned int i;
+ tree cleanup;
+ /* Evaluate the cleanups. */
+ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
+ cxx_eval_constant_expression (ctx, cleanup, false,
+ non_constant_p, overflow_p,
+ jump_target);
+ }
+ break;
+
case TRY_FINALLY_EXPR:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
non_constant_p, overflow_p,
r = TARGET_EXPR_INITIAL (r);
}
+ auto_vec<tree, 16> cleanups;
+ global_ctx.cleanups = &cleanups;
+
instantiate_constexpr_fns (r);
r = cxx_eval_constant_expression (&ctx, r,
false, &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)
+ cxx_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. */