c++: Fix ICE with constexpr init and [[no_unique_address]] [PR93803]

Message ID 20200224164440.390576-1-polacek@redhat.com
State New
Headers show
Series
  • c++: Fix ICE with constexpr init and [[no_unique_address]] [PR93803]
Related show

Commit Message

Marek Polacek Feb. 24, 2020, 4:44 p.m.
Here we crash when constexpr-initializing a class member of empty class
type with [[no_unique_address]].  Without the attribute we would have
a ctor (that initializes bar) of the form

  { .D.2173 = { .x = {} } }

but with the attribute reduced_constant_expression_p gets

  { .x = {} }

That means that "idx != field" is true for the latter and we see that
foo, the base class of bar, is an empty class, so we want to look at
the next initializable field (since empty class fields may not have an
initializer).  But in this case there are no more, therefore accessing
DECL_CHAIN (field) crashes.  Long story short, we need to avoid a crash
on a null field when we're initializing a class that only contains an
empty base class.

While poking into this I discovered c++/93898, but that's a different
problem.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2020-02-24  Marek Polacek  <polacek@redhat.com>

	PR c++/93803 - ICE with constexpr init and [[no_unique_address]].
	* constexpr.c (reduced_constant_expression_p): Don't crash on a null
	field.

	* g++.dg/cpp2a/constexpr-init16.C: New test.
	* g++.dg/cpp2a/constexpr-init17.C: New test.
---
 gcc/cp/constexpr.c                            | 16 +++++++---------
 gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C | 15 +++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C | 15 +++++++++++++++
 3 files changed, 37 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C


base-commit: 004f2c07b6d3fa543f0fe86c55a7b3c227de2bb6
-- 
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA

Comments

Jason Merrill Feb. 26, 2020, 5:03 a.m. | #1
On 2/24/20 11:44 AM, Marek Polacek wrote:
> Here we crash when constexpr-initializing a class member of empty class

> type with [[no_unique_address]].  Without the attribute we would have

> a ctor (that initializes bar) of the form

> 

>    { .D.2173 = { .x = {} } }

> 

> but with the attribute reduced_constant_expression_p gets

> 

>    { .x = {} }

> 

> That means that "idx != field" is true for the latter and we see that

> foo, the base class of bar, is an empty class, so we want to look at

> the next initializable field (since empty class fields may not have an

> initializer).  But in this case there are no more, therefore accessing

> DECL_CHAIN (field) crashes.  Long story short, we need to avoid a crash

> on a null field when we're initializing a class that only contains an

> empty base class.

> 

> While poking into this I discovered c++/93898, but that's a different

> problem.

> 

> Bootstrapped/regtested on x86_64-linux, ok for trunk?


OK.

> 2020-02-24  Marek Polacek  <polacek@redhat.com>

> 

> 	PR c++/93803 - ICE with constexpr init and [[no_unique_address]].

> 	* constexpr.c (reduced_constant_expression_p): Don't crash on a null

> 	field.

> 

> 	* g++.dg/cpp2a/constexpr-init16.C: New test.

> 	* g++.dg/cpp2a/constexpr-init17.C: New test.

> ---

>   gcc/cp/constexpr.c                            | 16 +++++++---------

>   gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C | 15 +++++++++++++++

>   gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C | 15 +++++++++++++++

>   3 files changed, 37 insertions(+), 9 deletions(-)

>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C

>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C

> 

> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c

> index 128f760778b..3da1609cdaa 100644

> --- a/gcc/cp/constexpr.c

> +++ b/gcc/cp/constexpr.c

> @@ -2603,16 +2603,14 @@ reduced_constant_expression_p (tree t)

>   	     element.  */

>   	  if (!reduced_constant_expression_p (val))

>   	    return false;

> +	  /* Empty class field may or may not have an initializer.  */

> +	  for (; field && idx != field;

> +	       field = next_initializable_field (DECL_CHAIN (field)))

> +	    if (!is_really_empty_class (TREE_TYPE (field),

> +					/*ignore_vptr*/false))

> +	      return false;

>   	  if (field)

> -	    {

> -	      /* Empty class field may or may not have an initializer.  */

> -	      for (; idx != field;

> -		   field = next_initializable_field (DECL_CHAIN (field)))

> -		if (!is_really_empty_class (TREE_TYPE (field),

> -					    /*ignore_vptr*/false))

> -		  return false;

> -	      field = next_initializable_field (DECL_CHAIN (field));

> -	    }

> +	    field = next_initializable_field (DECL_CHAIN (field));

>   	}

>         /* There could be a non-empty field at the end.  */

>         for (; field; field = next_initializable_field (DECL_CHAIN (field)))

> diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C

> new file mode 100644

> index 00000000000..16db2974f2a

> --- /dev/null

> +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C

> @@ -0,0 +1,15 @@

> +// PR c++/93803 - ICE with constexpr init and [[no_unique_address]].

> +// { dg-do compile { target c++2a } }

> +

> +struct empty { };

> +

> +struct foo {

> +  [[no_unique_address]] empty x;

> +  constexpr foo() : x{} { }

> +};

> +

> +struct bar : foo {

> +  using foo::foo;

> +};

> +

> +constexpr bar a{};

> diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C

> new file mode 100644

> index 00000000000..34d9fe58ecb

> --- /dev/null

> +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C

> @@ -0,0 +1,15 @@

> +// PR c++/93803 - ICE with constexpr init and [[no_unique_address]].

> +// { dg-do compile { target c++2a } }

> +

> +struct empty { };

> +

> +struct foo {

> +  [[no_unique_address]] empty x, x2, x3;

> +  constexpr foo() : x{}, x2{} { }

> +};

> +

> +struct bar : foo {

> +  using foo::foo;

> +};

> +

> +constexpr bar a{};

> 

> base-commit: 004f2c07b6d3fa543f0fe86c55a7b3c227de2bb6

>

Patch

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 128f760778b..3da1609cdaa 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2603,16 +2603,14 @@  reduced_constant_expression_p (tree t)
 	     element.  */
 	  if (!reduced_constant_expression_p (val))
 	    return false;
+	  /* Empty class field may or may not have an initializer.  */
+	  for (; field && idx != field;
+	       field = next_initializable_field (DECL_CHAIN (field)))
+	    if (!is_really_empty_class (TREE_TYPE (field),
+					/*ignore_vptr*/false))
+	      return false;
 	  if (field)
-	    {
-	      /* Empty class field may or may not have an initializer.  */
-	      for (; idx != field;
-		   field = next_initializable_field (DECL_CHAIN (field)))
-		if (!is_really_empty_class (TREE_TYPE (field),
-					    /*ignore_vptr*/false))
-		  return false;
-	      field = next_initializable_field (DECL_CHAIN (field));
-	    }
+	    field = next_initializable_field (DECL_CHAIN (field));
 	}
       /* There could be a non-empty field at the end.  */
       for (; field; field = next_initializable_field (DECL_CHAIN (field)))
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C
new file mode 100644
index 00000000000..16db2974f2a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init16.C
@@ -0,0 +1,15 @@ 
+// PR c++/93803 - ICE with constexpr init and [[no_unique_address]].
+// { dg-do compile { target c++2a } }
+
+struct empty { };
+
+struct foo {
+  [[no_unique_address]] empty x;
+  constexpr foo() : x{} { }
+};
+
+struct bar : foo {
+  using foo::foo;
+};
+
+constexpr bar a{};
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C
new file mode 100644
index 00000000000..34d9fe58ecb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init17.C
@@ -0,0 +1,15 @@ 
+// PR c++/93803 - ICE with constexpr init and [[no_unique_address]].
+// { dg-do compile { target c++2a } }
+
+struct empty { };
+
+struct foo {
+  [[no_unique_address]] empty x, x2, x3;
+  constexpr foo() : x{}, x2{} { }
+};
+
+struct bar : foo {
+  using foo::foo;
+};
+
+constexpr bar a{};