C++ PATCH for c++/88337 - P1327R1: Allow polymorphic typeid in constexpr
diff mbox series

Message ID 20191112005408.GB21634@redhat.com
State New
Headers show
Series
  • C++ PATCH for c++/88337 - P1327R1: Allow polymorphic typeid in constexpr
Related show

Commit Message

Marek Polacek Nov. 12, 2019, 12:54 a.m. UTC
Part of P1327R1 is to allow typeid with an operand of polymorphic type in
constexpr.  I found that we pretty much support it already, the only tweak
was to allow TYPEID_EXPR (only created in a template) in constexpr in C++20.

I also noticed this in build_typeid:
  /* FIXME when integrating with c_fully_fold, mark
     resolves_to_fixed_type_p case as a non-constant expression.  */
  if (TYPE_POLYMORPHIC_P (TREE_TYPE (exp))
      && ! resolves_to_fixed_type_p (exp, &nonnull)
      && ! nonnull)
but I'm not quite sure what to do with it.

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

2019-11-11  Marek Polacek  <polacek@redhat.com>

	PR c++/88337 - P1327R1: Allow polymorphic typeid in constexpr.
	* constexpr.c (potential_constant_expression_1): Allow a typeid
	expression whose operand is of polymorphic type in constexpr in
	C++20.

	* g++.dg/cpp2a/constexpr-typeid1.C: New test.
	* g++.dg/cpp2a/constexpr-typeid2.C: New test.
	* g++.dg/cpp2a/constexpr-typeid3.C: New test.
	* g++.dg/cpp2a/constexpr-typeid4.C: New test.

Comments

Jason Merrill Nov. 22, 2019, 9:14 p.m. UTC | #1
On 11/11/19 7:54 PM, Marek Polacek wrote:
> Part of P1327R1 is to allow typeid with an operand of polymorphic type in
> constexpr.  I found that we pretty much support it already, the only tweak
> was to allow TYPEID_EXPR (only created in a template) in constexpr in C++20.
> 
> I also noticed this in build_typeid:
>    /* FIXME when integrating with c_fully_fold, mark
>       resolves_to_fixed_type_p case as a non-constant expression.  */
>    if (TYPE_POLYMORPHIC_P (TREE_TYPE (exp))
>        && ! resolves_to_fixed_type_p (exp, &nonnull)
>        && ! nonnull)
> but I'm not quite sure what to do with it.

Remove it, I think.

> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2019-11-11  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/88337 - P1327R1: Allow polymorphic typeid in constexpr.
> 	* constexpr.c (potential_constant_expression_1): Allow a typeid
> 	expression whose operand is of polymorphic type in constexpr in
> 	C++20.
> 
> 	* g++.dg/cpp2a/constexpr-typeid1.C: New test.
> 	* g++.dg/cpp2a/constexpr-typeid2.C: New test.
> 	* g++.dg/cpp2a/constexpr-typeid3.C: New test.
> 	* g++.dg/cpp2a/constexpr-typeid4.C: New test.
> 
> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
> index 20fddc57825..430c65694b7 100644
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -7018,11 +7018,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
>         return false;
>   
>       case TYPEID_EXPR:
> -      /* -- a typeid expression whose operand is of polymorphic
> -            class type;  */
> +      /* In C++20, a typeid expression whose operand is of polymorphic
> +	 class type can be constexpr.  */
>         {
>           tree e = TREE_OPERAND (t, 0);
> -        if (!TYPE_P (e) && !type_dependent_expression_p (e)
> +	if (cxx_dialect < cxx2a

Do we want to allow this before C++20 if !strict?  OK either way.

Jason
Marek Polacek Nov. 22, 2019, 10:43 p.m. UTC | #2
On Fri, Nov 22, 2019 at 04:14:06PM -0500, Jason Merrill wrote:
> On 11/11/19 7:54 PM, Marek Polacek wrote:
> > Part of P1327R1 is to allow typeid with an operand of polymorphic type in
> > constexpr.  I found that we pretty much support it already, the only tweak
> > was to allow TYPEID_EXPR (only created in a template) in constexpr in C++20.
> > 
> > I also noticed this in build_typeid:
> >    /* FIXME when integrating with c_fully_fold, mark
> >       resolves_to_fixed_type_p case as a non-constant expression.  */
> >    if (TYPE_POLYMORPHIC_P (TREE_TYPE (exp))
> >        && ! resolves_to_fixed_type_p (exp, &nonnull)
> >        && ! nonnull)
> > but I'm not quite sure what to do with it.
> 
> Remove it, I think.

Ack.

> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
> > 
> > 2019-11-11  Marek Polacek  <polacek@redhat.com>
> > 
> > 	PR c++/88337 - P1327R1: Allow polymorphic typeid in constexpr.
> > 	* constexpr.c (potential_constant_expression_1): Allow a typeid
> > 	expression whose operand is of polymorphic type in constexpr in
> > 	C++20.
> > 
> > 	* g++.dg/cpp2a/constexpr-typeid1.C: New test.
> > 	* g++.dg/cpp2a/constexpr-typeid2.C: New test.
> > 	* g++.dg/cpp2a/constexpr-typeid3.C: New test.
> > 	* g++.dg/cpp2a/constexpr-typeid4.C: New test.
> > 
> > diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
> > index 20fddc57825..430c65694b7 100644
> > --- gcc/cp/constexpr.c
> > +++ gcc/cp/constexpr.c
> > @@ -7018,11 +7018,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> >         return false;
> >       case TYPEID_EXPR:
> > -      /* -- a typeid expression whose operand is of polymorphic
> > -            class type;  */
> > +      /* In C++20, a typeid expression whose operand is of polymorphic
> > +	 class type can be constexpr.  */
> >         {
> >           tree e = TREE_OPERAND (t, 0);
> > -        if (!TYPE_P (e) && !type_dependent_expression_p (e)
> > +	if (cxx_dialect < cxx2a
> 
> Do we want to allow this before C++20 if !strict?  OK either way.

Since we allow non-template constexpr typeid use, we might as well.

I'll commit the following after the usual testing.

2019-11-22  Marek Polacek  <polacek@redhat.com>

	PR c++/88337 - P1327R1: Allow polymorphic typeid in constexpr.
	* constexpr.c (potential_constant_expression_1): Allow a typeid
	expression whose operand is of polymorphic type in constexpr in
	C++20.
	* rtti.c (build_typeid): Remove obsolete FIXME comment.

	* g++.dg/cpp2a/constexpr-typeid1.C: New test.
	* g++.dg/cpp2a/constexpr-typeid2.C: New test.
	* g++.dg/cpp2a/constexpr-typeid3.C: New test.
	* g++.dg/cpp2a/constexpr-typeid4.C: New test.

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 9ce768bb2e6..658455cce96 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -7021,11 +7021,14 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
       return false;
 
     case TYPEID_EXPR:
-      /* -- a typeid expression whose operand is of polymorphic
-            class type;  */
+      /* In C++20, a typeid expression whose operand is of polymorphic
+	 class type can be constexpr.  */
       {
         tree e = TREE_OPERAND (t, 0);
-        if (!TYPE_P (e) && !type_dependent_expression_p (e)
+	if (cxx_dialect < cxx2a
+	    && strict
+	    && !TYPE_P (e)
+	    && !type_dependent_expression_p (e)
 	    && TYPE_POLYMORPHIC_P (TREE_TYPE (e)))
           {
             if (flags & tf_error)
diff --git gcc/cp/rtti.c gcc/cp/rtti.c
index d987f8b4d83..da685961c70 100644
--- gcc/cp/rtti.c
+++ gcc/cp/rtti.c
@@ -353,8 +353,6 @@ build_typeid (tree exp, tsubst_flags_t complain)
   if (processing_template_decl)
     return build_min (TYPEID_EXPR, const_type_info_type_node, exp);
 
-  /* FIXME when integrating with c_fully_fold, mark
-     resolves_to_fixed_type_p case as a non-constant expression.  */
   if (TYPE_POLYMORPHIC_P (TREE_TYPE (exp))
       && ! resolves_to_fixed_type_p (exp, &nonnull)
       && ! nonnull)
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-typeid1.C gcc/testsuite/g++.dg/cpp2a/constexpr-typeid1.C
new file mode 100644
index 00000000000..a81f649b44b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid1.C
@@ -0,0 +1,47 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test non-polymorphic type.
+
+#include <typeinfo>
+
+struct B {
+  const std::type_info &ti = typeid (*this);
+};
+
+struct D : B { };
+
+constexpr B b;
+constexpr D d;
+
+static_assert (&b.ti == &typeid (B));
+static_assert (&B{}.ti == &typeid (B));
+static_assert (&B().ti == &typeid (B));
+static_assert (&typeid ((B())) == &typeid (B));
+static_assert (&typeid ((B{})) == &typeid (B));
+
+static_assert (&d.ti == &typeid (B));
+static_assert (&D{}.ti == &typeid (B));
+static_assert (&D().ti == &typeid (B));
+static_assert (&typeid ((D())) == &typeid (D));
+static_assert (&typeid ((D{})) == &typeid (D));
+
+extern D ed;
+static_assert (&typeid (ed) == &typeid (D));
+
+constexpr const B &r = d;
+static_assert (&typeid (r) == &typeid (B));
+
+constexpr bool
+test ()
+{
+  // If expression is not a glvalue expression of polymorphic type,
+  // typeid does not evaluate the expression
+  bool ok = true;
+  (void) typeid (ok = false, D());
+  (void) typeid (ok = false, B());
+  (void) typeid (ok = false, b);
+  (void) typeid (ok = false, d);
+  return ok;
+}
+
+static_assert (test ());
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-typeid2.C gcc/testsuite/g++.dg/cpp2a/constexpr-typeid2.C
new file mode 100644
index 00000000000..51c8fb451b8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid2.C
@@ -0,0 +1,60 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test polymorphic type.
+
+#include <typeinfo>
+
+struct B {
+  virtual void foo ();
+  const std::type_info &ti_base = typeid (*this);
+};
+
+struct D : B {
+  const std::type_info &ti = typeid (*this);
+};
+
+constexpr B b;
+constexpr D d;
+
+static_assert (&b.ti_base == &typeid (B));
+static_assert (&B{}.ti_base == &typeid (B));
+static_assert (&B().ti_base == &typeid (B));
+static_assert (&typeid ((B())) == &typeid (B));
+static_assert (&typeid ((B{})) == &typeid (B));
+
+static_assert (&d.ti == &typeid (D));
+static_assert (&D{}.ti == &typeid (D));
+static_assert (&D().ti == &typeid (D));
+static_assert (&typeid ((D())) == &typeid (D));
+static_assert (&typeid ((D{})) == &typeid (D));
+
+extern D ed;
+// ??? Should this succeed?
+static_assert (&typeid (ed) == &typeid (D));
+
+constexpr const B &r = d;
+static_assert (&typeid (r) == &typeid (D));
+
+constexpr bool
+test ()
+{
+  // If expression is a glvalue expression that identifies an object
+  // of a polymorphic type, the typeid expression evaluates the expression.
+  bool ok = true;
+  // Not a glvalue.
+  (void) typeid (ok = false, D());
+  (void) typeid (ok = false, B());
+  if (!ok)
+    return false;
+
+  // Polymorphic glvalue -- this needs to be evaluated.
+  ok = false;
+  (void) typeid (ok = true, b);
+  if (!ok)
+    return false;
+  ok = false;
+  (void) typeid (ok = true, d);
+  return ok;
+}
+
+static_assert (test ());
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-typeid3.C gcc/testsuite/g++.dg/cpp2a/constexpr-typeid3.C
new file mode 100644
index 00000000000..38b9e609a9f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid3.C
@@ -0,0 +1,33 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test typeid in a template.
+
+#include <typeinfo>
+
+struct B { virtual void fn (); };
+struct D : B { };
+
+void abort ();
+
+template<typename>
+constexpr void
+fn ()
+{
+  D d;
+  if (&typeid (d) != &typeid (D))
+   abort ();
+}
+constexpr bool b1 = (fn<int>(), true);
+
+// Type-dependent.
+template<typename T>
+constexpr void
+fn2 ()
+{
+  T t{};
+  if (&typeid (t) != &typeid (T))
+   abort ();
+}
+constexpr bool b2 = (fn2<int>(), true);
+constexpr bool b3 = (fn2<B>(), true);
+constexpr bool b4 = (fn2<D>(), true);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-typeid4.C gcc/testsuite/g++.dg/cpp2a/constexpr-typeid4.C
new file mode 100644
index 00000000000..0a99aae9bf9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid4.C
@@ -0,0 +1,19 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++17 } }
+// Test typeid in a template.
+
+#include <typeinfo>
+
+struct B { virtual void f(); };
+struct B2 : B { };
+
+template<typename T>
+constexpr bool
+fn ()
+{
+  constexpr B2 b2;
+  static_assert(&typeid(b2) == &typeid(B2)); // { dg-error ".typeid. is not a constant expression because .b2. is of polymorphic type|non-constant condition" "" { target c++17_down } }
+  return true;
+}
+
+static_assert (fn<int>());

Patch
diff mbox series

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 20fddc57825..430c65694b7 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -7018,11 +7018,13 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
       return false;
 
     case TYPEID_EXPR:
-      /* -- a typeid expression whose operand is of polymorphic
-            class type;  */
+      /* In C++20, a typeid expression whose operand is of polymorphic
+	 class type can be constexpr.  */
       {
         tree e = TREE_OPERAND (t, 0);
-        if (!TYPE_P (e) && !type_dependent_expression_p (e)
+	if (cxx_dialect < cxx2a
+	    && !TYPE_P (e)
+	    && !type_dependent_expression_p (e)
 	    && TYPE_POLYMORPHIC_P (TREE_TYPE (e)))
           {
             if (flags & tf_error)
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-typeid1.C gcc/testsuite/g++.dg/cpp2a/constexpr-typeid1.C
new file mode 100644
index 00000000000..a81f649b44b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid1.C
@@ -0,0 +1,47 @@ 
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test non-polymorphic type.
+
+#include <typeinfo>
+
+struct B {
+  const std::type_info &ti = typeid (*this);
+};
+
+struct D : B { };
+
+constexpr B b;
+constexpr D d;
+
+static_assert (&b.ti == &typeid (B));
+static_assert (&B{}.ti == &typeid (B));
+static_assert (&B().ti == &typeid (B));
+static_assert (&typeid ((B())) == &typeid (B));
+static_assert (&typeid ((B{})) == &typeid (B));
+
+static_assert (&d.ti == &typeid (B));
+static_assert (&D{}.ti == &typeid (B));
+static_assert (&D().ti == &typeid (B));
+static_assert (&typeid ((D())) == &typeid (D));
+static_assert (&typeid ((D{})) == &typeid (D));
+
+extern D ed;
+static_assert (&typeid (ed) == &typeid (D));
+
+constexpr const B &r = d;
+static_assert (&typeid (r) == &typeid (B));
+
+constexpr bool
+test ()
+{
+  // If expression is not a glvalue expression of polymorphic type,
+  // typeid does not evaluate the expression
+  bool ok = true;
+  (void) typeid (ok = false, D());
+  (void) typeid (ok = false, B());
+  (void) typeid (ok = false, b);
+  (void) typeid (ok = false, d);
+  return ok;
+}
+
+static_assert (test ());
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-typeid2.C gcc/testsuite/g++.dg/cpp2a/constexpr-typeid2.C
new file mode 100644
index 00000000000..51c8fb451b8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid2.C
@@ -0,0 +1,60 @@ 
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test polymorphic type.
+
+#include <typeinfo>
+
+struct B {
+  virtual void foo ();
+  const std::type_info &ti_base = typeid (*this);
+};
+
+struct D : B {
+  const std::type_info &ti = typeid (*this);
+};
+
+constexpr B b;
+constexpr D d;
+
+static_assert (&b.ti_base == &typeid (B));
+static_assert (&B{}.ti_base == &typeid (B));
+static_assert (&B().ti_base == &typeid (B));
+static_assert (&typeid ((B())) == &typeid (B));
+static_assert (&typeid ((B{})) == &typeid (B));
+
+static_assert (&d.ti == &typeid (D));
+static_assert (&D{}.ti == &typeid (D));
+static_assert (&D().ti == &typeid (D));
+static_assert (&typeid ((D())) == &typeid (D));
+static_assert (&typeid ((D{})) == &typeid (D));
+
+extern D ed;
+// ??? Should this succeed?
+static_assert (&typeid (ed) == &typeid (D));
+
+constexpr const B &r = d;
+static_assert (&typeid (r) == &typeid (D));
+
+constexpr bool
+test ()
+{
+  // If expression is a glvalue expression that identifies an object
+  // of a polymorphic type, the typeid expression evaluates the expression.
+  bool ok = true;
+  // Not a glvalue.
+  (void) typeid (ok = false, D());
+  (void) typeid (ok = false, B());
+  if (!ok)
+    return false;
+
+  // Polymorphic glvalue -- this needs to be evaluated.
+  ok = false;
+  (void) typeid (ok = true, b);
+  if (!ok)
+    return false;
+  ok = false;
+  (void) typeid (ok = true, d);
+  return ok;
+}
+
+static_assert (test ());
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-typeid3.C gcc/testsuite/g++.dg/cpp2a/constexpr-typeid3.C
new file mode 100644
index 00000000000..38b9e609a9f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid3.C
@@ -0,0 +1,33 @@ 
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test typeid in a template.
+
+#include <typeinfo>
+
+struct B { virtual void fn (); };
+struct D : B { };
+
+void abort ();
+
+template<typename>
+constexpr void
+fn ()
+{
+  D d;
+  if (&typeid (d) != &typeid (D))
+   abort ();
+}
+constexpr bool b1 = (fn<int>(), true);
+
+// Type-dependent.
+template<typename T>
+constexpr void
+fn2 ()
+{
+  T t{};
+  if (&typeid (t) != &typeid (T))
+   abort ();
+}
+constexpr bool b2 = (fn2<int>(), true);
+constexpr bool b3 = (fn2<B>(), true);
+constexpr bool b4 = (fn2<D>(), true);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-typeid4.C gcc/testsuite/g++.dg/cpp2a/constexpr-typeid4.C
new file mode 100644
index 00000000000..0a99aae9bf9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-typeid4.C
@@ -0,0 +1,19 @@ 
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++17 } }
+// Test typeid in a template.
+
+#include <typeinfo>
+
+struct B { virtual void f(); };
+struct B2 : B { };
+
+template<typename T>
+constexpr bool
+fn ()
+{
+  constexpr B2 b2;
+  static_assert(&typeid(b2) == &typeid(B2)); // { dg-error ".typeid. is not a constant expression because .b2. is of polymorphic type|non-constant condition" "" { target c++17_down } }
+  return true;
+}
+
+static_assert (fn<int>());