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

Message ID 20191108212449.GZ21634@redhat.com
State New
Headers show
Series
  • C++ PATCH for c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr
Related show

Commit Message

Marek Polacek Nov. 8, 2019, 9:24 p.m. UTC
After much weeping and gnashing of teeth, here's a patch to handle dynamic_cast
in constexpr evaluation.  While the change in the standard is trivial (see
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1327r1.html>), the
change in the compiler is less so.

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.

This should be easy in theory: let the constexpr machinery find out the dynamic
type and then handle a sidecast and upcast.  That's ultimately what the patch
is trying to do but there was a number of hindrances.

1) We can't use __dynamic_cast's type_info parameters, this type is not a
literal class.  But that means we have no idea what we're converting to!
I noticed that build_dynamic_cast_1 will create a cast via cp_convert
to the target type for both pointer/reference dynamic_cast.  So we can save
this type to the constexpr values hash map under a magic key; I abused
dynamic_cast_node for this...

2) [class.cdtor] says that when a dynamic_cast is used in a constructor or
destructor and the operand of the dynamic_cast refers to the object under
construction or destruction, this object is considered to be a most derived
object.  This was tricky, and the only thing that seemed to work was to add
a new member to constexpr_global_ctx.  I was happy to find out that I could
use new_obj I'd added recently.  Note that destruction is *not* handled at
all and in fact I couldn't even construct a testcase where that would make
a difference.

3) We can't rely on the hint __dynamic_cast gave us; the comment in
cxx_eval_dynamic_cast_fn explains why the accessible_base_p checks were
necessary.

There are many various scanarios regarding inheritance so special care was
devoted to test as much as possible, but testing the "dynamic_cast in
a constructor" could be expanded. 

This patch doesn't handle polymorphic typeid yet.  I think it will be easier
to review to separate these two.  Hopefully the typeid part will be much
easier.

Bootstrapped/regtested on x86_64-linux.

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

	PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
	* call.c (is_base_field_ref): No longer static.
	* constexpr.c (struct constexpr_global_ctx): Add ctor_object member
	and initialize it.
	(cxx_dynamic_cast_fn_p): New function.
	(cxx_eval_dynamic_cast_fn): Likewise.
	(cxx_eval_call_expression): Call cxx_eval_dynamic_cast_fn for a call
	to __dynamic_cast.  Save the object a constexpr constructor is
	constructing.
	(cxx_eval_constant_expression) <case UNARY_PLUS_EXPR>: Save the target
	type of a call to __dynamic_cast.
	(potential_constant_expression_1): Don't give up on
	cxx_dynamic_cast_fn_p.
	* cp-tree.h (is_base_field_ref): Declare.
	* parser.c (cp_parser_postfix_expression): Set location of expression.
	* 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-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.

Comments

Jason Merrill Nov. 22, 2019, 9:11 p.m. UTC | #1
On 11/8/19 4:24 PM, Marek Polacek wrote:
> After much weeping and gnashing of teeth, here's a patch to handle dynamic_cast
> in constexpr evaluation.  While the change in the standard is trivial (see
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1327r1.html>), the
> change in the compiler is less so.
> 
> 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.
> 
> This should be easy in theory: let the constexpr machinery find out the dynamic
> type and then handle a sidecast and upcast.  That's ultimately what the patch
> is trying to do but there was a number of hindrances.
> 
> 1) We can't use __dynamic_cast's type_info parameters, this type is not a
> literal class.  But that means we have no idea what we're converting to!

get_tinfo_decl sets the TREE_TYPE of the DECL_NAME of the tinfo decl to 
the relevant type, can't you use that?

> 2) [class.cdtor] says that when a dynamic_cast is used in a constructor or
> destructor and the operand of the dynamic_cast refers to the object under
> construction or destruction, this object is considered to be a most derived
> object.

This means that during the 'tor the vtable pointer refers to the 
type_info for that class and the offset-to-top is 0.  Can you use that?

> This was tricky, and the only thing that seemed to work was to add
> a new member to constexpr_global_ctx.  I was happy to find out that I could
> use new_obj I'd added recently.  Note that destruction is *not* handled at
> all and in fact I couldn't even construct a testcase where that would make
> a difference.

> 3) We can't rely on the hint __dynamic_cast gave us; the comment in
> cxx_eval_dynamic_cast_fn explains why the accessible_base_p checks were
> necessary.
> 
> There are many various scanarios regarding inheritance so special care was
> devoted to test as much as possible, but testing the "dynamic_cast in
> a constructor" could be expanded.
> 
> This patch doesn't handle polymorphic typeid yet.  I think it will be easier
> to review to separate these two.  Hopefully the typeid part will be much
> easier.
> 
> Bootstrapped/regtested on x86_64-linux.
> 
> 2019-11-08  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
> 	* call.c (is_base_field_ref): No longer static.
> 	* constexpr.c (struct constexpr_global_ctx): Add ctor_object member
> 	and initialize it.
> 	(cxx_dynamic_cast_fn_p): New function.
> 	(cxx_eval_dynamic_cast_fn): Likewise.
> 	(cxx_eval_call_expression): Call cxx_eval_dynamic_cast_fn for a call
> 	to __dynamic_cast.  Save the object a constexpr constructor is
> 	constructing.
> 	(cxx_eval_constant_expression) <case UNARY_PLUS_EXPR>: Save the target
> 	type of a call to __dynamic_cast.
> 	(potential_constant_expression_1): Don't give up on
> 	cxx_dynamic_cast_fn_p.
> 	* cp-tree.h (is_base_field_ref): Declare.
> 	* parser.c (cp_parser_postfix_expression): Set location of expression.
> 	* 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-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/call.c gcc/cp/call.c
> index 0034c1cee0d..5de2aca1358 100644
> --- gcc/cp/call.c
> +++ gcc/cp/call.c
> @@ -8193,7 +8193,7 @@ call_copy_ctor (tree a, tsubst_flags_t complain)
>   
>   /* Return true iff T refers to a base field.  */
>   
> -static bool
> +bool
>   is_base_field_ref (tree t)
>   {
>     STRIP_NOPS (t);
> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
> index 20fddc57825..ef7706347bc 100644
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -1025,8 +1025,11 @@ struct constexpr_global_ctx {
>     /* Heap VAR_DECLs created during the evaluation of the outermost constant
>        expression.  */
>     auto_vec<tree, 16> heap_vars;
> +  /* For a constructor, this is the object we're constructing.  */
> +  tree ctor_object;
>     /* Constructor.  */
> -  constexpr_global_ctx () : constexpr_ops_count (0) {}
> +  constexpr_global_ctx () : constexpr_ops_count (0), ctor_object (NULL_TREE)
> +    {}
>   };
>   
>   /* The constexpr expansion context.  CALL is the current function
> @@ -1663,6 +1666,244 @@ 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);
> +}
> +
> +/* 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
> +  or -2.  */
> +
> +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);
> +  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 we've stashed.  */
> +  tree type;
> +  if (tree *p = ctx->global->values.get (dynamic_cast_node))
> +    type = *p;
> +  else
> +    {
> +      *non_constant_p = true;
> +      return call;
> +    }
> +  /* Don't need it anymore.  */
> +  ctx->global->values.remove (dynamic_cast_node);
> +
> +  const bool reference_p = TYPE_REF_P (type);
> +  /* TYPE can only be either T* or T&.  Get what T points or refers to.  */
> +  type = TREE_TYPE (type);
> +
> +  /* 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.  */
> +  if (TREE_CODE (obj) == POINTER_PLUS_EXPR)
> +    obj = TREE_OPERAND (obj, 0);
> +  /* If OBJ doesn't refer to a base field, we're done.  */
> +  if (!is_base_field_ref (obj))
> +    return integer_zero_node;
> +  STRIP_NOPS (obj);
> +  /* Strip the &.  */
> +  if (TREE_CODE (obj) == ADDR_EXPR)
> +    obj = TREE_OPERAND (obj, 0);
> +
> +  /* 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.  This is a downcast.  */

Please avoid using up/down to refer to inheritance relationships, people 
disagree about what they mean.  :)

> +  if (hint >= 0)
> +    {
> +      /* We now have something like
> +
> +	  g.D.2181.D.2154.D.2102.D.2093
> +				 ^~~~~~
> +				 OBJ
> +
> +	 and we're looking for a component with type TYPE.  */
> +      tree objtype = TREE_TYPE (obj);
> +      tree ctor_object = ctx->global->ctor_object;
> +
> +      for (;;)
> +	{
> +	  /* Unfortunately, we can't rely on HINT, we need to do some
> +	     verification here:
> +
> +	     1) Consider
> +		  dynamic_cast<E*>((A*)(B*)(D*)&e);
> +		and imagine that there's an accessible base A from E (so HINT
> +		is >= 0), but it's a different A than where OBJ points to.
> +		We need to check that the one we're accessing via E->D->B->A is
> +		in fact accessible.  If e.g. B on this path is private, we gotta
> +		fail.  So check that every base on the way can be reached from
> +		the preceding class.
> +
> +	     2) Further, consider
> +
> +		struct A { virtual void a(); };
> +		struct AA : A {};
> +		struct B : A {};
> +		struct Y : AA, private B {};
> +
> +		dynamic_cast<Y*>((A*)(B*)&y);
> +
> +		Here HINT is >=0, because A is a public unique base of Y,
> +		but that's not the A accessed via Y->B->A.  */
> +	  if (!accessible_base_p (TREE_TYPE (obj), objtype, false)
> +	      || !accessible_base_p (type, TREE_TYPE (obj), false))
> +	    {
> +	      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;
> +	    }
> +
> +	  if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (obj), type))
> +	    /* The result points to the TYPE object.  */
> +	    return cp_build_addr_expr (obj, complain);
> +	  else if (TREE_CODE (obj) == COMPONENT_REF
> +		   && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1)))
> +	    obj = TREE_OPERAND (obj, 0);

In the structural_type_p patch I tried to set 
TREE_PRIVATE/TREE_PROTECTED in build_base_field_1, would that be useful 
to check instead of accessible_base_p?  Though I'm not sure I was 
successful, I got a bug report about structural type vs. private bases 
that I haven't looked at yet.

Jason

Patch
diff mbox series

diff --git gcc/cp/call.c gcc/cp/call.c
index 0034c1cee0d..5de2aca1358 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -8193,7 +8193,7 @@  call_copy_ctor (tree a, tsubst_flags_t complain)
 
 /* Return true iff T refers to a base field.  */
 
-static bool
+bool
 is_base_field_ref (tree t)
 {
   STRIP_NOPS (t);
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 20fddc57825..ef7706347bc 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1025,8 +1025,11 @@  struct constexpr_global_ctx {
   /* Heap VAR_DECLs created during the evaluation of the outermost constant
      expression.  */
   auto_vec<tree, 16> heap_vars;
+  /* For a constructor, this is the object we're constructing.  */
+  tree ctor_object;
   /* Constructor.  */
-  constexpr_global_ctx () : constexpr_ops_count (0) {}
+  constexpr_global_ctx () : constexpr_ops_count (0), ctor_object (NULL_TREE)
+    {}
 };
 
 /* The constexpr expansion context.  CALL is the current function
@@ -1663,6 +1666,244 @@  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);
+}
+
+/* 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
+  or -2.  */
+
+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);
+  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 we've stashed.  */
+  tree type;
+  if (tree *p = ctx->global->values.get (dynamic_cast_node))
+    type = *p;
+  else
+    {
+      *non_constant_p = true;
+      return call;
+    }
+  /* Don't need it anymore.  */
+  ctx->global->values.remove (dynamic_cast_node);
+
+  const bool reference_p = TYPE_REF_P (type);
+  /* TYPE can only be either T* or T&.  Get what T points or refers to.  */
+  type = TREE_TYPE (type);
+
+  /* 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.  */
+  if (TREE_CODE (obj) == POINTER_PLUS_EXPR)
+    obj = TREE_OPERAND (obj, 0);
+  /* If OBJ doesn't refer to a base field, we're done.  */
+  if (!is_base_field_ref (obj))
+    return integer_zero_node;
+  STRIP_NOPS (obj);
+  /* Strip the &.  */
+  if (TREE_CODE (obj) == ADDR_EXPR)
+    obj = TREE_OPERAND (obj, 0);
+
+  /* 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.  This is a downcast.  */
+  if (hint >= 0)
+    {
+      /* We now have something like
+
+	  g.D.2181.D.2154.D.2102.D.2093
+				 ^~~~~~
+				 OBJ
+
+	 and we're looking for a component with type TYPE.  */
+      tree objtype = TREE_TYPE (obj);
+      tree ctor_object = ctx->global->ctor_object;
+
+      for (;;)
+	{
+	  /* Unfortunately, we can't rely on HINT, we need to do some
+	     verification here:
+
+	     1) Consider
+		  dynamic_cast<E*>((A*)(B*)(D*)&e);
+		and imagine that there's an accessible base A from E (so HINT
+		is >= 0), but it's a different A than where OBJ points to.
+		We need to check that the one we're accessing via E->D->B->A is
+		in fact accessible.  If e.g. B on this path is private, we gotta
+		fail.  So check that every base on the way can be reached from
+		the preceding class.
+
+	     2) Further, consider
+
+		struct A { virtual void a(); };
+		struct AA : A {};
+		struct B : A {};
+		struct Y : AA, private B {};
+
+		dynamic_cast<Y*>((A*)(B*)&y);
+
+		Here HINT is >=0, because A is a public unique base of Y,
+		but that's not the A accessed via Y->B->A.  */
+	  if (!accessible_base_p (TREE_TYPE (obj), objtype, false)
+	      || !accessible_base_p (type, TREE_TYPE (obj), false))
+	    {
+	      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;
+	    }
+
+	  if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (obj), type))
+	    /* The result points to the TYPE object.  */
+	    return cp_build_addr_expr (obj, complain);
+	  else if (TREE_CODE (obj) == COMPONENT_REF
+		   && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1)))
+	    obj = TREE_OPERAND (obj, 0);
+	  else if (ctor_object != NULL_TREE)
+	    {
+	      /* If we get here, it means we didn't find TYPE in OBJ.  But
+		 HINT told us that the source type is a unique public base
+		 of TYPE.  So it must be the case that we're looking at an
+		 object under construction.  Try again.  */
+	      obj = ctor_object;
+	      ctor_object = NULL_TREE;
+	    }
+	  else
+	    /* Make sure we've either found a problem or the desired type.  */
+	    gcc_unreachable ();
+	}
+    }
+  /* This could be a sidecast:
+
+     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.  */
+  else if (hint == -2)
+    {
+      tree objtype = TREE_TYPE (obj);
+      /* Get the most derived object.  */
+      while (TREE_CODE (obj) == COMPONENT_REF
+	     && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1)))
+	{
+	  /* [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.  */
+	  if (ctx->global->ctor_object
+	      && cp_tree_equal (obj, ctx->global->ctor_object))
+	    break;
+	  obj = TREE_OPERAND (obj, 0);
+	}
+
+      tree mdtype = TREE_TYPE (obj);
+      /* Check that OBJ refers to a public base class subobject of most derived
+	 object.  */
+      if (!accessible_base_p (mdtype, objtype, /*consider_local_p=*/false))
+	{
+	  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;
+	}
+
+      /* 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);
+    }
+  else
+    gcc_unreachable ();
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Evaluate the call expression tree T in the context of OLD_CALL expression
    evaluation.  */
@@ -1824,6 +2065,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))
@@ -2061,6 +2305,11 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  else
 	    ctx->global->values.put (res, NULL_TREE);
 
+	  /* Stash the object we're constructing.  */
+	  tree save_ctor_object = ctx->global->ctor_object;
+	  if (new_obj)
+	    ctx->global->ctor_object = new_obj;
+
 	  /* Track the callee's evaluated SAVE_EXPRs so that we can forget
 	     their values after the call.  */
 	  constexpr_ctx ctx_with_save_exprs = *ctx;
@@ -2094,6 +2343,9 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 		}
 	    }
 
+	  if (new_obj)
+	    ctx->global->ctor_object = save_ctor_object;
+
 	  /* At this point, the object's constructor will have run, so
 	     the object is no longer under construction, and its possible
 	     'const' semantics now apply.  Make a note of this fact by
@@ -5376,13 +5628,20 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case UNARY_PLUS_EXPR:
       {
 	tree oldop = TREE_OPERAND (t, 0);
+	tree type = TREE_TYPE (t);
+
+	/* A call to dynamic_cast encodes the target type in a type_info
+	   parameter.  But in constexpr evaluation we can't use it (it's
+	   not a literal type), so save it to the hash map under a magic
+	   key.  */
+	if (dynamic_cast_node)
+	  ctx->global->values.put (dynamic_cast_node, type);
 
 	tree op = cxx_eval_constant_expression (ctx, oldop,
 						lval,
 						non_constant_p, overflow_p);
 	if (*non_constant_p)
 	  return t;
-	tree type = TREE_TYPE (t);
 
 	if (VOID_TYPE_P (type))
 	  return void_node;
@@ -6613,7 +6872,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/cp-tree.h gcc/cp/cp-tree.h
index adc021b2a5c..80e2b8b4247 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6257,6 +6257,7 @@  extern void complain_about_bad_argument	(location_t arg_loc,
 						 tree from_type, tree to_type,
 						 tree fndecl, int parmnum);
 extern void maybe_inform_about_fndecl_for_bogus_argument_init (tree, int);
+extern bool is_base_field_ref			(tree);
 
 
 /* A class for recording information about access failures (e.g. private
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 7138aebebce..ef6980727be 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -6878,6 +6878,7 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	matching_parens parens;
 	parens.require_open (parser);
 	expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
+	protected_set_expr_location (expression, loc);
 	cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
 						   RT_CLOSE_PAREN);
 	location_t end_loc = close_paren ?
diff --git gcc/cp/rtti.c gcc/cp/rtti.c
index 1ba4a46c5cc..a0028244421 100644
--- gcc/cp/rtti.c
+++ gcc/cp/rtti.c
@@ -557,6 +557,7 @@  build_dynamic_cast_1 (tree type, tree expr, tsubst_flags_t complain)
   tree dcast_fn;
   tree old_expr = expr;
   const char *errstr = NULL;
+  location_t loc = cp_expr_loc_or_input_loc (expr);
 
   /* Save casted types in the function's used types hash table.  */
   used_types_insert (type);
@@ -775,6 +776,7 @@  build_dynamic_cast_1 (tree type, tree expr, tsubst_flags_t complain)
 	      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..391ca99e0b5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
@@ -0,0 +1,29 @@ 
+// 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) { }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  // well-defined: v of type V*, V base of B results in B*
+  dynamic_cast<B*>(v);
+    
+  // FIXME: UB in constexpr should be detected.  
+  dynamic_cast<B*>(a);          // undefined behavior, a has type A*, A not a base of B
+}
+
+constexpr D d;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
new file mode 100644
index 00000000000..f6d081a9de9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
@@ -0,0 +1,23 @@ 
+// 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);
+}
+
+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..77e59ee44b1
--- /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 .const 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 .const 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 .const 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 .const 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 .const 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 .const 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 .const 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 .const 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 .const 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 .const 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 .const 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 .const KK." "" { target *-*-* } .-1 }
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..7e858fea1e0
--- /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 ".const A. is an ambiguous base class of dynamic type .const 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 .const 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 .const 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 .const 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 .const 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 .const 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..c010d811885
--- /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 .const 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 .const 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..8d3e7d39208
--- /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 .const 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 .const 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..e2320a0cebb
--- /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 .const 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 .const 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 .const 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..dc836747a2f
--- /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 .const E. of its operand" "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<A*>((D*)&e) == nullptr);