* RFA (tree-inline): PATCH for more C++14 constexpr support
@ 2014-11-16 6:35 Jason Merrill
2014-11-16 8:01 ` Jason Merrill
2014-11-17 10:34 ` Richard Biener
0 siblings, 2 replies; 6+ messages in thread
From: Jason Merrill @ 2014-11-16 6:35 UTC (permalink / raw)
To: gcc-patches List
[-- Attachment #1: Type: text/plain, Size: 1018 bytes --]
This patch implements more support for C++14 constexpr: it allows
arbitrary modification of variables in a constexpr function, but does
not currently handle jumping -- multiple returns, loops, switches.
The approach I took for this was to just use the DECL_SAVED_TREE for a
constexpr function as the basis for expansion rather than trying to
massage it into a magic expression. And now the values of local
variables, including parameters, are kept in the values hash map that I
introduced with the aggregate NSDMI patch.
But in the presence of recursive constexpr calls we can't use the same
PARM_DECL as a key, so we need to remap it. Thus I've added
remap_fn_body to tree-inline.c to unshare the entire function body and
remap the parms and result to avoid clashes.
This handles some more C++14 testcases and has no regressions on C++11
constexpr testcases. Support for jumps will follow soon.
Tested x86_64-pc-linux-gnu and powerpc64-unknown-linux-gnu.
Is the remap_fn_body function ok for trunk?
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: cx14.patch --]
[-- Type: text/x-patch; name="cx14.patch", Size: 40338 bytes --]
commit decc90baa31ae1535b4b0ab80aeee185471a5ddb
Author: Jason Merrill <jason@redhat.com>
Date: Sat Nov 15 10:46:55 2014 -0500
C++14 constexpr support (minus loops and multiple returns)
gcc/
* tree-inline.c (remap_fn_body): 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.
(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.
(cxx_eval_array_reference): Call build_cplus_new.
(cxx_eval_component_reference): Likewise.
(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.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 66d356f..53cfb18 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -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;
@@ -1048,10 +1057,12 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
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 +1084,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);
}
@@ -1251,18 +1263,82 @@ 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)
+ {
+ 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));
+ }
+ 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 = remap_fn_body (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. 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);
+
+ /* 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 +1353,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
}
entry->result = result;
}
+ else
+ result = void_node;
}
pop_cx_call_context ();
@@ -1464,6 +1542,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
/* If it's within the array bounds but doesn't have an explicit
initializer, it's value-initialized. */
tree val = build_value_init (elem_type, tf_warning_or_error);
+ val = build_cplus_new (elem_type, val, tf_warning_or_error);
return cxx_eval_constant_expression (ctx, val,
allow_non_constant, addr,
non_constant_p, overflow_p);
@@ -1537,7 +1616,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)
@@ -1563,6 +1648,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
/* If there's no explicit init for this field, it's value-initialized. */
value = build_value_init (TREE_TYPE (t), tf_warning_or_error);
+ value = build_cplus_new (TREE_TYPE (t), value, tf_warning_or_error);
return cxx_eval_constant_expression (ctx, value,
allow_non_constant, addr,
non_constant_p, overflow_p);
@@ -2317,6 +2403,164 @@ 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;
+ new_ctx.object = target;
+
+ /* 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;
+ }
+ gcc_assert (!AGGREGATE_TYPE_P (TREE_TYPE (object))
+ || refs->is_empty()
+ || (TREE_CODE (*valp) == CONSTRUCTOR
+ && (same_type_ignoring_top_level_qualifiers_p
+ (TREE_TYPE (*valp), TREE_TYPE (object)))));
+ while (!refs->is_empty())
+ {
+ tree type = refs->pop();
+ constructor_elt ce;
+ 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)
+ {
+ if (!refs->is_empty())
+ {
+ ce.value = new_ctx.ctor = build_constructor (type, NULL);
+ CONSTRUCTOR_NO_IMPLICIT_ZERO (ce.value) = true;
+ }
+ 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;
+ }
+
+ 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 */
@@ -2348,6 +2592,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 +2609,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 +2626,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 +2649,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 +2701,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 +2711,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 +2739,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 +2975,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 +3046,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 +3092,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
@@ -2996,6 +3325,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 +3409,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,6 +3423,7 @@ 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:
return true;
@@ -3103,6 +3436,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 +3607,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 DYNAMIC_CAST_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 +3693,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 +3749,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 +3774,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 +3799,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:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b69c736..31e1620 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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;
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 1ef9763..0dbda96 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -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))
{
@@ -14010,6 +14018,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));
}
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 111ec10..3eff5fa 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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)
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C
index c8043e3..708f5f2 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C
@@ -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 } }
};
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
index 55a2329..85dfca4 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C
index 8cb32c9..8c51c9d 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C
index fb4c012..35f5e8e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C
@@ -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:
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-incr1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-incr1.C
new file mode 100644
index 0000000..2b099c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-incr1.C
@@ -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);
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 520546e..e416dab 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -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
+remap_fn_body (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);
+}
diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h
index 9eb75c7..ac0009b 100644
--- a/gcc/tree-inline.h
+++ b/gcc/tree-inline.h
@@ -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 remap_fn_body (tree, tree&, tree&);
/* This is in tree-inline.c since the routine uses
data structures from the inliner. */
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: RFA (tree-inline): PATCH for more C++14 constexpr support
2014-11-16 6:35 RFA (tree-inline): PATCH for more C++14 constexpr support Jason Merrill
@ 2014-11-16 8:01 ` Jason Merrill
2014-11-17 10:34 ` Richard Biener
1 sibling, 0 replies; 6+ messages in thread
From: Jason Merrill @ 2014-11-16 8:01 UTC (permalink / raw)
To: gcc-patches List
[-- Attachment #1: Type: text/plain, Size: 235 bytes --]
On 11/15/2014 11:59 PM, Jason Merrill wrote:
> This handles some more C++14 testcases and has no regressions on C++11
> constexpr testcases. Support for jumps will follow soon.
Like this, though it still needs a bit of cleaning up.
[-- Attachment #2: cx14-2.patch --]
[-- Type: text/x-patch, Size: 28357 bytes --]
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 53cfb18..611d4f2 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -871,7 +871,7 @@ struct constexpr_ctx {
static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
- bool, bool, bool *, bool *);
+ bool, bool, bool *, bool *, tree *);
/* Compute a hash value for a constexpr call representation. */
@@ -993,7 +993,8 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t,
{
args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, i),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
if (allow_non_constant && *non_constant_p)
return t;
}
@@ -1070,7 +1071,7 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
}
arg = cxx_eval_constant_expression (ctx, x, allow_non_constant,
TREE_CODE (type) == REFERENCE_TYPE,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p && allow_non_constant)
return;
@@ -1151,7 +1152,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
/* Might be a constexpr function pointer. */
fun = cxx_eval_constant_expression (ctx, fun, allow_non_constant,
/*addr*/false, non_constant_p,
- overflow_p);
+ overflow_p, NULL);
STRIP_NOPS (fun);
if (TREE_CODE (fun) == ADDR_EXPR)
fun = TREE_OPERAND (fun, 0);
@@ -1187,7 +1188,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
{
tree arg = convert_from_reference (get_nth_callarg (t, 1));
return cxx_eval_constant_expression (ctx, arg, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p,
+ overflow_p, NULL);
}
else if (TREE_CODE (t) == AGGR_INIT_EXPR
&& AGGR_INIT_ZERO_FIRST (t))
@@ -1270,7 +1272,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
result = (cxx_eval_constant_expression
(&new_ctx, new_call.fundef->body,
allow_non_constant, addr,
- non_constant_p, overflow_p));
+ non_constant_p, overflow_p, NULL));
}
else
{
@@ -1310,8 +1312,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
else
ctx->values->put (res, NULL_TREE);
+ tree jump_target = NULL_TREE;
cxx_eval_constant_expression (ctx, body, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p,
+ &jump_target);
if (VOID_TYPE_P (TREE_TYPE (res)))
/* This can be null for a subobject constructor call, in
@@ -1320,7 +1324,16 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
to put such a call in the hash table. */
result = addr ? ctx->object : ctx->ctor;
else
- result = *ctx->values->get (slot ? slot : res);
+ {
+ 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
@@ -1433,7 +1446,8 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
tree r;
tree orig_arg = TREE_OPERAND (t, 0);
tree arg = cxx_eval_constant_expression (ctx, orig_arg, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p,
+ NULL);
VERIFY_CONSTANT (arg);
if (arg == orig_arg)
return t;
@@ -1456,11 +1470,11 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
tree lhs, rhs;
lhs = cxx_eval_constant_expression (ctx, orig_lhs,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (lhs);
rhs = cxx_eval_constant_expression (ctx, orig_rhs,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (rhs);
if (lhs == orig_lhs && rhs == orig_rhs)
return t;
@@ -1476,20 +1490,24 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
static tree
cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
bool allow_non_constant, bool addr,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
VERIFY_CONSTANT (val);
/* Don't VERIFY_CONSTANT the other operands. */
if (integer_zerop (val))
return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
}
/* Subroutine of cxx_eval_constant_expression.
@@ -1503,7 +1521,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
tree oldary = TREE_OPERAND (t, 0);
tree ary = cxx_eval_constant_expression (ctx, oldary,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
tree index, oldidx;
HOST_WIDE_INT i;
tree elem_type;
@@ -1513,7 +1531,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
oldidx = TREE_OPERAND (t, 1);
index = cxx_eval_constant_expression (ctx, oldidx,
allow_non_constant, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (index);
if (addr && ary == oldary && index == oldidx)
return t;
@@ -1545,7 +1563,8 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
val = build_cplus_new (elem_type, val, tf_warning_or_error);
return cxx_eval_constant_expression (ctx, val,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
}
if (!allow_non_constant)
@@ -1591,7 +1610,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
tree orig_whole = TREE_OPERAND (t, 0);
tree whole = cxx_eval_constant_expression (ctx, orig_whole,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
if (whole == orig_whole)
return t;
if (addr)
@@ -1651,7 +1670,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
value = build_cplus_new (TREE_TYPE (t), value, tf_warning_or_error);
return cxx_eval_constant_expression (ctx, value,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
}
/* Subroutine of cxx_eval_constant_expression.
@@ -1669,7 +1688,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
HOST_WIDE_INT istart, isize;
tree whole = cxx_eval_constant_expression (ctx, orig_whole,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
tree start, field, value;
unsigned HOST_WIDE_INT i;
@@ -1752,13 +1771,14 @@ cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t,
tree r;
tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (lhs);
if (tree_int_cst_equal (lhs, bailout_value))
return lhs;
gcc_assert (tree_int_cst_equal (lhs, continue_value));
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
- allow_non_constant, addr, non_constant_p, overflow_p);
+ allow_non_constant, addr, non_constant_p,
+ overflow_p, NULL);
VERIFY_CONSTANT (r);
return r;
}
@@ -1901,7 +1921,8 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
CONSTRUCTOR_APPEND_ELT (*p, index, new_ctx.ctor);
tree elt = cxx_eval_constant_expression (&new_ctx, value,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
/* Don't VERIFY_CONSTANT here. */
if (allow_non_constant && *non_constant_p)
break;
@@ -2018,7 +2039,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
we just pre-built above. */
eltinit = (cxx_eval_constant_expression
(&new_ctx, init, allow_non_constant,
- addr, non_constant_p, overflow_p));
+ addr, non_constant_p, overflow_p, NULL));
}
else
{
@@ -2032,7 +2053,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
eltinit = force_rvalue (eltinit, tf_warning_or_error);
eltinit = (cxx_eval_constant_expression
(&new_ctx, eltinit, allow_non_constant, addr,
- non_constant_p, overflow_p));
+ non_constant_p, overflow_p, NULL));
}
if (*non_constant_p && !allow_non_constant)
break;
@@ -2268,7 +2289,8 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
{
tree orig_op0 = TREE_OPERAND (t, 0);
tree op0 = cxx_eval_constant_expression (ctx, orig_op0, allow_non_constant,
- /*addr*/false, non_constant_p, overflow_p);
+ /*addr*/false, non_constant_p,
+ overflow_p, NULL);
bool empty_base = false;
tree r;
@@ -2281,7 +2303,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
if (r)
r = cxx_eval_constant_expression (ctx, r, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p, NULL);
else
{
tree sub = op0;
@@ -2383,7 +2405,8 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
{
args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
VERIFY_CONSTANT (args[i]);
}
@@ -2416,7 +2439,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
tree target = TREE_OPERAND (t, 0);
target = cxx_eval_constant_expression (ctx, target,
allow_non_constant, true,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
if (*non_constant_p)
return t;
new_ctx.object = target;
@@ -2497,7 +2520,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
tree init = cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (t, 1),
allow_non_constant, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
if (target == object)
/* The hash table might have moved since the get earlier. */
ctx->values->put (object, init);
@@ -2527,12 +2550,12 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
/* The operand as an lvalue. */
op = cxx_eval_constant_expression (ctx, op, allow_non_constant, true,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
/* 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);
+ non_constant_p, overflow_p, NULL);
VERIFY_CONSTANT (val);
/* The modified value. */
@@ -2544,7 +2567,7 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
/* 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);
+ true, non_constant_p, overflow_p, NULL);
/* And the value of the expression. */
if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
@@ -2561,6 +2584,146 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
return val;
}
+static bool
+jump_returns (tree *jump_target)
+{
+ return *jump_target
+ && TREE_CODE (*jump_target) == RETURN_EXPR;
+}
+
+static bool
+jump_breaks (tree *jump_target)
+{
+ return *jump_target
+ && TREE_CODE (*jump_target) == LABEL_DECL
+ && LABEL_DECL_BREAK (*jump_target);
+}
+
+#if 0
+static bool
+jump_continues (tree *jump_target)
+{
+ return *jump_target
+ && TREE_CODE (*jump_target) == LABEL_DECL
+ && LABEL_DECL_CONTINUE (*jump_target);
+}
+#endif
+
+static bool
+jump_case (tree *jump_target)
+{
+ return *jump_target
+ && TREE_CODE (*jump_target) == INTEGER_CST;
+}
+
+static bool
+label_matches (tree *jump_target, tree_stmt_iterator i,
+ tree_stmt_iterator& default_label)
+{
+ tree stmt = tsi_stmt (i);
+ switch (TREE_CODE (*jump_target))
+ {
+ case LABEL_DECL:
+ if (TREE_CODE (stmt) == LABEL_EXPR
+ && LABEL_EXPR_LABEL (stmt) == *jump_target)
+ return true;
+ break;
+
+ case INTEGER_CST:
+ if (TREE_CODE (stmt) == CASE_LABEL_EXPR)
+ {
+ if (!CASE_LOW (stmt))
+ default_label = i;
+ else if (tree_int_cst_equal (*jump_target, CASE_LOW (stmt)))
+ return true;
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ return false;
+}
+
+static tree
+cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
+ bool allow_non_constant,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ tree_stmt_iterator i;
+ tree_stmt_iterator default_label = tree_stmt_iterator();
+ for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
+ {
+ reenter:
+ tree stmt = tsi_stmt (i);
+ if (*jump_target)
+ {
+ if (TREE_CODE (stmt) == STATEMENT_LIST)
+ /* The label we want might be inside. */;
+ else if (label_matches (jump_target, i, default_label))
+ /* Found it. */
+ *jump_target = NULL_TREE;
+ else
+ continue;
+ }
+ cxx_eval_constant_expression (ctx, stmt,
+ allow_non_constant, false,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*non_constant_p)
+ break;
+ if (jump_returns (jump_target) || jump_breaks (jump_target))
+ break;
+ }
+ if (jump_case (jump_target) && !tsi_end_p (default_label))
+ {
+ i = default_label;
+ *jump_target = NULL_TREE;
+ goto reenter;
+ }
+ return NULL_TREE;
+}
+
+static tree
+cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
+ bool allow_non_constant,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ tree body = TREE_OPERAND (t, 0);
+ while (true)
+ {
+ cxx_eval_statement_list (ctx, body, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
+ if (jump_returns (jump_target) || jump_breaks (jump_target))
+ break;
+ }
+ if (jump_breaks (jump_target))
+ *jump_target = NULL_TREE;
+ return NULL_TREE;
+}
+
+static tree
+cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
+ bool allow_non_constant,
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
+{
+ tree cond = TREE_OPERAND (t, 0);
+ cond = cxx_eval_constant_expression (ctx, cond, allow_non_constant, false,
+ non_constant_p, overflow_p, NULL);
+ VERIFY_CONSTANT (cond);
+ *jump_target = cond;
+
+ tree body = TREE_OPERAND (t, 1);
+ cxx_eval_statement_list (ctx, body, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
+ if (jump_breaks (jump_target) || jump_case (jump_target))
+ *jump_target = NULL_TREE;
+ return NULL_TREE;
+}
+
/* 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 */
@@ -2568,7 +2731,8 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
static tree
cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
bool allow_non_constant, bool addr,
- bool *non_constant_p, bool *overflow_p)
+ bool *non_constant_p, bool *overflow_p,
+ tree *jump_target)
{
constexpr_ctx new_ctx;
tree r = t;
@@ -2623,6 +2787,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case FUNCTION_DECL:
case TEMPLATE_DECL:
case LABEL_DECL:
+ case LABEL_EXPR:
+ case CASE_LABEL_EXPR:
return t;
case PARM_DECL:
@@ -2667,7 +2833,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
{
init = cxx_eval_constant_expression (ctx, init,
allow_non_constant, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
ctx->values->put (r, init);
}
else if (ctx == &new_ctx)
@@ -2705,7 +2872,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
initialization of a temporary. */
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, false,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
if (!*non_constant_p)
/* Adjust the type of the result to the type of the temporary. */
r = adjust_temp_type (TREE_TYPE (t), r);
@@ -2718,7 +2885,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
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);
+ non_constant_p, overflow_p, NULL);
break;
}
/* else fall through */
@@ -2730,10 +2897,17 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case SCOPE_REF:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p, NULL);
break;
case RETURN_EXPR:
+ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+ allow_non_constant, addr,
+ non_constant_p, overflow_p,
+ jump_target);
+ *jump_target = t;
+ break;
+
case NON_LVALUE_EXPR:
case TRY_CATCH_EXPR:
case CLEANUP_POINT_EXPR:
@@ -2743,7 +2917,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case EH_SPEC_BLOCK:
r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
/* These differ from cxx_eval_unary_expression in that this doesn't
@@ -2760,7 +2935,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
tree op = cxx_eval_constant_expression (ctx, oldop,
allow_non_constant,
/*addr*/true,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
/* Don't VERIFY_CONSTANT here. */
if (*non_constant_p)
return t;
@@ -2811,15 +2987,18 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0))
|| TREE_CODE (op1) == EMPTY_CLASS_EXPR)
r = cxx_eval_constant_expression (ctx, op0, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p,
+ jump_target);
else
{
/* Check that the LHS is constant and then discard it. */
cxx_eval_constant_expression (ctx, op0, allow_non_constant,
- false, non_constant_p, overflow_p);
+ false, non_constant_p, overflow_p,
+ jump_target);
op1 = TREE_OPERAND (t, 1);
r = cxx_eval_constant_expression (ctx, op1, allow_non_constant,
- addr, non_constant_p, overflow_p);
+ addr, non_constant_p, overflow_p,
+ jump_target);
}
}
break;
@@ -2913,7 +3092,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
case COND_EXPR:
case VEC_COND_EXPR:
r = cxx_eval_conditional_expression (ctx, t, allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
break;
case CONSTRUCTOR:
@@ -2944,7 +3124,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
tree oldop = TREE_OPERAND (t, 0);
tree op = cxx_eval_constant_expression (ctx, oldop,
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ NULL);
if (*non_constant_p)
return t;
if (POINTER_TYPE_P (TREE_TYPE (t))
@@ -2976,25 +3157,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
return t;
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;
+ new_ctx = *ctx;
+ new_ctx.ctor = new_ctx.object = NULL_TREE;
+ return cxx_eval_statement_list (&new_ctx, t, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
case BIND_EXPR:
return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
allow_non_constant, addr,
- non_constant_p, overflow_p);
+ non_constant_p, overflow_p,
+ jump_target);
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
@@ -3042,16 +3214,23 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
gcc_assert (same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (t), TREE_TYPE (ctor)));
return cxx_eval_constant_expression
- (ctx, ctor, allow_non_constant, addr, non_constant_p, overflow_p);
+ (ctx, ctor, allow_non_constant, addr,
+ non_constant_p, overflow_p, NULL);
}
break;
case GOTO_EXPR:
+ *jump_target = TREE_OPERAND (t, 0);
+ break;
+
case LOOP_EXPR:
+ cxx_eval_loop_expr (ctx, t, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
+ break;
+
case SWITCH_EXPR:
- if (!allow_non_constant)
- sorry ("%qs in constant expression", get_tree_code_name (TREE_CODE (t)));
- *non_constant_p = true;
+ cxx_eval_switch_expr (ctx, t, allow_non_constant,
+ non_constant_p, overflow_p, jump_target);
break;
default:
@@ -3112,7 +3291,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
}
r = cxx_eval_constant_expression (&ctx, r, allow_non_constant,
- false, &non_constant_p, &overflow_p);
+ false, &non_constant_p, &overflow_p, NULL);
verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
@@ -3196,7 +3375,7 @@ is_sub_constant_expr (tree t)
CONSTRUCTOR_NO_IMPLICIT_ZERO (ctx.ctor) = true;
}
cxx_eval_constant_expression (&ctx, t, true, false, &non_constant_p,
- &overflow_p);
+ &overflow_p, NULL);
return !non_constant_p && !overflow_p;
}
@@ -3425,6 +3604,8 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
case USING_DECL:
case USING_STMT:
case PLACEHOLDER_EXPR:
+ case BREAK_STMT:
+ case CONTINUE_STMT:
return true;
case AGGR_INIT_EXPR:
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 81b26d2..82be90b 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -74,6 +74,10 @@ begin_bc_block (enum bc_t bc, location_t location)
tree label = create_artificial_label (location);
DECL_CHAIN (label) = bc_label[bc];
bc_label[bc] = label;
+ if (bc == bc_break)
+ LABEL_DECL_BREAK (label) = true;
+ else
+ LABEL_DECL_CONTINUE (label) = true;
return label;
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 31e1620..e784589 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -148,12 +148,14 @@ c-common.h, not after.
DECL_LOCAL_FUNCTION_P (in FUNCTION_DECL)
DECL_MUTABLE_P (in FIELD_DECL)
DECL_DEPENDENT_P (in USING_DECL)
+ LABEL_DECL_BREAK (in LABEL_DECL)
1: C_TYPEDEF_EXPLICITLY_SIGNED (in TYPE_DECL).
DECL_TEMPLATE_INSTANTIATED (in a VAR_DECL or a FUNCTION_DECL)
DECL_MEMBER_TEMPLATE_P (in TEMPLATE_DECL)
USING_DECL_TYPENAME_P (in USING_DECL)
DECL_VLA_CAPTURE_P (in FIELD_DECL)
DECL_ARRAY_PARAMETER_P (in PARM_DECL)
+ LABEL_DECL_CONTINUE (in LABEL_DECL)
2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL).
DECL_IMPLICIT_TYPEDEF_P (in a TYPE_DECL)
3: DECL_IN_AGGR_P.
@@ -3243,6 +3245,14 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
#define DECL_LOCAL_FUNCTION_P(NODE) \
DECL_LANG_FLAG_0 (FUNCTION_DECL_CHECK (NODE))
+/* Nonzero if NODE is the target for genericization of 'break' stmts. */
+#define LABEL_DECL_BREAK(NODE) \
+ DECL_LANG_FLAG_0 (LABEL_DECL_CHECK (NODE))
+
+/* Nonzero if NODE is the target for genericization of 'continue' stmts. */
+#define LABEL_DECL_CONTINUE(NODE) \
+ DECL_LANG_FLAG_1 (LABEL_DECL_CHECK (NODE))
+
/* True if NODE was declared with auto in its return type, but it has
started compilation and so the return type might have been changed by
return type deduction; its declared return type should be found in
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C
new file mode 100644
index 0000000..fc5d4f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ int j = 0;
+ for (; i > 0; --i)
+ ++j;
+ return j;
+}
+
+constexpr int i = f(42);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C
new file mode 100644
index 0000000..b114e21
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ return 24;
+ return 36;
+}
+
+constexpr int i = f(42);
+#define SA(X) static_assert((X),#X)
+SA(i==24);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C
new file mode 100644
index 0000000..ae2628d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+}
+
+constexpr int i = f(42); // { dg-error "flows off the end" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C
new file mode 100644
index 0000000..add7323
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ switch (i)
+ {
+ case 1:
+ return 42;
+ default:
+ return 0;
+ }
+}
+
+constexpr int i = f(1);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C
new file mode 100644
index 0000000..a459a5e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ int j = 0;
+ switch (i)
+ {
+ case 1:
+ j = 42;
+ break;
+ default:
+ j = 24;
+ break;
+ }
+ return j;
+}
+
+constexpr int i = f(1);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C
new file mode 100644
index 0000000..1aa1cf6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+ int j = 0;
+ switch (i)
+ {
+ case 1:
+ j = 42;
+ break;
+ default:
+ j = 24;
+ break;
+ }
+ return j;
+}
+
+constexpr int i = f(2);
+#define SA(X) static_assert((X),#X)
+SA(i==24);
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: RFA (tree-inline): PATCH for more C++14 constexpr support
2014-11-16 6:35 RFA (tree-inline): PATCH for more C++14 constexpr support Jason Merrill
2014-11-16 8:01 ` Jason Merrill
@ 2014-11-17 10:34 ` Richard Biener
2014-11-17 18:30 ` Jason Merrill
1 sibling, 1 reply; 6+ messages in thread
From: Richard Biener @ 2014-11-17 10:34 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches List
On Sun, Nov 16, 2014 at 5:59 AM, Jason Merrill <jason@redhat.com> wrote:
> This patch implements more support for C++14 constexpr: it allows arbitrary
> modification of variables in a constexpr function, but does not currently
> handle jumping -- multiple returns, loops, switches.
>
> The approach I took for this was to just use the DECL_SAVED_TREE for a
> constexpr function as the basis for expansion rather than trying to massage
> it into a magic expression. And now the values of local variables,
> including parameters, are kept in the values hash map that I introduced with
> the aggregate NSDMI patch.
>
> But in the presence of recursive constexpr calls we can't use the same
> PARM_DECL as a key, so we need to remap it. Thus I've added remap_fn_body
> to tree-inline.c to unshare the entire function body and remap the parms and
> result to avoid clashes.
>
> This handles some more C++14 testcases and has no regressions on C++11
> constexpr testcases. Support for jumps will follow soon.
>
> Tested x86_64-pc-linux-gnu and powerpc64-unknown-linux-gnu.
>
> Is the remap_fn_body function ok for trunk?
can you rename it to copy_fn please? It really copies parameter and
result and then the body.
Ok with that change.
Thanks,
Richard.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: RFA (tree-inline): PATCH for more C++14 constexpr support
2014-11-17 10:34 ` Richard Biener
@ 2014-11-17 18:30 ` Jason Merrill
2014-11-18 14:36 ` Andreas Schwab
2014-11-18 15:39 ` Kyrill Tkachov
0 siblings, 2 replies; 6+ messages in thread
From: Jason Merrill @ 2014-11-17 18:30 UTC (permalink / raw)
To: Richard Biener; +Cc: gcc-patches List
[-- Attachment #1: Type: text/plain, Size: 277 bytes --]
On 11/17/2014 05:29 AM, Richard Biener wrote:
> can you rename it to copy_fn please? It really copies parameter and
> result and then the body.
>
> Ok with that change.
Done. Here's what I'm checking in, along with a second patch to enable
the new code for C++11 as well:
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: cx14.patch --]
[-- Type: text/x-patch; name="cx14.patch", Size: 42326 bytes --]
commit 71b7bdd54c7bf0734336a3e80989b07a81f0c201
Author: Jason Merrill <jason@redhat.com>
Date: Sat Nov 15 10:46:55 2014 -0500
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.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 66d356f..57d0c46 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -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 = ↦
- 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:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 54f7e9b..adc8aa7 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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;
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 1f22c26..47da0ca 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -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));
}
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 111ec10..3eff5fa 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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)
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C
index c8043e3..708f5f2 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C
@@ -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 } }
};
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
index 55a2329..85dfca4 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C
index 8cb32c9..8c51c9d 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-function2.C
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C
index fb4c012..35f5e8e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C
@@ -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:
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-incr1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-incr1.C
new file mode 100644
index 0000000..2b099c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-incr1.C
@@ -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);
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 510b53e..4b937ca 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -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);
+}
diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h
index 9eb75c7..9d70e3e 100644
--- a/gcc/tree-inline.h
+++ b/gcc/tree-inline.h
@@ -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. */
[-- Attachment #3: cx14-always.patch --]
[-- Type: text/x-patch, Size: 735 bytes --]
commit e52e82e56507d1de1932abcafd80683c4dc00d1e
Author: Jason Merrill <jason@redhat.com>
Date: Sun Nov 16 17:14:12 2014 -0500
* constexpr.c (use_new_call): Always use new call handling.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 57d0c46..8881271 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1021,8 +1021,8 @@ adjust_temp_type (tree type, tree 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)
+ DECL_SAVED_TREE. */
+#define use_new_call true
/* Subroutine of cxx_eval_call_expression.
We are processing a call expression (either CALL_EXPR or
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: RFA (tree-inline): PATCH for more C++14 constexpr support
2014-11-17 18:30 ` Jason Merrill
@ 2014-11-18 14:36 ` Andreas Schwab
2014-11-18 15:39 ` Kyrill Tkachov
1 sibling, 0 replies; 6+ messages in thread
From: Andreas Schwab @ 2014-11-18 14:36 UTC (permalink / raw)
To: Jason Merrill; +Cc: Richard Biener, gcc-patches List
Jason Merrill <jason@redhat.com> writes:
> commit e52e82e56507d1de1932abcafd80683c4dc00d1e
> Author: Jason Merrill <jason@redhat.com>
> Date: Sun Nov 16 17:14:12 2014 -0500
>
> * constexpr.c (use_new_call): Always use new call handling.
>
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index 57d0c46..8881271 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -1021,8 +1021,8 @@ adjust_temp_type (tree type, tree 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)
> + DECL_SAVED_TREE. */
> +#define use_new_call true
>
> /* Subroutine of cxx_eval_call_expression.
> We are processing a call expression (either CALL_EXPR or
FAIL: 18_support/numeric_limits/requirements/constexpr_functions.cc (test for excess errors)
$ gcc/xg++ -shared-libgcc -Bgcc -nostdinc++ -Lm68k-linux/libstdc++-v3/src -Lm68k-linux/libstdc++-v3/src/.libs -Lm68k-linux/libstdc++-v3/libsupc++/.libs -B/daten/cross/m68k-linux/m68k-linux/bin/ -B/daten/cross/m68k-linux/m68k-linux/lib/ -isystem /daten/cross/m68k-linux/m68k-linux/include -isystem /daten/cross/m68k-linux/m68k-linux/sys-include -Bm68k-linux/./libstdc++-v3/src/.libs -D_GLIBCXX_ASSERT -fmessage-length=0 -ffunction-sections -fdata-sections -O2 -g -D_GNU_SOURCE -DLOCALEDIR="." -nostdinc++ -Im68k-linux/libstdc++-v3/include/m68k-linux -Im68k-linux/libstdc++-v3/include -I../libstdc++-v3/libsupc++ -I../libstdc++-v3/include/backward -I../libstdc++-v3/testsuite/util ../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc -std=gnu++14 -S -o constexpr_functions.s
In file included from ../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:21:0:
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc: In instantiation of ‘void __gnu_test::constexpr_member_functions::operator()()::_Concept::__constraint() [with _Ttesttype = std::numeric_limits<char>; _Tbasetype = char]’:
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:55:2: required from ‘void __gnu_test::constexpr_member_functions::operator()() [with _Ttesttype = std::numeric_limits<char>; _Tbasetype = char]’
m68k-linux/libstdc++-v3/include/ext/typelist.h:197:2: required from ‘void __gnu_cxx::typelist::detail::apply_generator2_<Gn, __gnu_cxx::typelist::chain<Hd, Tl>, __gnu_cxx::typelist::chain<Hd2, TlV> >::operator()(Gn&) [with Gn = __gnu_test::constexpr_member_functions; Hd1 = std::numeric_limits<char>; TlT = __gnu_cxx::typelist::chain<std::numeric_limits<signed char>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned char>, __gnu_cxx::typelist::chain<std::numeric_limits<short int>, __gnu_cxx::typelist::chain<std::numeric_limits<short unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<int>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<wchar_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char16_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char32_t>, __gnu_cxx::typelist::null_type> > > > > > > > > > > > >; Hd2 = char; TlV = __gnu_cxx::typelist::chain<signed char, __gnu_cxx::typelist::chain<unsigned char, __gnu_cxx::typelist::chain<short int, __gnu_cxx::typelist::chain<short unsigned int, __gnu_cxx::typelist::chain<int, __gnu_cxx::typelist::chain<unsigned int, __gnu_cxx::typelist::chain<long int, __gnu_cxx::typelist::chain<long unsigned int, __gnu_cxx::typelist::chain<long long int, __gnu_cxx::typelist::chain<long long unsigned int, __gnu_cxx::typelist::chain<wchar_t, __gnu_cxx::typelist::chain<char16_t, __gnu_cxx::typelist::chain<char32_t, __gnu_cxx::typelist::null_type> > > > > > > > > > > > >]’
m68k-linux/libstdc++-v3/include/ext/typelist.h:199:6: required from ‘void __gnu_cxx::typelist::detail::apply_generator2_<Gn, __gnu_cxx::typelist::chain<Hd, Tl>, __gnu_cxx::typelist::chain<Hd2, TlV> >::operator()(Gn&) [with Gn = __gnu_test::constexpr_member_functions; Hd1 = std::numeric_limits<bool>; TlT = __gnu_cxx::typelist::chain<std::numeric_limits<char>, __gnu_cxx::typelist::chain<std::numeric_limits<signed char>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned char>, __gnu_cxx::typelist::chain<std::numeric_limits<short int>, __gnu_cxx::typelist::chain<std::numeric_limits<short unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<int>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<wchar_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char16_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char32_t>, __gnu_cxx::typelist::null_type> > > > > > > > > > > > > >; Hd2 = bool; TlV = __gnu_cxx::typelist::chain<char, __gnu_cxx::typelist::chain<signed char, __gnu_cxx::typelist::chain<unsigned char, __gnu_cxx::typelist::chain<short int, __gnu_cxx::typelist::chain<short unsigned int, __gnu_cxx::typelist::chain<int, __gnu_cxx::typelist::chain<unsigned int, __gnu_cxx::typelist::chain<long int, __gnu_cxx::typelist::chain<long unsigned int, __gnu_cxx::typelist::chain<long long int, __gnu_cxx::typelist::chain<long long unsigned int, __gnu_cxx::typelist::chain<wchar_t, __gnu_cxx::typelist::chain<char16_t, __gnu_cxx::typelist::chain<char32_t, __gnu_cxx::typelist::null_type> > > > > > > > > > > > > >]’
m68k-linux/libstdc++-v3/include/ext/typelist.h:424:8: required from ‘void __gnu_cxx::typelist::apply_generator(Gn&, TypelistT, TypelistV) [with Gn = __gnu_test::constexpr_member_functions; TypelistT = __gnu_cxx::typelist::node<__gnu_cxx::typelist::chain<std::numeric_limits<bool>, __gnu_cxx::typelist::chain<std::numeric_limits<char>, __gnu_cxx::typelist::chain<std::numeric_limits<signed char>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned char>, __gnu_cxx::typelist::chain<std::numeric_limits<short int>, __gnu_cxx::typelist::chain<std::numeric_limits<short unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<int>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<wchar_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char16_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char32_t>, __gnu_cxx::typelist::null_type> > > > > > > > > > > > > > > >; TypelistV = __gnu_cxx::typelist::node<__gnu_cxx::typelist::chain<bool, __gnu_cxx::typelist::chain<char, __gnu_cxx::typelist::chain<signed char, __gnu_cxx::typelist::chain<unsigned char, __gnu_cxx::typelist::chain<short int, __gnu_cxx::typelist::chain<short unsigned int, __gnu_cxx::typelist::chain<int, __gnu_cxx::typelist::chain<unsigned int, __gnu_cxx::typelist::chain<long int, __gnu_cxx::typelist::chain<long unsigned int, __gnu_cxx::typelist::chain<long long int, __gnu_cxx::typelist::chain<long long unsigned int, __gnu_cxx::typelist::chain<wchar_t, __gnu_cxx::typelist::chain<char16_t, __gnu_cxx::typelist::chain<char32_t, __gnu_cxx::typelist::null_type> > > > > > > > > > > > > > > >]’
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:68:50: required from here
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:37:25: in constexpr expansion of ‘std::numeric_limits<char>::min()’
m68k-linux/libstdc++-v3/include/limits:456:44: error: ‘(1u * 8u)’ is not a constant expression
min() _GLIBCXX_USE_NOEXCEPT { return __glibcxx_min(char); }
^
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:39:25: in constexpr expansion of ‘std::numeric_limits<char>::max()’
m68k-linux/libstdc++-v3/include/limits:459:44: error: ‘(1u * 8u)’ is not a constant expression
max() _GLIBCXX_USE_NOEXCEPT { return __glibcxx_max(char); }
^
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:41:28: in constexpr expansion of ‘std::numeric_limits<char>::lowest()’
m68k-linux/libstdc++-v3/include/limits:463:37: in constexpr expansion of ‘std::numeric_limits<char>::min()’
m68k-linux/libstdc++-v3/include/limits:456:44: error: ‘(1u * 8u)’ is not a constant expression
min() _GLIBCXX_USE_NOEXCEPT { return __glibcxx_min(char); }
^
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc: In instantiation of ‘void __gnu_test::constexpr_member_functions::operator()()::_Concept::__constraint() [with _Ttesttype = std::numeric_limits<wchar_t>; _Tbasetype = wchar_t]’:
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:55:2: required from ‘void __gnu_test::constexpr_member_functions::operator()() [with _Ttesttype = std::numeric_limits<wchar_t>; _Tbasetype = wchar_t]’
m68k-linux/libstdc++-v3/include/ext/typelist.h:199:6: recursively required from ‘void __gnu_cxx::typelist::detail::apply_generator2_<Gn, __gnu_cxx::typelist::chain<Hd, Tl>, __gnu_cxx::typelist::chain<Hd2, TlV> >::operator()(Gn&) [with Gn = __gnu_test::constexpr_member_functions; Hd1 = std::numeric_limits<char>; TlT = __gnu_cxx::typelist::chain<std::numeric_limits<signed char>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned char>, __gnu_cxx::typelist::chain<std::numeric_limits<short int>, __gnu_cxx::typelist::chain<std::numeric_limits<short unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<int>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<wchar_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char16_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char32_t>, __gnu_cxx::typelist::null_type> > > > > > > > > > > > >; Hd2 = char; TlV = __gnu_cxx::typelist::chain<signed char, __gnu_cxx::typelist::chain<unsigned char, __gnu_cxx::typelist::chain<short int, __gnu_cxx::typelist::chain<short unsigned int, __gnu_cxx::typelist::chain<int, __gnu_cxx::typelist::chain<unsigned int, __gnu_cxx::typelist::chain<long int, __gnu_cxx::typelist::chain<long unsigned int, __gnu_cxx::typelist::chain<long long int, __gnu_cxx::typelist::chain<long long unsigned int, __gnu_cxx::typelist::chain<wchar_t, __gnu_cxx::typelist::chain<char16_t, __gnu_cxx::typelist::chain<char32_t, __gnu_cxx::typelist::null_type> > > > > > > > > > > > >]’
m68k-linux/libstdc++-v3/include/ext/typelist.h:199:6: required from ‘void __gnu_cxx::typelist::detail::apply_generator2_<Gn, __gnu_cxx::typelist::chain<Hd, Tl>, __gnu_cxx::typelist::chain<Hd2, TlV> >::operator()(Gn&) [with Gn = __gnu_test::constexpr_member_functions; Hd1 = std::numeric_limits<bool>; TlT = __gnu_cxx::typelist::chain<std::numeric_limits<char>, __gnu_cxx::typelist::chain<std::numeric_limits<signed char>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned char>, __gnu_cxx::typelist::chain<std::numeric_limits<short int>, __gnu_cxx::typelist::chain<std::numeric_limits<short unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<int>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<wchar_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char16_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char32_t>, __gnu_cxx::typelist::null_type> > > > > > > > > > > > > >; Hd2 = bool; TlV = __gnu_cxx::typelist::chain<char, __gnu_cxx::typelist::chain<signed char, __gnu_cxx::typelist::chain<unsigned char, __gnu_cxx::typelist::chain<short int, __gnu_cxx::typelist::chain<short unsigned int, __gnu_cxx::typelist::chain<int, __gnu_cxx::typelist::chain<unsigned int, __gnu_cxx::typelist::chain<long int, __gnu_cxx::typelist::chain<long unsigned int, __gnu_cxx::typelist::chain<long long int, __gnu_cxx::typelist::chain<long long unsigned int, __gnu_cxx::typelist::chain<wchar_t, __gnu_cxx::typelist::chain<char16_t, __gnu_cxx::typelist::chain<char32_t, __gnu_cxx::typelist::null_type> > > > > > > > > > > > > >]’
m68k-linux/libstdc++-v3/include/ext/typelist.h:424:8: required from ‘void __gnu_cxx::typelist::apply_generator(Gn&, TypelistT, TypelistV) [with Gn = __gnu_test::constexpr_member_functions; TypelistT = __gnu_cxx::typelist::node<__gnu_cxx::typelist::chain<std::numeric_limits<bool>, __gnu_cxx::typelist::chain<std::numeric_limits<char>, __gnu_cxx::typelist::chain<std::numeric_limits<signed char>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned char>, __gnu_cxx::typelist::chain<std::numeric_limits<short int>, __gnu_cxx::typelist::chain<std::numeric_limits<short unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<int>, __gnu_cxx::typelist::chain<std::numeric_limits<unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long int>, __gnu_cxx::typelist::chain<std::numeric_limits<long long unsigned int>, __gnu_cxx::typelist::chain<std::numeric_limits<wchar_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char16_t>, __gnu_cxx::typelist::chain<std::numeric_limits<char32_t>, __gnu_cxx::typelist::null_type> > > > > > > > > > > > > > > >; TypelistV = __gnu_cxx::typelist::node<__gnu_cxx::typelist::chain<bool, __gnu_cxx::typelist::chain<char, __gnu_cxx::typelist::chain<signed char, __gnu_cxx::typelist::chain<unsigned char, __gnu_cxx::typelist::chain<short int, __gnu_cxx::typelist::chain<short unsigned int, __gnu_cxx::typelist::chain<int, __gnu_cxx::typelist::chain<unsigned int, __gnu_cxx::typelist::chain<long int, __gnu_cxx::typelist::chain<long unsigned int, __gnu_cxx::typelist::chain<long long int, __gnu_cxx::typelist::chain<long long unsigned int, __gnu_cxx::typelist::chain<wchar_t, __gnu_cxx::typelist::chain<char16_t, __gnu_cxx::typelist::chain<char32_t, __gnu_cxx::typelist::null_type> > > > > > > > > > > > > > > >]’
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:68:50: required from here
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:37:25: in constexpr expansion of ‘std::numeric_limits<wchar_t>::min()’
m68k-linux/libstdc++-v3/include/limits:666:44: error: ‘(4u * 8u)’ is not a constant expression
min() _GLIBCXX_USE_NOEXCEPT { return __glibcxx_min (wchar_t); }
^
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:39:25: in constexpr expansion of ‘std::numeric_limits<wchar_t>::max()’
m68k-linux/libstdc++-v3/include/limits:669:44: error: ‘(4u * 8u)’ is not a constant expression
max() _GLIBCXX_USE_NOEXCEPT { return __glibcxx_max (wchar_t); }
^
../libstdc++-v3/testsuite/18_support/numeric_limits/requirements/constexpr_functions.cc:41:28: in constexpr expansion of ‘std::numeric_limits<wchar_t>::lowest()’
m68k-linux/libstdc++-v3/include/limits:673:37: in constexpr expansion of ‘std::numeric_limits<wchar_t>::min()’
m68k-linux/libstdc++-v3/include/limits:666:44: error: ‘(4u * 8u)’ is not a constant expression
min() _GLIBCXX_USE_NOEXCEPT { return __glibcxx_min (wchar_t); }
^
Andreas.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: RFA (tree-inline): PATCH for more C++14 constexpr support
2014-11-17 18:30 ` Jason Merrill
2014-11-18 14:36 ` Andreas Schwab
@ 2014-11-18 15:39 ` Kyrill Tkachov
1 sibling, 0 replies; 6+ messages in thread
From: Kyrill Tkachov @ 2014-11-18 15:39 UTC (permalink / raw)
To: Jason Merrill, Richard Biener; +Cc: gcc-patches List
On 17/11/14 18:15, Jason Merrill wrote:
> On 11/17/2014 05:29 AM, Richard Biener wrote:
>> can you rename it to copy_fn please? It really copies parameter and
>> result and then the body.
>>
>> Ok with that change.
> Done. Here's what I'm checking in, along with a second patch to enable
> the new code for C++11 as well:
Hi Jason,
These broke building libstdc++ on arm-none-eabi.
I've filed https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63936 for it.
Cheers,
Kyrill
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2014-11-18 15:26 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-16 6:35 RFA (tree-inline): PATCH for more C++14 constexpr support Jason Merrill
2014-11-16 8:01 ` Jason Merrill
2014-11-17 10:34 ` Richard Biener
2014-11-17 18:30 ` Jason Merrill
2014-11-18 14:36 ` Andreas Schwab
2014-11-18 15:39 ` Kyrill Tkachov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).