diff mbox

C++ PATCH for libstdc++/48760 (list-initialization of complex)

Message ID 4DB8C483.8060108@redhat.com
State New
Headers show

Commit Message

Jason Merrill April 28, 2011, 1:36 a.m. UTC
In the discussion of 48760, Gaby suggested that we allow 
list-initialization of built-in complex numbers.  This made a lot of 
sense to me, so I've gone ahead and implemented it.

Basically it works as though complex were an aggregate except for one 
thing: for backwards compatibility, we never assume elided braces when 
initializing a complex number.  So given

struct A
{
   _Complex int c;
   int i;
};

A a = { 1, 2 };

a.c is initialized to 1+0i rather than 1+2i as it has been previously, 
and a.i is initialized to 2.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 0131da0c5a789fc9f952a05d9d06262b2c72b980
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Apr 27 20:25:50 2011 -0400

    	PR libstdc++/48760
    	Implement list-initialization of _Complex.
    	* decl.c (reshape_init_r): Allow {real,imag} for _Complex.
    	(check_initializer): Likewise.
    	* call.c (build_complex_conv): New.
    	(implicit_conversion): Call it.
    	(convert_like_real): Handle it.
    	* typeck2.c (check_narrowing): Handle it.

Comments

Gabriel Dos Reis April 28, 2011, 1:51 a.m. UTC | #1
Jason Merrill <jason@redhat.com> writes:

| In the discussion of 48760, Gaby suggested that we allow
| list-initialization of built-in complex numbers.  This made a lot of
| sense to me, so I've gone ahead and implemented it.
| 
| Basically it works as though complex were an aggregate except for one
| thing: for backwards compatibility, we never assume elided braces when
| initializing a complex number.  So given
| 
| struct A
| {
|   _Complex int c;
|   int i;
| };
| 
| A a = { 1, 2 };
| 
| a.c is initialized to 1+0i rather than 1+2i as it has been previously,
| and a.i is initialized to 2.

Many thanks, Jason.

-- Gaby
Jason Merrill April 28, 2011, 2:02 a.m. UTC | #2
On 04/27/2011 09:36 PM, Jason Merrill wrote:
> A a = { 1, 2 };
>
> a.c is initialized to 1+0i rather than 1+2i as it has been previously,

This should have said "a.c is initialized to 1+0i, as it has been 
previously, rather than 1+2i, as it would be if it were an aggregate".

Jason
Lawrence Crowl April 28, 2011, 3:51 a.m. UTC | #3
On 4/27/11, Jason Merrill <jason@redhat.com> wrote:
> On 04/27/2011 09:36 PM, Jason Merrill wrote:
>> A a = { 1, 2 };
>>
>> a.c is initialized to 1+0i rather than 1+2i as it has been previously,
>
> This should have said "a.c is initialized to 1+0i, as it has been
> previously, rather than 1+2i, as it would be if it were an aggregate".

This looks right to me.  It's more in line with C99.
Paolo Carlini April 28, 2011, 9:36 a.m. UTC | #4
On 04/28/2011 03:36 AM, Jason Merrill wrote:
> In the discussion of 48760, Gaby suggested that we allow 
> list-initialization of built-in complex numbers.  This made a lot of 
> sense to me, so I've gone ahead and implemented it.
Great. Thus, as regards the std::complex constructor application of 
this, I'm going to fix the problem completely in mainline (either in the 
initializer for c++0x, or in the body for c++03) and only in c++03 mode 
in the branch.

Thanks again,
Paolo.
diff mbox

Patch

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 10efd1c..dcc3859 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -847,6 +847,49 @@  build_array_conv (tree type, tree ctor, int flags)
   return c;
 }
 
+/* Represent a conversion from CTOR, a braced-init-list, to TYPE, a
+   complex type, if such a conversion is possible.  */
+
+static conversion *
+build_complex_conv (tree type, tree ctor, int flags)
+{
+  conversion *c;
+  unsigned HOST_WIDE_INT len = CONSTRUCTOR_NELTS (ctor);
+  tree elttype = TREE_TYPE (type);
+  unsigned i;
+  tree val;
+  bool bad = false;
+  bool user = false;
+  enum conversion_rank rank = cr_exact;
+
+  if (len != 2)
+    return NULL;
+
+  FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (ctor), i, val)
+    {
+      conversion *sub
+	= implicit_conversion (elttype, TREE_TYPE (val), val,
+			       false, flags);
+      if (sub == NULL)
+	return NULL;
+
+      if (sub->rank > rank)
+	rank = sub->rank;
+      if (sub->user_conv_p)
+	user = true;
+      if (sub->bad_p)
+	bad = true;
+    }
+
+  c = alloc_conversion (ck_aggr);
+  c->type = type;
+  c->rank = rank;
+  c->user_conv_p = user;
+  c->bad_p = bad;
+  c->u.next = NULL;
+  return c;
+}
+
 /* Build a representation of the identity conversion from EXPR to
    itself.  The TYPE should match the type of EXPR, if EXPR is non-NULL.  */
 
@@ -1646,6 +1689,14 @@  implicit_conversion (tree to, tree from, tree expr, bool c_cast_p,
       if (is_std_init_list (to))
 	return build_list_conv (to, expr, flags);
 
+      /* As an extension, allow list-initialization of _Complex.  */
+      if (TREE_CODE (to) == COMPLEX_TYPE)
+	{
+	  conv = build_complex_conv (to, expr, flags);
+	  if (conv)
+	    return conv;
+	}
+
       /* Allow conversion from an initializer-list with one element to a
 	 scalar type.  */
       if (SCALAR_TYPE_P (to))
@@ -5516,6 +5567,17 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
       }
 
     case ck_aggr:
+      if (TREE_CODE (totype) == COMPLEX_TYPE)
+	{
+	  tree real = CONSTRUCTOR_ELT (expr, 0)->value;
+	  tree imag = CONSTRUCTOR_ELT (expr, 1)->value;
+	  real = perform_implicit_conversion (TREE_TYPE (totype),
+					      real, complain);
+	  imag = perform_implicit_conversion (TREE_TYPE (totype),
+					      imag, complain);
+	  expr = build2 (COMPLEX_EXPR, totype, real, imag);
+	  return fold_if_not_in_template (expr);
+	}
       return get_target_expr (digest_init (totype, expr));
 
     default:
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index ccc5fd0..5bf637e 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5058,6 +5058,27 @@  reshape_init_r (tree type, reshape_iter *d, bool first_initializer_p)
   if (error_operand_p (init))
     return error_mark_node;
 
+  if (TREE_CODE (type) == COMPLEX_TYPE)
+    {
+      /* A complex type can be initialized from one or two initializers,
+	 but braces are not elided.  */
+      d->cur++;
+      if (BRACE_ENCLOSED_INITIALIZER_P (init))
+	{
+	  if (CONSTRUCTOR_NELTS (init) > 2)
+	    error ("too many initializers for %qT", type);
+	}
+      else if (first_initializer_p && d->cur != d->end)
+	{
+	  VEC(constructor_elt, gc) *v = 0;
+	  CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
+	  CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, d->cur->value);
+	  d->cur++;
+	  init = build_constructor (init_list_type_node, v);
+	}
+      return init;
+    }
+
   /* A non-aggregate type is always initialized with a single
      initializer.  */
   if (!CP_AGGREGATE_TYPE_P (type))
@@ -5325,7 +5346,7 @@  check_initializer (tree decl, tree init, int flags, tree *cleanup)
 	      maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
 	      init = build_zero_init (type, NULL_TREE, false);
 	    }
-	  else if (init_len != 1)
+	  else if (init_len != 1 && TREE_CODE (type) != COMPLEX_TYPE)
 	    {
 	      error ("scalar object %qD requires one element in initializer",
 		     decl);
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 5522868..aec54f9 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -728,6 +728,16 @@  check_narrowing (tree type, tree init)
   if (!ARITHMETIC_TYPE_P (type))
     return;
 
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && TREE_CODE (type) == COMPLEX_TYPE)
+    {
+      tree elttype = TREE_TYPE (type);
+      check_narrowing (elttype, CONSTRUCTOR_ELT (init, 0)->value);
+      if (CONSTRUCTOR_NELTS (init) > 1)
+	check_narrowing (elttype, CONSTRUCTOR_ELT (init, 1)->value);
+      return;
+    }
+
   init = maybe_constant_value (init);
 
   if (TREE_CODE (type) == INTEGER_TYPE
diff --git a/gcc/testsuite/g++.dg/ext/complex8.C b/gcc/testsuite/g++.dg/ext/complex8.C
new file mode 100644
index 0000000..9b8ac1b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/complex8.C
@@ -0,0 +1,67 @@ 
+// PR libstdc++/48760
+// { dg-options -std=c++0x }
+// { dg-do run }
+
+constexpr _Complex int i{1,2};
+constexpr _Complex int j{3};
+
+#define SA(X) static_assert((X),#X)
+
+SA(__real i == 1);
+SA(__imag i == 2);
+SA(__real j == 3);
+SA(__imag j == 0);
+
+struct A
+{
+  _Complex int c;
+  constexpr A(int i, int j): c{i,j} { }
+  constexpr A(int i): c{i} { }
+};
+
+constexpr A a1(1,2);
+constexpr A a2(3);
+
+SA(__real a1.c == 1);
+SA(__imag a1.c == 2);
+SA(__real a2.c == 3);
+SA(__imag a2.c == 0);
+
+typedef _Complex int ci;
+
+SA((__real ci{1,2} == 1));
+SA((__imag ci{1,2} == 2));
+SA((__real ci{3} == 3));
+SA((__imag ci{3} == 0));
+
+struct B
+{
+  _Complex int c;
+  int i;
+};
+
+constexpr B b1 = { { 1,2 }, 42 };
+constexpr B b2 = { { 3 }, 24 };
+// No brace elision for complex.
+constexpr B b3 = { 5, 6 };
+
+SA(__real b1.c == 1);
+SA(__imag b1.c == 2);
+SA(b1.i == 42);
+SA(__real b2.c == 3);
+SA(__imag b2.c == 0);
+SA(b2.i == 24);
+SA(__real b3.c == 5);
+SA(__imag b3.c == 0);
+SA(b3.i == 6);
+
+int main()
+{
+  ci* p = new ci{1,2};
+  if (__real *p != 1 || __imag *p != 2)
+    return 1;
+  delete p;
+  p = new ci{3};
+  if (__real *p != 3 || __imag *p != 0)
+    return 1;
+}