Commit 3e605b20 by Jason Merrill Committed by Jason Merrill

Implement N3653 (Member initializers and aggregates) and fix references to…

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
parent ddc8de03
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.
......
......@@ -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. */
......
......@@ -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
......
......@@ -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);
......
......@@ -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. */
......
......@@ -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);
}
......
......@@ -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 */
......
......@@ -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,14 +1406,18 @@ 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. */
skipped = 1;
continue;
}
}
/* If this is a bitfield, now convert to the lowered type. */
if (type != TREE_TYPE (field))
......
......@@ -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 "" }
// { 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);
// { 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);
......@@ -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;
......
// { 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();
}
// { 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" });
}
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment