From 3e605b20a0c804d57d4be0f4f2bbab8b4e42fce6 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 27 Oct 2014 13:42:12 -0400 Subject: [PATCH] Implement N3653 (Member initializers and aggregates) and fix references to 'this' in constexpr constructors. Implement N3653 (Member initializers and aggregates) and fix references to 'this' in constexpr constructors. * class.c (check_field_decls): In C++14 an NSDMI does not make the class non-aggregate. * constexpr.c (struct constexpr_ctx): New. (cxx_bind_parameters_in_call): Handle 'this'. (cxx_eval_call_expression): Create new constexpr_ctx. (cxx_eval_component_reference): Check CONSTRUCTOR_NO_IMPLICIT_ZERO. (initialized_type, init_subob_ctx, verify_ctor_sanity): New. (cxx_eval_bare_aggregate): Use them. Build CONSTRUCTOR early. (cxx_eval_vec_init_1): Likewise. (cxx_eval_constant_expression) [PARM_DECL]: Allow 'this'. [TARGET_EXPR]: Build new constexpr_ctx. [PLACEHOLDER_EXPR]: New. (cxx_eval_outermost_constant_expr): Build new constexpr_ctx. Add object parameter. (is_sub_constant_expr): Build new constexpr_ctx. (potential_constant_expression_1): Handle PLACEHOLDER_EXPR. Allow 'this'. * cp-gimplify.c (cp_gimplify_init_expr): Call replace_placeholders. * cp-tree.h (CONSTRUCTOR_NO_IMPLICIT_ZERO): New. * error.c (dump_expr): Handle PLACEHOLDER_EXPR. * init.c (get_nsdmi): Generate PLACEHOLDER_EXPR. * tree.c (lvalue_kind): Handle PLACEHOLDER_EXPR. (build_ctor_subob_ref, replace_placeholders): New. * typeck2.c (store_init_value): Use replace_placeholders. (process_init_constructor_record): Make zero-init before NSDMI explicit. From-SVN: r216750 --- gcc/cp/ChangeLog | 31 ++ gcc/cp/class.c | 4 +- gcc/cp/constexpr.c | 512 ++++++++++++------ gcc/cp/cp-gimplify.c | 4 + gcc/cp/cp-tree.h | 14 +- gcc/cp/error.c | 4 + gcc/cp/init.c | 11 +- gcc/cp/tree.c | 98 ++++ gcc/cp/typeck2.c | 39 +- gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C | 16 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C | 14 + .../g++.dg/cpp0x/constexpr-ctor14a.C | 14 + gcc/testsuite/g++.dg/cpp0x/nsdmi7.C | 1 + gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C | 41 ++ gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C | 10 + gcc/testsuite/lib/prune.exp | 1 + 16 files changed, 624 insertions(+), 190 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 39c03d67e54..90e69a36bc4 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,34 @@ +2014-10-24 Jason Merrill + + Implement N3653 (Member initializers and aggregates) and fix + references to 'this' in constexpr constructors. + * class.c (check_field_decls): In C++14 an NSDMI does not make the + class non-aggregate. + * constexpr.c (struct constexpr_ctx): New. + (cxx_bind_parameters_in_call): Handle 'this'. + (cxx_eval_call_expression): Create new constexpr_ctx. + (cxx_eval_component_reference): Check CONSTRUCTOR_NO_IMPLICIT_ZERO. + (initialized_type, init_subob_ctx, verify_ctor_sanity): New. + (cxx_eval_bare_aggregate): Use them. Build CONSTRUCTOR early. + (cxx_eval_vec_init_1): Likewise. + (cxx_eval_constant_expression) [PARM_DECL]: Allow 'this'. + [TARGET_EXPR]: Build new constexpr_ctx. + [PLACEHOLDER_EXPR]: New. + (cxx_eval_outermost_constant_expr): Build new constexpr_ctx. Add + object parameter. + (is_sub_constant_expr): Build new constexpr_ctx. + (potential_constant_expression_1): Handle PLACEHOLDER_EXPR. + Allow 'this'. + * cp-gimplify.c (cp_gimplify_init_expr): Call replace_placeholders. + * cp-tree.h (CONSTRUCTOR_NO_IMPLICIT_ZERO): New. + * error.c (dump_expr): Handle PLACEHOLDER_EXPR. + * init.c (get_nsdmi): Generate PLACEHOLDER_EXPR. + * tree.c (lvalue_kind): Handle PLACEHOLDER_EXPR. + (build_ctor_subob_ref, replace_placeholders): New. + * typeck2.c (store_init_value): Use replace_placeholders. + (process_init_constructor_record): Make zero-init before NSDMI + explicit. + 2014-10-27 Andrew MacLeod * cp-gimplify.c: Adjust include files. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 99bfa957f63..5f50aff2417 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -3659,8 +3659,8 @@ check_field_decls (tree t, tree *access_decls, /* Now that we've removed bit-field widths from DECL_INITIAL, anything left in DECL_INITIAL is an NSDMI that makes the class - non-aggregate. */ - if (DECL_INITIAL (x)) + non-aggregate in C++11. */ + if (DECL_INITIAL (x) && cxx_dialect < cxx14) CLASSTYPE_NON_AGGREGATE (t) = true; /* If any field is const, the structure type is pseudo-const. */ diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 30a5e08b55b..cd10766aebd 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -846,14 +846,26 @@ struct constexpr_call_hasher : ggc_hasher { static hashval_t hash (constexpr_call *); static bool equal (constexpr_call *, constexpr_call *); - }; +}; + +/* The constexpr expansion context. CALL is the current function + expansion, CTOR is the current aggregate initializer, OBJECT is the + object being initialized by CTOR, either a VAR_DECL or a _REF. VALUES + is a map of values of variables initialized within the expression. */ + +struct constexpr_ctx { + constexpr_call *call; + hash_map *values; + tree ctor; + tree object; +}; /* A table of all constexpr calls that have been evaluated by the compiler in this translation unit. */ static GTY (()) hash_table *constexpr_call_table; -static tree cxx_eval_constant_expression (const constexpr_call *, tree, +static tree cxx_eval_constant_expression (const constexpr_ctx *, tree, bool, bool, bool *, bool *); /* Compute a hash value for a constexpr call representation. */ @@ -964,7 +976,7 @@ lookup_parameter_binding (const constexpr_call *call, tree t) represented by _CST nodes. */ static tree -cxx_eval_builtin_function_call (const constexpr_call *call, tree t, +cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { @@ -974,7 +986,7 @@ cxx_eval_builtin_function_call (const constexpr_call *call, tree t, int i; for (i = 0; i < nargs; ++i) { - args[i] = cxx_eval_constant_expression (call, CALL_EXPR_ARG (t, i), + args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, i), allow_non_constant, addr, non_constant_p, overflow_p); if (allow_non_constant && *non_constant_p) @@ -1006,12 +1018,12 @@ adjust_temp_type (tree type, tree temp) /* Subroutine of cxx_eval_call_expression. We are processing a call expression (either CALL_EXPR or - AGGR_INIT_EXPR) in the call context of OLD_CALL. Evaluate + AGGR_INIT_EXPR) in the context of CTX. Evaluate all arguments and bind their values to correspondings parameters, making up the NEW_CALL context. */ static void -cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t, +cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, constexpr_call *new_call, bool allow_non_constant, bool *non_constant_p, bool *overflow_p) @@ -1024,12 +1036,18 @@ cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t, { tree x, arg; tree type = parms ? TREE_TYPE (parms) : void_type_node; - /* For member function, the first argument is a pointer to the implied - object. And for an object construction, don't bind `this' before - it is fully constructed. */ - if (i == 0 && DECL_CONSTRUCTOR_P (fun)) - goto next; 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. */ + 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)) { /* cp_genericize made this a reference for argument passing, but @@ -1039,7 +1057,7 @@ cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t, type = TREE_TYPE (type); x = convert_from_reference (x); } - arg = cxx_eval_constant_expression (old_call, x, allow_non_constant, + arg = cxx_eval_constant_expression (ctx, x, allow_non_constant, TREE_CODE (type) == REFERENCE_TYPE, non_constant_p, overflow_p); /* Don't VERIFY_CONSTANT here. */ @@ -1104,7 +1122,7 @@ cx_error_context (void) evaluation. */ static tree -cxx_eval_call_expression (const constexpr_call *old_call, tree t, +cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { @@ -1119,7 +1137,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t, if (TREE_CODE (fun) != FUNCTION_DECL) { /* Might be a constexpr function pointer. */ - fun = cxx_eval_constant_expression (old_call, fun, allow_non_constant, + fun = cxx_eval_constant_expression (ctx, fun, allow_non_constant, /*addr*/false, non_constant_p, overflow_p); STRIP_NOPS (fun); @@ -1137,7 +1155,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t, if (DECL_CLONED_FUNCTION_P (fun)) fun = DECL_CLONED_FUNCTION (fun); if (is_builtin_fn (fun)) - return cxx_eval_builtin_function_call (old_call, t, allow_non_constant, + return cxx_eval_builtin_function_call (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); if (!DECL_DECLARED_CONSTEXPR_P (fun)) { @@ -1156,7 +1174,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t, if (call_expr_nargs (t) == 2) { tree arg = convert_from_reference (get_nth_callarg (t, 1)); - return cxx_eval_constant_expression (old_call, arg, allow_non_constant, + return cxx_eval_constant_expression (ctx, arg, allow_non_constant, addr, non_constant_p, overflow_p); } else if (TREE_CODE (t) == AGGR_INIT_EXPR @@ -1165,8 +1183,8 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t, } /* If in direct recursive call, optimize definition search. */ - if (old_call != NULL && old_call->fundef->decl == fun) - new_call.fundef = old_call->fundef; + if (ctx && ctx->call && ctx->call->fundef->decl == fun) + new_call.fundef = ctx->call->fundef; else { new_call.fundef = retrieve_constexpr_fundef (fun); @@ -1187,7 +1205,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t, return t; } } - cxx_bind_parameters_in_call (old_call, t, &new_call, + cxx_bind_parameters_in_call (ctx, t, &new_call, allow_non_constant, non_constant_p, overflow_p); if (*non_constant_p) return t; @@ -1232,10 +1250,14 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t, { result = entry->result; if (!result || result == error_mark_node) - result = (cxx_eval_constant_expression - (&new_call, new_call.fundef->body, - allow_non_constant, addr, - non_constant_p, overflow_p)); + { + 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 (result == error_mark_node) *non_constant_p = true; if (*non_constant_p) @@ -1326,13 +1348,13 @@ verify_constant (tree t, bool allow_non_constant, bool *non_constant_p, and return error_mark_node. */ static tree -cxx_eval_unary_expression (const constexpr_call *call, tree t, +cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { tree r; tree orig_arg = TREE_OPERAND (t, 0); - tree arg = cxx_eval_constant_expression (call, orig_arg, allow_non_constant, + tree arg = cxx_eval_constant_expression (ctx, orig_arg, allow_non_constant, addr, non_constant_p, overflow_p); VERIFY_CONSTANT (arg); if (arg == orig_arg) @@ -1346,7 +1368,7 @@ cxx_eval_unary_expression (const constexpr_call *call, tree t, Like cxx_eval_unary_expression, except for binary expressions. */ static tree -cxx_eval_binary_expression (const constexpr_call *call, tree t, +cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { @@ -1354,11 +1376,11 @@ cxx_eval_binary_expression (const constexpr_call *call, tree t, tree orig_lhs = TREE_OPERAND (t, 0); tree orig_rhs = TREE_OPERAND (t, 1); tree lhs, rhs; - lhs = cxx_eval_constant_expression (call, orig_lhs, + lhs = cxx_eval_constant_expression (ctx, orig_lhs, allow_non_constant, addr, non_constant_p, overflow_p); VERIFY_CONSTANT (lhs); - rhs = cxx_eval_constant_expression (call, orig_rhs, + rhs = cxx_eval_constant_expression (ctx, orig_rhs, allow_non_constant, addr, non_constant_p, overflow_p); VERIFY_CONSTANT (rhs); @@ -1374,20 +1396,20 @@ cxx_eval_binary_expression (const constexpr_call *call, tree t, looked into. */ static tree -cxx_eval_conditional_expression (const constexpr_call *call, tree t, +cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { - tree val = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), + tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), allow_non_constant, addr, non_constant_p, overflow_p); VERIFY_CONSTANT (val); /* Don't VERIFY_CONSTANT the other operands. */ if (integer_zerop (val)) - return cxx_eval_constant_expression (call, TREE_OPERAND (t, 2), + return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2), allow_non_constant, addr, non_constant_p, overflow_p); - return cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), + return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), allow_non_constant, addr, non_constant_p, overflow_p); } @@ -1396,12 +1418,12 @@ cxx_eval_conditional_expression (const constexpr_call *call, tree t, Attempt to reduce a reference to an array slot. */ static tree -cxx_eval_array_reference (const constexpr_call *call, tree t, +cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { tree oldary = TREE_OPERAND (t, 0); - tree ary = cxx_eval_constant_expression (call, oldary, + tree ary = cxx_eval_constant_expression (ctx, oldary, allow_non_constant, addr, non_constant_p, overflow_p); tree index, oldidx; @@ -1411,7 +1433,7 @@ cxx_eval_array_reference (const constexpr_call *call, tree t, if (*non_constant_p) return t; oldidx = TREE_OPERAND (t, 1); - index = cxx_eval_constant_expression (call, oldidx, + index = cxx_eval_constant_expression (ctx, oldidx, allow_non_constant, false, non_constant_p, overflow_p); VERIFY_CONSTANT (index); @@ -1442,7 +1464,7 @@ cxx_eval_array_reference (const constexpr_call *call, 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); - return cxx_eval_constant_expression (call, val, + return cxx_eval_constant_expression (ctx, val, allow_non_constant, addr, non_constant_p, overflow_p); } @@ -1479,7 +1501,7 @@ cxx_eval_array_reference (const constexpr_call *call, tree t, Attempt to reduce a field access of a value of class type. */ static tree -cxx_eval_component_reference (const constexpr_call *call, tree t, +cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { @@ -1488,7 +1510,7 @@ cxx_eval_component_reference (const constexpr_call *call, tree t, tree value; tree part = TREE_OPERAND (t, 1); tree orig_whole = TREE_OPERAND (t, 0); - tree whole = cxx_eval_constant_expression (call, orig_whole, + tree whole = cxx_eval_constant_expression (ctx, orig_whole, allow_non_constant, addr, non_constant_p, overflow_p); if (whole == orig_whole) @@ -1528,9 +1550,20 @@ cxx_eval_component_reference (const constexpr_call *call, tree t, return t; } + if (CONSTRUCTOR_NO_IMPLICIT_ZERO (whole)) + { + /* 'whole' is part of the aggregate initializer we're currently + building; if there's no initializer for this member yet, that's an + error. */ + if (!allow_non_constant) + error ("accessing uninitialized member %qD", part); + *non_constant_p = true; + return 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); - return cxx_eval_constant_expression (call, value, + return cxx_eval_constant_expression (ctx, value, allow_non_constant, addr, non_constant_p, overflow_p); } @@ -1540,7 +1573,7 @@ cxx_eval_component_reference (const constexpr_call *call, tree t, expressed as a BIT_FIELD_REF. */ static tree -cxx_eval_bit_field_ref (const constexpr_call *call, tree t, +cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { @@ -1548,7 +1581,7 @@ cxx_eval_bit_field_ref (const constexpr_call *call, tree t, tree retval, fldval, utype, mask; bool fld_seen = false; HOST_WIDE_INT istart, isize; - tree whole = cxx_eval_constant_expression (call, orig_whole, + tree whole = cxx_eval_constant_expression (ctx, orig_whole, allow_non_constant, addr, non_constant_p, overflow_p); tree start, field, value; @@ -1625,20 +1658,20 @@ cxx_eval_bit_field_ref (const constexpr_call *call, tree t, sanity check purposes. */ static tree -cxx_eval_logical_expression (const constexpr_call *call, tree t, +cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t, tree bailout_value, tree continue_value, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { tree r; - tree lhs = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), + tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), allow_non_constant, addr, non_constant_p, overflow_p); 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 (call, TREE_OPERAND (t, 1), + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), allow_non_constant, addr, non_constant_p, overflow_p); VERIFY_CONSTANT (r); return r; @@ -1674,59 +1707,153 @@ base_field_constructor_elt (vec *v, tree ref) return NULL; } +/* Some of the expressions fed to the constexpr mechanism are calls to + constructors, which have type void. In that case, return the type being + initialized by the constructor. */ + +static tree +initialized_type (tree t) +{ + if (TYPE_P (t)) + return t; + tree type = cv_unqualified (TREE_TYPE (t)); + if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR) + { + /* A constructor call has void type, so we need to look deeper. */ + tree fn = get_function_named_in_call (t); + if (fn && TREE_CODE (fn) == FUNCTION_DECL + && DECL_CXX_CONSTRUCTOR_P (fn)) + type = DECL_CONTEXT (fn); + } + return type; +} + +/* We're about to initialize element INDEX of an array or class from VALUE. + Set up NEW_CTX appropriately by adjusting .object to refer to the + subobject and creating a new CONSTRUCTOR if the element is itself + a class or array. */ + +static void +init_subob_ctx (const constexpr_ctx *ctx, constexpr_ctx &new_ctx, + tree index, tree &value) +{ + new_ctx = *ctx; + + if (index && TREE_CODE (index) != INTEGER_CST + && TREE_CODE (index) != FIELD_DECL) + /* This won't have an element in the new CONSTRUCTOR. */ + return; + + tree type = initialized_type (value); + if (!AGGREGATE_TYPE_P (type) && !VECTOR_TYPE_P (type)) + /* A non-aggregate member doesn't get its own CONSTRUCTOR. */ + return; + + /* The sub-aggregate initializer might contain a placeholder; + update object to refer to the subobject and ctor to refer to + the (newly created) sub-initializer. */ + if (ctx->object) + new_ctx.object = build_ctor_subob_ref (index, type, ctx->object); + tree elt = build_constructor (type, NULL); + CONSTRUCTOR_NO_IMPLICIT_ZERO (elt) = true; + new_ctx.ctor = elt; + + if (TREE_CODE (value) == TARGET_EXPR) + /* Avoid creating another CONSTRUCTOR when we expand the TARGET_EXPR. */ + value = TARGET_EXPR_INITIAL (value); +} + +/* We're about to process an initializer for a class or array TYPE. Make + sure that CTX is set up appropriately. */ + +static void +verify_ctor_sanity (const constexpr_ctx *ctx, tree type) +{ + /* We don't bother building a ctor for an empty base subobject. */ + if (is_empty_class (type)) + return; + + /* We're in the middle of an initializer that might involve placeholders; + our caller should have created a CONSTRUCTOR for us to put the + initializer into. We will either return that constructor or T. */ + gcc_assert (ctx->ctor); + gcc_assert (same_type_ignoring_top_level_qualifiers_p + (type, TREE_TYPE (ctx->ctor))); + gcc_assert (CONSTRUCTOR_NELTS (ctx->ctor) == 0); + if (ctx->object) + gcc_assert (same_type_ignoring_top_level_qualifiers_p + (type, TREE_TYPE (ctx->object))); + gcc_assert (!ctx->object || !DECL_P (ctx->object) + || *(ctx->values->get (ctx->object)) == ctx->ctor); +} + /* Subroutine of cxx_eval_constant_expression. The expression tree T denotes a C-style array or a C-style aggregate. Reduce it to a constant expression. */ static tree -cxx_eval_bare_aggregate (const constexpr_call *call, tree t, +cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { vec *v = CONSTRUCTOR_ELTS (t); - vec *n; - vec_alloc (n, vec_safe_length (v)); - constructor_elt *ce; - HOST_WIDE_INT i; bool changed = false; gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (t)); - for (i = 0; vec_safe_iterate (v, i, &ce); ++i) + + verify_ctor_sanity (ctx, TREE_TYPE (t)); + vec **p = &CONSTRUCTOR_ELTS (ctx->ctor); + vec_alloc (*p, vec_safe_length (v)); + + unsigned i; tree index, value; + FOR_EACH_CONSTRUCTOR_ELT (v, i, index, value) { - tree elt = cxx_eval_constant_expression (call, ce->value, + constexpr_ctx new_ctx; + init_subob_ctx (ctx, new_ctx, index, value); + if (new_ctx.ctor != ctx->ctor) + /* If we built a new CONSTRUCTOR, attach it now so that other + initializers can refer to it. */ + 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); /* Don't VERIFY_CONSTANT here. */ if (allow_non_constant && *non_constant_p) - goto fail; - if (elt != ce->value) + break; + if (elt != value) changed = true; - if (ce->index && TREE_CODE (ce->index) == COMPONENT_REF) + if (index && TREE_CODE (index) == COMPONENT_REF) { /* This is an initialization of a vfield inside a base subaggregate that we already initialized; push this initialization into the previous initialization. */ - constructor_elt *inner = base_field_constructor_elt (n, ce->index); + constructor_elt *inner = base_field_constructor_elt (*p, index); inner->value = elt; + changed = true; } - else if (ce->index - && (TREE_CODE (ce->index) == NOP_EXPR - || TREE_CODE (ce->index) == POINTER_PLUS_EXPR)) + else if (index + && (TREE_CODE (index) == NOP_EXPR + || TREE_CODE (index) == POINTER_PLUS_EXPR)) { /* This is an initializer for an empty base; now that we've checked that it's constant, we can ignore it. */ - gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (ce->index)))); + gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (index)))); + changed = true; + } + else if (new_ctx.ctor != ctx->ctor) + { + /* We appended this element above; update the value. */ + gcc_assert ((*p)->last().index == index); + (*p)->last().value = elt; } else - CONSTRUCTOR_APPEND_ELT (n, ce->index, elt); + CONSTRUCTOR_APPEND_ELT (*p, index, elt); } if (*non_constant_p || !changed) - { - fail: - vec_free (n); - return t; - } - t = build_constructor (TREE_TYPE (t), n); - TREE_CONSTANT (t) = true; + return t; + t = ctx->ctor; + /* We're done building this CONSTRUCTOR, so now we can interpret an + element without an explicit initializer as value-initialized. */ + CONSTRUCTOR_NO_IMPLICIT_ZERO (t) = false; if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE) t = fold (t); return t; @@ -1745,14 +1872,15 @@ cxx_eval_bare_aggregate (const constexpr_call *call, tree t, for the copy/move constructor. */ static tree -cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init, +cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, bool value_init, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { tree elttype = TREE_TYPE (atype); int max = tree_to_shwi (array_type_nelts (atype)); - vec *n; - vec_alloc (n, max + 1); + verify_ctor_sanity (ctx, atype); + vec **p = &CONSTRUCTOR_ELTS (ctx->ctor); + vec_alloc (*p, max + 1); bool pre_init = false; int i; @@ -1766,8 +1894,6 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init, else if (value_init) { init = build_value_init (elttype, tf_warning_or_error); - init = cxx_eval_constant_expression - (call, init, allow_non_constant, addr, non_constant_p, overflow_p); pre_init = true; } else if (!init) @@ -1777,18 +1903,17 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init, &argvec, elttype, LOOKUP_NORMAL, tf_warning_or_error); release_tree_vector (argvec); - init = cxx_eval_constant_expression (call, init, allow_non_constant, - addr, non_constant_p, overflow_p); pre_init = true; } - if (*non_constant_p && !allow_non_constant) - goto fail; - for (i = 0; i <= max; ++i) { tree idx = build_int_cst (size_type_node, i); tree eltinit; + constexpr_ctx new_ctx; + init_subob_ctx (ctx, new_ctx, idx, pre_init ? init : elttype); + if (new_ctx.ctor != ctx->ctor) + CONSTRUCTOR_APPEND_ELT (*p, idx, new_ctx.ctor); if (TREE_CODE (elttype) == ARRAY_TYPE) { /* A multidimensional array; recurse. */ @@ -1797,7 +1922,7 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init, else eltinit = cp_build_array_ref (input_location, init, idx, tf_warning_or_error); - eltinit = cxx_eval_vec_init_1 (call, elttype, eltinit, value_init, + eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit, value_init, allow_non_constant, addr, non_constant_p, overflow_p); } @@ -1805,10 +1930,9 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init, { /* Initializing an element using value or default initialization we just pre-built above. */ - if (i == 0) - eltinit = init; - else - eltinit = unshare_expr (init); + eltinit = (cxx_eval_constant_expression + (&new_ctx, init, allow_non_constant, + addr, non_constant_p, overflow_p)); } else { @@ -1820,34 +1944,38 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init, if (!real_lvalue_p (init)) eltinit = move (eltinit); eltinit = force_rvalue (eltinit, tf_warning_or_error); - eltinit = cxx_eval_constant_expression - (call, eltinit, allow_non_constant, addr, non_constant_p, overflow_p); + eltinit = (cxx_eval_constant_expression + (&new_ctx, eltinit, allow_non_constant, addr, + non_constant_p, overflow_p)); } if (*non_constant_p && !allow_non_constant) - goto fail; - CONSTRUCTOR_APPEND_ELT (n, idx, eltinit); + break; + if (new_ctx.ctor != ctx->ctor) + { + /* We appended this element above; update the value. */ + gcc_assert ((*p)->last().index == idx); + (*p)->last().value = eltinit; + } + else + CONSTRUCTOR_APPEND_ELT (*p, idx, eltinit); } if (!*non_constant_p) { - init = build_constructor (atype, n); - TREE_CONSTANT (init) = true; - return init; + init = ctx->ctor; + CONSTRUCTOR_NO_IMPLICIT_ZERO (init) = false; } - - fail: - vec_free (n); return init; } static tree -cxx_eval_vec_init (const constexpr_call *call, tree t, +cxx_eval_vec_init (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { tree atype = TREE_TYPE (t); tree init = VEC_INIT_EXPR_INIT (t); - tree r = cxx_eval_vec_init_1 (call, atype, init, + tree r = cxx_eval_vec_init_1 (ctx, atype, init, VEC_INIT_EXPR_VALUE_INIT (t), allow_non_constant, addr, non_constant_p, overflow_p); if (*non_constant_p) @@ -2048,12 +2176,12 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base) } static tree -cxx_eval_indirect_ref (const constexpr_call *call, tree t, +cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { tree orig_op0 = TREE_OPERAND (t, 0); - tree op0 = cxx_eval_constant_expression (call, orig_op0, allow_non_constant, + tree op0 = cxx_eval_constant_expression (ctx, orig_op0, allow_non_constant, /*addr*/false, non_constant_p, overflow_p); bool empty_base = false; tree r; @@ -2066,7 +2194,7 @@ cxx_eval_indirect_ref (const constexpr_call *call, tree t, &empty_base); if (r) - r = cxx_eval_constant_expression (call, r, allow_non_constant, + r = cxx_eval_constant_expression (ctx, r, allow_non_constant, addr, non_constant_p, overflow_p); else { @@ -2157,7 +2285,7 @@ non_const_var_error (tree r) Like cxx_eval_unary_expression, except for trinary expressions. */ static tree -cxx_eval_trinary_expression (const constexpr_call *call, tree t, +cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { @@ -2167,7 +2295,7 @@ cxx_eval_trinary_expression (const constexpr_call *call, tree t, for (i = 0; i < 3; i++) { - args[i] = cxx_eval_constant_expression (call, TREE_OPERAND (t, i), + args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i), allow_non_constant, addr, non_constant_p, overflow_p); VERIFY_CONSTANT (args[i]); @@ -2194,10 +2322,11 @@ var_in_constexpr_fn (tree t) /* FIXME unify with c_fully_fold */ static tree -cxx_eval_constant_expression (const constexpr_call *call, tree t, +cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, bool allow_non_constant, bool addr, bool *non_constant_p, bool *overflow_p) { + constexpr_ctx new_ctx; tree r = t; if (t == error_mark_node) @@ -2228,11 +2357,14 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, if (TREE_CODE (r) == TARGET_EXPR && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR) r = TARGET_EXPR_INITIAL (r); - if (DECL_P (r) && var_in_constexpr_fn (r) + if (TREE_CODE (r) == VAR_DECL && var_in_constexpr_fn (r) && DECL_INITIAL (r)) - r = cxx_eval_constant_expression (call, 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; if (DECL_P (r)) { if (!allow_non_constant) @@ -2247,18 +2379,8 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, return t; case PARM_DECL: - if (call && DECL_CONTEXT (t) == call->fundef->decl) - { - if (DECL_ARTIFICIAL (t) && DECL_CONSTRUCTOR_P (DECL_CONTEXT (t))) - { - if (!allow_non_constant) - sorry ("use of the value of the object being constructed " - "in a constant expression"); - *non_constant_p = true; - } - else - r = lookup_parameter_binding (call, t); - } + if (ctx && ctx->call && DECL_CONTEXT (t) == ctx->call->fundef->decl) + r = lookup_parameter_binding (ctx->call, t); else if (addr) /* Defer in case this is only used for its type. */; else @@ -2271,7 +2393,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, case CALL_EXPR: case AGGR_INIT_EXPR: - r = cxx_eval_call_expression (call, t, allow_non_constant, addr, + r = cxx_eval_call_expression (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; @@ -2287,11 +2409,23 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, *non_constant_p = true; break; } + if ((AGGREGATE_TYPE_P (TREE_TYPE (t)) || VECTOR_TYPE_P (TREE_TYPE (t)))) + { + /* We're being expanded without an explicit target, so start + initializing a new object; expansion with an explicit target + strips the TARGET_EXPR before we get here. */ + new_ctx = *ctx; + new_ctx.ctor = build_constructor (TREE_TYPE (t), NULL); + CONSTRUCTOR_NO_IMPLICIT_ZERO (new_ctx.ctor) = true; + new_ctx.object = TARGET_EXPR_SLOT (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 initialization of a temporary. */ - r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), allow_non_constant, false, non_constant_p, overflow_p); if (!*non_constant_p) @@ -2300,7 +2434,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, break; case SCOPE_REF: - r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1), + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), allow_non_constant, addr, non_constant_p, overflow_p); break; @@ -2311,7 +2445,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, case CLEANUP_POINT_EXPR: case MUST_NOT_THROW_EXPR: case SAVE_EXPR: - r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0), + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), allow_non_constant, addr, non_constant_p, overflow_p); break; @@ -2320,14 +2454,14 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, check for a constant operand or result; an address can be constant without its operand being, and vice versa. */ case INDIRECT_REF: - r = cxx_eval_indirect_ref (call, t, allow_non_constant, addr, + r = cxx_eval_indirect_ref (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; case ADDR_EXPR: { tree oldop = TREE_OPERAND (t, 0); - tree op = cxx_eval_constant_expression (call, oldop, + tree op = cxx_eval_constant_expression (ctx, oldop, allow_non_constant, /*addr*/true, non_constant_p, overflow_p); @@ -2351,7 +2485,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, case BIT_NOT_EXPR: case TRUTH_NOT_EXPR: case FIXED_CONVERT_EXPR: - r = cxx_eval_unary_expression (call, t, allow_non_constant, addr, + r = cxx_eval_unary_expression (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; @@ -2380,15 +2514,15 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, STRIP_NOPS (op1); if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0)) || TREE_CODE (op1) == EMPTY_CLASS_EXPR) - r = cxx_eval_constant_expression (call, op0, allow_non_constant, + r = cxx_eval_constant_expression (ctx, op0, allow_non_constant, addr, non_constant_p, overflow_p); else { /* Check that the LHS is constant and then discard it. */ - cxx_eval_constant_expression (call, op0, allow_non_constant, + cxx_eval_constant_expression (ctx, op0, allow_non_constant, false, non_constant_p, overflow_p); op1 = TREE_OPERAND (t, 1); - r = cxx_eval_constant_expression (call, op1, allow_non_constant, + r = cxx_eval_constant_expression (ctx, op1, allow_non_constant, addr, non_constant_p, overflow_p); } } @@ -2433,7 +2567,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, case LTGT_EXPR: case RANGE_EXPR: case COMPLEX_EXPR: - r = cxx_eval_binary_expression (call, t, allow_non_constant, addr, + r = cxx_eval_binary_expression (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; @@ -2441,7 +2575,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, short-circuiting. */ case TRUTH_AND_EXPR: case TRUTH_ANDIF_EXPR: - r = cxx_eval_logical_expression (call, t, boolean_false_node, + r = cxx_eval_logical_expression (ctx, t, boolean_false_node, boolean_true_node, allow_non_constant, addr, non_constant_p, overflow_p); @@ -2449,14 +2583,14 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, case TRUTH_OR_EXPR: case TRUTH_ORIF_EXPR: - r = cxx_eval_logical_expression (call, t, boolean_true_node, + r = cxx_eval_logical_expression (ctx, t, boolean_true_node, boolean_false_node, allow_non_constant, addr, non_constant_p, overflow_p); break; case ARRAY_REF: - r = cxx_eval_array_reference (call, t, allow_non_constant, addr, + r = cxx_eval_array_reference (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; @@ -2471,23 +2605,23 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, *non_constant_p = true; return t; } - r = cxx_eval_component_reference (call, t, allow_non_constant, addr, + r = cxx_eval_component_reference (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; case BIT_FIELD_REF: - r = cxx_eval_bit_field_ref (call, t, allow_non_constant, addr, + r = cxx_eval_bit_field_ref (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; case COND_EXPR: case VEC_COND_EXPR: - r = cxx_eval_conditional_expression (call, t, allow_non_constant, addr, + r = cxx_eval_conditional_expression (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; case CONSTRUCTOR: - r = cxx_eval_bare_aggregate (call, t, allow_non_constant, addr, + r = cxx_eval_bare_aggregate (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; @@ -2497,13 +2631,13 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, be NULL, meaning default-initialization, or it will be an lvalue or xvalue of the same type, meaning direct-initialization from the corresponding member. */ - r = cxx_eval_vec_init (call, t, allow_non_constant, addr, + r = cxx_eval_vec_init (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; case FMA_EXPR: case VEC_PERM_EXPR: - r = cxx_eval_trinary_expression (call, t, allow_non_constant, addr, + r = cxx_eval_trinary_expression (ctx, t, allow_non_constant, addr, non_constant_p, overflow_p); break; @@ -2512,7 +2646,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, case NOP_EXPR: { tree oldop = TREE_OPERAND (t, 0); - tree op = cxx_eval_constant_expression (call, oldop, + tree op = cxx_eval_constant_expression (ctx, oldop, allow_non_constant, addr, non_constant_p, overflow_p); if (*non_constant_p) @@ -2573,6 +2707,29 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, *non_constant_p = true; break; + case PLACEHOLDER_EXPR: + if (!ctx || !ctx->ctor || (addr && !ctx->object)) + { + /* A placeholder without a referent. We can get here when + checking whether NSDMIs are noexcept, or in massage_init_elt; + just say it's non-constant for now. */ + gcc_assert (allow_non_constant); + *non_constant_p = true; + break; + } + else + { + /* Use of the value or address of the current object. We could + use ctx->object unconditionally, but using ctx->ctor when we + can is a minor optimization. */ + tree ctor = addr ? ctx->object : ctx->ctor; + 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); + } + break; + default: internal_error ("unexpected expression %qE of kind %s", t, get_tree_code_name (TREE_CODE (t))); @@ -2590,12 +2747,43 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t, } static tree -cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant) +cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, + tree object = NULL_TREE) { bool non_constant_p = false; bool overflow_p = false; - tree r = cxx_eval_constant_expression (NULL, t, allow_non_constant, - false, &non_constant_p, &overflow_p); + constexpr_ctx ctx = { NULL, NULL, NULL, NULL }; + hash_map map; + ctx.values = ↦ + tree type = initialized_type (t); + if (!object && TREE_CODE (t) == TARGET_EXPR) + object = TARGET_EXPR_SLOT (t); + tree r = t; + if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)) + { + /* In C++14 an NSDMI can participate in aggregate initialization, + and can refer to the address of the object being initialized, so + we need to pass in the relevant VAR_DECL if we want to do the + evaluation in a single pass. The evaluation will dynamically + update ctx.values for the VAR_DECL. We use the same strategy + for C++11 constexpr constructors that refer to the object being + initialized. */ + ctx.ctor = build_constructor (type, NULL); + CONSTRUCTOR_NO_IMPLICIT_ZERO (ctx.ctor) = true; + ctx.object = object; + if (object) + gcc_assert (same_type_ignoring_top_level_qualifiers_p + (type, TREE_TYPE (object))); + if (object && DECL_P (object)) + map.put (object, ctx.ctor); + if (TREE_CODE (r) == TARGET_EXPR) + /* Avoid creating another CONSTRUCTOR when we expand the + TARGET_EXPR. */ + r = TARGET_EXPR_INITIAL (r); + } + + r = cxx_eval_constant_expression (&ctx, r, allow_non_constant, + false, &non_constant_p, &overflow_p); verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); @@ -2607,7 +2795,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant) constexpr variables. */ if (!allow_non_constant) error ("%qT cannot be the type of a complete constant expression " - "because it has mutable sub-objects", TREE_TYPE (t)); + "because it has mutable sub-objects", type); non_constant_p = true; } @@ -2668,7 +2856,17 @@ is_sub_constant_expr (tree t) { bool non_constant_p = false; bool overflow_p = false; - cxx_eval_constant_expression (NULL, t, true, false, &non_constant_p, + 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; } @@ -2678,9 +2876,9 @@ is_sub_constant_expr (tree t) return NULL. */ tree -cxx_constant_value (tree t) +cxx_constant_value (tree t, tree decl) { - return cxx_eval_outermost_constant_expr (t, false); + return cxx_eval_outermost_constant_expr (t, false, decl); } /* If T is a constant expression, returns its reduced value. @@ -2688,7 +2886,7 @@ cxx_constant_value (tree t) Otherwise, returns a version of T without TREE_CONSTANT. */ tree -maybe_constant_value (tree t) +maybe_constant_value (tree t, tree decl) { tree r; @@ -2705,7 +2903,7 @@ maybe_constant_value (tree t) return t; } - r = cxx_eval_outermost_constant_expr (t, true); + r = cxx_eval_outermost_constant_expr (t, true, decl); #ifdef ENABLE_CHECKING /* cp_tree_equal looks through NOPs, so allow them. */ gcc_assert (r == t @@ -2720,14 +2918,14 @@ maybe_constant_value (tree t) than wrapped in a TARGET_EXPR. */ tree -maybe_constant_init (tree t) +maybe_constant_init (tree t, tree decl) { if (TREE_CODE (t) == EXPR_STMT) t = TREE_OPERAND (t, 0); if (TREE_CODE (t) == CONVERT_EXPR && VOID_TYPE_P (TREE_TYPE (t))) t = TREE_OPERAND (t, 0); - t = maybe_constant_value (t); + t = maybe_constant_value (t, decl); if (TREE_CODE (t) == TARGET_EXPR) { tree init = TARGET_EXPR_INITIAL (t); @@ -2822,6 +3020,7 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags) case FIELD_DECL: case PARM_DECL: case USING_DECL: + case PLACEHOLDER_EXPR: return true; case AGGR_INIT_EXPR: @@ -2861,18 +3060,7 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags) { tree x = get_nth_callarg (t, 0); if (is_this_parameter (x)) - { - if (DECL_CONTEXT (x) == NULL_TREE - || DECL_CONSTRUCTOR_P (DECL_CONTEXT (x))) - { - if (flags & tf_error) - sorry ("calling a member function of the " - "object being constructed in a constant " - "expression"); - return false; - } - /* Otherwise OK. */; - } + return true; else if (!potential_constant_expression_1 (x, rval, flags)) return false; i = 1; @@ -3002,14 +3190,6 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags) error ("use of % in a constant expression"); return false; } - if (want_rval && DECL_CONTEXT (x) - && DECL_CONSTRUCTOR_P (DECL_CONTEXT (x))) - { - if (flags & tf_error) - sorry ("use of the value of the object being constructed " - "in a constant expression"); - return false; - } return true; } return potential_constant_expression_1 (x, rval, flags); diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index 52750727dc3..e5436bbc747 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -495,6 +495,10 @@ cp_gimplify_init_expr (tree *expr_p) TREE_TYPE (from) = void_type_node; } + if (cxx_dialect >= cxx14 && TREE_CODE (sub) == CONSTRUCTOR) + /* Handle aggregate NSDMI. */ + replace_placeholders (sub, to); + if (t == sub) break; else diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 0923d9f0f7c..abc3d6fd19f 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -98,6 +98,7 @@ c-common.h, not after. DECL_FINAL_P (in FUNCTION_DECL) QUALIFIED_NAME_IS_TEMPLATE (in SCOPE_REF) DECLTYPE_FOR_INIT_CAPTURE (in DECLTYPE_TYPE) + CONSTRUCTOR_NO_IMPLICIT_ZERO (in CONSTRUCTOR) 2: IDENTIFIER_OPNAME_P (in IDENTIFIER_NODE) ICS_THIS_FLAG (in _CONV) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL) @@ -3479,6 +3480,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) B b{1,2}, not B b({1,2}) or B b = {1,2}. */ #define CONSTRUCTOR_IS_DIRECT_INIT(NODE) (TREE_LANG_FLAG_0 (CONSTRUCTOR_CHECK (NODE))) +/* True if an uninitialized element in NODE should not be treated as + implicitly value-initialized. Only used in constexpr evaluation. */ +#define CONSTRUCTOR_NO_IMPLICIT_ZERO(NODE) \ + (TREE_LANG_FLAG_1 (CONSTRUCTOR_CHECK (NODE))) + #define DIRECT_LIST_INIT_P(NODE) \ (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE)) @@ -6033,6 +6039,8 @@ extern tree bind_template_template_parm (tree, tree); extern tree array_type_nelts_total (tree); extern tree array_type_nelts_top (tree); extern tree break_out_target_exprs (tree); +extern tree build_ctor_subob_ref (tree, tree, tree); +extern tree replace_placeholders (tree, tree); extern tree get_type_decl (tree); extern tree decl_namespace_context (tree); extern bool decl_anon_ns_mem_p (const_tree); @@ -6320,9 +6328,9 @@ extern bool potential_constant_expression (tree); extern bool potential_rvalue_constant_expression (tree); extern bool require_potential_constant_expression (tree); extern bool require_potential_rvalue_constant_expression (tree); -extern tree cxx_constant_value (tree); -extern tree maybe_constant_value (tree); -extern tree maybe_constant_init (tree); +extern tree cxx_constant_value (tree, tree = NULL_TREE); +extern tree maybe_constant_value (tree, tree = NULL_TREE); +extern tree maybe_constant_init (tree, tree = NULL_TREE); extern bool is_sub_constant_expr (tree); extern bool reduced_constant_expression_p (tree); extern bool is_instantiation_of_constexpr (tree); diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 755bb00f71b..76f86cb0d74 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -2673,6 +2673,10 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) pp_cxx_right_paren (pp); break; + case PLACEHOLDER_EXPR: + pp_string (pp, M_("*this")); + break; + /* This list is incomplete, but should suffice for now. It is very important that `sorry' does not call `report_error_function'. That could cause an infinite loop. */ diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 6851fe9dc7e..5c61107b7f4 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -540,7 +540,12 @@ get_nsdmi (tree member, bool in_ctor) tree save_ccp = current_class_ptr; tree save_ccr = current_class_ref; if (!in_ctor) - inject_this_parameter (DECL_CONTEXT (member), TYPE_UNQUALIFIED); + { + /* Use a PLACEHOLDER_EXPR when we don't have a 'this' parameter to + refer to; constexpr evaluation knows what to do with it. */ + current_class_ref = build0 (PLACEHOLDER_EXPR, DECL_CONTEXT (member)); + current_class_ptr = build_address (current_class_ref); + } if (DECL_LANG_SPECIFIC (member) && DECL_TEMPLATE_INFO (member)) { /* Do deferred instantiation of the NSDMI. */ @@ -560,7 +565,7 @@ get_nsdmi (tree member, bool in_ctor) error ("constructor required before non-static data member " "for %qD has been parsed", member); DECL_INITIAL (member) = error_mark_node; - init = NULL_TREE; + init = error_mark_node; } /* Strip redundant TARGET_EXPR so we don't need to remap it, and so the aggregate init code below will see a CONSTRUCTOR. */ @@ -1723,7 +1728,7 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags, tree fn = get_callee_fndecl (rval); if (fn && DECL_DECLARED_CONSTEXPR_P (fn)) { - tree e = maybe_constant_init (rval); + tree e = maybe_constant_init (rval, exp); if (TREE_CONSTANT (e)) rval = build2 (INIT_EXPR, type, exp, e); } diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 9008cdb4da9..ef7f6756ef6 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -158,6 +158,7 @@ lvalue_kind (const_tree ref) case ARRAY_NOTATION_REF: case PARM_DECL: case RESULT_DECL: + case PLACEHOLDER_EXPR: return clk_ordinary; /* A scope ref in a template, left as SCOPE_REF to support later @@ -2450,6 +2451,103 @@ break_out_target_exprs (tree t) return t; } +/* Build an expression for the subobject of OBJ at CONSTRUCTOR index INDEX, + which we expect to have type TYPE. */ + +tree +build_ctor_subob_ref (tree index, tree type, tree obj) +{ + if (index == NULL_TREE) + /* Can't refer to a particular member of a vector. */ + obj = NULL_TREE; + else if (TREE_CODE (index) == INTEGER_CST) + obj = cp_build_array_ref (input_location, obj, index, tf_none); + else + obj = build_class_member_access_expr (obj, index, NULL_TREE, + /*reference*/false, tf_none); + if (obj) + gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, + TREE_TYPE (obj))); + return obj; +} + +/* Like substitute_placeholder_in_expr, but handle C++ tree codes and + build up subexpressions as we go deeper. */ + +struct replace_placeholders_t +{ + tree obj; + hash_set *pset; +}; + +static tree +replace_placeholders_r (tree* t, int* walk_subtrees, void* data_) +{ + tree obj = static_cast(data_); + + if (TREE_CONSTANT (*t)) + { + *walk_subtrees = false; + return NULL_TREE; + } + + switch (TREE_CODE (*t)) + { + case PLACEHOLDER_EXPR: + gcc_assert (same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (*t), TREE_TYPE (obj))); + *t = obj; + *walk_subtrees = false; + break; + + case TARGET_EXPR: + /* Don't mess with placeholders in an unrelated object. */ + *walk_subtrees = false; + break; + + case CONSTRUCTOR: + { + constructor_elt *ce; + vec *v = CONSTRUCTOR_ELTS (*t); + for (unsigned i = 0; vec_safe_iterate (v, i, &ce); ++i) + { + tree *valp = &ce->value; + tree type = TREE_TYPE (*valp); + tree subob = obj; + + if (TREE_CODE (*valp) == CONSTRUCTOR + && AGGREGATE_TYPE_P (type)) + { + subob = build_ctor_subob_ref (ce->index, type, obj); + if (TREE_CODE (*valp) == TARGET_EXPR) + valp = &TARGET_EXPR_INITIAL (*valp); + } + + cp_walk_tree (valp, replace_placeholders_r, + subob, NULL); + } + *walk_subtrees = false; + break; + } + + default: + break; + } + + return NULL_TREE; +} + +tree +replace_placeholders (tree exp, tree obj) +{ + hash_set pset; + tree *tp = &exp; + if (TREE_CODE (exp) == TARGET_EXPR) + tp = &TARGET_EXPR_INITIAL (exp); + cp_walk_tree (tp, replace_placeholders_r, obj, NULL); + return exp; +} + /* Similar to `build_nt', but for template definitions of dependent expressions */ diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index ad69668765a..d57f75dadc5 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -806,15 +806,19 @@ store_init_value (tree decl, tree init, vec** cleanups, int flags) && !require_potential_constant_expression (value)) value = error_mark_node; else - value = cxx_constant_value (value); + value = cxx_constant_value (value, decl); } - value = maybe_constant_init (value); + value = maybe_constant_init (value, decl); const_init = (reduced_constant_expression_p (value) || error_operand_p (value)); DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = const_init; TREE_CONSTANT (decl) = const_init && decl_maybe_constant_var_p (decl); } + if (cxx_dialect >= cxx14) + /* Handle aggregate NSDMI in non-constant initializers, too. */ + value = replace_placeholders (value, decl); + /* If the initializer is not a constant, fill in DECL_INITIAL with the bits that are constant, and then return an expression that will perform the dynamic initialization. */ @@ -1292,9 +1296,8 @@ process_init_constructor_record (tree type, tree init, tsubst_flags_t complain) { vec *v = NULL; - int flags = 0; tree field; - unsigned HOST_WIDE_INT idx = 0; + int skipped = 0; gcc_assert (TREE_CODE (type) == RECORD_TYPE); gcc_assert (!CLASSTYPE_VBASECLASSES (type)); @@ -1302,6 +1305,9 @@ process_init_constructor_record (tree type, tree init, || !BINFO_N_BASE_BINFOS (TYPE_BINFO (type))); gcc_assert (!TYPE_POLYMORPHIC_P (type)); + restart: + int flags = 0; + unsigned HOST_WIDE_INT idx = 0; /* Generally, we will always have an index for each initializer (which is a FIELD_DECL, put by reshape_init), but compound literals don't go trough reshape_init. So we need to handle both cases. */ @@ -1345,6 +1351,19 @@ process_init_constructor_record (tree type, tree init, next = massage_init_elt (type, ce->value, complain); ++idx; } + else if (DECL_INITIAL (field)) + { + if (skipped > 0) + { + /* We're using an NSDMI past a field with implicit + zero-init. Go back and make it explicit. */ + skipped = -1; + vec_safe_truncate (v, 0); + goto restart; + } + /* C++14 aggregate NSDMI. */ + next = get_nsdmi (field, /*ctor*/false); + } else if (type_build_ctor_call (TREE_TYPE (field))) { /* If this type needs constructors run for @@ -1387,13 +1406,17 @@ process_init_constructor_record (tree type, tree init, warning (OPT_Wmissing_field_initializers, "missing initializer for member %qD", field); - if (!zero_init_p (TREE_TYPE (field))) + if (!zero_init_p (TREE_TYPE (field)) + || skipped < 0) next = build_zero_init (TREE_TYPE (field), /*nelts=*/NULL_TREE, /*static_storage_p=*/false); else - /* The default zero-initialization is fine for us; don't - add anything to the CONSTRUCTOR. */ - continue; + { + /* The default zero-initialization is fine for us; don't + add anything to the CONSTRUCTOR. */ + skipped = 1; + continue; + } } /* If this is a bitfield, now convert to the lowered type. */ diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C index 88d442691c6..31010ed2355 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C @@ -10,18 +10,18 @@ // R() is well-formed because i is initialized before j. struct s { - constexpr s() : v(v) { } // { dg-message "" } + constexpr s() : v(v) { } int v; }; -constexpr s bang; // { dg-message "" } +constexpr s bang; // { dg-error "" } struct R { int i,j; - constexpr R() : i(42),j(i) { } // { dg-bogus "" "" { xfail *-*-* } } + constexpr R() : i(42),j(i) { } // { dg-bogus "" } }; -constexpr R r; // { dg-bogus "" "" { xfail *-*-* } } +constexpr R r; // { dg-bogus "" } // Ill-formed (no diagnostic required) struct T { @@ -41,10 +41,10 @@ struct U { constexpr int f(int _i) { return _i; } constexpr int g() { return i; } constexpr U(): i(0), j(0) { } - constexpr U(const U& t) : i(f(t.i)),j(0) { } // { dg-bogus "" "" { xfail *-*-* } } - constexpr U(int _i) : i(_i),j(g()) { } // { dg-bogus "" "" { xfail *-*-* } } + constexpr U(const U& t) : i(f(t.i)),j(0) { } // { dg-bogus "" } + constexpr U(int _i) : i(_i),j(g()) { } // { dg-bogus "" } }; constexpr U u1; -constexpr U u2(u1); // { dg-bogus "" "" { xfail *-*-* } } -constexpr U u3(1); // { dg-bogus "" "" { xfail *-*-* } } +constexpr U u2(u1); // { dg-bogus "" } +constexpr U u3(1); // { dg-bogus "" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C new file mode 100644 index 00000000000..17309952886 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++11 } } + +struct A +{ + void *p; + constexpr A(): p(this) {} +}; + +constexpr A a; +constexpr A b = A(); + +#define SA(X) static_assert ((X), #X) +SA(a.p == &a); +SA(b.p == &b); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C new file mode 100644 index 00000000000..644ae63fe0d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-fno-elide-constructors" } + +struct A +{ + void *p; + constexpr A(): p(this) {} +}; + +constexpr A a; +constexpr A b = A(); // { dg-error "" } + +#define SA(X) static_assert ((X), #X) +SA(a.p == &a); diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi7.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi7.C index 4b08474aaf9..e378360a759 100644 --- a/gcc/testsuite/g++.dg/cpp0x/nsdmi7.C +++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi7.C @@ -11,6 +11,7 @@ struct A struct B { + virtual void g(); const int d; // { dg-warning "non-static const member" } int &e; // { dg-warning "non-static reference" } int f = 7; diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C new file mode 100644 index 00000000000..97bcc056b43 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C @@ -0,0 +1,41 @@ +// { dg-do run { target c++14 } } + +struct S { int a; const char* b; int c; int d = b[a]; void *p = this+1; }; +constexpr S ss = S(S{ 1, "asdf" }); + +#define SA(X) static_assert ((X),#X) + +SA(ss.a==1); +SA(ss.b[0] == 'a' && ss.b[1] == 's' && ss.b[2] == 'd' && ss.b[3] == 'f'); +SA(ss.d == 's'); +SA(ss.p == &ss+1); + +struct A +{ + struct B { + int i; + int j = i+1; + } b; + int a = b.j+1; +}; + +extern constexpr A a = { }; +SA(a.b.i == 0 && a.b.j == 1 && a.a == 2); + +int f(const A& ar) { return ar.a; } + +int main() +{ + S ss2 = { 1, "asdf" }; + if (ss2.a != 1 + || __builtin_strcmp(ss2.b,"asdf") != 0 + || ss2.c != int() + || ss2.d != 's' + || ss2.p != &ss2+1) + __builtin_abort(); + + A a = {}; + int i = f(A{}); + if (a.a != 2 || i != 2) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C new file mode 100644 index 00000000000..83cb6c2439b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++14 } } + +struct S { int a; const char* b; int c; int d = b[a]; }; + +constexpr int f(const S& s) { return s.a; } + +int main() +{ + constexpr int i = f(S{ 1, "asdf" }); +} diff --git a/gcc/testsuite/lib/prune.exp b/gcc/testsuite/lib/prune.exp index 679d894e267..859d25ef801 100644 --- a/gcc/testsuite/lib/prune.exp +++ b/gcc/testsuite/lib/prune.exp @@ -28,6 +28,7 @@ proc prune_gcc_output { text } { regsub -all "(^|\n)\[^\n\]*(: )?At (top level|global scope):\[^\n\]*" $text "" text regsub -all "(^|\n)\[^\n\]*: (recursively )?required \[^\n\]*" $text "" text regsub -all "(^|\n)\[^\n\]*: . skipping \[0-9\]* instantiation contexts \[^\n\]*" $text "" text + regsub -all "(^|\n)\[^\n\]*: in constexpr expansion \[^\n\]*" $text "" text regsub -all "(^|\n) inlined from \[^\n\]*" $text "" text regsub -all "(^|\n)collect2: error: ld returned \[^\n\]*" $text "" text regsub -all "(^|\n)collect: re(compiling|linking)\[^\n\]*" $text "" text -- 2.30.2