C++ PATCH for c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr

Marek Polacek polacek@redhat.com
Sun Dec 29 19:49:00 GMT 2019


On Sat, Dec 21, 2019 at 04:50:41PM -0500, Jason Merrill wrote:
> On 12/17/19 5:34 PM, Marek Polacek wrote:
> > +  /* [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.

Exactly.

> But this only finds that there is an object under construction, not whether
> obj is part of the same object.  

That is true also.  :/

> 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.

Agreed; I've opened PR93096.  Here's what I've committed after another
bootstrap/regtest:

commit 9d3f24adb6d09184fd348ef8d92e6d0b965e3f00
Author: mpolacek <mpolacek@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Sun Dec 29 16:44:41 2019 +0000

            PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
    
    This patch implements
    <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1327r1.html>.
    
    When build_dynamic_cast realizes that a dynamic_cast needs a run-time check, it
    generates a call to __dynamic_cast -- see dyncast.cc in libsupc++ for its
    definition.  The gist of my approach is to evaluate such a call at compile time.
    
            * constexpr.c (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.
    
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@279755 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 334438c7c6d..37d776c2aee 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,17 @@
+2019-12-29  Marek Polacek  <polacek@redhat.com>
+
+	PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
+	* constexpr.c (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.
+
 2019-12-26  Jakub Jelinek  <jakub@redhat.com>
 
 	PR c++/92438
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index b95da0f8342..31ad6d56d26 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1699,6 +1699,238 @@ 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);
+
+  /* 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.  */
@@ -1864,6 +2096,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))
@@ -6740,7 +6975,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 a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index 1b6b87ba8d6..9a242dc64e4 100644
--- a/gcc/cp/rtti.c
+++ b/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 a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 8de7d8eb783..5db30163e91 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,24 @@
+2019-12-29  Marek Polacek  <polacek@redhat.com>
+
+	PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
+	* 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.
+
 2019-12-29  Jakub Jelinek  <jakub@redhat.com>
 
 	PR target/93078
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
new file mode 100644
index 00000000000..e8ba63d9609
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
new file mode 100644
index 00000000000..c226292a07d
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
new file mode 100644
index 00000000000..6069fbfd01c
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
new file mode 100644
index 00000000000..0ce9beb8d72
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
new file mode 100644
index 00000000000..203067a2581
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
new file mode 100644
index 00000000000..f739c6df94b
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C
new file mode 100644
index 00000000000..fcf507289c4
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C
new file mode 100644
index 00000000000..f0394d130a3
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
new file mode 100644
index 00000000000..6b443d249ec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
@@ -0,0 +1,31 @@
+// 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.
+// See <https://gcc.gnu.org/ml/gcc-patches/2019-12/msg01521.html>.
+
+struct V {
+  virtual void f();
+};
+
+struct A : V { };
+
+struct B : V {
+  constexpr B(V*, A*);
+};
+
+struct D : B, A {
+  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  dynamic_cast<B*>(a);
+}
+
+constexpr D d; // { dg-error "accessing uninitialized member" }
+// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
new file mode 100644
index 00000000000..aae03f691ca
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
new file mode 100644
index 00000000000..c3e09808e32
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
new file mode 100644
index 00000000000..3adc524379d
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
new file mode 100644
index 00000000000..743b3018d2f
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
new file mode 100644
index 00000000000..23434734e26
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
new file mode 100644
index 00000000000..d71497aae6d
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
new file mode 100644
index 00000000000..8056f30bb99
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
new file mode 100644
index 00000000000..d8cbb2f2f0d
--- /dev/null
+++ b/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);



More information about the Gcc-patches mailing list