C++14 constexpr support (minus loops and multiple returns)

C++14 constexpr support (minus loops and multiple returns)
gcc/
	* tree-inline.c (copy_fn): New.
	* tree-inline.h: Declare it.
gcc/cp/
	* constexpr.c (use_new_call): New macro.
	(build_data_member_initialization): Ignore non-mem-inits.
	(check_constexpr_bind_expr_vars): Remove C++14 checks.
	(constexpr_fn_retval): Likewise.
	(check_constexpr_ctor_body): Do nothing in C++14.
	(massage_constexpr_body): In C++14 only collect mem-inits.
	(get_function_named_in_call): Handle null CALL_EXPR_FN.
	(cxx_bind_parameters_in_call): Build bindings in same order as
	parameters.  Don't treat iniviref parms specially in new call mode.
	(cxx_eval_call_expression): If use_new_call, do constexpr expansion
	based on DECL_SAVED_TREE rather than the massaged constexpr body.
	Set up ctx->object from AGGR_INIT_EXPR_SLOT if we don't have one.
	(is_sub_constant_expr): Don't mess with ctx.ctor here.
	(cxx_eval_component_reference): A null element means we're mid-
	initialization.
	(cxx_eval_store_expression, cxx_eval_increment_expression): New.
	(cxx_eval_constant_expression): Handle RESULT_DECL, DECL_EXPR,
	MODIFY_EXPR, STATEMENT_LIST, BIND_EXPR, USING_STMT,
	PREINCREMENT_EXPR, POSTINCREMENT_EXPR, PREDECREMENT_EXPR,
	POSTDECREMENT_EXPR.  Don't look into DECL_INITIAL of variables in
	constexpr functions.  In new-call mode find parms in the values table.
	(potential_constant_expression_1): Handle null CALL_EXPR_FN.
	Handle STATEMENT_LIST, MODIFY_EXPR, MODOP_EXPR, IF_STMT,
	PREINCREMENT_EXPR, POSTINCREMENT_EXPR, PREDECREMENT_EXPR,
	POSTDECREMENT_EXPR, BIND_EXPR, WITH_CLEANUP_EXPR,
	CLEANUP_POINT_EXPR, MUST_NOT_THROW_EXPR, TRY_CATCH_EXPR,
	EH_SPEC_BLOCK, EXPR_STMT, DECL_EXPR, CASE_LABEL_EXPR, BREAK_STMT,
	CONTINUE_STMT, USING_STMT, IF_STMT, DO_STMT, FOR_STMT, WHILE_STMT,
	SWITCH_STMT, ASM_EXPR.
	(cxx_eval_vec_init_1): Call build_aggr_init_expr.
	(cxx_eval_indirect_ref): Don't return a CONSTRUCTOR when the
	caller wants an lvalue.
	(cxx_eval_outermost_constant_expr): Pull object out of AGGR_INIT_EXPR.
	(maybe_constant_init): Look through INIT_EXPR.
	(ensure_literal_type_for_constexpr_object): Set
	cp_function_chain->invalid_constexpr.
	* cp-tree.h (struct language_function): Add invalid_constexpr bitfield.
	* decl.c (start_decl): Set cp_function_chain->invalid_constexpr.
	(check_for_uninitialized_const_var): Likewise.
	(maybe_save_function_definition): Check it.
	* parser.c (cp_parser_jump_statement): Set
	cp_function_chain->invalid_constexpr.
	(cp_parser_asm_definition): Likewise.

From-SVN: r217663
This commit is contained in:
Jason Merrill 2014-11-17 13:16:14 -05:00 committed by Jason Merrill
parent 544009d369
commit 60813a463b
13 changed files with 653 additions and 93 deletions

View File

@ -1,3 +1,8 @@
2014-11-17 Jason Merrill <jason@redhat.com>
* tree-inline.c (copy_fn): New.
* tree-inline.h: Declare it.
2014-11-17 Alan Lawrence <alan.lawrence@arm.com>
* config/aarch64/aarch64-builtins.c (TYPES_CREATE): Remove.

View File

@ -1,5 +1,50 @@
2014-11-17 Jason Merrill <jason@redhat.com>
C++14 constexpr support (minus loops and multiple returns)
* constexpr.c (use_new_call): New macro.
(build_data_member_initialization): Ignore non-mem-inits.
(check_constexpr_bind_expr_vars): Remove C++14 checks.
(constexpr_fn_retval): Likewise.
(check_constexpr_ctor_body): Do nothing in C++14.
(massage_constexpr_body): In C++14 only collect mem-inits.
(get_function_named_in_call): Handle null CALL_EXPR_FN.
(cxx_bind_parameters_in_call): Build bindings in same order as
parameters. Don't treat iniviref parms specially in new call mode.
(cxx_eval_call_expression): If use_new_call, do constexpr expansion
based on DECL_SAVED_TREE rather than the massaged constexpr body.
Set up ctx->object from AGGR_INIT_EXPR_SLOT if we don't have one.
(is_sub_constant_expr): Don't mess with ctx.ctor here.
(cxx_eval_component_reference): A null element means we're mid-
initialization.
(cxx_eval_store_expression, cxx_eval_increment_expression): New.
(cxx_eval_constant_expression): Handle RESULT_DECL, DECL_EXPR,
MODIFY_EXPR, STATEMENT_LIST, BIND_EXPR, USING_STMT,
PREINCREMENT_EXPR, POSTINCREMENT_EXPR, PREDECREMENT_EXPR,
POSTDECREMENT_EXPR. Don't look into DECL_INITIAL of variables in
constexpr functions. In new-call mode find parms in the values table.
(potential_constant_expression_1): Handle null CALL_EXPR_FN.
Handle STATEMENT_LIST, MODIFY_EXPR, MODOP_EXPR, IF_STMT,
PREINCREMENT_EXPR, POSTINCREMENT_EXPR, PREDECREMENT_EXPR,
POSTDECREMENT_EXPR, BIND_EXPR, WITH_CLEANUP_EXPR,
CLEANUP_POINT_EXPR, MUST_NOT_THROW_EXPR, TRY_CATCH_EXPR,
EH_SPEC_BLOCK, EXPR_STMT, DECL_EXPR, CASE_LABEL_EXPR, BREAK_STMT,
CONTINUE_STMT, USING_STMT, IF_STMT, DO_STMT, FOR_STMT, WHILE_STMT,
SWITCH_STMT, ASM_EXPR.
(cxx_eval_vec_init_1): Call build_aggr_init_expr.
(cxx_eval_indirect_ref): Don't return a CONSTRUCTOR when the
caller wants an lvalue.
(cxx_eval_outermost_constant_expr): Pull object out of AGGR_INIT_EXPR.
(maybe_constant_init): Look through INIT_EXPR.
(ensure_literal_type_for_constexpr_object): Set
cp_function_chain->invalid_constexpr.
* cp-tree.h (struct language_function): Add invalid_constexpr bitfield.
* decl.c (start_decl): Set cp_function_chain->invalid_constexpr.
(check_for_uninitialized_const_var): Likewise.
(maybe_save_function_definition): Check it.
* parser.c (cp_parser_jump_statement): Set
cp_function_chain->invalid_constexpr.
(cp_parser_asm_definition): Likewise.
PR c++/52282
* decl.c (build_ptrmemfunc_type): Don't build a different
RECORD_TYPE for a qualified PMF.

View File

@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-iterator.h"
#include "gimplify.h"
#include "builtins.h"
#include "tree-inline.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
@ -93,8 +94,11 @@ ensure_literal_type_for_constexpr_object (tree decl)
error ("the type %qT of constexpr variable %qD is not literal",
type, decl);
else
error ("variable %qD of non-literal type %qT in %<constexpr%> "
"function", decl, type);
{
error ("variable %qD of non-literal type %qT in %<constexpr%> "
"function", decl, type);
cp_function_chain->invalid_constexpr = true;
}
explain_non_literal_class (type);
return NULL;
}
@ -310,13 +314,20 @@ build_data_member_initialization (tree t, vec<constructor_elt, va_gc> **vec)
if (TREE_CODE (t) == CONVERT_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == INIT_EXPR
|| TREE_CODE (t) == MODIFY_EXPR)
/* vptr initialization shows up as a MODIFY_EXPR. In C++14 we only
use what this function builds for cx_check_missing_mem_inits, and
assignment in the ctor body doesn't count. */
|| (cxx_dialect < cxx14 && TREE_CODE (t) == MODIFY_EXPR))
{
member = TREE_OPERAND (t, 0);
init = break_out_target_exprs (TREE_OPERAND (t, 1));
}
else if (TREE_CODE (t) == CALL_EXPR)
{
tree fn = get_callee_fndecl (t);
if (!fn || !DECL_CONSTRUCTOR_P (fn))
/* We're only interested in calls to subobject constructors. */
return true;
member = CALL_EXPR_ARG (t, 0);
/* We don't use build_cplus_new here because it complains about
abstract bases. Leaving the call unwrapped means that it has the
@ -325,13 +336,9 @@ build_data_member_initialization (tree t, vec<constructor_elt, va_gc> **vec)
}
else if (TREE_CODE (t) == BIND_EXPR)
return build_data_member_initialization (BIND_EXPR_BODY (t), vec);
else if (TREE_CODE (t) == DECL_EXPR
|| TREE_CODE (t) == USING_STMT)
/* Declaring a temporary, don't add it to the CONSTRUCTOR.
Likewise for using directives. */
return true;
else
gcc_unreachable ();
/* Don't add anything else to the CONSTRUCTOR. */
return true;
if (INDIRECT_REF_P (member))
member = TREE_OPERAND (member, 0);
if (TREE_CODE (member) == NOP_EXPR)
@ -390,9 +397,6 @@ check_constexpr_bind_expr_vars (tree t)
{
gcc_assert (TREE_CODE (t) == BIND_EXPR);
if (cxx_dialect >= cxx14)
return true;
for (tree var = BIND_EXPR_VARS (t); var; var = DECL_CHAIN (var))
if (TREE_CODE (var) == TYPE_DECL
&& DECL_IMPLICIT_TYPEDEF_P (var))
@ -410,8 +414,6 @@ check_constexpr_ctor_body_1 (tree last, tree list)
case DECL_EXPR:
if (TREE_CODE (DECL_EXPR_DECL (list)) == USING_DECL)
return true;
if (cxx_dialect >= cxx14)
return true;
return false;
case CLEANUP_POINT_EXPR:
@ -440,6 +442,10 @@ check_constexpr_ctor_body_1 (tree last, tree list)
bool
check_constexpr_ctor_body (tree last, tree list, bool complain)
{
/* C++14 doesn't require a constexpr ctor to have an empty body. */
if (cxx_dialect >= cxx14)
return true;
bool ok = true;
if (TREE_CODE (list) == STATEMENT_LIST)
{
@ -612,8 +618,6 @@ constexpr_fn_retval (tree body)
case DECL_EXPR:
if (TREE_CODE (DECL_EXPR_DECL (body)) == USING_DECL)
return NULL_TREE;
if (cxx_dialect >= cxx14)
return NULL_TREE;
return error_mark_node;
case CLEANUP_POINT_EXPR:
@ -642,7 +646,7 @@ massage_constexpr_body (tree fun, tree body)
if (DECL_CONSTRUCTOR_P (fun))
body = build_constexpr_constructor_member_initializers
(DECL_CONTEXT (fun), body);
else
else if (cxx_dialect < cxx14)
{
if (TREE_CODE (body) == EH_SPEC_BLOCK)
body = EH_SPEC_STMTS (body);
@ -936,7 +940,7 @@ get_function_named_in_call (tree t)
gcc_unreachable();
break;
}
if (TREE_CODE (fun) == ADDR_EXPR
if (fun && TREE_CODE (fun) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (fun, 0)) == FUNCTION_DECL)
fun = TREE_OPERAND (fun, 0);
return fun;
@ -1016,6 +1020,10 @@ adjust_temp_type (tree type, tree temp)
return cp_fold_convert (type, temp);
}
/* True if we want to use the new handling of constexpr calls based on
DECL_SAVED_TREE. Currently only active for C++14 mode. */
#define use_new_call (cxx_dialect >= cxx14)
/* Subroutine of cxx_eval_call_expression.
We are processing a call expression (either CALL_EXPR or
AGGR_INIT_EXPR) in the context of CTX. Evaluate
@ -1032,6 +1040,7 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
tree fun = new_call->fundef->decl;
tree parms = DECL_ARGUMENTS (fun);
int i;
tree *p = &new_call->bindings;
for (i = 0; i < nargs; ++i)
{
tree x, arg;
@ -1039,19 +1048,19 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
x = get_nth_callarg (t, i);
/* For member function, the first argument is a pointer to the implied
object. For a constructor, it might still be a dummy object, in
which case we get the real argument from ctx or the AGGR_INIT_EXPR. */
which case we get the real argument from ctx. */
if (i == 0 && DECL_CONSTRUCTOR_P (fun)
&& is_dummy_object (x))
{
x = ctx->object;
if (!x)
x = AGGR_INIT_EXPR_SLOT (t);
x = cp_build_addr_expr (x, tf_warning_or_error);
}
if (parms && DECL_BY_REFERENCE (parms))
if (parms && DECL_BY_REFERENCE (parms) && !use_new_call)
{
/* cp_genericize made this a reference for argument passing, but
we don't want to treat it like one for constexpr evaluation. */
we don't want to treat it like one for C++11 constexpr
evaluation. C++14 constexpr evaluation uses the genericized
DECL_SAVED_TREE. */
gcc_assert (TREE_CODE (type) == REFERENCE_TYPE);
gcc_assert (TREE_CODE (TREE_TYPE (x)) == REFERENCE_TYPE);
type = TREE_TYPE (type);
@ -1073,7 +1082,8 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
/* Make sure the binding has the same type as the parm. */
if (TREE_CODE (type) != REFERENCE_TYPE)
arg = adjust_temp_type (type, arg);
new_call->bindings = tree_cons (parms, arg, new_call->bindings);
*p = build_tree_list (parms, arg);
p = &TREE_CHAIN (*p);
next:
parms = TREE_CHAIN (parms);
}
@ -1205,6 +1215,20 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
return t;
}
}
constexpr_ctx new_ctx = *ctx;
if (DECL_CONSTRUCTOR_P (fun) && !ctx->object
&& TREE_CODE (t) == AGGR_INIT_EXPR)
{
/* We want to have an initialization target for an AGGR_INIT_EXPR.
If we don't already have one in CTX, use the AGGR_INIT_EXPR_SLOT. */
new_ctx.object = AGGR_INIT_EXPR_SLOT (t);
tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL);
CONSTRUCTOR_NO_IMPLICIT_ZERO (ctor) = true;
ctx->values->put (new_ctx.object, ctor);
ctx = &new_ctx;
}
cxx_bind_parameters_in_call (ctx, t, &new_call,
allow_non_constant, non_constant_p, overflow_p);
if (*non_constant_p)
@ -1251,18 +1275,91 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
result = entry->result;
if (!result || result == error_mark_node)
{
constexpr_ctx new_ctx = *ctx;
new_ctx.call = &new_call;
result = (cxx_eval_constant_expression
(&new_ctx, new_call.fundef->body,
allow_non_constant, addr,
non_constant_p, overflow_p));
if (!use_new_call)
{
new_ctx.call = &new_call;
result = (cxx_eval_constant_expression
(&new_ctx, new_call.fundef->body,
allow_non_constant, addr,
non_constant_p, overflow_p));
}
else
{
if (DECL_SAVED_TREE (fun) == NULL_TREE
&& (DECL_CONSTRUCTOR_P (fun) || DECL_DESTRUCTOR_P (fun)))
/* The maybe-in-charge 'tor had its DECL_SAVED_TREE
cleared, try the first clone. */
fun = DECL_CHAIN (fun);
gcc_assert (DECL_SAVED_TREE (fun));
tree parms, res;
/* Unshare the whole function body. */
tree body = copy_fn (fun, parms, res);
/* Associate the bindings with the remapped parms. */
tree bound = new_call.bindings;
tree remapped = parms;
while (bound)
{
tree oparm = TREE_PURPOSE (bound);
tree arg = TREE_VALUE (bound);
gcc_assert (DECL_NAME (remapped) == DECL_NAME (oparm));
ctx->values->put (remapped, arg);
bound = TREE_CHAIN (bound);
remapped = DECL_CHAIN (remapped);
}
/* Add the RESULT_DECL to the values map, too. */
tree slot = NULL_TREE;
if (DECL_BY_REFERENCE (res))
{
slot = AGGR_INIT_EXPR_SLOT (t);
tree addr = build_address (slot);
addr = build_nop (TREE_TYPE (res), addr);
ctx->values->put (res, addr);
ctx->values->put (slot, NULL_TREE);
}
else
ctx->values->put (res, NULL_TREE);
cxx_eval_constant_expression (ctx, body, allow_non_constant,
addr, non_constant_p, overflow_p);
if (VOID_TYPE_P (TREE_TYPE (res)))
/* This can be null for a subobject constructor call, in
which case what we care about is the initialization
side-effects rather than the value. We could get at the
value by evaluating *this, but we don't bother; there's
no need to put such a call in the hash table. */
result = addr ? ctx->object : ctx->ctor;
else
{
result = *ctx->values->get (slot ? slot : res);
if (result == NULL_TREE)
{
if (!allow_non_constant)
error ("constexpr call flows off the end "
"of the function");
*non_constant_p = true;
}
}
/* 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->values->remove (res);
if (slot)
ctx->values->remove (slot);
for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
ctx->values->remove (parm);
}
}
if (result == error_mark_node)
*non_constant_p = true;
if (*non_constant_p)
entry->result = result = error_mark_node;
else
else if (result)
{
/* If this was a call to initialize an object, set the type of
the CONSTRUCTOR to the type of that object. */
@ -1277,6 +1374,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
}
entry->result = result;
}
else
result = void_node;
}
pop_cx_call_context ();
@ -1537,7 +1636,13 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (whole), i, field, value)
{
if (field == part)
return value;
{
if (value)
return value;
else
/* We're in the middle of initializing it. */
break;
}
}
if (TREE_CODE (TREE_TYPE (whole)) == UNION_TYPE
&& CONSTRUCTOR_NELTS (whole) > 0)
@ -1903,6 +2008,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
&argvec, elttype, LOOKUP_NORMAL,
tf_warning_or_error);
release_tree_vector (argvec);
init = build_aggr_init_expr (TREE_TYPE (init), init);
pre_init = true;
}
@ -2219,7 +2325,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
/* If we're pulling out the value of an empty base, make sure
that the whole object is constant and then return an empty
CONSTRUCTOR. */
if (empty_base)
if (empty_base && !addr)
{
VERIFY_CONSTANT (r);
r = build_constructor (TREE_TYPE (t), NULL);
@ -2317,9 +2423,163 @@ var_in_constexpr_fn (tree t)
&& DECL_DECLARED_CONSTEXPR_P (ctx));
}
/* Evaluate an INIT_EXPR or MODIFY_EXPR. */
static tree
cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
bool allow_non_constant, bool addr,
bool *non_constant_p, bool *overflow_p)
{
constexpr_ctx new_ctx = *ctx;
/* First we figure out where we're storing to. */
tree target = TREE_OPERAND (t, 0);
target = cxx_eval_constant_expression (ctx, target,
allow_non_constant, true,
non_constant_p, overflow_p);
if (*non_constant_p)
return t;
/* And then find the underlying variable. */
vec<tree,va_gc> *refs = make_tree_vector();
tree object = NULL_TREE;
for (tree probe = target; object == NULL_TREE; )
{
switch (TREE_CODE (probe))
{
case BIT_FIELD_REF:
case COMPONENT_REF:
case ARRAY_REF:
vec_safe_push (refs, TREE_OPERAND (probe, 1));
vec_safe_push (refs, TREE_TYPE (probe));
probe = TREE_OPERAND (probe, 0);
break;
default:
object = probe;
gcc_assert (DECL_P (object));
}
}
/* And then find/build up our initializer for the path to the subobject
we're initializing. */
tree *valp = ctx->values->get (object);
if (!valp)
{
/* A constant-expression cannot modify objects from outside the
constant-expression. */
if (!allow_non_constant)
error ("modification of %qD is not a constant-expression", object);
*non_constant_p = true;
return t;
}
tree type = TREE_TYPE (object);
while (!refs->is_empty())
{
if (*valp == NULL_TREE)
{
*valp = build_constructor (type, NULL);
CONSTRUCTOR_NO_IMPLICIT_ZERO (*valp) = true;
}
constructor_elt ce;
type = refs->pop();
ce.index = refs->pop();
ce.value = NULL_TREE;
unsigned HOST_WIDE_INT idx = 0;
constructor_elt *cep = NULL;
for (idx = 0;
vec_safe_iterate (CONSTRUCTOR_ELTS (*valp), idx, &cep);
idx++)
/* ??? slow */
if (cp_tree_equal (ce.index, cep->index))
break;
if (!cep)
cep = vec_safe_push (CONSTRUCTOR_ELTS (*valp), ce);
valp = &cep->value;
}
release_tree_vector (refs);
if ((AGGREGATE_TYPE_P (TREE_TYPE (t)) || VECTOR_TYPE_P (TREE_TYPE (t))))
{
/* Create a new CONSTRUCTOR in case evaluation of the initializer
wants to modify it. */
*valp = new_ctx.ctor = build_constructor (TREE_TYPE (t), NULL);
CONSTRUCTOR_NO_IMPLICIT_ZERO (new_ctx.ctor) = true;
new_ctx.object = target;
}
tree init = cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (t, 1),
allow_non_constant, false,
non_constant_p, overflow_p);
if (target == object)
/* The hash table might have moved since the get earlier. */
ctx->values->put (object, init);
else
*valp = init;
if (*non_constant_p)
return t;
else if (addr)
return target;
else
return *valp;
}
/* Evaluate a ++ or -- expression. */
static tree
cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
bool allow_non_constant, bool addr,
bool *non_constant_p, bool *overflow_p)
{
enum tree_code code = TREE_CODE (t);
tree type = TREE_TYPE (t);
tree op = TREE_OPERAND (t, 0);
tree offset = TREE_OPERAND (t, 1);
gcc_assert (TREE_CONSTANT (offset));
/* The operand as an lvalue. */
op = cxx_eval_constant_expression (ctx, op, allow_non_constant, true,
non_constant_p, overflow_p);
/* The operand as an rvalue. */
tree val = rvalue (op);
val = cxx_eval_constant_expression (ctx, val, allow_non_constant, false,
non_constant_p, overflow_p);
VERIFY_CONSTANT (val);
/* The modified value. */
bool inc = (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR);
tree mod = fold_build2 (inc ? PLUS_EXPR : MINUS_EXPR,
type, val, offset);
VERIFY_CONSTANT (mod);
/* Storing the modified value. */
tree store = build2 (MODIFY_EXPR, type, op, mod);
cxx_eval_constant_expression (ctx, store, allow_non_constant,
true, non_constant_p, overflow_p);
/* And the value of the expression. */
if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
{
/* Prefix ops are lvalues. */
if (addr)
return op;
else
/* But we optimize when the caller wants an rvalue. */
return mod;
}
else
/* Postfix ops are rvalues. */
return val;
}
/* Attempt to reduce the expression T to a constant value.
On failure, issue diagnostic and return error_mark_node. */
/* FIXME unify with c_fully_fold */
/* FIXME overflow_p is too global */
static tree
cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
@ -2348,6 +2608,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
switch (TREE_CODE (t))
{
case RESULT_DECL:
if (addr)
return t;
/* We ask for an rvalue for the RESULT_DECL when indirecting
through an invisible reference. */
gcc_assert (DECL_BY_REFERENCE (t));
return (*ctx->values->get (t));
case VAR_DECL:
if (addr)
return t;
@ -2357,11 +2625,6 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
if (TREE_CODE (r) == TARGET_EXPR
&& TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR)
r = TARGET_EXPR_INITIAL (r);
if (TREE_CODE (r) == VAR_DECL && var_in_constexpr_fn (r)
&& DECL_INITIAL (r))
r = cxx_eval_constant_expression (ctx, DECL_INITIAL (r),
allow_non_constant, false,
non_constant_p, overflow_p);
if (TREE_CODE (r) == VAR_DECL)
if (tree *p = ctx->values->get (r))
r = *p;
@ -2379,8 +2642,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
return t;
case PARM_DECL:
if (ctx && ctx->call && DECL_CONTEXT (t) == ctx->call->fundef->decl)
if (!use_new_call && ctx
&& ctx->call && DECL_CONTEXT (t) == ctx->call->fundef->decl)
r = lookup_parameter_binding (ctx->call, t);
else if (addr && TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE)
/* glvalue use. */;
else if (tree *p = ctx->values->get (r))
r = *p;
else if (addr)
/* Defer in case this is only used for its type. */;
else
@ -2397,6 +2665,34 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
non_constant_p, overflow_p);
break;
case DECL_EXPR:
{
r = DECL_EXPR_DECL (t);
if (AGGREGATE_TYPE_P (TREE_TYPE (r))
|| VECTOR_TYPE_P (TREE_TYPE (r)))
{
new_ctx = *ctx;
new_ctx.object = r;
new_ctx.ctor = build_constructor (TREE_TYPE (r), NULL);
CONSTRUCTOR_NO_IMPLICIT_ZERO (new_ctx.ctor) = true;
new_ctx.values->put (r, new_ctx.ctor);
ctx = &new_ctx;
}
if (tree init = DECL_INITIAL (r))
{
init = cxx_eval_constant_expression (ctx, init,
allow_non_constant, false,
non_constant_p, overflow_p);
ctx->values->put (r, init);
}
else if (ctx == &new_ctx)
/* We gave it a CONSTRUCTOR above. */;
else
ctx->values->put (r, NULL_TREE);
}
break;
case TARGET_EXPR:
if (!literal_type_p (TREE_TYPE (t)))
{
@ -2421,9 +2717,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
ctx->values->put (new_ctx.object, new_ctx.ctor);
ctx = &new_ctx;
}
/* else fall through. */
case INIT_EXPR:
/* Pass false for 'addr' because these codes indicate
/* Pass false for 'addr' because this indicates
initialization of a temporary. */
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, false,
@ -2433,6 +2727,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
r = adjust_temp_type (TREE_TYPE (t), r);
break;
case INIT_EXPR:
if (!use_new_call)
{
/* In C++11 constexpr evaluation we are looking for the value,
not the side-effect of the initialization. */
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, false,
non_constant_p, overflow_p);
break;
}
/* else fall through */
case MODIFY_EXPR:
r = cxx_eval_store_expression (ctx, t, allow_non_constant, addr,
non_constant_p, overflow_p);
break;
case SCOPE_REF:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, addr,
@ -2445,6 +2755,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case CLEANUP_POINT_EXPR:
case MUST_NOT_THROW_EXPR:
case SAVE_EXPR:
case EXPR_STMT:
case EH_SPEC_BLOCK:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
allow_non_constant, addr,
non_constant_p, overflow_p);
@ -2679,27 +2991,47 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
used, and they can't do anything with it, so just return it. */
return t;
case LAMBDA_EXPR:
case STATEMENT_LIST:
{
new_ctx = *ctx;
new_ctx.ctor = new_ctx.object = NULL_TREE;
tree_stmt_iterator i;
for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
{
cxx_eval_constant_expression (&new_ctx, tsi_stmt (i),
allow_non_constant, false,
non_constant_p, overflow_p);
if (*non_constant_p)
break;
}
}
break;
case BIND_EXPR:
return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
allow_non_constant, addr,
non_constant_p, overflow_p);
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTDECREMENT_EXPR:
return cxx_eval_increment_expression (ctx, t, allow_non_constant,
addr, non_constant_p, overflow_p);
case LAMBDA_EXPR:
case NEW_EXPR:
case VEC_NEW_EXPR:
case DELETE_EXPR:
case VEC_DELETE_EXPR:
case THROW_EXPR:
case MODIFY_EXPR:
case MODOP_EXPR:
/* GCC internal stuff. */
case VA_ARG_EXPR:
case OBJ_TYPE_REF:
case WITH_CLEANUP_EXPR:
case STATEMENT_LIST:
case BIND_EXPR:
case NON_DEPENDENT_EXPR:
case BASELINK:
case EXPR_STMT:
case OFFSET_REF:
if (!allow_non_constant)
error_at (EXPR_LOC_OR_LOC (t, input_location),
@ -2730,6 +3062,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
}
break;
case GOTO_EXPR:
case LOOP_EXPR:
case SWITCH_EXPR:
if (!allow_non_constant)
sorry ("%qs in constant expression", get_tree_code_name (TREE_CODE (t)));
*non_constant_p = true;
break;
default:
internal_error ("unexpected expression %qE of kind %s", t,
get_tree_code_name (TREE_CODE (t)));
@ -2768,8 +3108,13 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
initialized. */
ctx.ctor = build_constructor (type, NULL);
CONSTRUCTOR_NO_IMPLICIT_ZERO (ctx.ctor) = true;
if (!object && TREE_CODE (t) == TARGET_EXPR)
object = TARGET_EXPR_SLOT (t);
if (!object)
{
if (TREE_CODE (t) == TARGET_EXPR)
object = TARGET_EXPR_SLOT (t);
else if (TREE_CODE (t) == AGGR_INIT_EXPR)
object = AGGR_INIT_EXPR_SLOT (t);
}
ctx.object = object;
if (object)
gcc_assert (same_type_ignoring_top_level_qualifiers_p
@ -2859,13 +3204,6 @@ is_sub_constant_expr (tree t)
constexpr_ctx ctx = { NULL, NULL, NULL, NULL };
hash_map <tree, tree> map;
ctx.values = &map;
tree type = initialized_type (t);
if ((AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
&& TREE_CODE (t) != TARGET_EXPR)
{
ctx.ctor = build_constructor (type, NULL);
CONSTRUCTOR_NO_IMPLICIT_ZERO (ctx.ctor) = true;
}
cxx_eval_constant_expression (&ctx, t, true, false, &non_constant_p,
&overflow_p);
return !non_constant_p && !overflow_p;
@ -2996,6 +3334,8 @@ maybe_constant_init (tree t, tree decl)
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);
t = maybe_constant_value (t, decl);
if (TREE_CODE (t) == TARGET_EXPR)
{
@ -3078,6 +3418,7 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
case TEMPLATE_ID_EXPR:
case LABEL_DECL:
case LABEL_EXPR:
case CASE_LABEL_EXPR:
case CONST_DECL:
case SIZEOF_EXPR:
case ALIGNOF_EXPR:
@ -3091,7 +3432,10 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
case FIELD_DECL:
case PARM_DECL:
case USING_DECL:
case USING_STMT:
case PLACEHOLDER_EXPR:
case BREAK_STMT:
case CONTINUE_STMT:
return true;
case AGGR_INIT_EXPR:
@ -3103,6 +3447,14 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
const int nargs = call_expr_nargs (t);
i = 0;
if (fun == NULL_TREE)
{
/* fold_call_expr can't do anything with IFN calls. */
if (flags & tf_error)
error_at (EXPR_LOC_OR_LOC (t, input_location),
"call to internal function");
return false;
}
if (is_overloaded_fn (fun))
{
if (TREE_CODE (fun) == FUNCTION_DECL)
@ -3266,20 +3618,85 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
return potential_constant_expression_1 (x, rval, flags);
}
case STATEMENT_LIST:
{
tree_stmt_iterator i;
for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
{
if (!potential_constant_expression_1 (tsi_stmt (i), any, flags))
return false;
}
return true;
}
break;
case MODIFY_EXPR:
if (cxx_dialect < cxx14)
goto fail;
if (!potential_constant_expression_1 (TREE_OPERAND (t, 0), any, flags))
return false;
if (!potential_constant_expression_1 (TREE_OPERAND (t, 1), rval, flags))
return false;
return true;
case MODOP_EXPR:
if (cxx_dialect < cxx14)
goto fail;
if (!potential_constant_expression_1 (TREE_OPERAND (t, 0), rval, flags))
return false;
if (!potential_constant_expression_1 (TREE_OPERAND (t, 2), rval, flags))
return false;
return true;
case IF_STMT:
if (!potential_constant_expression_1 (IF_COND (t), rval, flags))
return false;
if (!potential_constant_expression_1 (THEN_CLAUSE (t), any, flags))
return false;
if (!potential_constant_expression_1 (ELSE_CLAUSE (t), any, flags))
return false;
return true;
case DO_STMT:
if (!potential_constant_expression_1 (DO_COND (t), rval, flags))
return false;
if (!potential_constant_expression_1 (DO_BODY (t), any, flags))
return false;
return true;
case FOR_STMT:
if (!potential_constant_expression_1 (FOR_INIT_STMT (t), any, flags))
return false;
if (!potential_constant_expression_1 (FOR_COND (t), rval, flags))
return false;
if (!potential_constant_expression_1 (FOR_EXPR (t), any, flags))
return false;
if (!potential_constant_expression_1 (FOR_BODY (t), any, flags))
return false;
return true;
case WHILE_STMT:
if (!potential_constant_expression_1 (WHILE_COND (t), rval, flags))
return false;
if (!potential_constant_expression_1 (WHILE_BODY (t), any, flags))
return false;
return true;
case SWITCH_STMT:
if (!potential_constant_expression_1 (SWITCH_STMT_COND (t), rval, flags))
return false;
if (!potential_constant_expression_1 (SWITCH_STMT_BODY (t), any, flags))
return false;
return true;
case LAMBDA_EXPR:
case DYNAMIC_CAST_EXPR:
case PSEUDO_DTOR_EXPR:
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTDECREMENT_EXPR:
case NEW_EXPR:
case VEC_NEW_EXPR:
case DELETE_EXPR:
case VEC_DELETE_EXPR:
case THROW_EXPR:
case MODIFY_EXPR:
case MODOP_EXPR:
case OMP_ATOMIC:
case OMP_ATOMIC_READ:
case OMP_ATOMIC_CAPTURE_OLD:
@ -3287,22 +3704,10 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
/* GCC internal stuff. */
case VA_ARG_EXPR:
case OBJ_TYPE_REF:
case WITH_CLEANUP_EXPR:
case CLEANUP_POINT_EXPR:
case MUST_NOT_THROW_EXPR:
case TRY_CATCH_EXPR:
case STATEMENT_LIST:
/* Don't bother trying to define a subset of statement-expressions to
be constant-expressions, at least for now. */
case STMT_EXPR:
case EXPR_STMT:
case BIND_EXPR:
case TRANSACTION_EXPR:
case IF_STMT:
case DO_STMT:
case FOR_STMT:
case WHILE_STMT:
case DECL_EXPR:
case ASM_EXPR:
fail:
if (flags & tf_error)
error ("expression %qE is not a constant-expression", t);
return false;
@ -3355,6 +3760,14 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
want_rval = true;
goto binary;
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTDECREMENT_EXPR:
if (cxx_dialect < cxx14)
goto fail;
goto unary;
case BIT_NOT_EXPR:
/* A destructor. */
if (TYPE_P (TREE_OPERAND (t, 0)))
@ -3372,6 +3785,7 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
case TRUTH_NOT_EXPR:
case FIXED_CONVERT_EXPR:
case UNARY_PLUS_EXPR:
unary:
return potential_constant_expression_1 (TREE_OPERAND (t, 0), rval,
flags);
@ -3396,7 +3810,18 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
(TREE_OPERAND (t, 0),
TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE, flags));
case BIND_EXPR:
return potential_constant_expression_1 (BIND_EXPR_BODY (t),
want_rval, flags);
case WITH_CLEANUP_EXPR:
case CLEANUP_POINT_EXPR:
case MUST_NOT_THROW_EXPR:
case TRY_CATCH_EXPR:
case EH_SPEC_BLOCK:
case EXPR_STMT:
case PAREN_EXPR:
case DECL_EXPR:
case NON_DEPENDENT_EXPR:
/* For convenience. */
case RETURN_EXPR:

View File

@ -1182,6 +1182,8 @@ struct GTY(()) language_function {
/* True if this function can throw an exception. */
BOOL_BITFIELD can_throw : 1;
BOOL_BITFIELD invalid_constexpr : 1;
hash_table<named_label_hasher> *x_named_labels;
cp_binding_level *bindings;
vec<tree, va_gc> *x_local_names;

View File

@ -4779,11 +4779,16 @@ start_decl (const cp_declarator *declarator,
if (current_function_decl && VAR_P (decl)
&& DECL_DECLARED_CONSTEXPR_P (current_function_decl))
{
bool ok = false;
if (DECL_THREAD_LOCAL_P (decl))
error ("%qD declared %<thread_local%> in %<constexpr%> function",
decl);
else if (TREE_STATIC (decl))
error ("%qD declared %<static%> in %<constexpr%> function", decl);
else
ok = true;
if (!ok)
cp_function_chain->invalid_constexpr = true;
}
if (!processing_template_decl && VAR_P (decl))
@ -5165,9 +5170,12 @@ check_for_uninitialized_const_var (tree decl)
permerror (DECL_SOURCE_LOCATION (decl),
"uninitialized const %qD", decl);
else
error_at (DECL_SOURCE_LOCATION (decl),
"uninitialized variable %qD in %<constexpr%> function",
decl);
{
error_at (DECL_SOURCE_LOCATION (decl),
"uninitialized variable %qD in %<constexpr%> function",
decl);
cp_function_chain->invalid_constexpr = true;
}
if (CLASS_TYPE_P (type))
{
@ -13995,6 +14003,7 @@ maybe_save_function_definition (tree fun)
{
if (!processing_template_decl
&& DECL_DECLARED_CONSTEXPR_P (fun)
&& !cp_function_chain->invalid_constexpr
&& !DECL_CLONED_FUNCTION_P (fun))
register_constexpr_fundef (fun, DECL_SAVED_TREE (fun));
}

View File

@ -11004,7 +11004,10 @@ cp_parser_jump_statement (cp_parser* parser)
case RID_GOTO:
if (parser->in_function_body
&& DECL_DECLARED_CONSTEXPR_P (current_function_decl))
error ("%<goto%> in %<constexpr%> function");
{
error ("%<goto%> in %<constexpr%> function");
cp_function_chain->invalid_constexpr = true;
}
/* Create the goto-statement. */
if (cp_lexer_next_token_is (parser->lexer, CPP_MULT))
@ -16588,7 +16591,10 @@ cp_parser_asm_definition (cp_parser* parser)
if (parser->in_function_body
&& DECL_DECLARED_CONSTEXPR_P (current_function_decl))
error ("%<asm%> in %<constexpr%> function");
{
error ("%<asm%> in %<constexpr%> function");
cp_function_chain->invalid_constexpr = true;
}
/* See if the next token is `volatile'. */
if (cp_parser_allow_gnu_extensions_p (parser)

View File

@ -23,20 +23,20 @@ struct C
struct D
{
constexpr D() { return;} // { dg-error "does not have empty body" }
constexpr D() { return;} // { dg-error "does not have empty body" "" { target c++11_only } }
};
struct D1
{
A a;
constexpr D1() { return;} // { dg-error "does not have empty body" }
constexpr D1() { return;} // { dg-error "does not have empty body" "" { target c++11_only } }
};
struct D2
{
A a;
A b;
constexpr D2() { return;} // { dg-error "does not have empty body" }
constexpr D2() { return;} // { dg-error "does not have empty body" "" { target c++11_only } }
};
struct D3
@ -44,5 +44,5 @@ struct D3
A a;
A b;
A c;
constexpr D3() { return;} // { dg-error "does not have empty body" }
constexpr D3() { return;} // { dg-error "does not have empty body" "" { target c++11_only } }
};

View File

@ -12,12 +12,14 @@ template <class T>
struct C
{
friend constexpr int f(C) { return 0; }
friend constexpr int g(C, A) { return 0; } // { dg-error "double" }
friend constexpr int g(C, A) { return 0; }
constexpr int m(C) { return 0; }
constexpr int m(A) { return 0; } // { dg-error "double" }
constexpr int m(A) { return 0; }
};
constexpr int i = f(C<int>());
constexpr int j = C<int>().m(C<int>());
constexpr int k = C<double>().m(A()); // { dg-error "constexpr" }
constexpr int l = g(C<double>(),A()); // { dg-error "constexpr" }
constexpr int k = C<double>().m(A()); // { dg-error "" }
constexpr int l = g(C<double>(),A()); // { dg-error "" }
// { dg-prune-output "parameter" }

View File

@ -27,14 +27,14 @@ constexpr void f(int x) // { dg-error "return type .void" }
{ /* ... */ }
constexpr int prev(int x)
{ return --x; } // { dg-error "--" }
{ return --x; } // { dg-error "--" "" { target c++11_only } }
constexpr int g(int x, int n) // error: body not just return expr
{
int r = 1;
while (--n > 0) r *= x;
return r;
} // { dg-error "not a return-statement" }
} // { dg-error "not a return-statement" "" { target c++11_only } }
constexpr int
bar(int x, int y) { return x + y + x * y; } // { dg-message "previously" }

View File

@ -33,14 +33,14 @@ constexpr void f(int x) // { dg-error "void" }
{ /* ... */ }
// error: use of decrement
constexpr int prev(int x)
{ return --x; } // { dg-error "-- x" }
{ return --x; } // { dg-error "-- x" "" { target c++11_only } }
// error: body not just return expr
constexpr int g(int x, int n) {
int r = 1;
while (--n > 0) r *= x;
return r;
} // { dg-error "body of constexpr function" }
} // { dg-error "body of constexpr function" "" { target c++11_only } }
class debug_flag {
public:

View File

@ -0,0 +1,13 @@
// { dg-do compile { target c++14 } }
constexpr int f (int i)
{
++i;
int x = i;
++x;
return x;
}
constexpr int i = f(42);
#define SA(X) static_assert((X),#X)
SA(i==44);

View File

@ -5817,3 +5817,55 @@ build_duplicate_type (tree type)
return type;
}
/* Unshare the entire DECL_SAVED_TREE of FN and return the remapped
parameters and RESULT_DECL in PARMS and RESULT. Used by C++ constexpr
evaluation. */
tree
copy_fn (tree fn, tree& parms, tree& result)
{
copy_body_data id;
tree param;
hash_map<tree, tree> decl_map;
tree *p = &parms;
*p = NULL_TREE;
memset (&id, 0, sizeof (id));
id.src_fn = fn;
id.dst_fn = current_function_decl;
id.src_cfun = DECL_STRUCT_FUNCTION (fn);
id.decl_map = &decl_map;
id.copy_decl = copy_decl_no_change;
id.transform_call_graph_edges = CB_CGE_DUPLICATE;
id.transform_new_cfg = false;
id.transform_return_to_modify = false;
id.transform_parameter = true;
id.transform_lang_insert_block = NULL;
/* Make sure not to unshare trees behind the front-end's back
since front-end specific mechanisms may rely on sharing. */
id.regimplify = false;
id.do_not_unshare = true;
/* We're not inside any EH region. */
id.eh_lp_nr = 0;
/* Remap the parameters and result and return them to the caller. */
for (param = DECL_ARGUMENTS (fn);
param;
param = DECL_CHAIN (param))
{
*p = remap_decl (param, &id);
p = &DECL_CHAIN (*p);
}
if (DECL_RESULT (fn))
result = remap_decl (DECL_RESULT (fn), &id);
else
result = NULL_TREE;
return copy_tree_body (&id);
}

View File

@ -209,6 +209,7 @@ extern tree remap_decl (tree decl, copy_body_data *id);
extern tree remap_type (tree type, copy_body_data *id);
extern gimple_seq copy_gimple_seq_and_replace_locals (gimple_seq seq);
extern bool debug_find_tree (tree, tree);
extern tree copy_fn (tree, tree&, tree&);
/* This is in tree-inline.c since the routine uses
data structures from the inliner. */