From 60813a463b1e1398cd9786b8c4761283efefb831 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 17 Nov 2014 13:16:14 -0500 Subject: [PATCH] C++14 constexpr support (minus loops and multiple returns) C++14 constexpr support (minus loops and multiple returns) gcc/ * tree-inline.c (copy_fn): New. * tree-inline.h: Declare it. gcc/cp/ * constexpr.c (use_new_call): New macro. (build_data_member_initialization): Ignore non-mem-inits. (check_constexpr_bind_expr_vars): Remove C++14 checks. (constexpr_fn_retval): Likewise. (check_constexpr_ctor_body): Do nothing in C++14. (massage_constexpr_body): In C++14 only collect mem-inits. (get_function_named_in_call): Handle null CALL_EXPR_FN. (cxx_bind_parameters_in_call): Build bindings in same order as parameters. Don't treat iniviref parms specially in new call mode. (cxx_eval_call_expression): If use_new_call, do constexpr expansion based on DECL_SAVED_TREE rather than the massaged constexpr body. Set up ctx->object from AGGR_INIT_EXPR_SLOT if we don't have one. (is_sub_constant_expr): Don't mess with ctx.ctor here. (cxx_eval_component_reference): A null element means we're mid- initialization. (cxx_eval_store_expression, cxx_eval_increment_expression): New. (cxx_eval_constant_expression): Handle RESULT_DECL, DECL_EXPR, MODIFY_EXPR, STATEMENT_LIST, BIND_EXPR, USING_STMT, PREINCREMENT_EXPR, POSTINCREMENT_EXPR, PREDECREMENT_EXPR, POSTDECREMENT_EXPR. Don't look into DECL_INITIAL of variables in constexpr functions. In new-call mode find parms in the values table. (potential_constant_expression_1): Handle null CALL_EXPR_FN. Handle STATEMENT_LIST, MODIFY_EXPR, MODOP_EXPR, IF_STMT, PREINCREMENT_EXPR, POSTINCREMENT_EXPR, PREDECREMENT_EXPR, POSTDECREMENT_EXPR, BIND_EXPR, WITH_CLEANUP_EXPR, CLEANUP_POINT_EXPR, MUST_NOT_THROW_EXPR, TRY_CATCH_EXPR, EH_SPEC_BLOCK, EXPR_STMT, DECL_EXPR, CASE_LABEL_EXPR, BREAK_STMT, CONTINUE_STMT, USING_STMT, IF_STMT, DO_STMT, FOR_STMT, WHILE_STMT, SWITCH_STMT, ASM_EXPR. (cxx_eval_vec_init_1): Call build_aggr_init_expr. (cxx_eval_indirect_ref): Don't return a CONSTRUCTOR when the caller wants an lvalue. (cxx_eval_outermost_constant_expr): Pull object out of AGGR_INIT_EXPR. (maybe_constant_init): Look through INIT_EXPR. (ensure_literal_type_for_constexpr_object): Set cp_function_chain->invalid_constexpr. * cp-tree.h (struct language_function): Add invalid_constexpr bitfield. * decl.c (start_decl): Set cp_function_chain->invalid_constexpr. (check_for_uninitialized_const_var): Likewise. (maybe_save_function_definition): Check it. * parser.c (cp_parser_jump_statement): Set cp_function_chain->invalid_constexpr. (cp_parser_asm_definition): Likewise. From-SVN: r217663 --- gcc/ChangeLog | 5 + gcc/cp/ChangeLog | 45 ++ gcc/cp/constexpr.c | 577 +++++++++++++++--- gcc/cp/cp-tree.h | 2 + gcc/cp/decl.c | 15 +- gcc/cp/parser.c | 10 +- gcc/testsuite/g++.dg/cpp0x/constexpr-diag5.C | 8 +- gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 10 +- .../g++.dg/cpp0x/constexpr-function2.C | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-neg1.C | 4 +- gcc/testsuite/g++.dg/cpp1y/constexpr-incr1.C | 13 + gcc/tree-inline.c | 52 ++ gcc/tree-inline.h | 1 + 13 files changed, 653 insertions(+), 93 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-incr1.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d661ec12cf6..1b5d8a0be4e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2014-11-17 Jason Merrill + + * tree-inline.c (copy_fn): New. + * tree-inline.h: Declare it. + 2014-11-17 Alan Lawrence * config/aarch64/aarch64-builtins.c (TYPES_CREATE): Remove. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 42521c9b767..ae347d8b730 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,50 @@ 2014-11-17 Jason Merrill + C++14 constexpr support (minus loops and multiple returns) + * constexpr.c (use_new_call): New macro. + (build_data_member_initialization): Ignore non-mem-inits. + (check_constexpr_bind_expr_vars): Remove C++14 checks. + (constexpr_fn_retval): Likewise. + (check_constexpr_ctor_body): Do nothing in C++14. + (massage_constexpr_body): In C++14 only collect mem-inits. + (get_function_named_in_call): Handle null CALL_EXPR_FN. + (cxx_bind_parameters_in_call): Build bindings in same order as + parameters. Don't treat iniviref parms specially in new call mode. + (cxx_eval_call_expression): If use_new_call, do constexpr expansion + based on DECL_SAVED_TREE rather than the massaged constexpr body. + Set up ctx->object from AGGR_INIT_EXPR_SLOT if we don't have one. + (is_sub_constant_expr): Don't mess with ctx.ctor here. + (cxx_eval_component_reference): A null element means we're mid- + initialization. + (cxx_eval_store_expression, cxx_eval_increment_expression): New. + (cxx_eval_constant_expression): Handle RESULT_DECL, DECL_EXPR, + MODIFY_EXPR, STATEMENT_LIST, BIND_EXPR, USING_STMT, + PREINCREMENT_EXPR, POSTINCREMENT_EXPR, PREDECREMENT_EXPR, + POSTDECREMENT_EXPR. Don't look into DECL_INITIAL of variables in + constexpr functions. In new-call mode find parms in the values table. + (potential_constant_expression_1): Handle null CALL_EXPR_FN. + Handle STATEMENT_LIST, MODIFY_EXPR, MODOP_EXPR, IF_STMT, + PREINCREMENT_EXPR, POSTINCREMENT_EXPR, PREDECREMENT_EXPR, + POSTDECREMENT_EXPR, BIND_EXPR, WITH_CLEANUP_EXPR, + CLEANUP_POINT_EXPR, MUST_NOT_THROW_EXPR, TRY_CATCH_EXPR, + EH_SPEC_BLOCK, EXPR_STMT, DECL_EXPR, CASE_LABEL_EXPR, BREAK_STMT, + CONTINUE_STMT, USING_STMT, IF_STMT, DO_STMT, FOR_STMT, WHILE_STMT, + SWITCH_STMT, ASM_EXPR. + (cxx_eval_vec_init_1): Call build_aggr_init_expr. + (cxx_eval_indirect_ref): Don't return a CONSTRUCTOR when the + caller wants an lvalue. + (cxx_eval_outermost_constant_expr): Pull object out of AGGR_INIT_EXPR. + (maybe_constant_init): Look through INIT_EXPR. + (ensure_literal_type_for_constexpr_object): Set + cp_function_chain->invalid_constexpr. + * cp-tree.h (struct language_function): Add invalid_constexpr bitfield. + * decl.c (start_decl): Set cp_function_chain->invalid_constexpr. + (check_for_uninitialized_const_var): Likewise. + (maybe_save_function_definition): Check it. + * parser.c (cp_parser_jump_statement): Set + cp_function_chain->invalid_constexpr. + (cp_parser_asm_definition): Likewise. + PR c++/52282 * decl.c (build_ptrmemfunc_type): Don't build a different RECORD_TYPE for a qualified PMF. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 66d356f2416..57d0c465d5f 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 % " - "function", decl, type); + { + error ("variable %qD of non-literal type %qT in % " + "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 **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 **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 *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 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 54f7e9b8633..adc8aa72d97 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 *x_named_labels; cp_binding_level *bindings; vec *x_local_names; diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 1f22c265b8c..47da0ca4ecc 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 % in % function", decl); else if (TREE_STATIC (decl)) error ("%qD declared % in % 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 % function", - decl); + { + error_at (DECL_SOURCE_LOCATION (decl), + "uninitialized variable %qD in % 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 111ec1090b8..3eff5fad862 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 ("% in % function"); + { + error ("% in % 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 ("% in % function"); + { + error ("% in % 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 c8043e3bdb4..708f5f2b3f8 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 55a2329eb54..85dfca4ff1d 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C @@ -12,12 +12,14 @@ template 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()); constexpr int j = C().m(C()); -constexpr int k = C().m(A()); // { dg-error "constexpr" } -constexpr int l = g(C(),A()); // { dg-error "constexpr" } +constexpr int k = C().m(A()); // { dg-error "" } +constexpr int l = g(C(),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 8cb32c9ad94..8c51c9ddd25 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 fb4c01274d9..35f5e8e94f4 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 00000000000..2b099c821e0 --- /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 510b53ee416..4b937ca8daa 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 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 9eb75c7fc96..9d70e3efce9 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. */ -- 2.30.2