From patchwork Fri Nov 8 21:24:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 1192242 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-512857-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="lSjQ72Qz"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="inVL/bdj"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 478tbH15Kpz9sPV for ; Sat, 9 Nov 2019 08:25:12 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:mime-version:content-type :content-transfer-encoding; q=dns; s=default; b=llzBqXYgRfCfIve0 NMq+zVK7q+UeyuM74VY5KiZKqprc+jDhNhrvPKyouQAjV7niIR3z+e9cu1uVaUKM lskOZvlZRbQ71KMma76dks/+TB1YUWNw9r8u99o9uvIPvLwMTJk2ObR/ZYc1KiZo 3WxN6g+LCWqk11OFE5k44CUDf24= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:subject:message-id:mime-version:content-type :content-transfer-encoding; s=default; bh=bU8wK/yocD27yg5EIyTPXy +yX+Q=; b=lSjQ72QzTQ22wTagB5OHnSzLepK3S9uL25dFtC/TdAH4p4sJp1QiHT qzdaB84mXuY5hHTHhiFiyJgFOeKfL7EAP27a4jKjqBCPyj7sKzVj0pYaYSNVd629 ayxNsjYk7b9Wn1ztAp+qMn7x+yS2Jj5XH14yapGQOx4exzzelEP/Y= Received: (qmail 125867 invoked by alias); 8 Nov 2019 21:25:04 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 125853 invoked by uid 89); 8 Nov 2019 21:25:03 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-22.6 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3 autolearn=ham version=3.3.1 spammy=1.1, Private, navigate, teeth X-HELO: us-smtp-1.mimecast.com Received: from us-smtp-delivery-1.mimecast.com (HELO us-smtp-1.mimecast.com) (207.211.31.120) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 08 Nov 2019 21:24:58 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1573248296; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=AC5+HrDGw4ffU8zCEAx6NMhPxVaWcnz9nmbsNXUdGA8=; b=inVL/bdj6r6vS2OUUGlwt8u+ijZblzxcW+sjRUgOk1Bkcrh49+sGAfsuNAXxzfEktqhjJV iOlvOeKrmvLc/ltxviUj5M/iFevZl2iSq6oHQeet8vtiq9nndECuqU+dXA31/bUiiqf1pp 2dkrOP1tPG0s5l/T3odMdjYD0vHNpPQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-47-1C9BW--HOTWPw-EkNS1rdg-1; Fri, 08 Nov 2019 16:24:52 -0500 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 1C9D21800D7B for ; Fri, 8 Nov 2019 21:24:52 +0000 (UTC) Received: from redhat.com (unknown [10.20.4.51]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 7B7655D9C5; Fri, 8 Nov 2019 21:24:51 +0000 (UTC) Date: Fri, 8 Nov 2019 16:24:49 -0500 From: Marek Polacek To: GCC Patches , Jason Merrill Subject: C++ PATCH for c++/88337 - Implement P1327R1: Allow dynamic_cast in constexpr Message-ID: <20191108212449.GZ21634@redhat.com> MIME-Version: 1.0 User-Agent: Mutt/1.12.1 (2019-06-15) X-Mimecast-Spam-Score: 0 Content-Disposition: inline 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 ), 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 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) : 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 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(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((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((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 % 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 % 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 % 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(b1)) + ok = false; + + D d; + B *b2 = &d; + if (D *pd = dynamic_cast(b2)) + /*OK*/; + else + ok = false; + + return ok; +} + +static_assert(fn ()); + +constexpr D d; +constexpr B b; +constexpr B *b1 = const_cast(&b); +constexpr B *b2 = const_cast(&d); +static_assert(dynamic_cast(b2) == &d); +static_assert(&dynamic_cast(*b2) == &d); +static_assert(dynamic_cast(&d) == &d); +static_assert(&dynamic_cast(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((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(v); + + // FIXME: UB in constexpr should be detected. + dynamic_cast(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((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 ((A*)&d) == nullptr); +static_assert (dynamic_cast ((B*)&d) == nullptr); +static_assert (&d == dynamic_cast ((C*)&d)); +static_assert (dynamic_cast ((B*)&d) == nullptr); + +constexpr DD dd; +static_assert (dynamic_cast ((A*)&dd) == nullptr); +static_assert (dynamic_cast ((B*)&dd) == nullptr); + +constexpr DDD ddd; +static_assert (dynamic_cast ((A*)&ddd) == nullptr); +static_assert (dynamic_cast ((B*)&ddd) == nullptr); +static_assert (dynamic_cast ((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 ((A*)(D*)&e) == nullptr); +static_assert (dynamic_cast ((B*)(D*)&e) == nullptr); +static_assert (&e == dynamic_cast ((C*)(D*)&e)); +static_assert (&e == dynamic_cast ((B*)(CC*)&e)); +static_assert ((CC*)&e == dynamic_cast ((B*)(CC*)&e)); + +constexpr EE ee; +static_assert (dynamic_cast ((A*)(D*)&ee) == nullptr); +static_assert (dynamic_cast ((B*)(D*)&ee) == nullptr); +static_assert (&ee == dynamic_cast ((C*)(D*)&ee)); +static_assert (&ee == dynamic_cast ((B*)(CC*)&ee)); +static_assert ((CC*)&ee == dynamic_cast ((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((B*)(CC*)(E*)&x)); + +constexpr XX xx; +static_assert (dynamic_cast((B*)(CC*)(E*)&xx) == nullptr); + +constexpr Y y; +static_assert (dynamic_cast((B*)&y) == nullptr); +static_assert (dynamic_cast((A*)(B*)&y) == nullptr); + +// 2. crosscast + +struct J { virtual void j(); }; +struct K : CC, private J {}; +class KK : J, CC{}; + +static_assert (dynamic_cast ((B*)(D*)&e) == nullptr); +static_assert ((CC*)&e == dynamic_cast ((C*)(D*)&e)); + +constexpr K k; +static_assert (dynamic_cast ((B*)&k) == nullptr); +constexpr KK kk; +static_assert (dynamic_cast ((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 ((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 ((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 ((C&)d)); +constexpr bool b03 = (dynamic_cast ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((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 ((C&)(D&)e)); +static_assert (&e == &dynamic_cast ((B&)(CC&)e)); +static_assert (&(CC&)e == &dynamic_cast ((B&)(CC&)e)); + +constexpr EE ee; +constexpr bool b11 = (dynamic_cast ((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 ((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 ((C&)(D&)ee)); +static_assert (&ee == &dynamic_cast ((B&)(CC&)ee)); +static_assert (&(CC&)ee == &dynamic_cast ((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((B&)(CC&)(E&)x)); + +constexpr XX xx; +constexpr bool b13 = (dynamic_cast((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((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((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 ((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 ((C&)(D&)e)); + +constexpr K k; +constexpr bool b17 = (dynamic_cast ((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 ((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(b1)) + ok = false; + + D d; + B2 *b2 = &d; + if (D *pd = dynamic_cast(b2)) + /*OK*/; + else + ok = false; + + return ok; +} + +static_assert(fn ()); + +constexpr D d; +constexpr B2 *b = const_cast(&d); +static_assert(dynamic_cast(b) == &d); +static_assert(&dynamic_cast(*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(a)) + /*OK*/; + else + ok = false; + + A &ar = d; + B &br = dynamic_cast(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(static_cast(this)); }; +struct D { virtual void d(); }; +struct E { virtual void e(); }; +struct F : B, C, D, private E { void *f = dynamic_cast(static_cast(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(static_cast(&g)) == nullptr); + +// dynamic_cast produces a pointer to the object of the dynamic type. +static_assert(g.f == (void*)(F*)&g); +static_assert(dynamic_cast(static_cast(&g)) == &g); + +constexpr int d_a = (dynamic_cast(static_cast(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((A2&)(B&)g) == &(A&)(B&)g); +// ... and from B to its A ... +static_assert(&dynamic_cast((B&)g) == &(A&)(B&)g); +// ... but not from D. +static_assert(&dynamic_cast((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((A2&)(B&)g) == &(D&)g); + +// Cannot cast from private base E to derived class F. +constexpr int e_f = (dynamic_cast((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((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((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((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((A&)(C&)f) == &(B&)(C&)f); +// F->A->E == F->E +static_assert (&dynamic_cast((A&)f) == &(E&)f); +// F->E->D->C->B->A->C == F->C +static_assert (&dynamic_cast((A&)(B&)(C&)(D&)(E&)f) == &(C&)f); +// F->B->F == F +static_assert (&dynamic_cast((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((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((P1*)&a) == nullptr); + +constexpr bool b2 = (dynamic_cast((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((P1*)&a) == nullptr); +static_assert (dynamic_cast((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((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((P1*)&a) == nullptr); + +constexpr bool b2 = (dynamic_cast((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((P1*)&a) == nullptr); +static_assert (dynamic_cast((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((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((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((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((B*)&a) == nullptr); +static_assert (dynamic_cast((P1*)&a) == nullptr); +static_assert (dynamic_cast((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((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((D*)&e) == nullptr);