On Mon, Dec 16, 2019 at 04:00:14PM -0500, Jason Merrill wrote: > On 12/16/19 3:55 PM, Jason Merrill wrote: > > On 12/14/19 4:25 PM, Marek Polacek wrote: > > > On Fri, Dec 13, 2019 at 05:56:57PM -0500, Jason Merrill wrote: > > > > On 12/13/19 3:20 PM, Marek Polacek wrote: > > > > > + /* Given dynamic_cast<T>(v), > > > > > + > > > > > + [expr.dynamic.cast] If C is the class type to which T > > > > > points or refers, > > > > > + the runtime check logically executes as follows: > > > > > + > > > > > + If, in the most derived object pointed (referred) to > > > > > by v, v points > > > > > + (refers) to a public base class subobject of a C > > > > > object, and if only > > > > > + one object of type C is derived from the subobject > > > > > pointed (referred) > > > > > + to by v the result points (refers) to that C object. > > > > > + > > > > > + In this case, HINT >= 0. */ > > > > > + if (hint >= 0) > > > > > > > > Won't this code work for hint == -3 as well? > > > > > > Yes, it does. In fact, none of the tests was testing the hint == -3 > > > case, so > > > I've fixed the code up and added constexpr-dynamic15.C to test it. > > > > > > > > + { > > > > > + /* Look for a component with type TYPE. */ > > > > > + obj = get_component_with_type (obj, type); > > > > > > > > You don't seem to use mdtype at all in this case. Shouldn't > > > > get_component_with_type stop at mdtype if it hasn't found type yet? > > > > > > It was used for diagnostics but not in get_component_with_type. It makes > > > sense to stop at MDTYPE; I've adjusted the code to do so. E.g., if > > > we have OBJ in the form of g.D.2121.D.2122.D.2123.D.2124, usually the > > > component with the most derived type is "g", but in a 'tor, it can be > > > a different component too. > > > > > > > > + /* If not found or not accessible, give an error. */ > > > > > + if (obj == NULL_TREE || obj == error_mark_node) > > > > > + { > > > > > + if (reference_p) > > > > > + { > > > > > + if (!ctx->quiet) > > > > > + { > > > > > + error_at (loc, "reference %<dynamic_cast%> failed"); > > > > > + if (obj == NULL_TREE) > > > > > + inform (loc, "dynamic type %qT of its operand does not " > > > > > + "have an unambiguous public base class %qT", > > > > > + mdtype, type); > > > > > + else > > > > > + inform (loc, "static type %qT of its operand is a " > > > > > + "non-public base class of dynamic type %qT", > > > > > + objtype, type); > > > > > + > > > > > + } > > > > > + *non_constant_p = true; > > > > > + } > > > > > + return integer_zero_node; > > > > > + } > > > > > + else > > > > > + /* The result points to the TYPE object. */ > > > > > + return cp_build_addr_expr (obj, complain); > > > > > + } > > > > > + /* Otherwise, if v points (refers) to a public base class > > > > > subobject of the > > > > > + most derived object, and the type of the most derived > > > > > object has a base > > > > > + class, of type C, that is unambiguous and public, the > > > > > result points > > > > > + (refers) to the C subobject of the most derived object. > > > > > + > > > > > + But it can also be an invalid case. */ > > > > > > > > And I think we need to fall through to this code if the hint > > > > turns out to be > > > > wrong, i.e. V is a public base of C, but v is not that > > > > subobject, but rather > > > > a sibling base of C, like > > > > > > True. HINT is really just an optimization hint, nothing more. I've > > > adjusted > > > the code to fall through to the normal processing if the HINT >= 0 > > > or -3 case > > > doesn't succeed. > > > > > > > struct A { virtual void f(); }; > > > > struct B1: A { }; > > > > struct B2: A { }; > > > > struct C: B1, B2 { }; > > > > int main() > > > > { > > > > C c; > > > > A* ap = (B1*)c; > > > > constexpr auto p = dynamic_cast<B2*>(ap); // should succeed > > > > } > > > > > > Whew, there's always One More Case. :/ New constexpr-dynamic16.c > > > covers it. > > > > > > > > --- /dev/null > > > > > +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic11.C > > > > > @@ -0,0 +1,34 @@ > > > > > +// PR c++/88337 - Implement P1327R1: Allow > > > > > dynamic_cast/typeid in constexpr. > > > > > +// { dg-do compile { target c++2a } } > > > > > + > > > > > +// dynamic_cast in a constructor. > > > > > + > > > > > +struct V { > > > > > + virtual void f(); > > > > > +}; > > > > > + > > > > > +struct A : V { }; > > > > > + > > > > > +struct B : V { > > > > > + constexpr B(V*, A*); > > > > > +}; > > > > > + > > > > > +struct D : A, B { > > > > > + constexpr D() : B((A*)this, this) { } // { dg-message "in > > > > > 'constexpr' expansion of" } > > > > > +}; > > > > > + > > > > > +constexpr B::B(V* v, A* a) > > > > > +{ > > > > > + // well-defined: v of type V*, V base of B results in B* > > > > > + B* b = dynamic_cast<B*>(v); > > > > > + if (b != nullptr) > > > > > + __builtin_abort (); > > > > > + > > > > > + B& br = dynamic_cast<B&>(*v); // { dg-error "reference > > > > > .dynamic_cast. failed" } > > > > > +// { dg-message "dynamic type .A. of its operand does not > > > > > have an unambiguous public base class .B." "" { target *-*-* > > > > > } .-1 } > > > > > + > > > > > + // FIXME: UB in constexpr should be detected. > > > > > + dynamic_cast<B*>(a); // undefined behavior, a > > > > > has type A*, A not a base of B > > > > > > > > What undefined behavior? Seems to me the dynamic_cast should > > > > just fail and > > > > return a null pointer. > > > > > > This is <http://eel.is/c++draft/class.cdtor#6.sentence-3>: > > > "If the operand of the dynamic_cast refers to the object under > > > construction > > > or destruction and the static type of the operand is not a pointer to or > > > object of the constructor or destructor's own class or one of its > > > bases, the > > > dynamic_cast results in undefined behavior." > > > > > > Clang++ doesn't detect this either. > > > > Ah, interesting. That text goes back to C++98. I guess the reason for > > that was that the vtable pointer might give problematic answers in that > > situation. It should be straightforward to detect this in constexpr > > evaluation; if mdtype is not the actual complete object, we can see if > > *this is also part of mdtype (success), or otherwise the complete object > > (undefined). > > > It looks like get_component_with_type (<*this>, type, mdtype) should work > > well for that check. > > Well, the second argument is wrong, we'll need to find the complete object > type for it.
In both cases (dynamic_cast<B*>(v); and dynamic_cast<B*>(a);) *this is "d.D.2126" (types D, B) and mdtype is A, so the above get_component_with_type won't work as far as I can see. I've come up with this instead, which works for the testcases I have, but is probably not entirely correct in general, either :(. It's the if (!same_type_ignoring_top_level_qualifiers_p (mdtype, complete_type)) ... block, the UB is detected as demonstrated in constexpr-dynamic17.C. To find *this, I went looking in call_stack. What do you think about this? Bootstrapped/regtested on x86_64-linux. 2019-12-17 Marek Polacek <pola...@redhat.com> PR c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr. * constexpr.c (initialized_type): Forward declare. (cxx_dynamic_cast_fn_p): New function. (extract_obj_from_addr_offset): New function. (get_component_with_type): New function. (cxx_eval_dynamic_cast_fn): New function. (cxx_eval_call_expression): Call cxx_eval_dynamic_cast_fn for a call to __dynamic_cast. (potential_constant_expression_1): Don't give up on cxx_dynamic_cast_fn_p. * rtti.c (build_dynamic_cast_1): When creating a call to __dynamic_cast, use the location of the original expression. * g++.dg/cpp2a/constexpr-dynamic1.C: New test. * g++.dg/cpp2a/constexpr-dynamic10.C: New test. * g++.dg/cpp2a/constexpr-dynamic11.C: New test. * g++.dg/cpp2a/constexpr-dynamic12.C: New test. * g++.dg/cpp2a/constexpr-dynamic13.C: New test. * g++.dg/cpp2a/constexpr-dynamic14.C: New test. * g++.dg/cpp2a/constexpr-dynamic15.C: New test. * g++.dg/cpp2a/constexpr-dynamic16.C: New test. * g++.dg/cpp2a/constexpr-dynamic17.C: New test. * g++.dg/cpp2a/constexpr-dynamic2.C: New test. * g++.dg/cpp2a/constexpr-dynamic3.C: New test. * g++.dg/cpp2a/constexpr-dynamic4.C: New test. * g++.dg/cpp2a/constexpr-dynamic5.C: New test. * g++.dg/cpp2a/constexpr-dynamic6.C: New test. * g++.dg/cpp2a/constexpr-dynamic7.C: New test. * g++.dg/cpp2a/constexpr-dynamic8.C: New test. * g++.dg/cpp2a/constexpr-dynamic9.C: New test. diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index f3f03e7d621..4b3751a17ca 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -1070,6 +1070,7 @@ struct constexpr_ctx { static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table; +static tree initialized_type (tree); static tree cxx_eval_constant_expression (const constexpr_ctx *, tree, bool, bool *, bool *, tree * = NULL); @@ -1689,6 +1690,271 @@ is_std_allocator_allocate (tree fndecl) return decl_in_std_namespace_p (decl); } +/* Return true if FNDECL is __dynamic_cast. */ + +static inline bool +cxx_dynamic_cast_fn_p (tree fndecl) +{ + return (cxx_dialect >= cxx2a + && id_equal (DECL_NAME (fndecl), "__dynamic_cast") + && CP_DECL_CONTEXT (fndecl) == global_namespace); +} + +/* Often, we have an expression in the form of address + offset, e.g. + "&_ZTV1A + 16". Extract the object from it, i.e. "_ZTV1A". */ + +static tree +extract_obj_from_addr_offset (tree expr) +{ + if (TREE_CODE (expr) == POINTER_PLUS_EXPR) + expr = TREE_OPERAND (expr, 0); + STRIP_NOPS (expr); + if (TREE_CODE (expr) == ADDR_EXPR) + expr = TREE_OPERAND (expr, 0); + return expr; +} + +/* Given a PATH like + + g.D.2181.D.2154.D.2102.D.2093 + + find a component with type TYPE. Return NULL_TREE if not found, and + error_mark_node if the component is not accessible. If STOP is non-null, + this function will return NULL_TREE if STOP is found before TYPE. */ + +static tree +get_component_with_type (tree path, tree type, tree stop) +{ + while (true) + { + if (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), type)) + /* Found it. */ + return path; + else if (stop + && (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (path), + stop))) + return NULL_TREE; + else if (TREE_CODE (path) == COMPONENT_REF + && DECL_FIELD_IS_BASE (TREE_OPERAND (path, 1))) + { + /* We need to check that the component we're accessing is in fact + accessible. */ + if (TREE_PRIVATE (TREE_OPERAND (path, 1)) + || TREE_PROTECTED (TREE_OPERAND (path, 1))) + return error_mark_node; + path = TREE_OPERAND (path, 0); + } + else + return NULL_TREE; + } +} + +/* Evaluate a call to __dynamic_cast (permitted by P1327R1). + + The declaration of __dynamic_cast is: + + void* __dynamic_cast (const void* __src_ptr, + const __class_type_info* __src_type, + const __class_type_info* __dst_type, + ptrdiff_t __src2dst); + + where src2dst has the following possible values + + >-1: src_type is a unique public non-virtual base of dst_type + dst_ptr + src2dst == src_ptr + -1: unspecified relationship + -2: src_type is not a public base of dst_type + -3: src_type is a multiple public non-virtual base of dst_type + + Since literal types can't have virtual bases, we only expect hint >=0, + -2, or -3. */ + +static tree +cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, + bool *non_constant_p, bool *overflow_p) +{ + /* T will be something like + __dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8) + dismantle it. */ + gcc_assert (call_expr_nargs (call) == 4); + tsubst_flags_t complain = ctx->quiet ? tf_none : tf_warning_or_error; + tree obj = CALL_EXPR_ARG (call, 0); + tree type = CALL_EXPR_ARG (call, 2); + HOST_WIDE_INT hint = int_cst_value (CALL_EXPR_ARG (call, 3)); + location_t loc = cp_expr_loc_or_input_loc (call); + + /* Get the target type of the dynamic_cast. */ + gcc_assert (TREE_CODE (type) == ADDR_EXPR); + type = TREE_OPERAND (type, 0); + type = TREE_TYPE (DECL_NAME (type)); + + /* TYPE can only be either T* or T&. We can't know which of these it + is by looking at TYPE, but OBJ will be "(T*) x" in the first case, + and something like "(T*)(T&)(T*) x" in the second case. */ + bool reference_p = false; + while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR) + { + reference_p |= TYPE_REF_P (TREE_TYPE (obj)); + obj = TREE_OPERAND (obj, 0); + } + + /* Evaluate the object so that we know its dynamic type. */ + obj = cxx_eval_constant_expression (ctx, obj, /*lval*/false, non_constant_p, + overflow_p); + if (*non_constant_p) + return call; + + /* We expect OBJ to be in form of &d.D.2102 when HINT == 0, + but when HINT is > 0, it can also be something like + &d.D.2102 + 18446744073709551608, which includes the BINFO_OFFSET. */ + obj = extract_obj_from_addr_offset (obj); + const tree objtype = TREE_TYPE (obj); + /* If OBJ doesn't refer to a base field, we're done. */ + if (tree t = (TREE_CODE (obj) == COMPONENT_REF + ? TREE_OPERAND (obj, 1) : obj)) + if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t)) + return integer_zero_node; + + /* [class.cdtor] When a dynamic_cast is used in a constructor ... + or in a destructor ... if the operand of the dynamic_cast refers + to the object under construction or destruction, this object is + considered to be a most derived object that has the type of the + constructor or destructor's class. */ + tree vtable = build_vfield_ref (obj, TREE_TYPE (obj)); + vtable = cxx_eval_constant_expression (ctx, vtable, /*lval*/false, + non_constant_p, overflow_p); + if (*non_constant_p) + return call; + /* VTABLE will be &_ZTV1A + 16 or similar, get _ZTV1A. */ + vtable = extract_obj_from_addr_offset (vtable); + const tree mdtype = DECL_CONTEXT (vtable); + + tree t = obj; + while (TREE_CODE (t) == COMPONENT_REF + && DECL_FIELD_IS_BASE (TREE_OPERAND (t, 1))) + t = TREE_OPERAND (t, 0); + const tree complete_type = TREE_TYPE (t); + + /* [class.cdtor]/6 "If the operand of the dynamic_cast refers to + the object under construction or destruction and the static type + of the operand is not a pointer to or object of the constructor + or destructor's own class or one of its bases, the dynamic_cast + results in undefined behavior." And undefined behavior should be + detected in constexpr contexts. */ + if (!same_type_ignoring_top_level_qualifiers_p (mdtype, complete_type)) + { + unsigned ix; + FOR_EACH_VEC_ELT_REVERSE (call_stack, ix, t) + if (tree fn = cp_get_callee_fndecl_nofold (t)) + if (DECL_CONSTRUCTOR_P (fn)) + { + /* Get *this of the current constructor. */ + tree cdtor_type = initialized_type (t); + if (!DERIVED_FROM_P (objtype, cdtor_type)) + { + 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);