Commit 22edf943 by Marek Polacek Committed by Marek Polacek

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.

From-SVN: r279755
parent 6ec06754
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> 2019-12-26 Jakub Jelinek <jakub@redhat.com>
PR c++/92438 PR c++/92438
......
...@@ -1699,6 +1699,238 @@ is_std_allocator_allocate (tree fndecl) ...@@ -1699,6 +1699,238 @@ is_std_allocator_allocate (tree fndecl)
return decl_in_std_namespace_p (decl); 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. /* Subroutine of cxx_eval_constant_expression.
Evaluate the call expression tree T in the context of OLD_CALL expression Evaluate the call expression tree T in the context of OLD_CALL expression
evaluation. */ evaluation. */
...@@ -1864,6 +2096,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, ...@@ -1864,6 +2096,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
gcc_assert (arg1); gcc_assert (arg1);
return 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 (!ctx->quiet)
{ {
if (!lambda_static_thunk_p (fun)) if (!lambda_static_thunk_p (fun))
...@@ -6740,7 +6975,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, ...@@ -6740,7 +6975,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
&& (!cxx_placement_new_fn (fun) && (!cxx_placement_new_fn (fun)
|| TREE_CODE (t) != CALL_EXPR || TREE_CODE (t) != CALL_EXPR
|| current_function_decl == NULL_TREE || 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) if (flags & tf_error)
{ {
......
...@@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr, ...@@ -777,6 +777,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
dynamic_cast_node = dcast_fn; dynamic_cast_node = dcast_fn;
} }
result = build_cxx_call (dcast_fn, 4, elems, complain); result = build_cxx_call (dcast_fn, 4, elems, complain);
SET_EXPR_LOCATION (result, loc);
if (tc == REFERENCE_TYPE) if (tc == REFERENCE_TYPE)
{ {
......
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> 2019-12-29 Jakub Jelinek <jakub@redhat.com>
PR target/93078 PR target/93078
......
// 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);
// 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);
// 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" }
// 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;
// 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);
// 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 }
// 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);
// 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);
// 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 }
// 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);
// 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 ());
// 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 }
// 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);
// 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);
// 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);
// 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);
// 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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment