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
This commit is contained in:
Jason Merrill 2014-10-27 13:42:12 -04:00 committed by Jason Merrill
parent ddc8de034a
commit 3e605b20a0
16 changed files with 624 additions and 190 deletions

View File

@ -1,3 +1,34 @@
2014-10-24 Jason Merrill <jason@redhat.com>
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 <amacleod@redhat.com>
* cp-gimplify.c: Adjust include files.

View File

@ -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. */

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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);

View File

@ -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. */

View File

@ -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);
}

View File

@ -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<tree> *pset;
};
static tree
replace_placeholders_r (tree* t, int* walk_subtrees, void* data_)
{
tree obj = static_cast<tree>(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<constructor_elt,va_gc> *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<tree> 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 */

View File

@ -806,15 +806,19 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** 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<constructor_elt, va_gc> *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. */

View File

@ -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 "" }

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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" });
}

View File

@ -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