This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] C++11, implement delegating constructors
- From: Ville Voutilainen <ville dot voutilainen at gmail dot com>
- To: Jason Merrill <jason at redhat dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Wed, 21 Sep 2011 01:17:41 +0300
- Subject: Re: [PATCH] C++11, implement delegating constructors
- References: <87aa9z2kco.wl%ville@ville-laptop> <4E790033.1090600@redhat.com>
At Tue, 20 Sep 2011 17:05:55 -0400,
Jason Merrill wrote:
> Try to avoid reformatting lines that you don't actually change.
I find it hard to see what exactly changes there. Even if I
restore the indentation level, git still shows a diff. Perhaps
there were tabs in it...
> > + if (init == void_type_node)
> > + init = NULL_TREE;
> This is changing value-initialization to default-initialization, which
> isn't the same thing. What happens if you just remove these two lines?
Seems to work even without them. Here's a new patch:
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c707d66..7027b64 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -7048,18 +7048,19 @@ build_special_member_call (tree instance, tree name, VEC(tree,gc) **args,
vtt = DECL_CHAIN (CLASSTYPE_VTABLES (current_class_type));
vtt = decay_conversion (vtt);
vtt = build3 (COND_EXPR, TREE_TYPE (vtt),
- build2 (EQ_EXPR, boolean_type_node,
- current_in_charge_parm, integer_zero_node),
- current_vtt_parm,
- vtt);
- gcc_assert (BINFO_SUBVTT_INDEX (binfo));
- sub_vtt = fold_build_pointer_plus (vtt, BINFO_SUBVTT_INDEX (binfo));
-
+ build2 (EQ_EXPR, boolean_type_node,
+ current_in_charge_parm, integer_zero_node),
+ current_vtt_parm,
+ vtt);
+ if (BINFO_SUBVTT_INDEX (binfo))
+ sub_vtt = fold_build_pointer_plus (vtt, BINFO_SUBVTT_INDEX (binfo));
+ else
+ sub_vtt = vtt;
if (args == NULL)
- {
- allocated = make_tree_vector ();
- args = &allocated;
- }
+ {
+ allocated = make_tree_vector ();
+ args = &allocated;
+ }
VEC_safe_insert (tree, gc, *args, 0, sub_vtt);
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ae4cd07..31c600e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -394,7 +394,9 @@ typedef enum cpp0x_warn_str
/* inline namespaces */
CPP0X_INLINE_NAMESPACES,
/* override controls, override/final */
- CPP0X_OVERRIDE_CONTROLS
+ CPP0X_OVERRIDE_CONTROLS,
+ /* delegating constructors */
+ CPP0X_DELEGATING_CTORS
} cpp0x_warn_str;
/* The various kinds of operation used by composite_pointer_type. */
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 598ddf1..82f3175 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -3236,6 +3236,11 @@ maybe_warn_cpp0x (cpp0x_warn_str str)
"override controls (override/final) "
"only available with -std=c++0x or -std=gnu++0x");
break;
+ case CPP0X_DELEGATING_CTORS:
+ pedwarn (input_location, 0,
+ "delegating constructors "
+ "only available with -std=c++0x or -std=gnu++0x");
+ break;
default:
gcc_unreachable();
}
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index ff1884b..bef38e9 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -482,6 +482,31 @@ build_value_init_noctor (tree type, tsubst_flags_t complain)
return build_zero_init (type, NULL_TREE, /*static_storage_p=*/false);
}
+/* Initialize current class with INIT, a TREE_LIST of
+ arguments for a target constructor. If TREE_LIST is void_type_node,
+ an empty initializer list was given. */
+
+static void
+perform_target_ctor (tree init)
+{
+ tree decl = current_class_ref;
+ tree type = current_class_type;
+
+ finish_expr_stmt (build_aggr_init (decl, init, LOOKUP_NORMAL,
+ tf_warning_or_error));
+ if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
+ {
+ tree expr = build_delete (type, decl, sfk_complete_destructor,
+ LOOKUP_NORMAL
+ |LOOKUP_NONVIRTUAL
+ |LOOKUP_DESTRUCTOR,
+ 0, tf_warning_or_error);
+ if (expr != error_mark_node)
+ finish_eh_cleanup (expr);
+ }
+}
+
+
/* Initialize MEMBER, a FIELD_DECL, with INIT, a TREE_LIST of
arguments. If TREE_LIST is void_type_node, an empty initializer
list was given; if NULL_TREE no initializer was given. */
@@ -924,6 +949,16 @@ emit_mem_initializers (tree mem_inits)
if (!COMPLETE_TYPE_P (current_class_type))
return;
+ if (mem_inits
+ && TYPE_P (TREE_PURPOSE (mem_inits))
+ && same_type_p (TREE_PURPOSE (mem_inits), current_class_type))
+ {
+ /* Delegating constructor. */
+ gcc_assert (TREE_CHAIN (mem_inits) == NULL_TREE);
+ perform_target_ctor (TREE_VALUE (mem_inits));
+ return;
+ }
+
if (DECL_DEFAULTED_FN (current_function_decl))
flags |= LOOKUP_DEFAULTED;
@@ -1213,10 +1248,13 @@ expand_member_init (tree name)
{
tree basetype;
tree field;
+ tree delegate_target;
if (!current_class_ref)
return NULL_TREE;
+ delegate_target = NULL_TREE;
+
if (!name)
{
/* This is an obsolete unnamed base class initializer. The
@@ -1240,10 +1278,14 @@ expand_member_init (tree name)
else if (TYPE_P (name))
{
basetype = TYPE_MAIN_VARIANT (name);
+ delegate_target = name;
name = TYPE_NAME (name);
}
else if (TREE_CODE (name) == TYPE_DECL)
- basetype = TYPE_MAIN_VARIANT (TREE_TYPE (name));
+ {
+ basetype = TYPE_MAIN_VARIANT (TREE_TYPE (name));
+ delegate_target = TREE_TYPE (name);
+ }
else
basetype = NULL_TREE;
@@ -1254,6 +1296,9 @@ expand_member_init (tree name)
tree virtual_binfo;
int i;
+ if (same_type_p (delegate_target, current_class_type))
+ return delegate_target;
+
if (current_template_parms)
return basetype;
@@ -1494,13 +1539,34 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
else
parms = make_tree_vector_single (init);
- if (true_exp == exp)
- ctor_name = complete_ctor_identifier;
+ if (exp == current_class_ref && current_function_decl
+ && DECL_HAS_IN_CHARGE_PARM_P (current_function_decl))
+ {
+ /* Delegating constructor. */
+ tree complete;
+ tree base;
+ complete = build_special_member_call (exp, complete_ctor_identifier,
+ &parms, binfo, flags,
+ complain);
+ base = build_special_member_call (exp, base_ctor_identifier,
+ &parms, binfo, flags,
+ complain);
+ rval = build3 (COND_EXPR, TREE_TYPE (complete),
+ build2 (EQ_EXPR, boolean_type_node,
+ current_in_charge_parm, integer_zero_node),
+ base,
+ complete);
+ }
else
- ctor_name = base_ctor_identifier;
+ {
+ if (true_exp == exp)
+ ctor_name = complete_ctor_identifier;
+ else
+ ctor_name = base_ctor_identifier;
+ rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
+ complain);
+ }
- rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
- complain);
if (parms != NULL)
release_tree_vector (parms);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 03f75fc..e75ca20 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -10665,6 +10665,8 @@ static void
cp_parser_mem_initializer_list (cp_parser* parser)
{
tree mem_initializer_list = NULL_TREE;
+ tree target_ctor = error_mark_node;
+
cp_token *token = cp_lexer_peek_token (parser->lexer);
/* Let the semantic analysis code know that we are starting the
@@ -10702,6 +10704,31 @@ cp_parser_mem_initializer_list (cp_parser* parser)
if (mem_initializer != error_mark_node)
mem_initializer = make_pack_expansion (mem_initializer);
}
+ if (target_ctor != error_mark_node
+ && mem_initializer != error_mark_node)
+ {
+ error ("mem-initializer for %qD follows constructor delegation; "
+ "previous target constructor for %T must be sole initializer",
+ TREE_PURPOSE (mem_initializer),
+ TREE_PURPOSE (target_ctor));
+ mem_initializer = error_mark_node;
+ }
+ /* Look for a target constructor. */
+ if (mem_initializer != error_mark_node
+ && TYPE_P (TREE_PURPOSE (mem_initializer))
+ && same_type_p (TREE_PURPOSE (mem_initializer), current_class_type))
+ {
+ maybe_warn_cpp0x (CPP0X_DELEGATING_CTORS);
+ if (mem_initializer_list)
+ {
+ error ("target constructor for %T must be sole initializer; "
+ "constructor delegation follows mem-initializer for %qD",
+ TREE_PURPOSE (mem_initializer),
+ TREE_PURPOSE (mem_initializer_list));
+ mem_initializer = error_mark_node;
+ }
+ target_ctor = mem_initializer;
+ }
/* Add it to the list, unless it was erroneous. */
if (mem_initializer != error_mark_node)
{
diff --git a/gcc/testsuite/g++.dg/cpp0x/dc1.C b/gcc/testsuite/g++.dg/cpp0x/dc1.C
new file mode 100644
index 0000000..ba2e4f4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/dc1.C
@@ -0,0 +1,43 @@
+// { dg-do compile }
+// { dg-options --std=c++0x }
+
+struct B {
+ int i;
+ B (int _i) : i(_i) { }
+ ~B () { i = 0; }
+};
+
+struct A : public B {
+ A () : B(-1) { }
+ A (int i) : A() { }
+ A (double b) : A(static_cast<int>(b)) { }
+ A (double b, double b2) : A(b2) { }
+ ~A () { }
+};
+
+void f_A () { A a(2.0, 3.0); }
+
+struct C {
+ C () { }
+ virtual ~C() { }
+ virtual int f () = 0;
+};
+
+struct D : public C {
+ int i;
+ D (int _i) : C(), i(_i) { }
+ D () : D(-1) { }
+ virtual ~D() { }
+ virtual int f () { }
+};
+
+void f_D () { C* c = new D(); }
+
+template <typename T>
+struct E {
+ T t;
+ E () : E(T()) { }
+ E (T _t) : t(_t) { }
+};
+
+void f_E () { E<int> e; }
diff --git a/gcc/testsuite/g++.dg/cpp0x/dc2.C b/gcc/testsuite/g++.dg/cpp0x/dc2.C
new file mode 100644
index 0000000..e0c2681
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/dc2.C
@@ -0,0 +1,23 @@
+// { dg-do compile }
+// { dg-options --std=c++0x }
+
+struct A {
+ int i, j;
+ A () : A(0), j(0) { } // { dg-error "sole initializer" }
+ A (int _i) : i(_i) { }
+};
+
+struct B {
+ int i, j;
+ B () : i(0), B(0) { } // { dg-error "sole initializer" }
+ B (int _j) : j(_j) { }
+
+};
+
+struct C {};
+
+struct D : public C {
+ D () : C() { }
+ D (float) : D(), C() { } // { dg-error "sole initializer" }
+ D (float, float): C(), D() { } // { dg-error "sole initializer" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/dc3.C b/gcc/testsuite/g++.dg/cpp0x/dc3.C
new file mode 100644
index 0000000..b411c99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/dc3.C
@@ -0,0 +1,63 @@
+// { dg-do compile }
+// { dg-options --std=c++0x }
+
+struct X {};
+
+struct B {
+ int i;
+ B (int _i) : i(_i) { }
+ ~B () { i = 0; }
+};
+
+template <typename T>
+struct A : public B {
+ A () : B(-1) { }
+ ~A () { }
+};
+
+template <typename T>
+struct A<T*> : public B {
+ A () : B(-1) { }
+ A (int i) : A() { }
+ A (double b) : A(static_cast<int>(b)) { }
+ A (double b, double b2) : A(b2) { }
+ ~A () { }
+};
+
+void f_A () { A<X*> a(2.0, 3.0); }
+
+struct C {
+ C () { }
+ virtual ~C() { }
+ virtual int f () = 0;
+};
+
+template <typename T>
+struct D : public C {
+ int i;
+ D (int _i) : C(), i(_i) { }
+};
+
+template <>
+struct D<X> : public C {
+ int i;
+ D (int _i) : C(), i(_i) { }
+ D () : D(-1) { }
+ virtual ~D() { }
+ virtual int f () { }
+};
+
+void f_D () { D<X>* d = new D<X>(); }
+
+template <typename T>
+struct E {
+};
+
+template <>
+struct E<int> {
+ int i;
+ E () : E(0) { }
+ E (int _i) : i(_i) { }
+};
+
+void f_E () { E<int> e; }
diff --git a/gcc/testsuite/g++.dg/cpp0x/dc4.C b/gcc/testsuite/g++.dg/cpp0x/dc4.C
new file mode 100644
index 0000000..634b549
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/dc4.C
@@ -0,0 +1,7 @@
+// { dg-do compile }
+// { dg-options "--std=c++98" }
+
+struct X {
+ X() {}
+ X(int) : X() {} // { dg-warning "delegating constructors" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/dc5.C b/gcc/testsuite/g++.dg/cpp0x/dc5.C
new file mode 100644
index 0000000..0052b32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/dc5.C
@@ -0,0 +1,28 @@
+// { dg-do run }
+// { dg-options "--std=c++0x" }
+
+#include <cassert>
+
+int count = 0;
+struct VB
+{
+ VB() {++count;}
+};
+
+struct B : virtual VB
+{
+ B() : B(42) {}
+ B(int) {}
+};
+
+struct D : B
+{
+ D() {}
+ D(int) : D() {}
+};
+
+int main()
+{
+ D d{42};
+ assert(count == 1);
+}
diff --git a/gcc/testsuite/g++.dg/template/meminit1.C b/gcc/testsuite/g++.dg/template/meminit1.C
index b1c4d42..efdbe80 100644
--- a/gcc/testsuite/g++.dg/template/meminit1.C
+++ b/gcc/testsuite/g++.dg/template/meminit1.C
@@ -2,7 +2,7 @@
template <class T >
struct S
{
- S() : S() {} // { dg-error "base" }
+ S() : S() {} // { dg-warning "delegating constructors" }
};
-S<int> s; // { dg-message "required" }
+S<int> s;