diff mbox series

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

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

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
Marek Polacek Dec. 11, 2019, 10:50 p.m. UTC | #2
On Fri, Nov 22, 2019 at 04:11:53PM -0500, Jason Merrill wrote:
> 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?

Yes, lovely.  I hadn't noticed that :(.

It doesn't say if this is a reference dynamic_cast or a pointer dynamic_cast,
so I checked OBJ to wheedle that information out of it.

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

I can't seem to: For e.g.

struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };

the object under construction is C, the call to __dynamic_cast will be
__dynamic_cast (SAVE_EXPR <&((struct C *) this)->D.2119>, &_ZTI2C2, &_ZTI1A, -2)
here, OBJ is f.D.2156.D.2119 and ctx->global->ctor_object is f.D.2156.  So OBJ
refers to the object under construction.

But I don't see C anywhere; CLASSTYPE_TYPEINFO_VAR of OBJTYPE is _ZTI2C2.

Am I looking into the wrong place?

> > +  /* 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.  :)

OK, dropped that terminology.

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

This works now, awesome.  I remember being disappointed that that didn't work,
because accessible_base_p seems pretty expensive in comparison to just
checking a flag...  I've used TREE_PRIVATE/TREE_PROTECTED accessible_base_p
instead of accessible_base_p in the following.  (This patch is based on trunk
with this https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00720.html in.)

Thanks!

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

2019-12-11  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.
	(potential_constant_expression_1): Don't give up on
	cxx_dynamic_cast_fn_p.
	* cp-tree.h (is_base_field_ref): Declare.
	* 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 cbd5747d6ca..17d7e44575b 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -8225,7 +8225,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 19e09c74760..daaf83193ea 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1031,8 +1031,11 @@ struct constexpr_global_ctx {
   auto_vec<tree, 16> heap_vars;
   /* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR.  */
   vec<tree> *cleanups;
+  /* For a constructor, this is the object we're constructing.  */
+  tree ctor_object;
   /* Constructor.  */
-  constexpr_global_ctx () : constexpr_ops_count (0), cleanups (NULL) {}
+  constexpr_global_ctx () : constexpr_ops_count (0), cleanups (NULL),
+			    ctor_object (NULL_TREE) {}
 };
 
 /* The constexpr expansion context.  CALL is the current function
@@ -1692,6 +1695,247 @@ 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);
+  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))
+    {
+      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.  */
+  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.  */
+  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 (;;)
+	{
+	  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)))
+	    {
+	      /* 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 (TREE_PRIVATE (TREE_OPERAND (obj, 1))
+		  || TREE_PROTECTED (TREE_OPERAND (obj, 1)))
+		{
+		  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;
+		}
+	      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 ();
+	}
+    }
+  /* 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);
+      bool obj_accessible_p = true;
+      /* Get the most derived object.  */
+      while (TREE_CODE (obj) == COMPONENT_REF
+	     && DECL_FIELD_IS_BASE (TREE_OPERAND (obj, 1)))
+	{
+	  if (TREE_PRIVATE (TREE_OPERAND (obj, 1))
+	      || TREE_PROTECTED (TREE_OPERAND (obj, 1)))
+	    obj_accessible_p = false;
+	  /* [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 (!obj_accessible_p)
+	{
+	  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.  */
@@ -1857,6 +2101,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))
@@ -2094,6 +2341,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 and TARGET_EXPRs so that
 	     we can forget their values after the call.  */
 	  constexpr_ctx ctx_with_save_exprs = *ctx;
@@ -2127,6 +2379,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
@@ -6729,7 +6984,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 b47698e1d0c..fc00ac1bcb6 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6275,6 +6275,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/rtti.c gcc/cp/rtti.c
index 1b6b87ba8d6..9a242dc64e4 100644
--- gcc/cp/rtti.c
+++ gcc/cp/rtti.c
@@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
 	      dynamic_cast_node = dcast_fn;
 	    }
 	  result = build_cxx_call (dcast_fn, 4, elems, complain);
+	  SET_EXPR_LOCATION (result, loc);
 
 	  if (tc == REFERENCE_TYPE)
 	    {
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
new file mode 100644
index 00000000000..e8ba63d9609
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
@@ -0,0 +1,40 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast.
+
+struct B {
+  virtual void baz () {}
+};
+ 
+struct D : B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  B b;
+  B *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+    
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B b;
+constexpr B *b1 = const_cast<B*>(&b);
+constexpr B *b2 = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b2) == &d);
+static_assert(&dynamic_cast<D&>(*b2) == &d);
+static_assert(dynamic_cast<const B*>(&d) == &d);
+static_assert(&dynamic_cast<const B&>(d) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
new file mode 100644
index 00000000000..b05249d9795
--- /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); // { dg-error "reference .dynamic_cast. failed" }
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..a9eca8b6d96
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
@@ -0,0 +1,55 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// From clang's constant-expression-cxx2a.cpp.
+
+struct A2 { virtual void a2(); };
+struct A : A2 { virtual void a(); };
+struct B : A {}; 
+struct C2 { virtual void c2(); };
+struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
+struct D { virtual void d(); };
+struct E { virtual void e(); };
+struct F : B, C, D, private E { void *f = dynamic_cast<void*>(static_cast<D*>(this)); };
+struct Padding { virtual void padding(); };
+struct G : Padding, F {}; 
+
+constexpr G g;
+
+// During construction of C, A is unambiguous subobject of dynamic type C.
+static_assert(g.c == (C*)&g);
+// ... but in the complete object, the same is not true, so the runtime fails.
+static_assert(dynamic_cast<const A*>(static_cast<const C2*>(&g)) == nullptr);
+
+// dynamic_cast<void*> produces a pointer to the object of the dynamic type.
+static_assert(g.f == (void*)(F*)&g);
+static_assert(dynamic_cast<const void*>(static_cast<const D*>(&g)) == &g);
+
+constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .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);
Jason Merrill Dec. 12, 2019, 7:38 p.m. UTC | #3
On 12/11/19 5:50 PM, Marek Polacek wrote:
> On Fri, Nov 22, 2019 at 04:11:53PM -0500, Jason Merrill wrote:
>> On 11/8/19 4:24 PM, Marek Polacek wrote:

>>> 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?
> 
> I can't seem to: For e.g.
> 
> struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
> 
> the object under construction is C, the call to __dynamic_cast will be
> __dynamic_cast (SAVE_EXPR <&((struct C *) this)->D.2119>, &_ZTI2C2, &_ZTI1A, -2)
> here, OBJ is f.D.2156.D.2119 and ctx->global->ctor_object is f.D.2156.  So OBJ
> refers to the object under construction.
> 
> But I don't see C anywhere; CLASSTYPE_TYPEINFO_VAR of OBJTYPE is _ZTI2C2.
> 
> Am I looking into the wrong place?

Evaluating build_vfield_ref (obj, objtype) will give you the vtable 
pointer, in this case &_ZTV1C + 40.  And then you can get C from 
DECL_CONTEXT (_ZTV1C).

Or get_tinfo_decl_dynamic (obj) will give you the type_info.

Jason
Marek Polacek Dec. 13, 2019, 8:20 p.m. UTC | #4
On Thu, Dec 12, 2019 at 02:38:29PM -0500, Jason Merrill wrote:
> On 12/11/19 5:50 PM, Marek Polacek wrote:
> > On Fri, Nov 22, 2019 at 04:11:53PM -0500, Jason Merrill wrote:
> > > On 11/8/19 4:24 PM, Marek Polacek wrote:
> 
> > > > 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?
> > 
> > I can't seem to: For e.g.
> > 
> > struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
> > 
> > the object under construction is C, the call to __dynamic_cast will be
> > __dynamic_cast (SAVE_EXPR <&((struct C *) this)->D.2119>, &_ZTI2C2, &_ZTI1A, -2)
> > here, OBJ is f.D.2156.D.2119 and ctx->global->ctor_object is f.D.2156.  So OBJ
> > refers to the object under construction.
> > 
> > But I don't see C anywhere; CLASSTYPE_TYPEINFO_VAR of OBJTYPE is _ZTI2C2.
> > 
> > Am I looking into the wrong place?
> 
> Evaluating build_vfield_ref (obj, objtype) will give you the vtable pointer,
> in this case &_ZTV1C + 40.  And then you can get C from DECL_CONTEXT
> (_ZTV1C).
> 
> Or get_tinfo_decl_dynamic (obj) will give you the type_info.

Awesome, thanks a lot!  The following patch uses the first approach.  I've
also simplified the code somewhat.  constexpr-dynamic11.C was expanded a bit.

The error handling inflates the patch, but I'm not sure if a helper macro
would be preferable.

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

2019-12-13  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.

	* 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/constexpr.c gcc/cp/constexpr.c
index 19e09c74760..1db9c9a96a4 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1692,6 +1692,239 @@ 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.  */
+
+static tree
+get_component_with_type (tree path, tree type)
+{
+  while (true)
+    {
+      if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), type))
+	/* Found it.  */
+	return path;
+      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
+  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);
+  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);
+  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);
+  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.  */
+  if (hint >= 0)
+    {
+      /* Look for a component with type TYPE.  */
+      obj = get_component_with_type (obj, type);
+      /* If not found or not accessible, give an error.  */
+      if (obj == NULL_TREE || obj == error_mark_node)
+	{
+	  if (reference_p)
+	    {
+	      if (!ctx->quiet)
+		{
+		  error_at (loc, "reference %<dynamic_cast%> failed");
+		  if (obj == NULL_TREE)
+		    inform (loc, "dynamic type %qT of its operand does not "
+			    "have an unambiguous public base class %qT",
+			    mdtype, type);
+		  else
+		    inform (loc, "static type %qT of its operand is a "
+			    "non-public base class of dynamic type %qT",
+			    objtype, type);
+
+		}
+	      *non_constant_p = true;
+	    }
+	  return integer_zero_node;
+	}
+      else
+	/* The result points to the TYPE object.  */
+	return cp_build_addr_expr (obj, complain);
+    }
+  /* Otherwise, if v points (refers) to a public base class subobject of the
+     most derived object, and the type of the most derived object has a base
+     class, of type C, that is unambiguous and public, the result points
+     (refers) to the C subobject of the most derived object.
+
+     But it can also be an invalid case.  */
+  else if (hint == -2)
+    {
+      /* Get the most derived object.  */
+      obj = get_component_with_type (obj, mdtype);
+      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);
+    }
+  else
+    gcc_unreachable ();
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Evaluate the call expression tree T in the context of OLD_CALL expression
    evaluation.  */
@@ -1857,6 +2090,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  gcc_assert (arg1);
 	  return arg1;
 	}
+      else if (cxx_dynamic_cast_fn_p (fun))
+	return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
+
       if (!ctx->quiet)
 	{
 	  if (!lambda_static_thunk_p (fun))
@@ -6729,7 +6965,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
 		    && (!cxx_placement_new_fn (fun)
 			|| TREE_CODE (t) != CALL_EXPR
 			|| current_function_decl == NULL_TREE
-			|| !is_std_construct_at (current_function_decl)))
+			|| !is_std_construct_at (current_function_decl))
+		    && !cxx_dynamic_cast_fn_p (fun))
 		  {
 		    if (flags & tf_error)
 		      {
diff --git gcc/cp/rtti.c gcc/cp/rtti.c
index 1b6b87ba8d6..9a242dc64e4 100644
--- gcc/cp/rtti.c
+++ gcc/cp/rtti.c
@@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
 	      dynamic_cast_node = dcast_fn;
 	    }
 	  result = build_cxx_call (dcast_fn, 4, elems, complain);
+	  SET_EXPR_LOCATION (result, loc);
 
 	  if (tc == REFERENCE_TYPE)
 	    {
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
new file mode 100644
index 00000000000..e8ba63d9609
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
@@ -0,0 +1,40 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast.
+
+struct B {
+  virtual void baz () {}
+};
+ 
+struct D : B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  B b;
+  B *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+    
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B b;
+constexpr B *b1 = const_cast<B*>(&b);
+constexpr B *b2 = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b2) == &d);
+static_assert(&dynamic_cast<D&>(*b2) == &d);
+static_assert(dynamic_cast<const B*>(&d) == &d);
+static_assert(&dynamic_cast<const B&>(d) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
new file mode 100644
index 00000000000..c226292a07d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
@@ -0,0 +1,12 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Virtual base.
+
+struct C { virtual void a(); };
+struct B { virtual void b(); };
+struct A : virtual B, C { virtual void c(); }; // { dg-error ".struct A. has virtual base classes" }
+
+constexpr A a; // { dg-error "call" }
+
+constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
new file mode 100644
index 00000000000..cfa76d7be66
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
@@ -0,0 +1,34 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a constructor.
+
+struct V {
+  virtual void f();
+};
+
+struct A : V { };
+
+struct B : V {
+  constexpr B(V*, A*);
+};
+
+struct D : A, B {
+  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  // well-defined: v of type V*, V base of B results in B*
+  B* b = dynamic_cast<B*>(v);
+  if (b != nullptr)
+    __builtin_abort ();
+
+  B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target *-*-* } .-1 }
+    
+  // FIXME: UB in constexpr should be detected.  
+  dynamic_cast<B*>(a);          // undefined behavior, a has type A*, A not a base of B
+}
+
+constexpr D d; // { dg-message "in 'constexpr' expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
new file mode 100644
index 00000000000..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..f739c6df94b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
@@ -0,0 +1,105 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Adopted from g++.old-deja/g++.other/dyncast1.C.
+// But use reference dynamic_cast.
+
+// 1. downcast
+// 1.1. single inheritance case
+
+struct A { virtual void a(); };
+struct AA : A {};
+struct B : A {};
+struct BB : B {};
+class C : B {};
+struct D : C {};
+
+struct CC : B {};
+class DD : CC {};
+
+class CCC : protected B {};
+class DDD : protected CCC {};
+
+constexpr D d;
+constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+static_assert (&d == &dynamic_cast<const D&> ((C&)d));
+constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+
+constexpr DD dd;
+constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
+constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
+
+constexpr DDD ddd;
+constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+
+// 1.2. multiple inheritance case
+// 1.2.1. all bases are public
+ 
+struct E : D, CC {};
+struct EE : CC, D {}; //Will search in reverse order.
+
+constexpr E e;
+constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
+constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
+static_assert (&e == &dynamic_cast<E&> ((C&)(D&)e));
+static_assert (&e == &dynamic_cast<E&> ((B&)(CC&)e));
+static_assert (&(CC&)e == &dynamic_cast<CC&> ((B&)(CC&)e));
+
+constexpr EE ee;
+constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
+constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
+static_assert (&ee == &dynamic_cast<EE&> ((C&)(D&)ee));
+static_assert (&ee == &dynamic_cast<EE&> ((B&)(CC&)ee));
+static_assert (&(CC&)ee == &dynamic_cast<CC&> ((B&)(CC&)ee));
+
+// 1.2.2 one or more branches are nonpublic
+
+struct X : private BB, E {};
+struct Y : AA, private B {};
+
+class XX : BB, E {};
+
+constexpr X x;
+static_assert (&x == &dynamic_cast<X&>((B&)(CC&)(E&)x));
+
+constexpr XX xx;
+constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .XX." "" { target *-*-* } .-1 }
+
+constexpr Y y;
+constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
+constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
+
+// 2. crosscast
+
+struct J { virtual void j(); };
+struct K : CC, private J {}; 
+class KK : J, CC{};
+		
+constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .CC." "" { target *-*-* } .-1 }
+static_assert (&(CC&)e == &dynamic_cast<CC&> ((C&)(D&)e));
+
+constexpr K k;
+constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base class .J." "" { target *-*-* } .-1 }
+constexpr KK kk;
+constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic type .KK." "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
new file mode 100644
index 00000000000..aae03f691ca
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
@@ -0,0 +1,41 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast, with hint > 0.
+
+struct B {
+  virtual void baz () {}
+};
+
+struct B2 {
+  virtual void baz2 () {}
+};
+ 
+struct D : B, B2 { };
+
+constexpr bool
+fn ()
+{
+  // try &/&&, add address test
+  bool ok = true;
+  B2 b;
+  B2 *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B2 *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+    
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B2 *b = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b) == &d);
+static_assert(&dynamic_cast<D&>(*b) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
new file mode 100644
index 00000000000..c3e09808e32
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
@@ -0,0 +1,33 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Sidecast.
+
+struct A {
+  virtual void afn () {}
+};
+ 
+struct B {
+  virtual void bfn () {}
+};
+
+struct D : A, B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  D d;
+  A *a = &d;
+  if (B *bp = dynamic_cast<B*>(a))
+    /*OK*/;
+  else
+    ok = false;
+
+  A &ar = d;
+  B &br = dynamic_cast<B&>(ar);
+
+  return ok;
+}
+
+static_assert(fn ());
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
new file mode 100644
index 00000000000..3adc524379d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
@@ -0,0 +1,55 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// From clang's constant-expression-cxx2a.cpp.
+
+struct A2 { virtual void a2(); };
+struct A : A2 { virtual void a(); };
+struct B : A {}; 
+struct C2 { virtual void c2(); };
+struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
+struct D { virtual void d(); };
+struct E { virtual void e(); };
+struct F : B, C, D, private E { void *f = dynamic_cast<void*>(static_cast<D*>(this)); };
+struct Padding { virtual void padding(); };
+struct G : Padding, F {}; 
+
+constexpr G g;
+
+// During construction of C, A is unambiguous subobject of dynamic type C.
+static_assert(g.c == (C*)&g);
+// ... but in the complete object, the same is not true, so the runtime fails.
+static_assert(dynamic_cast<const A*>(static_cast<const C2*>(&g)) == nullptr);
+
+// dynamic_cast<void*> produces a pointer to the object of the dynamic type.
+static_assert(g.f == (void*)(F*)&g);
+static_assert(dynamic_cast<const void*>(static_cast<const D*>(&g)) == &g);
+
+constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Can navigate from A2 to its A...
+static_assert(&dynamic_cast<A&>((A2&)(B&)g) == &(A&)(B&)g);
+// ... and from B to its A ...
+static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
+// ... but not from D.
+static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant condition for static assertion|reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Can cast from A2 to sibling class D.
+static_assert(&dynamic_cast<D&>((A2&)(B&)g) == &(D&)g);
+
+// Cannot cast from private base E to derived class F.
+constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Cannot cast from B to private sibling E.
+constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .E." "" { target *-*-* } .-1 }
+
+struct Unrelated { virtual void unrelated(); };
+
+constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .Unrelated." "" { target *-*-* } .-1 }
+constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
new file mode 100644
index 00000000000..743b3018d2f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
@@ -0,0 +1,22 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Multiple levels.
+
+struct A { virtual void a(); };
+struct B : A { virtual void b(); };
+struct C : B { virtual void c(); };
+struct D : C { virtual void d(); };
+struct E : D { virtual void e(); };
+struct F : E { virtual void f(); };
+
+constexpr F f;
+
+// F->C->A->B == F->C->B
+static_assert (&dynamic_cast<B&>((A&)(C&)f) == &(B&)(C&)f);
+// F->A->E == F->E
+static_assert (&dynamic_cast<E&>((A&)f) == &(E&)f);
+// F->E->D->C->B->A->C == F->C
+static_assert (&dynamic_cast<C&>((A&)(B&)(C&)(D&)(E&)f) == &(C&)f);
+// F->B->F == F
+static_assert (&dynamic_cast<F&>((B&)f) == &f);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
new file mode 100644
index 00000000000..23434734e26
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
@@ -0,0 +1,25 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Private base.
+
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct B : private P1 { virtual void b(); };
+struct C { virtual void c(); };
+struct A : B, C, private P2 { virtual void a(); };
+
+constexpr A a;
+
+// P1 is a non-public base of A.
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+// Don't error here.
+static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
+
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
new file mode 100644
index 00000000000..d71497aae6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
@@ -0,0 +1,25 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Protected base.
+
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct B : protected P1 { virtual void b(); };
+struct C { virtual void c(); };
+struct A : B, C, protected P2 { virtual void a(); };
+
+constexpr A a;
+
+// P1 is a non-public base of A.
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+// Don't error here.
+static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
+
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
new file mode 100644
index 00000000000..8056f30bb99
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
@@ -0,0 +1,24 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Unrelated type.
+
+struct B { virtual void b(); };
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct A : public B, private P1, protected P2 { virtual void a(); };
+
+constexpr A a;
+
+struct U { virtual void u(); };
+
+constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .U." "" { target *-*-* } .-1 }
+constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<U*>((B*)&a) == nullptr);
+static_assert (dynamic_cast<U*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<U*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
new file mode 100644
index 00000000000..d8cbb2f2f0d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
@@ -0,0 +1,17 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Ambiguous base.
+
+struct A { virtual void a(); };
+struct B : A { virtual void b(); };
+struct C : A { virtual void c(); };
+struct D { virtual void a(); };
+struct E : B, C, D { virtual void d(); };
+
+constexpr E e;
+
+constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<A*>((D*)&e) == nullptr);
Jason Merrill Dec. 13, 2019, 10:56 p.m. UTC | #5
On 12/13/19 3:20 PM, Marek Polacek wrote:
> On Thu, Dec 12, 2019 at 02:38:29PM -0500, Jason Merrill wrote:
>> On 12/11/19 5:50 PM, Marek Polacek wrote:
>>> On Fri, Nov 22, 2019 at 04:11:53PM -0500, Jason Merrill wrote:
>>>> On 11/8/19 4:24 PM, Marek Polacek wrote:
>>
>>>>> 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?
>>>
>>> I can't seem to: For e.g.
>>>
>>> struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
>>>
>>> the object under construction is C, the call to __dynamic_cast will be
>>> __dynamic_cast (SAVE_EXPR <&((struct C *) this)->D.2119>, &_ZTI2C2, &_ZTI1A, -2)
>>> here, OBJ is f.D.2156.D.2119 and ctx->global->ctor_object is f.D.2156.  So OBJ
>>> refers to the object under construction.
>>>
>>> But I don't see C anywhere; CLASSTYPE_TYPEINFO_VAR of OBJTYPE is _ZTI2C2.
>>>
>>> Am I looking into the wrong place?
>>
>> Evaluating build_vfield_ref (obj, objtype) will give you the vtable pointer,
>> in this case &_ZTV1C + 40.  And then you can get C from DECL_CONTEXT
>> (_ZTV1C).
>>
>> Or get_tinfo_decl_dynamic (obj) will give you the type_info.
> 
> Awesome, thanks a lot!  The following patch uses the first approach.  I've
> also simplified the code somewhat.  constexpr-dynamic11.C was expanded a bit.
> 
> The error handling inflates the patch, but I'm not sure if a helper macro
> would be preferable.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2019-12-13  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.
> 
> 	* 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/constexpr.c gcc/cp/constexpr.c
> index 19e09c74760..1db9c9a96a4 100644
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -1692,6 +1692,239 @@ 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.  */
> +
> +static tree
> +get_component_with_type (tree path, tree type)
> +{
> +  while (true)
> +    {
> +      if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), type))
> +	/* Found it.  */
> +	return path;
> +      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
> +  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);
> +  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);
> +  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);
> +  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.  */
> +  if (hint >= 0)

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

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

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

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

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

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

> +  else if (hint == -2)
> +    {
> +      /* Get the most derived object.  */
> +      obj = get_component_with_type (obj, mdtype);
> +      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);
> +    }
> +  else
> +    gcc_unreachable ();
> +}
> +
>   /* Subroutine of cxx_eval_constant_expression.
>      Evaluate the call expression tree T in the context of OLD_CALL expression
>      evaluation.  */
> @@ -1857,6 +2090,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
>   	  gcc_assert (arg1);
>   	  return arg1;
>   	}
> +      else if (cxx_dynamic_cast_fn_p (fun))
> +	return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
> +
>         if (!ctx->quiet)
>   	{
>   	  if (!lambda_static_thunk_p (fun))
> @@ -6729,7 +6965,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
>   		    && (!cxx_placement_new_fn (fun)
>   			|| TREE_CODE (t) != CALL_EXPR
>   			|| current_function_decl == NULL_TREE
> -			|| !is_std_construct_at (current_function_decl)))
> +			|| !is_std_construct_at (current_function_decl))
> +		    && !cxx_dynamic_cast_fn_p (fun))
>   		  {
>   		    if (flags & tf_error)
>   		      {
> diff --git gcc/cp/rtti.c gcc/cp/rtti.c
> index 1b6b87ba8d6..9a242dc64e4 100644
> --- gcc/cp/rtti.c
> +++ gcc/cp/rtti.c
> @@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
>   	      dynamic_cast_node = dcast_fn;
>   	    }
>   	  result = build_cxx_call (dcast_fn, 4, elems, complain);
> +	  SET_EXPR_LOCATION (result, loc);
>   
>   	  if (tc == REFERENCE_TYPE)
>   	    {
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
> new file mode 100644
> index 00000000000..e8ba63d9609
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
> @@ -0,0 +1,40 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Downcast.
> +
> +struct B {
> +  virtual void baz () {}
> +};
> +
> +struct D : B { };
> +
> +constexpr bool
> +fn ()
> +{
> +  bool ok = true;
> +  B b;
> +  B *b1 = &b;
> +  if (D *pd = dynamic_cast<D*>(b1))
> +    ok = false;
> +
> +  D d;
> +  B *b2 = &d;
> +  if (D *pd = dynamic_cast<D*>(b2))
> +    /*OK*/;
> +  else
> +   ok = false;
> +
> +  return ok;
> +}
> +
> +static_assert(fn ());
> +
> +constexpr D d;
> +constexpr B b;
> +constexpr B *b1 = const_cast<B*>(&b);
> +constexpr B *b2 = const_cast<D*>(&d);
> +static_assert(dynamic_cast<D*>(b2) == &d);
> +static_assert(&dynamic_cast<D&>(*b2) == &d);
> +static_assert(dynamic_cast<const B*>(&d) == &d);
> +static_assert(&dynamic_cast<const B&>(d) == &d);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
> new file mode 100644
> index 00000000000..c226292a07d
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
> @@ -0,0 +1,12 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Virtual base.
> +
> +struct C { virtual void a(); };
> +struct B { virtual void b(); };
> +struct A : virtual B, C { virtual void c(); }; // { dg-error ".struct A. has virtual base classes" }
> +
> +constexpr A a; // { dg-error "call" }
> +
> +constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
> new file mode 100644
> index 00000000000..cfa76d7be66
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
> @@ -0,0 +1,34 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// dynamic_cast in a constructor.
> +
> +struct V {
> +  virtual void f();
> +};
> +
> +struct A : V { };
> +
> +struct B : V {
> +  constexpr B(V*, A*);
> +};
> +
> +struct D : A, B {
> +  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
> +};
> +
> +constexpr B::B(V* v, A* a)
> +{
> +  // well-defined: v of type V*, V base of B results in B*
> +  B* b = dynamic_cast<B*>(v);
> +  if (b != nullptr)
> +    __builtin_abort ();
> +
> +  B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target *-*-* } .-1 }
> +
> +  // FIXME: UB in constexpr should be detected.
> +  dynamic_cast<B*>(a);          // undefined behavior, a has type A*, A not a base of B

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

> +constexpr D d; // { dg-message "in 'constexpr' expansion of" }
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
> new file mode 100644
> index 00000000000..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);

Add a check to verify that you got the right value?

> +}
> +
> +struct D : B { virtual void d(); };
> +
> +constexpr D d;
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
> new file mode 100644
> index 00000000000..203067a2581
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
> @@ -0,0 +1,86 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Adopted from g++.old-deja/g++.other/dyncast1.C.
> +
> +// 1. downcast
> +// 1.1. single inheritance case
> +
> +struct A { virtual void a(); };
> +struct AA : A {};
> +struct B : A {};
> +struct BB : B {};
> +class C : B {};
> +struct D : C {};
> +
> +struct CC : B {};
> +class DD : CC {};
> +
> +class CCC : protected B {};
> +class DDD : protected CCC {};
> +
> +constexpr D d;
> +static_assert (dynamic_cast<D*> ((A*)&d) == nullptr);
> +static_assert (dynamic_cast<D*> ((B*)&d) == nullptr);
> +static_assert (&d == dynamic_cast<D*> ((C*)&d));
> +static_assert (dynamic_cast<C*> ((B*)&d) == nullptr);
> +
> +constexpr DD dd;
> +static_assert (dynamic_cast<DD*> ((A*)&dd) == nullptr);
> +static_assert (dynamic_cast<DD*> ((B*)&dd) == nullptr);
> +
> +constexpr DDD ddd;
> +static_assert (dynamic_cast<DDD*> ((A*)&ddd) == nullptr);
> +static_assert (dynamic_cast<DDD*> ((B*)&ddd) == nullptr);
> +static_assert (dynamic_cast<CCC*> ((B*)&ddd) == nullptr);
> +
> +// 1.2. multiple inheritance case
> +// 1.2.1. all bases are public
> +
> +struct E : D, CC {};
> +struct EE : CC, D {}; //Will search in reverse order.
> +
> +constexpr E e;
> +static_assert (dynamic_cast<E*> ((A*)(D*)&e) == nullptr);
> +static_assert (dynamic_cast<E*> ((B*)(D*)&e) == nullptr);
> +static_assert (&e == dynamic_cast<E*> ((C*)(D*)&e));
> +static_assert (&e == dynamic_cast<E*> ((B*)(CC*)&e));
> +static_assert ((CC*)&e == dynamic_cast<CC*> ((B*)(CC*)&e));
> +
> +constexpr EE ee;
> +static_assert (dynamic_cast<EE*> ((A*)(D*)&ee) == nullptr);
> +static_assert (dynamic_cast<EE*> ((B*)(D*)&ee) == nullptr);
> +static_assert (&ee == dynamic_cast<EE*> ((C*)(D*)&ee));
> +static_assert (&ee == dynamic_cast<EE*> ((B*)(CC*)&ee));
> +static_assert ((CC*)&ee == dynamic_cast<CC*> ((B*)(CC*)&ee));
> +
> +// 1.2.2 one or more branches are nonpublic
> +
> +struct X : private BB, E {};
> +struct Y : AA, private B {};
> +
> +class XX : BB, E {};
> +
> +constexpr X x;
> +static_assert (&x == dynamic_cast<X*>((B*)(CC*)(E*)&x));
> +
> +constexpr XX xx;
> +static_assert (dynamic_cast<XX*>((B*)(CC*)(E*)&xx) == nullptr);	
> +
> +constexpr Y y;
> +static_assert (dynamic_cast<Y*>((B*)&y) == nullptr);
> +static_assert (dynamic_cast<Y*>((A*)(B*)&y) == nullptr);
> +
> +// 2. crosscast
> +
> +struct J { virtual void j(); };
> +struct K : CC, private J {};
> +class KK : J, CC{};
> +		
> +static_assert (dynamic_cast<CC*> ((B*)(D*)&e) == nullptr);
> +static_assert ((CC*)&e == dynamic_cast<CC*> ((C*)(D*)&e));
> +
> +constexpr K k;
> +static_assert (dynamic_cast<J*> ((B*)&k) == nullptr);
> +constexpr KK kk;
> +static_assert (dynamic_cast<J*> ((CC*)&kk) == nullptr);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
> new file mode 100644
> index 00000000000..f739c6df94b
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
> @@ -0,0 +1,105 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Adopted from g++.old-deja/g++.other/dyncast1.C.
> +// But use reference dynamic_cast.
> +
> +// 1. downcast
> +// 1.1. single inheritance case
> +
> +struct A { virtual void a(); };
> +struct AA : A {};
> +struct B : A {};
> +struct BB : B {};
> +class C : B {};
> +struct D : C {};
> +
> +struct CC : B {};
> +class DD : CC {};
> +
> +class CCC : protected B {};
> +class DDD : protected CCC {};
> +
> +constexpr D d;
> +constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
> +constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
> +static_assert (&d == &dynamic_cast<const D&> ((C&)d));
> +constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
> +
> +constexpr DD dd;
> +constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
> +constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
> +
> +constexpr DDD ddd;
> +constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
> +constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
> +constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
> +
> +// 1.2. multiple inheritance case
> +// 1.2.1. all bases are public
> +
> +struct E : D, CC {};
> +struct EE : CC, D {}; //Will search in reverse order.
> +
> +constexpr E e;
> +constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
> +constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
> +static_assert (&e == &dynamic_cast<E&> ((C&)(D&)e));
> +static_assert (&e == &dynamic_cast<E&> ((B&)(CC&)e));
> +static_assert (&(CC&)e == &dynamic_cast<CC&> ((B&)(CC&)e));
> +
> +constexpr EE ee;
> +constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
> +constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
> +static_assert (&ee == &dynamic_cast<EE&> ((C&)(D&)ee));
> +static_assert (&ee == &dynamic_cast<EE&> ((B&)(CC&)ee));
> +static_assert (&(CC&)ee == &dynamic_cast<CC&> ((B&)(CC&)ee));
> +
> +// 1.2.2 one or more branches are nonpublic
> +
> +struct X : private BB, E {};
> +struct Y : AA, private B {};
> +
> +class XX : BB, E {};
> +
> +constexpr X x;
> +static_assert (&x == &dynamic_cast<X&>((B&)(CC&)(E&)x));
> +
> +constexpr XX xx;
> +constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .XX." "" { target *-*-* } .-1 }
> +
> +constexpr Y y;
> +constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
> +constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
> +
> +// 2. crosscast
> +
> +struct J { virtual void j(); };
> +struct K : CC, private J {};
> +class KK : J, CC{};
> +		
> +constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .CC." "" { target *-*-* } .-1 }
> +static_assert (&(CC&)e == &dynamic_cast<CC&> ((C&)(D&)e));
> +
> +constexpr K k;
> +constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base class .J." "" { target *-*-* } .-1 }
> +constexpr KK kk;
> +constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic type .KK." "" { target *-*-* } .-1 }
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
> new file mode 100644
> index 00000000000..aae03f691ca
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
> @@ -0,0 +1,41 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Downcast, with hint > 0.
> +
> +struct B {
> +  virtual void baz () {}
> +};
> +
> +struct B2 {
> +  virtual void baz2 () {}
> +};
> +
> +struct D : B, B2 { };
> +
> +constexpr bool
> +fn ()
> +{
> +  // try &/&&, add address test
> +  bool ok = true;
> +  B2 b;
> +  B2 *b1 = &b;
> +  if (D *pd = dynamic_cast<D*>(b1))
> +    ok = false;
> +
> +  D d;
> +  B2 *b2 = &d;
> +  if (D *pd = dynamic_cast<D*>(b2))
> +    /*OK*/;
> +  else
> +   ok = false;
> +
> +  return ok;
> +}
> +
> +static_assert(fn ());
> +
> +constexpr D d;
> +constexpr B2 *b = const_cast<D*>(&d);
> +static_assert(dynamic_cast<D*>(b) == &d);
> +static_assert(&dynamic_cast<D&>(*b) == &d);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
> new file mode 100644
> index 00000000000..c3e09808e32
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
> @@ -0,0 +1,33 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Sidecast.
> +
> +struct A {
> +  virtual void afn () {}
> +};
> +
> +struct B {
> +  virtual void bfn () {}
> +};
> +
> +struct D : A, B { };
> +
> +constexpr bool
> +fn ()
> +{
> +  bool ok = true;
> +  D d;
> +  A *a = &d;
> +  if (B *bp = dynamic_cast<B*>(a))
> +    /*OK*/;
> +  else
> +    ok = false;
> +
> +  A &ar = d;
> +  B &br = dynamic_cast<B&>(ar);
> +
> +  return ok;
> +}
> +
> +static_assert(fn ());
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
> new file mode 100644
> index 00000000000..3adc524379d
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
> @@ -0,0 +1,55 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// From clang's constant-expression-cxx2a.cpp.
> +
> +struct A2 { virtual void a2(); };
> +struct A : A2 { virtual void a(); };
> +struct B : A {};
> +struct C2 { virtual void c2(); };
> +struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
> +struct D { virtual void d(); };
> +struct E { virtual void e(); };
> +struct F : B, C, D, private E { void *f = dynamic_cast<void*>(static_cast<D*>(this)); };
> +struct Padding { virtual void padding(); };
> +struct G : Padding, F {};
> +
> +constexpr G g;
> +
> +// During construction of C, A is unambiguous subobject of dynamic type C.
> +static_assert(g.c == (C*)&g);
> +// ... but in the complete object, the same is not true, so the runtime fails.
> +static_assert(dynamic_cast<const A*>(static_cast<const C2*>(&g)) == nullptr);
> +
> +// dynamic_cast<void*> produces a pointer to the object of the dynamic type.
> +static_assert(g.f == (void*)(F*)&g);
> +static_assert(dynamic_cast<const void*>(static_cast<const D*>(&g)) == &g);
> +
> +constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
> +
> +// Can navigate from A2 to its A...
> +static_assert(&dynamic_cast<A&>((A2&)(B&)g) == &(A&)(B&)g);
> +// ... and from B to its A ...
> +static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
> +// ... but not from D.
> +static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant condition for static assertion|reference .dynamic_cast. failed" }
> +// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
> +
> +// Can cast from A2 to sibling class D.
> +static_assert(&dynamic_cast<D&>((A2&)(B&)g) == &(D&)g);
> +
> +// Cannot cast from private base E to derived class F.
> +constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
> +
> +// Cannot cast from B to private sibling E.
> +constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .E." "" { target *-*-* } .-1 }
> +
> +struct Unrelated { virtual void unrelated(); };
> +
> +constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .Unrelated." "" { target *-*-* } .-1 }
> +constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
> new file mode 100644
> index 00000000000..743b3018d2f
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
> @@ -0,0 +1,22 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Multiple levels.
> +
> +struct A { virtual void a(); };
> +struct B : A { virtual void b(); };
> +struct C : B { virtual void c(); };
> +struct D : C { virtual void d(); };
> +struct E : D { virtual void e(); };
> +struct F : E { virtual void f(); };
> +
> +constexpr F f;
> +
> +// F->C->A->B == F->C->B
> +static_assert (&dynamic_cast<B&>((A&)(C&)f) == &(B&)(C&)f);
> +// F->A->E == F->E
> +static_assert (&dynamic_cast<E&>((A&)f) == &(E&)f);
> +// F->E->D->C->B->A->C == F->C
> +static_assert (&dynamic_cast<C&>((A&)(B&)(C&)(D&)(E&)f) == &(C&)f);
> +// F->B->F == F
> +static_assert (&dynamic_cast<F&>((B&)f) == &f);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
> new file mode 100644
> index 00000000000..23434734e26
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
> @@ -0,0 +1,25 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Private base.
> +
> +struct P1 { virtual void p1(); };
> +struct P2 { virtual void p2(); };
> +struct B : private P1 { virtual void b(); };
> +struct C { virtual void c(); };
> +struct A : B, C, private P2 { virtual void a(); };
> +
> +constexpr A a;
> +
> +// P1 is a non-public base of A.
> +constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
> +
> +// Don't error here.
> +static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
> +
> +constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
> +
> +static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
> +static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
> new file mode 100644
> index 00000000000..d71497aae6d
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
> @@ -0,0 +1,25 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Protected base.
> +
> +struct P1 { virtual void p1(); };
> +struct P2 { virtual void p2(); };
> +struct B : protected P1 { virtual void b(); };
> +struct C { virtual void c(); };
> +struct A : B, C, protected P2 { virtual void a(); };
> +
> +constexpr A a;
> +
> +// P1 is a non-public base of A.
> +constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
> +
> +// Don't error here.
> +static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
> +
> +constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
> +
> +static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
> +static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
> new file mode 100644
> index 00000000000..8056f30bb99
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
> @@ -0,0 +1,24 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Unrelated type.
> +
> +struct B { virtual void b(); };
> +struct P1 { virtual void p1(); };
> +struct P2 { virtual void p2(); };
> +struct A : public B, private P1, protected P2 { virtual void a(); };
> +
> +constexpr A a;
> +
> +struct U { virtual void u(); };
> +
> +constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .U." "" { target *-*-* } .-1 }
> +constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
> +constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
> +
> +static_assert (dynamic_cast<U*>((B*)&a) == nullptr);
> +static_assert (dynamic_cast<U*>((P1*)&a) == nullptr);
> +static_assert (dynamic_cast<U*>((P2*)&a) == nullptr);
> diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
> new file mode 100644
> index 00000000000..d8cbb2f2f0d
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
> @@ -0,0 +1,17 @@
> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
> +// { dg-do compile { target c++2a } }
> +
> +// Ambiguous base.
> +
> +struct A { virtual void a(); };
> +struct B : A { virtual void b(); };
> +struct C : A { virtual void c(); };
> +struct D { virtual void a(); };
> +struct E : B, C, D { virtual void d(); };
> +
> +constexpr E e;
> +
> +constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference .dynamic_cast. failed" }
> +// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" "" { target *-*-* } .-1 }
> +
> +static_assert (dynamic_cast<A*>((D*)&e) == nullptr);
>
Marek Polacek Dec. 14, 2019, 9:25 p.m. UTC | #6
On Fri, Dec 13, 2019 at 05:56:57PM -0500, Jason Merrill wrote:
> On 12/13/19 3:20 PM, Marek Polacek wrote:
> > +  /* Given dynamic_cast<T>(v),
> > +
> > +     [expr.dynamic.cast] If C is the class type to which T points or refers,
> > +     the runtime check logically executes as follows:
> > +
> > +     If, in the most derived object pointed (referred) to by v, v points
> > +     (refers) to a public base class subobject of a C object, and if only
> > +     one object of type C is derived from the subobject pointed (referred)
> > +     to by v the result points (refers) to that C object.
> > +
> > +     In this case, HINT >= 0.  */
> > +  if (hint >= 0)
> 
> Won't this code work for hint == -3 as well?

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

> > +    {
> > +      /* Look for a component with type TYPE.  */
> > +      obj = get_component_with_type (obj, type);
> 
> You don't seem to use mdtype at all in this case.  Shouldn't
> get_component_with_type stop at mdtype if it hasn't found type yet?

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

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

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

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

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

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

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

Clang++ doesn't detect this either.

> > +constexpr D d; // { dg-message "in 'constexpr' expansion of" }
> > diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
> > new file mode 100644
> > index 00000000000..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);
> 
> Add a check to verify that you got the right value?

Added, and a reference dynamic_cast added too.

Here's another version with all the above fixed.

Thanks!

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

2019-12-14  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.

	* 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-dynamic2.C: New test.
	* g++.dg/cpp2a/constexpr-dynamic3.C: New test.
	* g++.dg/cpp2a/constexpr-dynamic4.C: New test.
	* g++.dg/cpp2a/constexpr-dynamic5.C: New test.
	* g++.dg/cpp2a/constexpr-dynamic6.C: New test.
	* g++.dg/cpp2a/constexpr-dynamic7.C: New test.
	* g++.dg/cpp2a/constexpr-dynamic8.C: New test.
	* g++.dg/cpp2a/constexpr-dynamic9.C: New test.

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 19e09c74760..de80520a0f6 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1692,6 +1692,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.  */
@@ -1857,6 +2089,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  gcc_assert (arg1);
 	  return arg1;
 	}
+      else if (cxx_dynamic_cast_fn_p (fun))
+	return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
+
       if (!ctx->quiet)
 	{
 	  if (!lambda_static_thunk_p (fun))
@@ -6729,7 +6964,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
 		    && (!cxx_placement_new_fn (fun)
 			|| TREE_CODE (t) != CALL_EXPR
 			|| current_function_decl == NULL_TREE
-			|| !is_std_construct_at (current_function_decl)))
+			|| !is_std_construct_at (current_function_decl))
+		    && !cxx_dynamic_cast_fn_p (fun))
 		  {
 		    if (flags & tf_error)
 		      {
diff --git gcc/cp/rtti.c gcc/cp/rtti.c
index 1b6b87ba8d6..9a242dc64e4 100644
--- gcc/cp/rtti.c
+++ gcc/cp/rtti.c
@@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
 	      dynamic_cast_node = dcast_fn;
 	    }
 	  result = build_cxx_call (dcast_fn, 4, elems, complain);
+	  SET_EXPR_LOCATION (result, loc);
 
 	  if (tc == REFERENCE_TYPE)
 	    {
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
new file mode 100644
index 00000000000..e8ba63d9609
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
@@ -0,0 +1,40 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast.
+
+struct B {
+  virtual void baz () {}
+};
+ 
+struct D : B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  B b;
+  B *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+    
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B b;
+constexpr B *b1 = const_cast<B*>(&b);
+constexpr B *b2 = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b2) == &d);
+static_assert(&dynamic_cast<D&>(*b2) == &d);
+static_assert(dynamic_cast<const B*>(&d) == &d);
+static_assert(&dynamic_cast<const B&>(d) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
new file mode 100644
index 00000000000..c226292a07d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
@@ -0,0 +1,12 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Virtual base.
+
+struct C { virtual void a(); };
+struct B { virtual void b(); };
+struct A : virtual B, C { virtual void c(); }; // { dg-error ".struct A. has virtual base classes" }
+
+constexpr A a; // { dg-error "call" }
+
+constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
new file mode 100644
index 00000000000..9f19bfc6e38
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
@@ -0,0 +1,38 @@
+// 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 }
+    
+  // 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; // { dg-message "in 'constexpr' expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
new file mode 100644
index 00000000000..0ce9beb8d72
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
@@ -0,0 +1,28 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a destructor.
+
+struct A2 { virtual void a2(); };
+
+struct A : A2 { virtual void a(); };
+
+struct C2 { virtual void c2(); };
+
+struct B : A, C2 {
+  constexpr ~B();
+};
+
+constexpr B::~B()
+{
+  A *a = dynamic_cast<A*>((C2*)this);
+  if (a != (A*) this)
+    __builtin_abort ();
+  A& ar = dynamic_cast<A&>((C2&)*this);
+  if (&ar != &(A&)*this)
+    __builtin_abort ();
+}
+
+struct D : B { virtual void d(); };
+
+constexpr D d;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
new file mode 100644
index 00000000000..203067a2581
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
@@ -0,0 +1,86 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Adopted from g++.old-deja/g++.other/dyncast1.C.
+
+// 1. downcast
+// 1.1. single inheritance case
+
+struct A { virtual void a(); };
+struct AA : A {};
+struct B : A {};
+struct BB : B {};
+class C : B {};
+struct D : C {};
+
+struct CC : B {};
+class DD : CC {};
+
+class CCC : protected B {};
+class DDD : protected CCC {};
+
+constexpr D d;
+static_assert (dynamic_cast<D*> ((A*)&d) == nullptr);
+static_assert (dynamic_cast<D*> ((B*)&d) == nullptr);
+static_assert (&d == dynamic_cast<D*> ((C*)&d));
+static_assert (dynamic_cast<C*> ((B*)&d) == nullptr);
+
+constexpr DD dd;
+static_assert (dynamic_cast<DD*> ((A*)&dd) == nullptr);
+static_assert (dynamic_cast<DD*> ((B*)&dd) == nullptr);
+
+constexpr DDD ddd;
+static_assert (dynamic_cast<DDD*> ((A*)&ddd) == nullptr);
+static_assert (dynamic_cast<DDD*> ((B*)&ddd) == nullptr);
+static_assert (dynamic_cast<CCC*> ((B*)&ddd) == nullptr);
+
+// 1.2. multiple inheritance case
+// 1.2.1. all bases are public
+ 
+struct E : D, CC {};
+struct EE : CC, D {}; //Will search in reverse order.
+
+constexpr E e;
+static_assert (dynamic_cast<E*> ((A*)(D*)&e) == nullptr);
+static_assert (dynamic_cast<E*> ((B*)(D*)&e) == nullptr);
+static_assert (&e == dynamic_cast<E*> ((C*)(D*)&e));
+static_assert (&e == dynamic_cast<E*> ((B*)(CC*)&e));
+static_assert ((CC*)&e == dynamic_cast<CC*> ((B*)(CC*)&e));
+
+constexpr EE ee;
+static_assert (dynamic_cast<EE*> ((A*)(D*)&ee) == nullptr);
+static_assert (dynamic_cast<EE*> ((B*)(D*)&ee) == nullptr);
+static_assert (&ee == dynamic_cast<EE*> ((C*)(D*)&ee));
+static_assert (&ee == dynamic_cast<EE*> ((B*)(CC*)&ee));
+static_assert ((CC*)&ee == dynamic_cast<CC*> ((B*)(CC*)&ee));
+
+// 1.2.2 one or more branches are nonpublic
+
+struct X : private BB, E {};
+struct Y : AA, private B {};
+
+class XX : BB, E {};
+
+constexpr X x;
+static_assert (&x == dynamic_cast<X*>((B*)(CC*)(E*)&x));
+
+constexpr XX xx;
+static_assert (dynamic_cast<XX*>((B*)(CC*)(E*)&xx) == nullptr);	
+
+constexpr Y y;
+static_assert (dynamic_cast<Y*>((B*)&y) == nullptr);
+static_assert (dynamic_cast<Y*>((A*)(B*)&y) == nullptr);
+
+// 2. crosscast
+
+struct J { virtual void j(); };
+struct K : CC, private J {}; 
+class KK : J, CC{};
+		
+static_assert (dynamic_cast<CC*> ((B*)(D*)&e) == nullptr);
+static_assert ((CC*)&e == dynamic_cast<CC*> ((C*)(D*)&e));
+
+constexpr K k;
+static_assert (dynamic_cast<J*> ((B*)&k) == nullptr);
+constexpr KK kk;
+static_assert (dynamic_cast<J*> ((CC*)&kk) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
new file mode 100644
index 00000000000..f739c6df94b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
@@ -0,0 +1,105 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Adopted from g++.old-deja/g++.other/dyncast1.C.
+// But use reference dynamic_cast.
+
+// 1. downcast
+// 1.1. single inheritance case
+
+struct A { virtual void a(); };
+struct AA : A {};
+struct B : A {};
+struct BB : B {};
+class C : B {};
+struct D : C {};
+
+struct CC : B {};
+class DD : CC {};
+
+class CCC : protected B {};
+class DDD : protected CCC {};
+
+constexpr D d;
+constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+static_assert (&d == &dynamic_cast<const D&> ((C&)d));
+constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+
+constexpr DD dd;
+constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
+constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
+
+constexpr DDD ddd;
+constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+
+// 1.2. multiple inheritance case
+// 1.2.1. all bases are public
+ 
+struct E : D, CC {};
+struct EE : CC, D {}; //Will search in reverse order.
+
+constexpr E e;
+constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
+constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
+static_assert (&e == &dynamic_cast<E&> ((C&)(D&)e));
+static_assert (&e == &dynamic_cast<E&> ((B&)(CC&)e));
+static_assert (&(CC&)e == &dynamic_cast<CC&> ((B&)(CC&)e));
+
+constexpr EE ee;
+constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
+constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
+static_assert (&ee == &dynamic_cast<EE&> ((C&)(D&)ee));
+static_assert (&ee == &dynamic_cast<EE&> ((B&)(CC&)ee));
+static_assert (&(CC&)ee == &dynamic_cast<CC&> ((B&)(CC&)ee));
+
+// 1.2.2 one or more branches are nonpublic
+
+struct X : private BB, E {};
+struct Y : AA, private B {};
+
+class XX : BB, E {};
+
+constexpr X x;
+static_assert (&x == &dynamic_cast<X&>((B&)(CC&)(E&)x));
+
+constexpr XX xx;
+constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .XX." "" { target *-*-* } .-1 }
+
+constexpr Y y;
+constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
+constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
+
+// 2. crosscast
+
+struct J { virtual void j(); };
+struct K : CC, private J {}; 
+class KK : J, CC{};
+		
+constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .CC." "" { target *-*-* } .-1 }
+static_assert (&(CC&)e == &dynamic_cast<CC&> ((C&)(D&)e));
+
+constexpr K k;
+constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base class .J." "" { target *-*-* } .-1 }
+constexpr KK kk;
+constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic type .KK." "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C
new file mode 100644
index 00000000000..fcf507289c4
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C
@@ -0,0 +1,14 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test HINT = -3 (SRC is a multiple public non-virtual base of DST).
+
+struct A { virtual void a() {} };
+struct C : A { };
+struct D : A { };
+struct B : C, D { };
+
+constexpr B b;
+static_assert (&dynamic_cast<B&>((A&)(C&)b) == &b);
+static_assert (&dynamic_cast<B&>((A&)(D&)b) == &b);
+static_assert (dynamic_cast<B*>((A*)(C*)&b) == &b);
+static_assert (dynamic_cast<B*>((A*)(D*)&b) == &b);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C
new file mode 100644
index 00000000000..f0394d130a3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C
@@ -0,0 +1,20 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Here the hint turns out to be wrong: A is a public base of B2, but the
+// dynamic_cast operand is not that subobject, but rather a sibling base of
+// B2.
+
+struct A { virtual void f(); };
+struct B1: A { };
+struct B2: A { };
+struct C: B1, B2 { };
+
+constexpr C c;
+constexpr A *ap = (B1*)&c;
+constexpr A &ar = (B1&)c;
+constexpr auto p = dynamic_cast<B2*>(ap);
+static_assert (p != nullptr);
+constexpr auto p2 = dynamic_cast<B2&>(ar);
+static_assert(dynamic_cast<B2*>(ap) == (B2*)&c);
+static_assert(dynamic_cast<B2*>((B1*)&c) == (B2*)&c);
+static_assert(&dynamic_cast<B2&>((B1&)c) == &(B2&)c);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
new file mode 100644
index 00000000000..aae03f691ca
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
@@ -0,0 +1,41 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast, with hint > 0.
+
+struct B {
+  virtual void baz () {}
+};
+
+struct B2 {
+  virtual void baz2 () {}
+};
+ 
+struct D : B, B2 { };
+
+constexpr bool
+fn ()
+{
+  // try &/&&, add address test
+  bool ok = true;
+  B2 b;
+  B2 *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B2 *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+    
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B2 *b = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b) == &d);
+static_assert(&dynamic_cast<D&>(*b) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
new file mode 100644
index 00000000000..c3e09808e32
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
@@ -0,0 +1,33 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Sidecast.
+
+struct A {
+  virtual void afn () {}
+};
+ 
+struct B {
+  virtual void bfn () {}
+};
+
+struct D : A, B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  D d;
+  A *a = &d;
+  if (B *bp = dynamic_cast<B*>(a))
+    /*OK*/;
+  else
+    ok = false;
+
+  A &ar = d;
+  B &br = dynamic_cast<B&>(ar);
+
+  return ok;
+}
+
+static_assert(fn ());
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
new file mode 100644
index 00000000000..3adc524379d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
@@ -0,0 +1,55 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// From clang's constant-expression-cxx2a.cpp.
+
+struct A2 { virtual void a2(); };
+struct A : A2 { virtual void a(); };
+struct B : A {}; 
+struct C2 { virtual void c2(); };
+struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
+struct D { virtual void d(); };
+struct E { virtual void e(); };
+struct F : B, C, D, private E { void *f = dynamic_cast<void*>(static_cast<D*>(this)); };
+struct Padding { virtual void padding(); };
+struct G : Padding, F {}; 
+
+constexpr G g;
+
+// During construction of C, A is unambiguous subobject of dynamic type C.
+static_assert(g.c == (C*)&g);
+// ... but in the complete object, the same is not true, so the runtime fails.
+static_assert(dynamic_cast<const A*>(static_cast<const C2*>(&g)) == nullptr);
+
+// dynamic_cast<void*> produces a pointer to the object of the dynamic type.
+static_assert(g.f == (void*)(F*)&g);
+static_assert(dynamic_cast<const void*>(static_cast<const D*>(&g)) == &g);
+
+constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Can navigate from A2 to its A...
+static_assert(&dynamic_cast<A&>((A2&)(B&)g) == &(A&)(B&)g);
+// ... and from B to its A ...
+static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
+// ... but not from D.
+static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant condition for static assertion|reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Can cast from A2 to sibling class D.
+static_assert(&dynamic_cast<D&>((A2&)(B&)g) == &(D&)g);
+
+// Cannot cast from private base E to derived class F.
+constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Cannot cast from B to private sibling E.
+constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .E." "" { target *-*-* } .-1 }
+
+struct Unrelated { virtual void unrelated(); };
+
+constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .Unrelated." "" { target *-*-* } .-1 }
+constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
new file mode 100644
index 00000000000..743b3018d2f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
@@ -0,0 +1,22 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Multiple levels.
+
+struct A { virtual void a(); };
+struct B : A { virtual void b(); };
+struct C : B { virtual void c(); };
+struct D : C { virtual void d(); };
+struct E : D { virtual void e(); };
+struct F : E { virtual void f(); };
+
+constexpr F f;
+
+// F->C->A->B == F->C->B
+static_assert (&dynamic_cast<B&>((A&)(C&)f) == &(B&)(C&)f);
+// F->A->E == F->E
+static_assert (&dynamic_cast<E&>((A&)f) == &(E&)f);
+// F->E->D->C->B->A->C == F->C
+static_assert (&dynamic_cast<C&>((A&)(B&)(C&)(D&)(E&)f) == &(C&)f);
+// F->B->F == F
+static_assert (&dynamic_cast<F&>((B&)f) == &f);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
new file mode 100644
index 00000000000..23434734e26
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
@@ -0,0 +1,25 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Private base.
+
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct B : private P1 { virtual void b(); };
+struct C { virtual void c(); };
+struct A : B, C, private P2 { virtual void a(); };
+
+constexpr A a;
+
+// P1 is a non-public base of A.
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+// Don't error here.
+static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
+
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
new file mode 100644
index 00000000000..d71497aae6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
@@ -0,0 +1,25 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Protected base.
+
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct B : protected P1 { virtual void b(); };
+struct C { virtual void c(); };
+struct A : B, C, protected P2 { virtual void a(); };
+
+constexpr A a;
+
+// P1 is a non-public base of A.
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+// Don't error here.
+static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
+
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
new file mode 100644
index 00000000000..8056f30bb99
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
@@ -0,0 +1,24 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Unrelated type.
+
+struct B { virtual void b(); };
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct A : public B, private P1, protected P2 { virtual void a(); };
+
+constexpr A a;
+
+struct U { virtual void u(); };
+
+constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .U." "" { target *-*-* } .-1 }
+constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<U*>((B*)&a) == nullptr);
+static_assert (dynamic_cast<U*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<U*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
new file mode 100644
index 00000000000..d8cbb2f2f0d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
@@ -0,0 +1,17 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Ambiguous base.
+
+struct A { virtual void a(); };
+struct B : A { virtual void b(); };
+struct C : A { virtual void c(); };
+struct D { virtual void a(); };
+struct E : B, C, D { virtual void d(); };
+
+constexpr E e;
+
+constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<A*>((D*)&e) == nullptr);
Jason Merrill Dec. 16, 2019, 8:55 p.m. UTC | #7
On 12/14/19 4:25 PM, Marek Polacek wrote:
> On Fri, Dec 13, 2019 at 05:56:57PM -0500, Jason Merrill wrote:
>> On 12/13/19 3:20 PM, Marek Polacek wrote:
>>> +  /* Given dynamic_cast<T>(v),
>>> +
>>> +     [expr.dynamic.cast] If C is the class type to which T points or refers,
>>> +     the runtime check logically executes as follows:
>>> +
>>> +     If, in the most derived object pointed (referred) to by v, v points
>>> +     (refers) to a public base class subobject of a C object, and if only
>>> +     one object of type C is derived from the subobject pointed (referred)
>>> +     to by v the result points (refers) to that C object.
>>> +
>>> +     In this case, HINT >= 0.  */
>>> +  if (hint >= 0)
>>
>> Won't this code work for hint == -3 as well?
> 
> Yes, it does.  In fact, none of the tests was testing the hint == -3 case, so
> I've fixed the code up and added constexpr-dynamic15.C to test it.
> 
>>> +    {
>>> +      /* Look for a component with type TYPE.  */
>>> +      obj = get_component_with_type (obj, type);
>>
>> You don't seem to use mdtype at all in this case.  Shouldn't
>> get_component_with_type stop at mdtype if it hasn't found type yet?
> 
> It was used for diagnostics but not in get_component_with_type.  It makes
> sense to stop at MDTYPE; I've adjusted the code to do so.  E.g., if
> we have OBJ in the form of g.D.2121.D.2122.D.2123.D.2124, usually the
> component with the most derived type is "g", but in a 'tor, it can be
> a different component too.
> 
>>> +      /* If not found or not accessible, give an error.  */
>>> +      if (obj == NULL_TREE || obj == error_mark_node)
>>> +	{
>>> +	  if (reference_p)
>>> +	    {
>>> +	      if (!ctx->quiet)
>>> +		{
>>> +		  error_at (loc, "reference %<dynamic_cast%> failed");
>>> +		  if (obj == NULL_TREE)
>>> +		    inform (loc, "dynamic type %qT of its operand does not "
>>> +			    "have an unambiguous public base class %qT",
>>> +			    mdtype, type);
>>> +		  else
>>> +		    inform (loc, "static type %qT of its operand is a "
>>> +			    "non-public base class of dynamic type %qT",
>>> +			    objtype, type);
>>> +
>>> +		}
>>> +	      *non_constant_p = true;
>>> +	    }
>>> +	  return integer_zero_node;
>>> +	}
>>> +      else
>>> +	/* The result points to the TYPE object.  */
>>> +	return cp_build_addr_expr (obj, complain);
>>> +    }
>>> +  /* Otherwise, if v points (refers) to a public base class subobject of the
>>> +     most derived object, and the type of the most derived object has a base
>>> +     class, of type C, that is unambiguous and public, the result points
>>> +     (refers) to the C subobject of the most derived object.
>>> +
>>> +     But it can also be an invalid case.  */
>>
>> And I think we need to fall through to this code if the hint turns out to be
>> wrong, i.e. V is a public base of C, but v is not that subobject, but rather
>> a sibling base of C, like
> 
> True.  HINT is really just an optimization hint, nothing more.  I've adjusted
> the code to fall through to the normal processing if the HINT >= 0 or -3 case
> doesn't succeed.
> 
>> struct A { virtual void f(); };
>> struct B1: A { };
>> struct B2: A { };
>> struct C: B1, B2 { };
>> int main()
>> {
>>    C c;
>>    A* ap = (B1*)c;
>>    constexpr auto p = dynamic_cast<B2*>(ap); // should succeed
>> }
> 
> Whew, there's always One More Case. :/  New constexpr-dynamic16.c covers it.
> 
>>> --- /dev/null
>>> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
>>> @@ -0,0 +1,34 @@
>>> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
>>> +// { dg-do compile { target c++2a } }
>>> +
>>> +// dynamic_cast in a constructor.
>>> +
>>> +struct V {
>>> +  virtual void f();
>>> +};
>>> +
>>> +struct A : V { };
>>> +
>>> +struct B : V {
>>> +  constexpr B(V*, A*);
>>> +};
>>> +
>>> +struct D : A, B {
>>> +  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
>>> +};
>>> +
>>> +constexpr B::B(V* v, A* a)
>>> +{
>>> +  // well-defined: v of type V*, V base of B results in B*
>>> +  B* b = dynamic_cast<B*>(v);
>>> +  if (b != nullptr)
>>> +    __builtin_abort ();
>>> +
>>> +  B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" }
>>> +// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target *-*-* } .-1 }
>>> +
>>> +  // FIXME: UB in constexpr should be detected.
>>> +  dynamic_cast<B*>(a);          // undefined behavior, a has type A*, A not a base of B
>>
>> What undefined behavior?  Seems to me the dynamic_cast should just fail and
>> return a null pointer.
> 
> This is <http://eel.is/c++draft/class.cdtor#6.sentence-3>:
> "If the operand of the dynamic_cast refers to the object under construction
> or destruction and the static type of the operand is not a pointer to or
> object of the constructor or destructor's own class or one of its bases, the
> dynamic_cast results in undefined behavior."
> 
> Clang++ doesn't detect this either.

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

Jason
Jason Merrill Dec. 16, 2019, 9 p.m. UTC | #8
On 12/16/19 3:55 PM, Jason Merrill wrote:
> On 12/14/19 4:25 PM, Marek Polacek wrote:
>> On Fri, Dec 13, 2019 at 05:56:57PM -0500, Jason Merrill wrote:
>>> On 12/13/19 3:20 PM, Marek Polacek wrote:
>>>> +  /* Given dynamic_cast<T>(v),
>>>> +
>>>> +     [expr.dynamic.cast] If C is the class type to which T points 
>>>> or refers,
>>>> +     the runtime check logically executes as follows:
>>>> +
>>>> +     If, in the most derived object pointed (referred) to by v, v 
>>>> points
>>>> +     (refers) to a public base class subobject of a C object, and 
>>>> if only
>>>> +     one object of type C is derived from the subobject pointed 
>>>> (referred)
>>>> +     to by v the result points (refers) to that C object.
>>>> +
>>>> +     In this case, HINT >= 0.  */
>>>> +  if (hint >= 0)
>>>
>>> Won't this code work for hint == -3 as well?
>>
>> Yes, it does.  In fact, none of the tests was testing the hint == -3 
>> case, so
>> I've fixed the code up and added constexpr-dynamic15.C to test it.
>>
>>>> +    {
>>>> +      /* Look for a component with type TYPE.  */
>>>> +      obj = get_component_with_type (obj, type);
>>>
>>> You don't seem to use mdtype at all in this case.  Shouldn't
>>> get_component_with_type stop at mdtype if it hasn't found type yet?
>>
>> It was used for diagnostics but not in get_component_with_type.  It makes
>> sense to stop at MDTYPE; I've adjusted the code to do so.  E.g., if
>> we have OBJ in the form of g.D.2121.D.2122.D.2123.D.2124, usually the
>> component with the most derived type is "g", but in a 'tor, it can be
>> a different component too.
>>
>>>> +      /* If not found or not accessible, give an error.  */
>>>> +      if (obj == NULL_TREE || obj == error_mark_node)
>>>> +    {
>>>> +      if (reference_p)
>>>> +        {
>>>> +          if (!ctx->quiet)
>>>> +        {
>>>> +          error_at (loc, "reference %<dynamic_cast%> failed");
>>>> +          if (obj == NULL_TREE)
>>>> +            inform (loc, "dynamic type %qT of its operand does not "
>>>> +                "have an unambiguous public base class %qT",
>>>> +                mdtype, type);
>>>> +          else
>>>> +            inform (loc, "static type %qT of its operand is a "
>>>> +                "non-public base class of dynamic type %qT",
>>>> +                objtype, type);
>>>> +
>>>> +        }
>>>> +          *non_constant_p = true;
>>>> +        }
>>>> +      return integer_zero_node;
>>>> +    }
>>>> +      else
>>>> +    /* The result points to the TYPE object.  */
>>>> +    return cp_build_addr_expr (obj, complain);
>>>> +    }
>>>> +  /* Otherwise, if v points (refers) to a public base class 
>>>> subobject of the
>>>> +     most derived object, and the type of the most derived object 
>>>> has a base
>>>> +     class, of type C, that is unambiguous and public, the result 
>>>> points
>>>> +     (refers) to the C subobject of the most derived object.
>>>> +
>>>> +     But it can also be an invalid case.  */
>>>
>>> And I think we need to fall through to this code if the hint turns 
>>> out to be
>>> wrong, i.e. V is a public base of C, but v is not that subobject, but 
>>> rather
>>> a sibling base of C, like
>>
>> True.  HINT is really just an optimization hint, nothing more.  I've 
>> adjusted
>> the code to fall through to the normal processing if the HINT >= 0 or 
>> -3 case
>> doesn't succeed.
>>
>>> struct A { virtual void f(); };
>>> struct B1: A { };
>>> struct B2: A { };
>>> struct C: B1, B2 { };
>>> int main()
>>> {
>>>    C c;
>>>    A* ap = (B1*)c;
>>>    constexpr auto p = dynamic_cast<B2*>(ap); // should succeed
>>> }
>>
>> Whew, there's always One More Case. :/  New constexpr-dynamic16.c 
>> covers it.
>>
>>>> --- /dev/null
>>>> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
>>>> @@ -0,0 +1,34 @@
>>>> +// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in 
>>>> constexpr.
>>>> +// { dg-do compile { target c++2a } }
>>>> +
>>>> +// dynamic_cast in a constructor.
>>>> +
>>>> +struct V {
>>>> +  virtual void f();
>>>> +};
>>>> +
>>>> +struct A : V { };
>>>> +
>>>> +struct B : V {
>>>> +  constexpr B(V*, A*);
>>>> +};
>>>> +
>>>> +struct D : A, B {
>>>> +  constexpr D() : B((A*)this, this) { } // { dg-message "in 
>>>> 'constexpr' expansion of" }
>>>> +};
>>>> +
>>>> +constexpr B::B(V* v, A* a)
>>>> +{
>>>> +  // well-defined: v of type V*, V base of B results in B*
>>>> +  B* b = dynamic_cast<B*>(v);
>>>> +  if (b != nullptr)
>>>> +    __builtin_abort ();
>>>> +
>>>> +  B& br = dynamic_cast<B&>(*v); // { dg-error "reference 
>>>> .dynamic_cast. failed" }
>>>> +// { dg-message "dynamic type .A. of its operand does not have an 
>>>> unambiguous public base class .B." "" { target *-*-* } .-1 }
>>>> +
>>>> +  // FIXME: UB in constexpr should be detected.
>>>> +  dynamic_cast<B*>(a);          // undefined behavior, a has type 
>>>> A*, A not a base of B
>>>
>>> What undefined behavior?  Seems to me the dynamic_cast should just 
>>> fail and
>>> return a null pointer.
>>
>> This is <http://eel.is/c++draft/class.cdtor#6.sentence-3>:
>> "If the operand of the dynamic_cast refers to the object under 
>> construction
>> or destruction and the static type of the operand is not a pointer to or
>> object of the constructor or destructor's own class or one of its 
>> bases, the
>> dynamic_cast results in undefined behavior."
>>
>> Clang++ doesn't detect this either.
> 
> Ah, interesting.  That text goes back to C++98.  I guess the reason for 
> that was that the vtable pointer might give problematic answers in that 
> situation.  It should be straightforward to detect this in constexpr 
> evaluation; if mdtype is not the actual complete object, we can see if 
> *this is also part of mdtype (success), or otherwise the complete object 
> (undefined).

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

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

Jason
Marek Polacek Dec. 17, 2019, 10:34 p.m. UTC | #9
On Mon, Dec 16, 2019 at 04:00:14PM -0500, Jason Merrill wrote:
> On 12/16/19 3:55 PM, Jason Merrill wrote:
> > On 12/14/19 4:25 PM, Marek Polacek wrote:
> > > On Fri, Dec 13, 2019 at 05:56:57PM -0500, Jason Merrill wrote:
> > > > On 12/13/19 3:20 PM, Marek Polacek wrote:
> > > > > +  /* Given dynamic_cast<T>(v),
> > > > > +
> > > > > +     [expr.dynamic.cast] If C is the class type to which T
> > > > > points or refers,
> > > > > +     the runtime check logically executes as follows:
> > > > > +
> > > > > +     If, in the most derived object pointed (referred) to
> > > > > by v, v points
> > > > > +     (refers) to a public base class subobject of a C
> > > > > object, and if only
> > > > > +     one object of type C is derived from the subobject
> > > > > pointed (referred)
> > > > > +     to by v the result points (refers) to that C object.
> > > > > +
> > > > > +     In this case, HINT >= 0.  */
> > > > > +  if (hint >= 0)
> > > > 
> > > > Won't this code work for hint == -3 as well?
> > > 
> > > Yes, it does.  In fact, none of the tests was testing the hint == -3
> > > case, so
> > > I've fixed the code up and added constexpr-dynamic15.C to test it.
> > > 
> > > > > +    {
> > > > > +      /* Look for a component with type TYPE.  */
> > > > > +      obj = get_component_with_type (obj, type);
> > > > 
> > > > You don't seem to use mdtype at all in this case.  Shouldn't
> > > > get_component_with_type stop at mdtype if it hasn't found type yet?
> > > 
> > > It was used for diagnostics but not in get_component_with_type.  It makes
> > > sense to stop at MDTYPE; I've adjusted the code to do so.  E.g., if
> > > we have OBJ in the form of g.D.2121.D.2122.D.2123.D.2124, usually the
> > > component with the most derived type is "g", but in a 'tor, it can be
> > > a different component too.
> > > 
> > > > > +      /* If not found or not accessible, give an error.  */
> > > > > +      if (obj == NULL_TREE || obj == error_mark_node)
> > > > > +    {
> > > > > +      if (reference_p)
> > > > > +        {
> > > > > +          if (!ctx->quiet)
> > > > > +        {
> > > > > +          error_at (loc, "reference %<dynamic_cast%> failed");
> > > > > +          if (obj == NULL_TREE)
> > > > > +            inform (loc, "dynamic type %qT of its operand does not "
> > > > > +                "have an unambiguous public base class %qT",
> > > > > +                mdtype, type);
> > > > > +          else
> > > > > +            inform (loc, "static type %qT of its operand is a "
> > > > > +                "non-public base class of dynamic type %qT",
> > > > > +                objtype, type);
> > > > > +
> > > > > +        }
> > > > > +          *non_constant_p = true;
> > > > > +        }
> > > > > +      return integer_zero_node;
> > > > > +    }
> > > > > +      else
> > > > > +    /* The result points to the TYPE object.  */
> > > > > +    return cp_build_addr_expr (obj, complain);
> > > > > +    }
> > > > > +  /* Otherwise, if v points (refers) to a public base class
> > > > > subobject of the
> > > > > +     most derived object, and the type of the most derived
> > > > > object has a base
> > > > > +     class, of type C, that is unambiguous and public, the
> > > > > result points
> > > > > +     (refers) to the C subobject of the most derived object.
> > > > > +
> > > > > +     But it can also be an invalid case.  */
> > > > 
> > > > And I think we need to fall through to this code if the hint
> > > > turns out to be
> > > > wrong, i.e. V is a public base of C, but v is not that
> > > > subobject, but rather
> > > > a sibling base of C, like
> > > 
> > > True.  HINT is really just an optimization hint, nothing more.  I've
> > > adjusted
> > > the code to fall through to the normal processing if the HINT >= 0
> > > or -3 case
> > > doesn't succeed.
> > > 
> > > > struct A { virtual void f(); };
> > > > struct B1: A { };
> > > > struct B2: A { };
> > > > struct C: B1, B2 { };
> > > > int main()
> > > > {
> > > >    C c;
> > > >    A* ap = (B1*)c;
> > > >    constexpr auto p = dynamic_cast<B2*>(ap); // should succeed
> > > > }
> > > 
> > > Whew, there's always One More Case. :/  New constexpr-dynamic16.c
> > > covers it.
> > > 
> > > > > --- /dev/null
> > > > > +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
> > > > > @@ -0,0 +1,34 @@
> > > > > +// PR c++/88337 - Implement P1327R1: Allow
> > > > > dynamic_cast/typeid in constexpr.
> > > > > +// { dg-do compile { target c++2a } }
> > > > > +
> > > > > +// dynamic_cast in a constructor.
> > > > > +
> > > > > +struct V {
> > > > > +  virtual void f();
> > > > > +};
> > > > > +
> > > > > +struct A : V { };
> > > > > +
> > > > > +struct B : V {
> > > > > +  constexpr B(V*, A*);
> > > > > +};
> > > > > +
> > > > > +struct D : A, B {
> > > > > +  constexpr D() : B((A*)this, this) { } // { dg-message "in
> > > > > 'constexpr' expansion of" }
> > > > > +};
> > > > > +
> > > > > +constexpr B::B(V* v, A* a)
> > > > > +{
> > > > > +  // well-defined: v of type V*, V base of B results in B*
> > > > > +  B* b = dynamic_cast<B*>(v);
> > > > > +  if (b != nullptr)
> > > > > +    __builtin_abort ();
> > > > > +
> > > > > +  B& br = dynamic_cast<B&>(*v); // { dg-error "reference
> > > > > .dynamic_cast. failed" }
> > > > > +// { dg-message "dynamic type .A. of its operand does not
> > > > > have an unambiguous public base class .B." "" { target *-*-*
> > > > > } .-1 }
> > > > > +
> > > > > +  // FIXME: UB in constexpr should be detected.
> > > > > +  dynamic_cast<B*>(a);          // undefined behavior, a
> > > > > has type A*, A not a base of B
> > > > 
> > > > What undefined behavior?  Seems to me the dynamic_cast should
> > > > just fail and
> > > > return a null pointer.
> > > 
> > > This is <http://eel.is/c++draft/class.cdtor#6.sentence-3>:
> > > "If the operand of the dynamic_cast refers to the object under
> > > construction
> > > or destruction and the static type of the operand is not a pointer to or
> > > object of the constructor or destructor's own class or one of its
> > > bases, the
> > > dynamic_cast results in undefined behavior."
> > > 
> > > Clang++ doesn't detect this either.
> > 
> > Ah, interesting.  That text goes back to C++98.  I guess the reason for
> > that was that the vtable pointer might give problematic answers in that
> > situation.  It should be straightforward to detect this in constexpr
> > evaluation; if mdtype is not the actual complete object, we can see if
> > *this is also part of mdtype (success), or otherwise the complete object
> > (undefined).
> 
> > It looks like get_component_with_type (<*this>, type, mdtype) should work well for that check.
> 
> Well, the second argument is wrong, we'll need to find the complete object
> type for it.

In both cases (dynamic_cast<B*>(v); and dynamic_cast<B*>(a);) *this is
"d.D.2126" (types D, B) and mdtype is A, so the above get_component_with_type
won't work as far as I can see.  I've come up with this instead, which works
for the testcases I have, but is probably not entirely correct in general,
either :(.

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

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

Bootstrapped/regtested on x86_64-linux.

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

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

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

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index f3f03e7d621..4b3751a17ca 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1070,6 +1070,7 @@ struct constexpr_ctx {
 
 static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
 
+static tree initialized_type (tree);
 static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
 					  bool, bool *, bool *, tree * = NULL);
 
@@ -1689,6 +1690,271 @@ is_std_allocator_allocate (tree fndecl)
   return decl_in_std_namespace_p (decl);
 }
 
+/* Return true if FNDECL is __dynamic_cast.  */
+
+static inline bool
+cxx_dynamic_cast_fn_p (tree fndecl)
+{
+  return (cxx_dialect >= cxx2a
+	  && id_equal (DECL_NAME (fndecl), "__dynamic_cast")
+	  && CP_DECL_CONTEXT (fndecl) == global_namespace);
+}
+
+/* Often, we have an expression in the form of address + offset, e.g.
+   "&_ZTV1A + 16".  Extract the object from it, i.e. "_ZTV1A".  */
+
+static tree
+extract_obj_from_addr_offset (tree expr)
+{
+  if (TREE_CODE (expr) == POINTER_PLUS_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+  STRIP_NOPS (expr);
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+  return expr;
+}
+
+/* Given a PATH like
+
+     g.D.2181.D.2154.D.2102.D.2093
+
+   find a component with type TYPE.  Return NULL_TREE if not found, and
+   error_mark_node if the component is not accessible.  If STOP is non-null,
+   this function will return NULL_TREE if STOP is found before TYPE.  */
+
+static tree
+get_component_with_type (tree path, tree type, tree stop)
+{
+  while (true)
+    {
+      if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), type))
+	/* Found it.  */
+	return path;
+      else if (stop
+	       && (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path),
+							      stop)))
+	return NULL_TREE;
+      else if (TREE_CODE (path) == COMPONENT_REF
+	       && DECL_FIELD_IS_BASE (TREE_OPERAND (path, 1)))
+	{
+	  /* We need to check that the component we're accessing is in fact
+	     accessible.  */
+	  if (TREE_PRIVATE (TREE_OPERAND (path, 1))
+	      || TREE_PROTECTED (TREE_OPERAND (path, 1)))
+	    return error_mark_node;
+	  path = TREE_OPERAND (path, 0);
+	}
+      else
+	return NULL_TREE;
+    }
+}
+
+/* Evaluate a call to __dynamic_cast (permitted by P1327R1).
+
+   The declaration of __dynamic_cast is:
+
+   void* __dynamic_cast (const void* __src_ptr,
+			 const __class_type_info* __src_type,
+			 const __class_type_info* __dst_type,
+			 ptrdiff_t __src2dst);
+
+   where src2dst has the following possible values
+
+   >-1: src_type is a unique public non-virtual base of dst_type
+	dst_ptr + src2dst == src_ptr
+   -1: unspecified relationship
+   -2: src_type is not a public base of dst_type
+   -3: src_type is a multiple public non-virtual base of dst_type
+
+  Since literal types can't have virtual bases, we only expect hint >=0,
+  -2, or -3.  */
+
+static tree
+cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
+			  bool *non_constant_p, bool *overflow_p)
+{
+  /* T will be something like
+      __dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8)
+     dismantle it.  */
+  gcc_assert (call_expr_nargs (call) == 4);
+  tsubst_flags_t complain = ctx->quiet ? tf_none : tf_warning_or_error;
+  tree obj = CALL_EXPR_ARG (call, 0);
+  tree type = CALL_EXPR_ARG (call, 2);
+  HOST_WIDE_INT hint = int_cst_value (CALL_EXPR_ARG (call, 3));
+  location_t loc = cp_expr_loc_or_input_loc (call);
+
+  /* Get the target type of the dynamic_cast.  */
+  gcc_assert (TREE_CODE (type) == ADDR_EXPR);
+  type = TREE_OPERAND (type, 0);
+  type = TREE_TYPE (DECL_NAME (type));
+
+  /* TYPE can only be either T* or T&.  We can't know which of these it
+     is by looking at TYPE, but OBJ will be "(T*) x" in the first case,
+     and something like "(T*)(T&)(T*) x" in the second case.  */
+  bool reference_p = false;
+  while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR)
+    {
+      reference_p |= TYPE_REF_P (TREE_TYPE (obj));
+      obj = TREE_OPERAND (obj, 0);
+    }
+
+  /* Evaluate the object so that we know its dynamic type.  */
+  obj = cxx_eval_constant_expression (ctx, obj, /*lval*/false, non_constant_p,
+				      overflow_p);
+  if (*non_constant_p)
+    return call;
+
+  /* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
+     but when HINT is > 0, it can also be something like
+     &d.D.2102 + 18446744073709551608, which includes the BINFO_OFFSET.  */
+  obj = extract_obj_from_addr_offset (obj);
+  const tree objtype = TREE_TYPE (obj);
+  /* If OBJ doesn't refer to a base field, we're done.  */
+  if (tree t = (TREE_CODE (obj) == COMPONENT_REF
+		? TREE_OPERAND (obj, 1) : obj))
+    if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t))
+      return integer_zero_node;
+
+  /* [class.cdtor] When a dynamic_cast is used in a constructor ...
+     or in a destructor ... if the operand of the dynamic_cast refers
+     to the object under construction or destruction, this object is
+     considered to be a most derived object that has the type of the
+     constructor or destructor's class.  */
+  tree vtable = build_vfield_ref (obj, TREE_TYPE (obj));
+  vtable = cxx_eval_constant_expression (ctx, vtable, /*lval*/false,
+					 non_constant_p, overflow_p);
+  if (*non_constant_p)
+    return call;
+  /* VTABLE will be &_ZTV1A + 16 or similar, get _ZTV1A.  */
+  vtable = extract_obj_from_addr_offset (vtable);
+  const tree mdtype = DECL_CONTEXT (vtable);
+
+  tree t = obj;
+  while (TREE_CODE (t) == COMPONENT_REF
+	 && DECL_FIELD_IS_BASE (TREE_OPERAND (t, 1)))
+    t = TREE_OPERAND (t, 0);
+  const tree complete_type = TREE_TYPE (t);
+  
+  /* [class.cdtor]/6 "If the operand of the dynamic_cast refers to
+     the object under construction or destruction and the static type
+     of the operand is not a pointer to or object of the constructor
+     or destructor's own class or one of its bases, the dynamic_cast
+     results in undefined behavior."  And undefined behavior should be
+     detected in constexpr contexts.  */
+  if (!same_type_ignoring_top_level_qualifiers_p (mdtype, complete_type))
+    {
+      unsigned ix;
+      FOR_EACH_VEC_ELT_REVERSE (call_stack, ix, t)
+	if (tree fn = cp_get_callee_fndecl_nofold (t))
+	  if (DECL_CONSTRUCTOR_P (fn))
+	    {
+	      /* Get *this of the current constructor.  */
+	      tree cdtor_type = initialized_type (t);
+	      if (!DERIVED_FROM_P (objtype, cdtor_type))
+		{
+		  if (!ctx->quiet)
+		    error_at (loc, "static type %qT of %<dynamic_cast%> "
+			      "not a base of %qT", objtype, cdtor_type);
+		  *non_constant_p = true;
+		  return integer_zero_node;
+		}
+	      break;
+	    }
+    }
+
+  /* Given dynamic_cast<T>(v),
+
+     [expr.dynamic.cast] If C is the class type to which T points or refers,
+     the runtime check logically executes as follows:
+
+     If, in the most derived object pointed (referred) to by v, v points
+     (refers) to a public base class subobject of a C object, and if only
+     one object of type C is derived from the subobject pointed (referred)
+     to by v the result points (refers) to that C object.
+
+     In this case, HINT >= 0 or -3.  */
+  if (hint >= 0 || hint == -3)
+    {
+      /* Look for a component with type TYPE.  */
+      tree t = get_component_with_type (obj, type, mdtype);
+      /* If not accessible, give an error.  */
+      if (t == error_mark_node)
+	{
+	  if (reference_p)
+	    {
+	      if (!ctx->quiet)
+		{
+		  error_at (loc, "reference %<dynamic_cast%> failed");
+		  inform (loc, "static type %qT of its operand is a "
+			  "non-public base class of dynamic type %qT",
+			  objtype, type);
+
+		}
+	      *non_constant_p = true;
+	    }
+	  return integer_zero_node;
+	}
+      else if (t)
+	/* The result points to the TYPE object.  */
+	return cp_build_addr_expr (t, complain);
+      /* Else, TYPE was not found, because the HINT turned out to be wrong.
+	 Fall through to the normal processing.  */
+    }
+
+  /* Otherwise, if v points (refers) to a public base class subobject of the
+     most derived object, and the type of the most derived object has a base
+     class, of type C, that is unambiguous and public, the result points
+     (refers) to the C subobject of the most derived object.
+
+     But it can also be an invalid case.  */
+      
+  /* Get the most derived object.  */
+  obj = get_component_with_type (obj, mdtype, NULL_TREE);
+  if (obj == error_mark_node)
+    {
+      if (reference_p)
+	{
+	  if (!ctx->quiet)
+	    {
+	      error_at (loc, "reference %<dynamic_cast%> failed");
+	      inform (loc, "static type %qT of its operand is a non-public"
+		      " base class of dynamic type %qT", objtype, mdtype);
+	    }
+	  *non_constant_p = true;
+	}
+      return integer_zero_node;
+    }
+  else
+    gcc_assert (obj);
+
+  /* Check that the type of the most derived object has a base class
+     of type TYPE that is unambiguous and public.  */
+  base_kind b_kind;
+  tree binfo = lookup_base (mdtype, type, ba_check, &b_kind, tf_none);
+  if (!binfo || binfo == error_mark_node)
+    {
+      if (reference_p)
+	{
+	  if (!ctx->quiet)
+	    {
+	      error_at (loc, "reference %<dynamic_cast%> failed");
+	      if (b_kind == bk_ambig)
+		inform (loc, "%qT is an ambiguous base class of dynamic "
+			"type %qT of its operand", type, mdtype);
+	      else
+		inform (loc, "dynamic type %qT of its operand does not "
+			"have an unambiguous public base class %qT",
+			mdtype, type);
+	    }
+	  *non_constant_p = true;
+	}
+      return integer_zero_node;
+    }
+  /* If so, return the TYPE subobject of the most derived object.  */
+  obj = convert_to_base_statically (obj, binfo);
+  return cp_build_addr_expr (obj, complain);
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Evaluate the call expression tree T in the context of OLD_CALL expression
    evaluation.  */
@@ -1854,6 +2120,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  gcc_assert (arg1);
 	  return arg1;
 	}
+      else if (cxx_dynamic_cast_fn_p (fun))
+	return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p);
+
       if (!ctx->quiet)
 	{
 	  if (!lambda_static_thunk_p (fun))
@@ -6729,7 +6998,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
 		    && (!cxx_placement_new_fn (fun)
 			|| TREE_CODE (t) != CALL_EXPR
 			|| current_function_decl == NULL_TREE
-			|| !is_std_construct_at (current_function_decl)))
+			|| !is_std_construct_at (current_function_decl))
+		    && !cxx_dynamic_cast_fn_p (fun))
 		  {
 		    if (flags & tf_error)
 		      {
diff --git gcc/cp/rtti.c gcc/cp/rtti.c
index 1b6b87ba8d6..9a242dc64e4 100644
--- gcc/cp/rtti.c
+++ gcc/cp/rtti.c
@@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
 	      dynamic_cast_node = dcast_fn;
 	    }
 	  result = build_cxx_call (dcast_fn, 4, elems, complain);
+	  SET_EXPR_LOCATION (result, loc);
 
 	  if (tc == REFERENCE_TYPE)
 	    {
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
new file mode 100644
index 00000000000..e8ba63d9609
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic1.C
@@ -0,0 +1,40 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast.
+
+struct B {
+  virtual void baz () {}
+};
+ 
+struct D : B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  B b;
+  B *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+    
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B b;
+constexpr B *b1 = const_cast<B*>(&b);
+constexpr B *b2 = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b2) == &d);
+static_assert(&dynamic_cast<D&>(*b2) == &d);
+static_assert(dynamic_cast<const B*>(&d) == &d);
+static_assert(&dynamic_cast<const B&>(d) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
new file mode 100644
index 00000000000..c226292a07d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic10.C
@@ -0,0 +1,12 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Virtual base.
+
+struct C { virtual void a(); };
+struct B { virtual void b(); };
+struct A : virtual B, C { virtual void c(); }; // { dg-error ".struct A. has virtual base classes" }
+
+constexpr A a; // { dg-error "call" }
+
+constexpr bool b1 = (dynamic_cast<C&>((B&)a), false);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
new file mode 100644
index 00000000000..6069fbfd01c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
@@ -0,0 +1,35 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a constructor.
+// [class.cdtor]#6: "If the operand of the dynamic_cast refers to the object
+// under construction or destruction and the static type of the operand is not
+// a pointer to or object of the constructor or destructor's own class or one
+// of its bases, the dynamic_cast results in undefined behavior.
+
+struct V {
+  virtual void f();
+};
+
+struct A : V { };
+
+struct B : V {
+  constexpr B(V*, A*);
+};
+
+struct D : A, B {
+  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  // well-defined: v of type V*, V base of B results in B*
+  B* b = dynamic_cast<B*>(v);
+  if (b != nullptr)
+    __builtin_abort ();
+
+  B& br = dynamic_cast<B&>(*v); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .B." "" { target *-*-* } .-1 }
+}
+
+constexpr D d; // { dg-message "in 'constexpr' expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
new file mode 100644
index 00000000000..0ce9beb8d72
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic12.C
@@ -0,0 +1,28 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a destructor.
+
+struct A2 { virtual void a2(); };
+
+struct A : A2 { virtual void a(); };
+
+struct C2 { virtual void c2(); };
+
+struct B : A, C2 {
+  constexpr ~B();
+};
+
+constexpr B::~B()
+{
+  A *a = dynamic_cast<A*>((C2*)this);
+  if (a != (A*) this)
+    __builtin_abort ();
+  A& ar = dynamic_cast<A&>((C2&)*this);
+  if (&ar != &(A&)*this)
+    __builtin_abort ();
+}
+
+struct D : B { virtual void d(); };
+
+constexpr D d;
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
new file mode 100644
index 00000000000..203067a2581
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic13.C
@@ -0,0 +1,86 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Adopted from g++.old-deja/g++.other/dyncast1.C.
+
+// 1. downcast
+// 1.1. single inheritance case
+
+struct A { virtual void a(); };
+struct AA : A {};
+struct B : A {};
+struct BB : B {};
+class C : B {};
+struct D : C {};
+
+struct CC : B {};
+class DD : CC {};
+
+class CCC : protected B {};
+class DDD : protected CCC {};
+
+constexpr D d;
+static_assert (dynamic_cast<D*> ((A*)&d) == nullptr);
+static_assert (dynamic_cast<D*> ((B*)&d) == nullptr);
+static_assert (&d == dynamic_cast<D*> ((C*)&d));
+static_assert (dynamic_cast<C*> ((B*)&d) == nullptr);
+
+constexpr DD dd;
+static_assert (dynamic_cast<DD*> ((A*)&dd) == nullptr);
+static_assert (dynamic_cast<DD*> ((B*)&dd) == nullptr);
+
+constexpr DDD ddd;
+static_assert (dynamic_cast<DDD*> ((A*)&ddd) == nullptr);
+static_assert (dynamic_cast<DDD*> ((B*)&ddd) == nullptr);
+static_assert (dynamic_cast<CCC*> ((B*)&ddd) == nullptr);
+
+// 1.2. multiple inheritance case
+// 1.2.1. all bases are public
+ 
+struct E : D, CC {};
+struct EE : CC, D {}; //Will search in reverse order.
+
+constexpr E e;
+static_assert (dynamic_cast<E*> ((A*)(D*)&e) == nullptr);
+static_assert (dynamic_cast<E*> ((B*)(D*)&e) == nullptr);
+static_assert (&e == dynamic_cast<E*> ((C*)(D*)&e));
+static_assert (&e == dynamic_cast<E*> ((B*)(CC*)&e));
+static_assert ((CC*)&e == dynamic_cast<CC*> ((B*)(CC*)&e));
+
+constexpr EE ee;
+static_assert (dynamic_cast<EE*> ((A*)(D*)&ee) == nullptr);
+static_assert (dynamic_cast<EE*> ((B*)(D*)&ee) == nullptr);
+static_assert (&ee == dynamic_cast<EE*> ((C*)(D*)&ee));
+static_assert (&ee == dynamic_cast<EE*> ((B*)(CC*)&ee));
+static_assert ((CC*)&ee == dynamic_cast<CC*> ((B*)(CC*)&ee));
+
+// 1.2.2 one or more branches are nonpublic
+
+struct X : private BB, E {};
+struct Y : AA, private B {};
+
+class XX : BB, E {};
+
+constexpr X x;
+static_assert (&x == dynamic_cast<X*>((B*)(CC*)(E*)&x));
+
+constexpr XX xx;
+static_assert (dynamic_cast<XX*>((B*)(CC*)(E*)&xx) == nullptr);	
+
+constexpr Y y;
+static_assert (dynamic_cast<Y*>((B*)&y) == nullptr);
+static_assert (dynamic_cast<Y*>((A*)(B*)&y) == nullptr);
+
+// 2. crosscast
+
+struct J { virtual void j(); };
+struct K : CC, private J {}; 
+class KK : J, CC{};
+		
+static_assert (dynamic_cast<CC*> ((B*)(D*)&e) == nullptr);
+static_assert ((CC*)&e == dynamic_cast<CC*> ((C*)(D*)&e));
+
+constexpr K k;
+static_assert (dynamic_cast<J*> ((B*)&k) == nullptr);
+constexpr KK kk;
+static_assert (dynamic_cast<J*> ((CC*)&kk) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
new file mode 100644
index 00000000000..f739c6df94b
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic14.C
@@ -0,0 +1,105 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Adopted from g++.old-deja/g++.other/dyncast1.C.
+// But use reference dynamic_cast.
+
+// 1. downcast
+// 1.1. single inheritance case
+
+struct A { virtual void a(); };
+struct AA : A {};
+struct B : A {};
+struct BB : B {};
+class C : B {};
+struct D : C {};
+
+struct CC : B {};
+class DD : CC {};
+
+class CCC : protected B {};
+class DDD : protected CCC {};
+
+constexpr D d;
+constexpr bool b01 = (dynamic_cast<D&> ((A&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+constexpr bool b02 = (dynamic_cast<D&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+static_assert (&d == &dynamic_cast<const D&> ((C&)d));
+constexpr bool b03 = (dynamic_cast<C&> ((B&)d), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .D." "" { target *-*-* } .-1 }
+
+constexpr DD dd;
+constexpr bool b04 = (dynamic_cast<DD&> ((A&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
+constexpr bool b05 = (dynamic_cast<DD&> ((B&)dd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DD." "" { target *-*-* } .-1 }
+
+constexpr DDD ddd;
+constexpr bool b06 = (dynamic_cast<DDD&> ((A&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const A. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b07 = (dynamic_cast<DDD&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+constexpr bool b08 = (dynamic_cast<CCC&> ((B&)ddd), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .DDD." "" { target *-*-* } .-1 }
+
+// 1.2. multiple inheritance case
+// 1.2.1. all bases are public
+ 
+struct E : D, CC {};
+struct EE : CC, D {}; //Will search in reverse order.
+
+constexpr E e;
+constexpr bool b09 = (dynamic_cast<E&> ((A&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
+constexpr bool b10 = (dynamic_cast<E&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .E." "" { target *-*-* } .-1 }
+static_assert (&e == &dynamic_cast<E&> ((C&)(D&)e));
+static_assert (&e == &dynamic_cast<E&> ((B&)(CC&)e));
+static_assert (&(CC&)e == &dynamic_cast<CC&> ((B&)(CC&)e));
+
+constexpr EE ee;
+constexpr bool b11 = (dynamic_cast<EE&> ((A&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
+constexpr bool b12 = (dynamic_cast<EE&> ((B&)(D&)ee), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .EE." "" { target *-*-* } .-1 }
+static_assert (&ee == &dynamic_cast<EE&> ((C&)(D&)ee));
+static_assert (&ee == &dynamic_cast<EE&> ((B&)(CC&)ee));
+static_assert (&(CC&)ee == &dynamic_cast<CC&> ((B&)(CC&)ee));
+
+// 1.2.2 one or more branches are nonpublic
+
+struct X : private BB, E {};
+struct Y : AA, private B {};
+
+class XX : BB, E {};
+
+constexpr X x;
+static_assert (&x == &dynamic_cast<X&>((B&)(CC&)(E&)x));
+
+constexpr XX xx;
+constexpr bool b13 = (dynamic_cast<XX&>((B&)(CC&)(E&)xx), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .XX." "" { target *-*-* } .-1 }
+
+constexpr Y y;
+constexpr bool b14 = (dynamic_cast<Y&>((B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const B. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
+constexpr bool b15 = (dynamic_cast<Y&>((A&)(B&)y), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .A. of its operand is a non-public base class of dynamic type .Y." "" { target *-*-* } .-1 }
+
+// 2. crosscast
+
+struct J { virtual void j(); };
+struct K : CC, private J {}; 
+class KK : J, CC{};
+		
+constexpr bool b16 = (dynamic_cast<CC&> ((B&)(D&)e), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .B. of its operand is a non-public base class of dynamic type .CC." "" { target *-*-* } .-1 }
+static_assert (&(CC&)e == &dynamic_cast<CC&> ((C&)(D&)e));
+
+constexpr K k;
+constexpr bool b17 = (dynamic_cast<J&> ((B&)k), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .K. of its operand does not have an unambiguous public base class .J." "" { target *-*-* } .-1 }
+constexpr KK kk;
+constexpr bool b18 = (dynamic_cast<J&> ((CC&)kk), true); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const CC. of its operand is a non-public base class of dynamic type .KK." "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C
new file mode 100644
index 00000000000..fcf507289c4
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic15.C
@@ -0,0 +1,14 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Test HINT = -3 (SRC is a multiple public non-virtual base of DST).
+
+struct A { virtual void a() {} };
+struct C : A { };
+struct D : A { };
+struct B : C, D { };
+
+constexpr B b;
+static_assert (&dynamic_cast<B&>((A&)(C&)b) == &b);
+static_assert (&dynamic_cast<B&>((A&)(D&)b) == &b);
+static_assert (dynamic_cast<B*>((A*)(C*)&b) == &b);
+static_assert (dynamic_cast<B*>((A*)(D*)&b) == &b);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C
new file mode 100644
index 00000000000..f0394d130a3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic16.C
@@ -0,0 +1,20 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+// Here the hint turns out to be wrong: A is a public base of B2, but the
+// dynamic_cast operand is not that subobject, but rather a sibling base of
+// B2.
+
+struct A { virtual void f(); };
+struct B1: A { };
+struct B2: A { };
+struct C: B1, B2 { };
+
+constexpr C c;
+constexpr A *ap = (B1*)&c;
+constexpr A &ar = (B1&)c;
+constexpr auto p = dynamic_cast<B2*>(ap);
+static_assert (p != nullptr);
+constexpr auto p2 = dynamic_cast<B2&>(ar);
+static_assert(dynamic_cast<B2*>(ap) == (B2*)&c);
+static_assert(dynamic_cast<B2*>((B1*)&c) == (B2*)&c);
+static_assert(&dynamic_cast<B2&>((B1&)c) == &(B2&)c);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
new file mode 100644
index 00000000000..391d7998556
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
@@ -0,0 +1,30 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// dynamic_cast in a constructor.
+// [class.cdtor]#6: "If the operand of the dynamic_cast refers to the object
+// under construction or destruction and the static type of the operand is not
+// a pointer to or object of the constructor or destructor's own class or one
+// of its bases, the dynamic_cast results in undefined behavior.
+
+struct V {
+  virtual void f();
+};
+
+struct A : V { };
+
+struct B : V {
+  constexpr B(V*, A*);
+};
+
+struct D : A, B {
+  constexpr D() : B((A*)this, this) { } // { dg-message "in 'constexpr' expansion of" }
+};
+
+constexpr B::B(V* v, A* a)
+{
+  // undefined behavior, a has type A*, A not a base of B
+  dynamic_cast<B*>(a); // { dg-error "static type .A. of .dynamic_cast. not a base of .B." }
+}
+
+constexpr D d; // { dg-message "in 'constexpr' expansion of" }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
new file mode 100644
index 00000000000..aae03f691ca
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic2.C
@@ -0,0 +1,41 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Downcast, with hint > 0.
+
+struct B {
+  virtual void baz () {}
+};
+
+struct B2 {
+  virtual void baz2 () {}
+};
+ 
+struct D : B, B2 { };
+
+constexpr bool
+fn ()
+{
+  // try &/&&, add address test
+  bool ok = true;
+  B2 b;
+  B2 *b1 = &b;
+  if (D *pd = dynamic_cast<D*>(b1))
+    ok = false;
+
+  D d;
+  B2 *b2 = &d;
+  if (D *pd = dynamic_cast<D*>(b2))
+    /*OK*/;
+  else
+   ok = false;
+    
+  return ok;
+}
+
+static_assert(fn ());
+
+constexpr D d;
+constexpr B2 *b = const_cast<D*>(&d);
+static_assert(dynamic_cast<D*>(b) == &d);
+static_assert(&dynamic_cast<D&>(*b) == &d);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
new file mode 100644
index 00000000000..c3e09808e32
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic3.C
@@ -0,0 +1,33 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Sidecast.
+
+struct A {
+  virtual void afn () {}
+};
+ 
+struct B {
+  virtual void bfn () {}
+};
+
+struct D : A, B { };
+
+constexpr bool
+fn ()
+{
+  bool ok = true;
+  D d;
+  A *a = &d;
+  if (B *bp = dynamic_cast<B*>(a))
+    /*OK*/;
+  else
+    ok = false;
+
+  A &ar = d;
+  B &br = dynamic_cast<B&>(ar);
+
+  return ok;
+}
+
+static_assert(fn ());
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
new file mode 100644
index 00000000000..3adc524379d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic4.C
@@ -0,0 +1,55 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// From clang's constant-expression-cxx2a.cpp.
+
+struct A2 { virtual void a2(); };
+struct A : A2 { virtual void a(); };
+struct B : A {}; 
+struct C2 { virtual void c2(); };
+struct C : A, C2 { A *c = dynamic_cast<A*>(static_cast<C2*>(this)); };
+struct D { virtual void d(); };
+struct E { virtual void e(); };
+struct F : B, C, D, private E { void *f = dynamic_cast<void*>(static_cast<D*>(this)); };
+struct Padding { virtual void padding(); };
+struct G : Padding, F {}; 
+
+constexpr G g;
+
+// During construction of C, A is unambiguous subobject of dynamic type C.
+static_assert(g.c == (C*)&g);
+// ... but in the complete object, the same is not true, so the runtime fails.
+static_assert(dynamic_cast<const A*>(static_cast<const C2*>(&g)) == nullptr);
+
+// dynamic_cast<void*> produces a pointer to the object of the dynamic type.
+static_assert(g.f == (void*)(F*)&g);
+static_assert(dynamic_cast<const void*>(static_cast<const D*>(&g)) == &g);
+
+constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Can navigate from A2 to its A...
+static_assert(&dynamic_cast<A&>((A2&)(B&)g) == &(A&)(B&)g);
+// ... and from B to its A ...
+static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
+// ... but not from D.
+static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g); // { dg-error "non-constant condition for static assertion|reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Can cast from A2 to sibling class D.
+static_assert(&dynamic_cast<D&>((A2&)(B&)g) == &(D&)g);
+
+// Cannot cast from private base E to derived class F.
+constexpr int e_f = (dynamic_cast<F&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
+
+// Cannot cast from B to private sibling E.
+constexpr int b_e = (dynamic_cast<E&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .E." "" { target *-*-* } .-1 }
+
+struct Unrelated { virtual void unrelated(); };
+
+constexpr int b_unrelated = (dynamic_cast<Unrelated&>((B&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .G. of its operand does not have an unambiguous public base class .Unrelated." "" { target *-*-* } .-1 }
+constexpr int e_unrelated = (dynamic_cast<Unrelated&>((E&)g), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const E. of its operand is a non-public base class of dynamic type .G." "" { target *-*-* } .-1 }
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
new file mode 100644
index 00000000000..743b3018d2f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic5.C
@@ -0,0 +1,22 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Multiple levels.
+
+struct A { virtual void a(); };
+struct B : A { virtual void b(); };
+struct C : B { virtual void c(); };
+struct D : C { virtual void d(); };
+struct E : D { virtual void e(); };
+struct F : E { virtual void f(); };
+
+constexpr F f;
+
+// F->C->A->B == F->C->B
+static_assert (&dynamic_cast<B&>((A&)(C&)f) == &(B&)(C&)f);
+// F->A->E == F->E
+static_assert (&dynamic_cast<E&>((A&)f) == &(E&)f);
+// F->E->D->C->B->A->C == F->C
+static_assert (&dynamic_cast<C&>((A&)(B&)(C&)(D&)(E&)f) == &(C&)f);
+// F->B->F == F
+static_assert (&dynamic_cast<F&>((B&)f) == &f);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
new file mode 100644
index 00000000000..23434734e26
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic6.C
@@ -0,0 +1,25 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Private base.
+
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct B : private P1 { virtual void b(); };
+struct C { virtual void c(); };
+struct A : B, C, private P2 { virtual void a(); };
+
+constexpr A a;
+
+// P1 is a non-public base of A.
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+// Don't error here.
+static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
+
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
new file mode 100644
index 00000000000..d71497aae6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic7.C
@@ -0,0 +1,25 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Protected base.
+
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct B : protected P1 { virtual void b(); };
+struct C { virtual void c(); };
+struct A : B, C, protected P2 { virtual void a(); };
+
+constexpr A a;
+
+// P1 is a non-public base of A.
+constexpr bool b1 = (dynamic_cast<B&>((P1&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+// Don't error here.
+static_assert (dynamic_cast<B*>((P1*)&a) == nullptr);
+
+constexpr bool b2 = (dynamic_cast<C&>((P2&)a), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<C*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<C*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
new file mode 100644
index 00000000000..8056f30bb99
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic8.C
@@ -0,0 +1,24 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Unrelated type.
+
+struct B { virtual void b(); };
+struct P1 { virtual void p1(); };
+struct P2 { virtual void p2(); };
+struct A : public B, private P1, protected P2 { virtual void a(); };
+
+constexpr A a;
+
+struct U { virtual void u(); };
+
+constexpr bool b1 = (dynamic_cast<U&>((B&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "dynamic type .A. of its operand does not have an unambiguous public base class .U." "" { target *-*-* } .-1 }
+constexpr bool b2 = (dynamic_cast<U&>((P1&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P1. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+constexpr bool b3 = (dynamic_cast<U&>((P2&)a), 0); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message "static type .const P2. of its operand is a non-public base class of dynamic type .A." "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<U*>((B*)&a) == nullptr);
+static_assert (dynamic_cast<U*>((P1*)&a) == nullptr);
+static_assert (dynamic_cast<U*>((P2*)&a) == nullptr);
diff --git gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
new file mode 100644
index 00000000000..d8cbb2f2f0d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic9.C
@@ -0,0 +1,17 @@
+// PR c++/88337 - Implement P1327R1: Allow dynamic_cast/typeid in constexpr.
+// { dg-do compile { target c++2a } }
+
+// Ambiguous base.
+
+struct A { virtual void a(); };
+struct B : A { virtual void b(); };
+struct C : A { virtual void c(); };
+struct D { virtual void a(); };
+struct E : B, C, D { virtual void d(); };
+
+constexpr E e;
+
+constexpr bool b1 = (dynamic_cast<A&>((D&)e), false); // { dg-error "reference .dynamic_cast. failed" }
+// { dg-message ".A. is an ambiguous base class of dynamic type .E. of its operand" "" { target *-*-* } .-1 }
+
+static_assert (dynamic_cast<A*>((D*)&e) == nullptr);
Jason Merrill Dec. 21, 2019, 9:50 p.m. UTC | #10
On 12/17/19 5:34 PM, Marek Polacek wrote:
> On Mon, Dec 16, 2019 at 04:00:14PM -0500, Jason Merrill wrote:
>> On 12/16/19 3:55 PM, Jason Merrill wrote:
>>> On 12/14/19 4:25 PM, Marek Polacek wrote:
>>>> On Fri, Dec 13, 2019 at 05:56:57PM -0500, Jason Merrill wrote:
>>>>> On 12/13/19 3:20 PM, Marek Polacek wrote:
>>>>>> +  /* Given dynamic_cast<T>(v),
>>>>>> +
>>>>>> +     [expr.dynamic.cast] If C is the class type to which T
>>>>>> points or refers,
>>>>>> +     the runtime check logically executes as follows:
>>>>>> +
>>>>>> +     If, in the most derived object pointed (referred) to
>>>>>> by v, v points
>>>>>> +     (refers) to a public base class subobject of a C
>>>>>> object, and if only
>>>>>> +     one object of type C is derived from the subobject
>>>>>> pointed (referred)
>>>>>> +     to by v the result points (refers) to that C object.
>>>>>> +
>>>>>> +     In this case, HINT >= 0.  */
>>>>>> +  if (hint >= 0)
>>>>>
>>>>> Won't this code work for hint == -3 as well?
>>>>
>>>> Yes, it does.  In fact, none of the tests was testing the hint == -3
>>>> case, so
>>>> I've fixed the code up and added constexpr-dynamic15.C to test it.
>>>>
>>>>>> +    {
>>>>>> +      /* Look for a component with type TYPE.  */
>>>>>> +      obj = get_component_with_type (obj, type);
>>>>>
>>>>> You don't seem to use mdtype at all in this case.  Shouldn't
>>>>> get_component_with_type stop at mdtype if it hasn't found type yet?
>>>>
>>>> It was used for diagnostics but not in get_component_with_type.  It makes
>>>> sense to stop at MDTYPE; I've adjusted the code to do so.  E.g., if
>>>> we have OBJ in the form of g.D.2121.D.2122.D.2123.D.2124, usually the
>>>> component with the most derived type is "g", but in a 'tor, it can be
>>>> a different component too.
>>>>
>>>>>> +      /* If not found or not accessible, give an error.  */
>>>>>> +      if (obj == NULL_TREE || obj == error_mark_node)
>>>>>> +    {
>>>>>> +      if (reference_p)
>>>>>> +        {
>>>>>> +          if (!ctx->quiet)
>>>>>> +        {
>>>>>> +          error_at (loc, "reference %<dynamic_cast%> failed");
>>>>>> +          if (obj == NULL_TREE)
>>>>>> +            inform (loc, "dynamic type %qT of its operand does not "
>>>>>> +                "have an unambiguous public base class %qT",
>>>>>> +                mdtype, type);
>>>>>> +          else
>>>>>> +            inform (loc, "static type %qT of its operand is a "
>>>>>> +                "non-public base class of dynamic type %qT",
>>>>>> +                objtype, type);
>>>>>> +
>>>>>> +        }
>>>>>> +          *non_constant_p = true;
>>>>>> +        }
>>>>>> +      return integer_zero_node;
>>>>>> +    }
>>>>>> +      else
>>>>>> +    /* The result points to the TYPE object.  */
>>>>>> +    return cp_build_addr_expr (obj, complain);
>>>>>> +    }
>>>>>> +  /* Otherwise, if v points (refers) to a public base class
>>>>>> subobject of the
>>>>>> +     most derived object, and the type of the most derived
>>>>>> object has a base
>>>>>> +     class, of type C, that is unambiguous and public, the
>>>>>> result points
>>>>>> +     (refers) to the C subobject of the most derived object.
>>>>>> +
>>>>>> +     But it can also be an invalid case.  */
>>>>>
>>>>> And I think we need to fall through to this code if the hint
>>>>> turns out to be
>>>>> wrong, i.e. V is a public base of C, but v is not that
>>>>> subobject, but rather
>>>>> a sibling base of C, like
>>>>
>>>> True.  HINT is really just an optimization hint, nothing more.  I've
>>>> adjusted
>>>> the code to fall through to the normal processing if the HINT >= 0
>>>> or -3 case
>>>> doesn't succeed.
>>>>
>>>>> struct A { virtual void f(); };
>>>>> struct B1: A { };
>>>>> struct B2: A { };
>>>>> struct C: B1, B2 { };
>>>>> int main()
>>>>> {
>>>>>     C c;
>>>>>     A* ap = (B1*)c;
>>>>>     constexpr auto p = dynamic_cast<B2*>(ap); // should succeed
>>>>> }
>>>>
>>>> Whew, there's always One More Case. :/  New constexpr-dynamic16.c
>>>> covers it.
>>>>
>>>>>> --- /dev/null
>>>>>> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C
>>>>>> @@ -0,0 +1,34 @@
>>>>>> +// PR c++/88337 - Implement P1327R1: Allow
>>>>>> dynamic_cast/typeid in constexpr.
>>>>>> +// { dg-do compile { target c++2a } }
>>>>>> +
>>>>>> +// dynamic_cast in a constructor.
>>>>>> +
>>>>>> +struct V {
>>>>>> +  virtual void f();
>>>>>> +};
>>>>>> +
>>>>>> +struct A : V { };
>>>>>> +
>>>>>> +struct B : V {
>>>>>> +  constexpr B(V*, A*);
>>>>>> +};
>>>>>> +
>>>>>> +struct D : A, B {
>>>>>> +  constexpr D() : B((A*)this, this) { } // { dg-message "in
>>>>>> 'constexpr' expansion of" }
>>>>>> +};
>>>>>> +
>>>>>> +constexpr B::B(V* v, A* a)
>>>>>> +{
>>>>>> +  // well-defined: v of type V*, V base of B results in B*
>>>>>> +  B* b = dynamic_cast<B*>(v);
>>>>>> +  if (b != nullptr)
>>>>>> +    __builtin_abort ();
>>>>>> +
>>>>>> +  B& br = dynamic_cast<B&>(*v); // { dg-error "reference
>>>>>> .dynamic_cast. failed" }
>>>>>> +// { dg-message "dynamic type .A. of its operand does not
>>>>>> have an unambiguous public base class .B." "" { target *-*-*
>>>>>> } .-1 }
>>>>>> +
>>>>>> +  // FIXME: UB in constexpr should be detected.
>>>>>> +  dynamic_cast<B*>(a);          // undefined behavior, a
>>>>>> has type A*, A not a base of B
>>>>>
>>>>> What undefined behavior?  Seems to me the dynamic_cast should
>>>>> just fail and
>>>>> return a null pointer.
>>>>
>>>> This is <http://eel.is/c++draft/class.cdtor#6.sentence-3>:
>>>> "If the operand of the dynamic_cast refers to the object under
>>>> construction
>>>> or destruction and the static type of the operand is not a pointer to or
>>>> object of the constructor or destructor's own class or one of its
>>>> bases, the
>>>> dynamic_cast results in undefined behavior."
>>>>
>>>> Clang++ doesn't detect this either.
>>>
>>> Ah, interesting.  That text goes back to C++98.  I guess the reason for
>>> that was that the vtable pointer might give problematic answers in that
>>> situation.  It should be straightforward to detect this in constexpr
>>> evaluation; if mdtype is not the actual complete object, we can see if
>>> *this is also part of mdtype (success), or otherwise the complete object
>>> (undefined).
>>
>>> It looks like get_component_with_type (<*this>, type, mdtype) should work well for that check.
>>
>> Well, the second argument is wrong, we'll need to find the complete object
>> type for it.
> 
> In both cases (dynamic_cast<B*>(v); and dynamic_cast<B*>(a);) *this is
> "d.D.2126" (types D, B) and mdtype is A, > so the above get_component_with_type won't work as far as I can see.

> I've come up with this instead, which works
> for the testcases I have, but is probably not entirely correct in general,
> either :(.
> 
> It's the
>    if (!same_type_ignoring_top_level_qualifiers_p (mdtype, complete_type))
>     ...
> block, the UB is detected as demonstrated in constexpr-dynamic17.C.
> 
> To find *this, I went looking in call_stack.  What do you think about this?
> 
> Bootstrapped/regtested on x86_64-linux.
> 
> 2019-12-17  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr.
> 	* constexpr.c (initialized_type): Forward declare.
> 	(cxx_dynamic_cast_fn_p): New function.
> 	(extract_obj_from_addr_offset): New function.
> 	(get_component_with_type): New function.
> 	(cxx_eval_dynamic_cast_fn): New function.
> 	(cxx_eval_call_expression): Call cxx_eval_dynamic_cast_fn for a call
> 	to __dynamic_cast.
> 	(potential_constant_expression_1): Don't give up on
> 	cxx_dynamic_cast_fn_p.
> 	* rtti.c (build_dynamic_cast_1): When creating a call to
> 	__dynamic_cast, use the location of the original expression.
> 
> 	* g++.dg/cpp2a/constexpr-dynamic1.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic10.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic11.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic12.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic13.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic14.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic15.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic16.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic17.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic2.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic3.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic4.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic5.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic6.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic7.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic8.C: New test.
> 	* g++.dg/cpp2a/constexpr-dynamic9.C: New test.
> 
> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
> index f3f03e7d621..4b3751a17ca 100644
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -1070,6 +1070,7 @@ struct constexpr_ctx {
>   
>   static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
>   
> +static tree initialized_type (tree);
>   static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
>   					  bool, bool *, bool *, tree * = NULL);
>   
> @@ -1689,6 +1690,271 @@ is_std_allocator_allocate (tree fndecl)
>     return decl_in_std_namespace_p (decl);
>   }
>   
> +/* Return true if FNDECL is __dynamic_cast.  */
> +
> +static inline bool
> +cxx_dynamic_cast_fn_p (tree fndecl)
> +{
> +  return (cxx_dialect >= cxx2a
> +	  && id_equal (DECL_NAME (fndecl), "__dynamic_cast")
> +	  && CP_DECL_CONTEXT (fndecl) == global_namespace);
> +}
> +
> +/* Often, we have an expression in the form of address + offset, e.g.
> +   "&_ZTV1A + 16".  Extract the object from it, i.e. "_ZTV1A".  */
> +
> +static tree
> +extract_obj_from_addr_offset (tree expr)
> +{
> +  if (TREE_CODE (expr) == POINTER_PLUS_EXPR)
> +    expr = TREE_OPERAND (expr, 0);
> +  STRIP_NOPS (expr);
> +  if (TREE_CODE (expr) == ADDR_EXPR)
> +    expr = TREE_OPERAND (expr, 0);
> +  return expr;
> +}
> +
> +/* Given a PATH like
> +
> +     g.D.2181.D.2154.D.2102.D.2093
> +
> +   find a component with type TYPE.  Return NULL_TREE if not found, and
> +   error_mark_node if the component is not accessible.  If STOP is non-null,
> +   this function will return NULL_TREE if STOP is found before TYPE.  */
> +
> +static tree
> +get_component_with_type (tree path, tree type, tree stop)
> +{
> +  while (true)
> +    {
> +      if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), type))
> +	/* Found it.  */
> +	return path;
> +      else if (stop
> +	       && (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path),
> +							      stop)))
> +	return NULL_TREE;
> +      else if (TREE_CODE (path) == COMPONENT_REF
> +	       && DECL_FIELD_IS_BASE (TREE_OPERAND (path, 1)))
> +	{
> +	  /* We need to check that the component we're accessing is in fact
> +	     accessible.  */
> +	  if (TREE_PRIVATE (TREE_OPERAND (path, 1))
> +	      || TREE_PROTECTED (TREE_OPERAND (path, 1)))
> +	    return error_mark_node;
> +	  path = TREE_OPERAND (path, 0);
> +	}
> +      else
> +	return NULL_TREE;
> +    }
> +}
> +
> +/* Evaluate a call to __dynamic_cast (permitted by P1327R1).
> +
> +   The declaration of __dynamic_cast is:
> +
> +   void* __dynamic_cast (const void* __src_ptr,
> +			 const __class_type_info* __src_type,
> +			 const __class_type_info* __dst_type,
> +			 ptrdiff_t __src2dst);
> +
> +   where src2dst has the following possible values
> +
> +   >-1: src_type is a unique public non-virtual base of dst_type
> +	dst_ptr + src2dst == src_ptr
> +   -1: unspecified relationship
> +   -2: src_type is not a public base of dst_type
> +   -3: src_type is a multiple public non-virtual base of dst_type
> +
> +  Since literal types can't have virtual bases, we only expect hint >=0,
> +  -2, or -3.  */
> +
> +static tree
> +cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
> +			  bool *non_constant_p, bool *overflow_p)
> +{
> +  /* T will be something like
> +      __dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8)
> +     dismantle it.  */
> +  gcc_assert (call_expr_nargs (call) == 4);
> +  tsubst_flags_t complain = ctx->quiet ? tf_none : tf_warning_or_error;
> +  tree obj = CALL_EXPR_ARG (call, 0);
> +  tree type = CALL_EXPR_ARG (call, 2);
> +  HOST_WIDE_INT hint = int_cst_value (CALL_EXPR_ARG (call, 3));
> +  location_t loc = cp_expr_loc_or_input_loc (call);
> +
> +  /* Get the target type of the dynamic_cast.  */
> +  gcc_assert (TREE_CODE (type) == ADDR_EXPR);
> +  type = TREE_OPERAND (type, 0);
> +  type = TREE_TYPE (DECL_NAME (type));
> +
> +  /* TYPE can only be either T* or T&.  We can't know which of these it
> +     is by looking at TYPE, but OBJ will be "(T*) x" in the first case,
> +     and something like "(T*)(T&)(T*) x" in the second case.  */
> +  bool reference_p = false;
> +  while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR)
> +    {
> +      reference_p |= TYPE_REF_P (TREE_TYPE (obj));
> +      obj = TREE_OPERAND (obj, 0);
> +    }
> +
> +  /* Evaluate the object so that we know its dynamic type.  */
> +  obj = cxx_eval_constant_expression (ctx, obj, /*lval*/false, non_constant_p,
> +				      overflow_p);
> +  if (*non_constant_p)
> +    return call;
> +
> +  /* We expect OBJ to be in form of &d.D.2102 when HINT == 0,
> +     but when HINT is > 0, it can also be something like
> +     &d.D.2102 + 18446744073709551608, which includes the BINFO_OFFSET.  */
> +  obj = extract_obj_from_addr_offset (obj);
> +  const tree objtype = TREE_TYPE (obj);
> +  /* If OBJ doesn't refer to a base field, we're done.  */
> +  if (tree t = (TREE_CODE (obj) == COMPONENT_REF
> +		? TREE_OPERAND (obj, 1) : obj))
> +    if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t))
> +      return integer_zero_node;
> +
> +  /* [class.cdtor] When a dynamic_cast is used in a constructor ...
> +     or in a destructor ... if the operand of the dynamic_cast refers
> +     to the object under construction or destruction, this object is
> +     considered to be a most derived object that has the type of the
> +     constructor or destructor's class.  */
> +  tree vtable = build_vfield_ref (obj, TREE_TYPE (obj));
> +  vtable = cxx_eval_constant_expression (ctx, vtable, /*lval*/false,
> +					 non_constant_p, overflow_p);
> +  if (*non_constant_p)
> +    return call;
> +  /* VTABLE will be &_ZTV1A + 16 or similar, get _ZTV1A.  */
> +  vtable = extract_obj_from_addr_offset (vtable);
> +  const tree mdtype = DECL_CONTEXT (vtable);
> +
> +  tree t = obj;
> +  while (TREE_CODE (t) == COMPONENT_REF
> +	 && DECL_FIELD_IS_BASE (TREE_OPERAND (t, 1)))
> +    t = TREE_OPERAND (t, 0);
> +  const tree complete_type = TREE_TYPE (t);
> +
> +  /* [class.cdtor]/6 "If the operand of the dynamic_cast refers to
> +     the object under construction or destruction and the static type
> +     of the operand is not a pointer to or object of the constructor
> +     or destructor's own class or one of its bases, the dynamic_cast
> +     results in undefined behavior."  And undefined behavior should be
> +     detected in constexpr contexts.  */
> +  if (!same_type_ignoring_top_level_qualifiers_p (mdtype, complete_type))
> +    {
> +      unsigned ix;
> +      FOR_EACH_VEC_ELT_REVERSE (call_stack, ix, t)
> +	if (tree fn = cp_get_callee_fndecl_nofold (t))
> +	  if (DECL_CONSTRUCTOR_P (fn))
> +	    {
> +	      /* Get *this of the current constructor.  */
> +	      tree cdtor_type = initialized_type (t);
> +	      if (!DERIVED_FROM_P (objtype, cdtor_type))

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

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

Something like

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

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

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

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

Patch

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);