On 12/17/19 5:34 PM, Marek Polacek wrote:
On Mon, Dec 16, 2019 at 04:00:14PM -0500, Jason Merrill wrote:
On 12/16/19 3:55 PM, Jason Merrill wrote:
On 12/14/19 4:25 PM, Marek Polacek wrote:
On Fri, Dec 13, 2019 at 05:56:57PM -0500, Jason Merrill wrote:
On 12/13/19 3:20 PM, Marek Polacek wrote:
+  /* Given dynamic_cast<T>(v),
+
+     [expr.dynamic.cast] If C is the class type to which T
points or refers,
+     the runtime check logically executes as follows:
+
+     If, in the most derived object pointed (referred) to
by v, v points
+     (refers) to a public base class subobject of a C
object, and if only
+     one object of type C is derived from the subobject
pointed (referred)
+     to by v the result points (refers) to that C object.
+
+     In this case, HINT >= 0.  */
+  if (hint >= 0)

Won't this code work for hint == -3 as well?

Yes, it does.  In fact, none of the tests was testing the hint == -3
case, so
I've fixed the code up and added constexpr-dynamic15.C to test it.

+    {
+      /* Look for a component with type TYPE.  */
+      obj = get_component_with_type (obj, type);

You don't seem to use mdtype at all in this case.  Shouldn't
get_component_with_type stop at mdtype if it hasn't found type yet?

It was used for diagnostics but not in get_component_with_type.  It makes
sense to stop at MDTYPE; I've adjusted the code to do so.  E.g., if
we have OBJ in the form of g.D.2121.D.2122.D.2123.D.2124, usually the
component with the most derived type is "g", but in a 'tor, it can be
a different component too.

+      /* If not found or not accessible, give an error.  */
+      if (obj == NULL_TREE || obj == error_mark_node)
+    {
+      if (reference_p)
+        {
+          if (!ctx->quiet)
+        {
+          error_at (loc, "reference %<dynamic_cast%> failed");
+          if (obj == NULL_TREE)
+            inform (loc, "dynamic type %qT of its operand does not "
+                "have an unambiguous public base class %qT",
+                mdtype, type);
+          else
+            inform (loc, "static type %qT of its operand is a "
+                "non-public base class of dynamic type %qT",
+                objtype, type);
+
+        }
+          *non_constant_p = true;
+        }
+      return integer_zero_node;
+    }
+      else
+    /* The result points to the TYPE object.  */
+    return cp_build_addr_expr (obj, complain);
+    }
+  /* Otherwise, if v points (refers) to a public base class
subobject of the
+     most derived object, and the type of the most derived
object has a base
+     class, of type C, that is unambiguous and public, the
result points
+     (refers) to the C subobject of the most derived object.
+
+     But it can also be an invalid case.  */

And I think we need to fall through to this code if the hint
turns out to be
wrong, i.e. V is a public base of C, but v is not that
subobject, but rather
a sibling base of C, like

True.  HINT is really just an optimization hint, nothing more.  I've
adjusted
the code to fall through to the normal processing if the HINT >= 0
or -3 case
doesn't succeed.

struct A { virtual void f(); };
struct B1: A { };
struct B2: A { };
struct C: B1, B2 { };
int main()
{
    C c;
    A* ap = (B1*)c;
    constexpr auto p = dynamic_cast<B2*>(ap); // should succeed
}

Whew, there's always One More Case. :/  New constexpr-dynamic16.c
covers it.

--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
@@ -0,0 +1,34 @@
+// PR c++/88337 - Implement P1327R1: Allow
dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a constructor.
+
+struct V {
+  virtual void f();
+};
+
+struct A : V { };
+
+struct B : V {
+  constexpr B(V*, A*);
+};
+
+struct D : A, B {
+  constexpr D() : B((A*)this, this) { } // { dg-message "in
'constexpr' expansion of" }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  // well-defined: v of type V*, V base of B results in B*
+  B* b = dynamic_cast<B*>(v);
+  if (b != nullptr)
+    __builtin_abort ();
+
+  B& br = dynamic_cast<B&>(*v); // { dg-error "reference
.dynamic_cast. failed" }
+// { dg-message "dynamic type .A. of its operand does not
have an unambiguous public base class .B." "" { target *-*-*
} .-1 }
+
+  // FIXME: UB in constexpr should be detected.
+  dynamic_cast<B*>(a);          // undefined behavior, a
has type A*, A not a base of B

What undefined behavior?  Seems to me the dynamic_cast should
just fail and
return a null pointer.

This is <http://eel.is/c++draft/class.cdtor#6.sentence-3>:
"If the operand of the dynamic_cast refers to the object under
construction
or destruction and the static type of the operand is not a pointer to or
object of the constructor or destructor's own class or one of its
bases, the
dynamic_cast results in undefined behavior."

Clang++ doesn't detect this either.

Ah, interesting.  That text goes back to C++98.  I guess the reason for
that was that the vtable pointer might give problematic answers in that
situation.  It should be straightforward to detect this in constexpr
evaluation; if mdtype is not the actual complete object, we can see if
*this is also part of mdtype (success), or otherwise the complete object
(undefined).

It looks like get_component_with_type (<*this>, type, mdtype) should work well 
for that check.

Well, the second argument is wrong, we'll need to find the complete object
type for it.

In both cases (dynamic_cast<B*>(v); and dynamic_cast<B*>(a);) *this is
"d.D.2126" (types D, B) and mdtype is A, > so the above get_component_with_type 
won't work as far as I can see.

I've come up with this instead, which works
for the testcases I have, but is probably not entirely correct in general,
either :(.

It's the
   if (!same_type_ignoring_top_level_qualifiers_p (mdtype, complete_type))
    ...
block, the UB is detected as demonstrated in constexpr-dynamic17.C.

To find *this, I went looking in call_stack.  What do you think about this?

Bootstrapped/regtested on x86_64-linux.

2019-12-17  Marek Polacek  <pola...@redhat.com>

        PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
        * constexpr.c (initialized_type): Forward declare.
        (cxx_dynamic_cast_fn_p): New function.
        (extract_obj_from_addr_offset): New function.
        (get_component_with_type): New function.
        (cxx_eval_dynamic_cast_fn): New function.
        (cxx_eval_call_expression): Call cxx_eval_dynamic_cast_fn for a call
        to __dynamic_cast.
        (potential_constant_expression_1): Don't give up on
        cxx_dynamic_cast_fn_p.
        * rtti.c (build_dynamic_cast_1): When creating a call to
        __dynamic_cast, use the location of the original expression.

        * g++.dg/cpp2a/constexpr-dynamic1.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic10.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic11.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic12.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic13.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic14.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic15.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic16.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic17.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic2.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic3.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic4.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic5.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic6.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic7.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic8.C: New test.
        * g++.dg/cpp2a/constexpr-dynamic9.C: New test.

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index f3f03e7d621..4b3751a17ca 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1070,6 +1070,7 @@ struct constexpr_ctx {
static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table; +static tree initialized_type (tree);
  static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
                                          bool, bool *, bool *, tree * = NULL);
@@ -1689,6 +1690,271 @@ is_std_allocator_allocate (tree fndecl)
    return decl_in_std_namespace_p (decl);
  }
+/* Return true if FNDECL is __dynamic_cast. */
+
+static inline bool
+cxx_dynamic_cast_fn_p (tree fndecl)
+{
+  return (cxx_dialect >= cxx2a
+         && id_equal (DECL_NAME (fndecl), "__dynamic_cast")
+         && CP_DECL_CONTEXT (fndecl) == global_namespace);
+}
+
+/* Often, we have an expression in the form of address + offset, e.g.
+   "&_ZTV1A + 16".  Extract the object from it, i.e. "_ZTV1A".  */
+
+static tree
+extract_obj_from_addr_offset (tree expr)
+{
+  if (TREE_CODE (expr) == POINTER_PLUS_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+  STRIP_NOPS (expr);
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+  return expr;
+}
+
+/* Given a PATH like
+
+     g.D.2181.D.2154.D.2102.D.2093
+
+   find a component with type TYPE.  Return NULL_TREE if not found, and
+   error_mark_node if the component is not accessible.  If STOP is non-null,
+   this function will return NULL_TREE if STOP is found before TYPE.  */
+
+static tree
+get_component_with_type (tree path, tree type, tree stop)
+{
+  while (true)
+    {
+      if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), type))
+       /* Found it.  */
+       return path;
+      else if (stop
+              && (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path),
+                                                             stop)))
+       return NULL_TREE;
+      else if (TREE_CODE (path) == COMPONENT_REF
+              && DECL_FIELD_IS_BASE (TREE_OPERAND (path, 1)))
+       {
+         /* We need to check that the component we're accessing is in fact
+            accessible.  */
+         if (TREE_PRIVATE (TREE_OPERAND (path, 1))
+             || TREE_PROTECTED (TREE_OPERAND (path, 1)))
+           return error_mark_node;
+         path = TREE_OPERAND (path, 0);
+       }
+      else
+       return NULL_TREE;
+    }
+}
+
+/* Evaluate a call to __dynamic_cast (permitted by P1327R1).
+
+   The declaration of __dynamic_cast is:
+
+   void* __dynamic_cast (const void* __src_ptr,
+                        const __class_type_info* __src_type,
+                        const __class_type_info* __dst_type,
+                        ptrdiff_t __src2dst);
+
+   where src2dst has the following possible values
+
+   >-1: src_type is a unique public non-virtual base of dst_type
+       dst_ptr + src2dst == src_ptr
+   -1: unspecified relationship
+   -2: src_type is not a public base of dst_type
+   -3: src_type is a multiple public non-virtual base of dst_type
+
+  Since literal types can't have virtual bases, we only expect hint >=0,
+  -2, or -3.  */
+
+static tree
+cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
+                         bool *non_constant_p, bool *overflow_p)
+{
+  /* T will be something like
+      __dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8)
+     dismantle it.  */
+  gcc_assert (call_expr_nargs (call) == 4);
+  tsubst_flags_t complain = ctx->quiet ? tf_none : tf_warning_or_error;
+  tree obj = CALL_EXPR_ARG (call, 0);
+  tree type = CALL_EXPR_ARG (call, 2);
+  HOST_WIDE_INT hint = int_cst_value (CALL_EXPR_ARG (call, 3));
+  location_t loc = cp_expr_loc_or_input_loc (call);
+
+  /* Get the target type of the dynamic_cast.  */
+  gcc_assert (TREE_CODE (type) == ADDR_EXPR);
+  type = TREE_OPERAND (type, 0);
+  type = TREE_TYPE (DECL_NAME (type));
+
+  /* TYPE can only be either T* or T&.  We can't know which of these it
+     is by looking at TYPE, but OBJ will be "(T*) x" in the first case,
+     and something like "(T*)(T&)(T*) x" in the second case.  */
+  bool reference_p = false;
+  while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR)
+    {
+      reference_p |= TYPE_REF_P (TREE_TYPE (obj));
+      obj = TREE_OPERAND (obj, 0);
+    }
+
+  /* Evaluate the object so that we know its dynamic type.  */
+  obj = cxx_eval_constant_expression (ctx, obj, /*lval*/false, non_constant_p,
+                                     overflow_p);
+  if (*non_constant_p)
+    return call;
+
+  /* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
+     but when HINT is > 0, it can also be something like
+     &d.D.2102 + 18446744073709551608, which includes the BINFO_OFFSET.  */
+  obj = extract_obj_from_addr_offset (obj);
+  const tree objtype = TREE_TYPE (obj);
+  /* If OBJ doesn't refer to a base field, we're done.  */
+  if (tree t = (TREE_CODE (obj) == COMPONENT_REF
+               ? TREE_OPERAND (obj, 1) : obj))
+    if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t))
+      return integer_zero_node;
+
+  /* [class.cdtor] When a dynamic_cast is used in a constructor ...
+     or in a destructor ... if the operand of the dynamic_cast refers
+     to the object under construction or destruction, this object is
+     considered to be a most derived object that has the type of the
+     constructor or destructor's class.  */
+  tree vtable = build_vfield_ref (obj, TREE_TYPE (obj));
+  vtable = cxx_eval_constant_expression (ctx, vtable, /*lval*/false,
+                                        non_constant_p, overflow_p);
+  if (*non_constant_p)
+    return call;
+  /* VTABLE will be &_ZTV1A + 16 or similar, get _ZTV1A.  */
+  vtable = extract_obj_from_addr_offset (vtable);
+  const tree mdtype = DECL_CONTEXT (vtable);
+
+  tree t = obj;
+  while (TREE_CODE (t) == COMPONENT_REF
+        && DECL_FIELD_IS_BASE (TREE_OPERAND (t, 1)))
+    t = TREE_OPERAND (t, 0);
+  const tree complete_type = TREE_TYPE (t);
+
+  /* [class.cdtor]/6 "If the operand of the dynamic_cast refers to
+     the object under construction or destruction and the static type
+     of the operand is not a pointer to or object of the constructor
+     or destructor's own class or one of its bases, the dynamic_cast
+     results in undefined behavior."  And undefined behavior should be
+     detected in constexpr contexts.  */
+  if (!same_type_ignoring_top_level_qualifiers_p (mdtype, complete_type))
+    {
+      unsigned ix;
+      FOR_EACH_VEC_ELT_REVERSE (call_stack, ix, t)
+       if (tree fn = cp_get_callee_fndecl_nofold (t))
+         if (DECL_CONSTRUCTOR_P (fn))
+           {
+             /* Get *this of the current constructor.  */
+             tree cdtor_type = initialized_type (t);
+             if (!DERIVED_FROM_P (objtype, cdtor_type))

Walking the call_stack is an interesting idea, since ctx only has the innermost call, which might not be the relevant constructor. And the innermost call might not even have a pointer to the object under construction.

But this only finds that there is an object under construction, not whether obj is part of the same object. It should be possible to construct a testcase where we start constructing one object X, and then pass a pointer to X to the constructor for Y; doing a dynamic_cast of the X pointer shouldn't give an error just because it isn't a base of Y, since the X pointer points to the X under construction, not the Y under construction.

Something like

struct X;
struct Y {
  virtual void f();
  Y(X* x) { dynamic_cast<Y>(x); } // returns NULL
};
struct X
{
  virtual void f();
  X() { Y(this); }
};
struct Z: X
{
  virtual void f();
} z;

Note that in constexpr-dynamic17.C, if you reverse the order of "D: A, B" to "D: B, A", we hit undefined behavior in the cast because the A vptr isn't set yet (though the diagnostic could be better). A better way to detect this undefined behavior for the A, B case might be to clear the vptrs for A after we're done constructing it; the most derived constructor will set them again once base constructors are done. This could happen either in emit_mem_initializers or in cxx_expand_call_expression.

But I think let's leave that for a follow-on patch. Let's drop this hunk and reverse the bases in constexpr-dynamic17.C as I mentioned above so we still get an error. OK with that change.

+               {
+                 if (!ctx->quiet)
+                   error_at (loc, "static type %qT of %<dynamic_cast%> "
+                             "not a base of %qT", objtype, cdtor_type);
+                 *non_constant_p = true;
+                 return integer_zero_node;
+               }
+             break;
+           }
+    }
+
+  /* Given dynamic_cast<T>(v),
+
+     [expr.dynamic.cast] If C is the class type to which T points or refers,
+     the runtime check logically executes as follows:
+
+     If, in the most derived object pointed (referred) to by v, v points
+     (refers) to a public base class subobject of a C object, and if only
+     one object of type C is derived from the subobject pointed (referred)
+     to by v the result points (refers) to that C object.
+
+     In this case, HINT >= 0 or -3.  */
+  if (hint >= 0 || hint == -3)
+    {
+      /* Look for a component with type TYPE.  */
+      tree t = get_component_with_type (obj, type, mdtype);
+      /* If not accessible, give an error.  */
+      if (t == error_mark_node)
+       {
+         if (reference_p)
+           {
+             if (!ctx->quiet)
+               {
+                 error_at (loc, "reference %<dynamic_cast%> failed");
+                 inform (loc, "static type %qT of its operand is a "
+                         "non-public base class of dynamic type %qT",
+                         objtype, type);
+
+               }
+             *non_constant_p = true;
+           }
+         return integer_zero_node;
+       }
+      else if (t)
+       /* The result points to the TYPE object.  */
+       return cp_build_addr_expr (t, complain);
+      /* Else, TYPE was not found, because the HINT turned out to be wrong.
+        Fall through to the normal processing.  */
+    }
+
+  /* Otherwise, if v points (refers) to a public base class subobject of the
+     most derived object, and the type of the most derived object has a base
+     class, of type C, that is unambiguous and public, the result points
+     (refers) to the C subobject of the most derived object.
+
+     But it can also be an invalid case.  */
+
+  /* Get the most derived object.  */
+  obj = get_component_with_type (obj, mdtype, NULL_TREE);
+  if (obj == error_mark_node)
+    {
+      if (reference_p)
+       {
+         if (!ctx->quiet)
+           {
+             error_at (loc, "reference %<dynamic_cast%> failed");
+             inform (loc, "static type %qT of its operand is a non-public"
+                     " base class of dynamic type %qT", objtype, mdtype);
+           }
+         *non_constant_p = true;
+       }
+      return integer_zero_node;
+    }
+  else
+    gcc_assert (obj);
+
+  /* Check that the type of the most derived object has a base class
+     of type TYPE that is unambiguous and public.  */
+  base_kind b_kind;
+  tree binfo = lookup_base (mdtype, type, ba_check, &b_kind, tf_none);
+  if (!binfo || binfo == error_mark_node)
+    {
+      if (reference_p)
+       {
+         if (!ctx->quiet)
+           {
+             error_at (loc, "reference %<dynamic_cast%> failed");
+             if (b_kind == bk_ambig)
+               inform (loc, "%qT is an ambiguous base class of dynamic "
+                       "type %qT of its operand", type, mdtype);
+             else
+               inform (loc, "dynamic type %qT of its operand does not "
+                       "have an unambiguous public base class %qT",
+                       mdtype, type);
+           }
+         *non_constant_p = true;
+       }
+      return integer_zero_node;
+    }
+  /* If so, return the TYPE subobject of the most derived object.  */
+  obj = convert_to_base_statically (obj, binfo);
+  return cp_build_addr_expr (obj, complain);
+}
+
  /* Subroutine of cxx_eval_constant_expression.
     Evaluate the call expression tree T in the context of OLD_CALL expression
     evaluation.  */
@@ -1854,6 +2120,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree 
t,
          gcc_assert (arg1);
          return arg1;
        }
+      else if (cxx_dynamic_cast_fn_p (fun))
+       return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
+
        if (!ctx->quiet)
        {
          if (!lambda_static_thunk_p (fun))
@@ -6729,7 +6998,8 @@ potential_constant_expression_1 (tree t, bool want_rval, 
bool strict, bool now,
                    && (!cxx_placement_new_fn (fun)
                        || TREE_CODE (t) != CALL_EXPR
                        || current_function_decl == NULL_TREE
-                       || !is_std_construct_at (current_function_decl)))
+                       || !is_std_construct_at (current_function_decl))
+                   && !cxx_dynamic_cast_fn_p (fun))
                  {
                    if (flags & tf_error)
                      {
diff --git gcc/cp/rtti.c gcc/cp/rtti.c
index 1b6b87ba8d6..9a242dc64e4 100644
--- gcc/cp/rtti.c
+++ gcc/cp/rtti.c
@@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
              dynamic_cast_node = dcast_fn;
            }
          result = build_cxx_call (dcast_fn, 4, elems, complain);
+         SET_EXPR_LOCATION (result, loc);
if (tc == REFERENCE_TYPE)
            {
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
new file mode 100644
index 00000000000..e8ba63d9609
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
@@ -0,0 +1,40 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast.
+
+struct B {
+  virtual void baz () {}
+};
+
+struct D : B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  B b;
+  B *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B b;
+constexpr B *b1 = const_cast<B*>(&b);
+constexpr B *b2 = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b2) == &d);
+static_assert(&dynamic_cast<D&>(*b2) == &d);
+static_assert(dynamic_cast<const B*>(&d) == &d);
+static_assert(&dynamic_cast<const B&>(d) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
new file mode 100644
index 00000000000..c226292a07d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
@@ -0,0 +1,12 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Virtual base.
+
+struct C { virtual void a(); };
+struct B { virtual void b(); };
+struct A : virtual B, C { virtual void c(); }; // { dg-error ".struct A. has 
virtual base classes" }
+
+constexpr A a; // { dg-error "call" }
+
+constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
new file mode 100644
index 00000000000..6069fbfd01c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
@@ -0,0 +1,35 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a constructor.
+// [class.cdtor]#6: "If the operand of the dynamic_cast refers to the object
+// under construction or destruction and the static type of the operand is not
+// a pointer to or object of the constructor or destructor's own class or one
+// of its bases, the dynamic_cast results in undefined behavior.
+
+struct V {
+  virtual void f();
+};
+
+struct A : V { };
+
+struct B : V {
+  constexpr B(V*, A*);
+};
+
+struct D : A, B {
+  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion 
of" }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  // well-defined: v of type V*, V base of B results in B*
+  B* b = dynamic_cast<B*>(v);
+  if (b != nullptr)
+    __builtin_abort ();
+
+  B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. 
failed" }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base 
class .B." "" { target *-*-* } .-1 }
+}
+
+constexpr D d; // { dg-message "in 'constexpr' expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
new file mode 100644
index 00000000000..0ce9beb8d72
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
@@ -0,0 +1,28 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a destructor.
+
+struct A2 { virtual void a2(); };
+
+struct A : A2 { virtual void a(); };
+
+struct C2 { virtual void c2(); };
+
+struct B : A, C2 {
+  constexpr ~B();
+};
+
+constexpr B::~B()
+{
+  A *a = dynamic_cast<A*>((C2*)this);
+  if (a != (A*) this)
+    __builtin_abort ();
+  A& ar = dynamic_cast<A&>((C2&)*this);
+  if (&ar != &(A&)*this)
+    __builtin_abort ();
+}
+
+struct D : B { virtual void d(); };
+
+constexpr D d;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
new file mode 100644
index 00000000000..203067a2581
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
@@ -0,0 +1,86 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Adopted from g++.old-deja/g++.other/dyncast1.C.
+
+// 1. downcast
+// 1.1. single inheritance case
+
+struct A { virtual void a(); };
+struct AA : A {};
+struct B : A {};
+struct BB : B {};
+class C : B {};
+struct D : C {};
+
+struct CC : B {};
+class DD : CC {};
+
+class CCC : protected B {};
+class DDD : protected CCC {};
+
+constexpr D d;
+static_assert (dynamic_cast<D*> ((A*)&d) == nullptr);
+static_assert (dynamic_cast<D*> ((B*)&d) == nullptr);
+static_assert (&d == dynamic_cast<D*> ((C*)&d));
+static_assert (dynamic_cast<C*> ((B*)&d) == nullptr);
+
+constexpr DD dd;
+static_assert (dynamic_cast<DD*> ((A*)&dd) == nullptr);
+static_assert (dynamic_cast<DD*> ((B*)&dd) == nullptr);
+
+constexpr DDD ddd;
+static_assert (dynamic_cast<DDD*> ((A*)&ddd) == nullptr);
+static_assert (dynamic_cast<DDD*> ((B*)&ddd) == nullptr);
+static_assert (dynamic_cast<CCC*> ((B*)&ddd) == nullptr);
+
+// 1.2. multiple inheritance case
+// 1.2.1. all bases are public
+
+struct E : D, CC {};
+struct EE : CC, D {}; //Will search in reverse order.
+
+constexpr E e;
+static_assert (dynamic_cast<E*> ((A*)(D*)&e) == nullptr);
+static_assert (dynamic_cast<E*> ((B*)(D*)&e) == nullptr);
+static_assert (&e == dynamic_cast<E*> ((C*)(D*)&e));
+static_assert (&e == dynamic_cast<E*> ((B*)(CC*)&e));
+static_assert ((CC*)&e == dynamic_cast<CC*> ((B*)(CC*)&e));
+
+constexpr EE ee;
+static_assert (dynamic_cast<EE*> ((A*)(D*)&ee) == nullptr);
+static_assert (dynamic_cast<EE*> ((B*)(D*)&ee) == nullptr);
+static_assert (&ee == dynamic_cast<EE*> ((C*)(D*)&ee));
+static_assert (&ee == dynamic_cast<EE*> ((B*)(CC*)&ee));
+static_assert ((CC*)&ee == dynamic_cast<CC*> ((B*)(CC*)&ee));
+
+// 1.2.2 one or more branches are nonpublic
+
+struct X : private BB, E {};
+struct Y : AA, private B {};
+
+class XX : BB, E {};
+
+constexpr X x;
+static_assert (&x == dynamic_cast<X*>((B*)(CC*)(E*)&x));
+
+constexpr XX xx;
+static_assert (dynamic_cast<XX*>((B*)(CC*)(E*)&xx) == nullptr);      
+
+constexpr Y y;
+static_assert (dynamic_cast<Y*>((B*)&y) == nullptr);
+static_assert (dynamic_cast<Y*>((A*)(B*)&y) == nullptr);
+
+// 2. crosscast
+
+struct J { virtual void j(); };
+struct K : CC, private J {};
+class KK : J, CC{};
+               
+static_assert (dynamic_cast<CC*> ((B*)(D*)&e) == nullptr);
+static_assert ((CC*)&e == dynamic_cast<CC*> ((C*)(D*)&e));
+
+constexpr K k;
+static_assert (dynamic_cast<J*> ((B*)&k) == nullptr);
+constexpr KK kk;
+static_assert (dynamic_cast<J*> ((CC*)&kk) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
new file mode 100644
index 00000000000..f739c6df94b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
@@ -0,0 +1,105 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Adopted from g++.old-deja/g++.other/dyncast1.C.
+// But use reference dynamic_cast.
+
+// 1. downcast
+// 1.1. single inheritance case
+
+struct A { virtual void a(); };
+struct AA : A {};
+struct B : A {};
+struct BB : B {};
+class C : B {};
+struct D : C {};
+
+struct CC : B {};
+class DD : CC {};
+
+class CCC : protected B {};
+class DDD : protected CCC {};
+
+constexpr D d;
+constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic 
type .D." "" { target *-*-* } .-1 }
+constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic 
type .D." "" { target *-*-* } .-1 }
+static_assert (&d == &dynamic_cast<const D&> ((C&)d));
+constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic 
type .D." "" { target *-*-* } .-1 }
+
+constexpr DD dd;
+constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic 
type .DD." "" { target *-*-* } .-1 }
+constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic 
type .DD." "" { target *-*-* } .-1 }
+
+constexpr DDD ddd;
+constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic 
type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic 
type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic 
type .DDD." "" { target *-*-* } .-1 }
+
+// 1.2. multiple inheritance case
+// 1.2.1. all bases are public
+
+struct E : D, CC {};
+struct EE : CC, D {}; //Will search in reverse order.
+
+constexpr E e;
+constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type 
.E." "" { target *-*-* } .-1 }
+constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type 
.E." "" { target *-*-* } .-1 }
+static_assert (&e == &dynamic_cast<E&> ((C&)(D&)e));
+static_assert (&e == &dynamic_cast<E&> ((B&)(CC&)e));
+static_assert (&(CC&)e == &dynamic_cast<CC&> ((B&)(CC&)e));
+
+constexpr EE ee;
+constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error 
"reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type 
.EE." "" { target *-*-* } .-1 }
+constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error 
"reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type 
.EE." "" { target *-*-* } .-1 }
+static_assert (&ee == &dynamic_cast<EE&> ((C&)(D&)ee));
+static_assert (&ee == &dynamic_cast<EE&> ((B&)(CC&)ee));
+static_assert (&(CC&)ee == &dynamic_cast<CC&> ((B&)(CC&)ee));
+
+// 1.2.2 one or more branches are nonpublic
+
+struct X : private BB, E {};
+struct Y : AA, private B {};
+
+class XX : BB, E {};
+
+constexpr X x;
+static_assert (&x == &dynamic_cast<X&>((B&)(CC&)(E&)x));
+
+constexpr XX xx;
+constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error 
"reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type 
.XX." "" { target *-*-* } .-1 }
+
+constexpr Y y;
+constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic 
type .Y." "" { target *-*-* } .-1 }
+constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type 
.Y." "" { target *-*-* } .-1 }
+
+// 2. crosscast
+
+struct J { virtual void j(); };
+struct K : CC, private J {};
+class KK : J, CC{};
+               
+constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error 
"reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type 
.CC." "" { target *-*-* } .-1 }
+static_assert (&(CC&)e == &dynamic_cast<CC&> ((C&)(D&)e));
+
+constexpr K k;
+constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base 
class .J." "" { target *-*-* } .-1 }
+constexpr KK kk;
+constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic 
type .KK." "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C
new file mode 100644
index 00000000000..fcf507289c4
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C
@@ -0,0 +1,14 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test HINT = -3 (SRC is a multiple public non-virtual base of DST).
+
+struct A { virtual void a() {} };
+struct C : A { };
+struct D : A { };
+struct B : C, D { };
+
+constexpr B b;
+static_assert (&dynamic_cast<B&>((A&)(C&)b) == &b);
+static_assert (&dynamic_cast<B&>((A&)(D&)b) == &b);
+static_assert (dynamic_cast<B*>((A*)(C*)&b) == &b);
+static_assert (dynamic_cast<B*>((A*)(D*)&b) == &b);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C
new file mode 100644
index 00000000000..f0394d130a3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C
@@ -0,0 +1,20 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Here the hint turns out to be wrong: A is a public base of B2, but the
+// dynamic_cast operand is not that subobject, but rather a sibling base of
+// B2.
+
+struct A { virtual void f(); };
+struct B1: A { };
+struct B2: A { };
+struct C: B1, B2 { };
+
+constexpr C c;
+constexpr A *ap = (B1*)&c;
+constexpr A &ar = (B1&)c;
+constexpr auto p = dynamic_cast<B2*>(ap);
+static_assert (p != nullptr);
+constexpr auto p2 = dynamic_cast<B2&>(ar);
+static_assert(dynamic_cast<B2*>(ap) == (B2*)&c);
+static_assert(dynamic_cast<B2*>((B1*)&c) == (B2*)&c);
+static_assert(&dynamic_cast<B2&>((B1&)c) == &(B2&)c);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
new file mode 100644
index 00000000000..391d7998556
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
@@ -0,0 +1,30 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a constructor.
+// [class.cdtor]#6: "If the operand of the dynamic_cast refers to the object
+// under construction or destruction and the static type of the operand is not
+// a pointer to or object of the constructor or destructor's own class or one
+// of its bases, the dynamic_cast results in undefined behavior.
+
+struct V {
+  virtual void f();
+};
+
+struct A : V { };
+
+struct B : V {
+  constexpr B(V*, A*);
+};
+
+struct D : A, B {
+  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion 
of" }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  // undefined behavior, a has type A*, A not a base of B
+  dynamic_cast<B*>(a); // { dg-error "static type .A. of .dynamic_cast. not a base 
of .B." }
+}
+
+constexpr D d; // { dg-message "in 'constexpr' expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
new file mode 100644
index 00000000000..aae03f691ca
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
@@ -0,0 +1,41 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast, with hint > 0.
+
+struct B {
+  virtual void baz () {}
+};
+
+struct B2 {
+  virtual void baz2 () {}
+};
+
+struct D : B, B2 { };
+
+constexpr bool
+fn ()
+{
+  // try &/&&, add address test
+  bool ok = true;
+  B2 b;
+  B2 *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B2 *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B2 *b = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b) == &d);
+static_assert(&dynamic_cast<D&>(*b) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
new file mode 100644
index 00000000000..c3e09808e32
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
@@ -0,0 +1,33 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Sidecast.
+
+struct A {
+  virtual void afn () {}
+};
+
+struct B {
+  virtual void bfn () {}
+};
+
+struct D : A, B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  D d;
+  A *a = &d;
+  if (B *bp = dynamic_cast<B*>(a))
+    /*OK*/;
+  else
+    ok = false;
+
+  A &ar = d;
+  B &br = dynamic_cast<B&>(ar);
+
+  return ok;
+}
+
+static_assert(fn ());
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
new file mode 100644
index 00000000000..3adc524379d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
@@ -0,0 +1,55 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// From clang's constant-expression-cxx2a.cpp.
+
+struct A2 { virtual void a2(); };
+struct A : A2 { virtual void a(); };
+struct B : A {};
+struct C2 { virtual void c2(); };
+struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
+struct D { virtual void d(); };
+struct E { virtual void e(); };
+struct F : B, C, D, private E { void *f = 
dynamic_cast<void*>(static_cast<D*>(this)); };
+struct Padding { virtual void padding(); };
+struct G : Padding, F {};
+
+constexpr G g;
+
+// During construction of C, A is unambiguous subobject of dynamic type C.
+static_assert(g.c == (C*)&g);
+// ... but in the complete object, the same is not true, so the runtime fails.
+static_assert(dynamic_cast<const A*>(static_cast<const C2*>(&g)) == nullptr);
+
+// dynamic_cast<void*> produces a pointer to the object of the dynamic type.
+static_assert(g.f == (void*)(F*)&g);
+static_assert(dynamic_cast<const void*>(static_cast<const D*>(&g)) == &g);
+
+constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error 
"reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { 
target *-*-* } .-1 }
+
+// Can navigate from A2 to its A...
+static_assert(&dynamic_cast<A&>((A2&)(B&)g) == &(A&)(B&)g);
+// ... and from B to its A ...
+static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
+// ... but not from D.
+static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant 
condition for static assertion|reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { 
target *-*-* } .-1 }
+
+// Can cast from A2 to sibling class D.
+static_assert(&dynamic_cast<D&>((A2&)(B&)g) == &(D&)g);
+
+// Cannot cast from private base E to derived class F.
+constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic 
type .G." "" { target *-*-* } .-1 }
+
+// Cannot cast from B to private sibling E.
+constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base 
class .E." "" { target *-*-* } .-1 }
+
+struct Unrelated { virtual void unrelated(); };
+
+constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error 
"reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base 
class .Unrelated." "" { target *-*-* } .-1 }
+constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error 
"reference .dynamic_cast. failed" }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic 
type .G." "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
new file mode 100644
index 00000000000..743b3018d2f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
@@ -0,0 +1,22 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Multiple levels.
+
+struct A { virtual void a(); };
+struct B : A { virtual void b(); };
+struct C : B { virtual void c(); };
+struct D : C { virtual void d(); };
+struct E : D { virtual void e(); };
+struct F : E { virtual void f(); };
+
+constexpr F f;
+
+// F->C->A->B == F->C->B
+static_assert (&dynamic_cast<B&>((A&)(C&)f) == &(B&)(C&)f);
+// F->A->E == F->E
+static_assert (&dynamic_cast<E&>((A&)f) == &(E&)f);
+// F->E->D->C->B->A->C == F->C
+static_assert (&dynamic_cast<C&>((A&)(B&)(C&)(D&)(E&)f) == &(C&)f);
+// F->B->F == F
+static_assert (&dynamic_cast<F&>((B&)f) == &f);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
new file mode 100644
index 00000000000..23434734e26
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
@@ -0,0 +1,25 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Private base.
+
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct B : private P1 { virtual void b(); };
+struct C { virtual void c(); };
+struct A : B, C, private P2 { virtual void a(); };
+
+constexpr A a;
+
+// P1 is a non-public base of A.
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic 
type .A." "" { target *-*-* } .-1 }
+
+// Don't error here.
+static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
+
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic 
type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
new file mode 100644
index 00000000000..d71497aae6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
@@ -0,0 +1,25 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Protected base.
+
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct B : protected P1 { virtual void b(); };
+struct C { virtual void c(); };
+struct A : B, C, protected P2 { virtual void a(); };
+
+constexpr A a;
+
+// P1 is a non-public base of A.
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic 
type .A." "" { target *-*-* } .-1 }
+
+// Don't error here.
+static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
+
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic 
type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
new file mode 100644
index 00000000000..8056f30bb99
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
@@ -0,0 +1,24 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Unrelated type.
+
+struct B { virtual void b(); };
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct A : public B, private P1, protected P2 { virtual void a(); };
+
+constexpr A a;
+
+struct U { virtual void u(); };
+
+constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base 
class .U." "" { target *-*-* } .-1 }
+constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic 
type .A." "" { target *-*-* } .-1 }
+constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic 
type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<U*>((B*)&a) == nullptr);
+static_assert (dynamic_cast<U*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<U*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C 
gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
new file mode 100644
index 00000000000..d8cbb2f2f0d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
@@ -0,0 +1,17 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Ambiguous base.
+
+struct A { virtual void a(); };
+struct B : A { virtual void b(); };
+struct C : A { virtual void c(); };
+struct D { virtual void a(); };
+struct E : B, C, D { virtual void d(); };
+
+constexpr E e;
+
+constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference 
.dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" 
"" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<A*>((D*)&e) == nullptr);


Reply via email to