On Thu, Aug 06, 2026 at 05:31:50PM +0200, Jakub Jelinek wrote: > > > Turns out it doesn't prevent that, because match.pd has also > > > /* Associate (p +p off1) +p off2 as (p +p (off1 + off2)). */ > > > (simplify > > > (pointer_plus (pointer_plus:s @0 @1) @3) > > > (pointer_plus @0 (plus @1 @3))) > > > #if GENERIC > > > (simplify > > > (pointer_plus (convert:s (pointer_plus:s @0 @1)) @3) > > > (pointer_plus (convert:type @0) (plus @1 @3))) > > > #endif
> Guess I'll try the cxx_fold_pointer_plus_expression change you're proposing. Actually cxx_fold_pointer_plus_expression can't help that much, the main optimization is folding of (convert (pointer_plus @0 @1)) into (pointer_plus (convert @0) @1) and similar optimizations, so upon fold_convert, which isn't called just from cp_fold_convert, but many other spots. cxx_fold_pointer_plus_expression could help only with the above mentioned (pointer_plus (convert:s (pointer_plus:s @0 @1)) @3) simplification, but even that not reliably, it can trigger also on (convert (pointer_plus (convert:s (pointer_plus:s @0 @1)) @3)) and again hit during cp_fold. So, this patch instead takes the fold-const.cc change to disable this optimization for C++, but only does that for the problematic downcasts, and reenables it during cp_fold_r recursion in cp_fold_function and cp_fully_fold_init, so that we actually get those optimized when we no longer need to diagnose static_cast violations. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2026-09-09 Jakub Jelinek <[email protected]> * langhooks.h (lang_hooks_for_types): Add fold_cast_pointer_plus_p member. * langhooks-def.h (LANG_HOOKS_FOLD_CAST_POINTER_PLUS_P): Define. (LANG_HOOKS_FOR_TYPES_INITIALIZER): Use it. * fold-const.h (fold_cast_pointer_plus_p): Declare. * fold-const.cc (fold_cast_pointer_plus_p): New function. (fold_unary): If fold_cast_pointer_plus_p returns false, return NULL_TREE. * match.pd ((p +p off1) +p off2 as (p +p (off1 + off2))): In the GENERIC version guard the simplification on fold_cast_pointer_plus_p returning true. * constexpr.cc (cxx_eval_constant_expression) <case CONVERT_EXPR>: Set *non_constant_p and optionally diagnose if a static_cast downcast results in undefined behavior. * typeck.cc (build_static_cast_1): Move COND_EXPR reference binding to NULL instrumentation before cp_fold_convert call and allow the type to be a pointer rather than reference, as long as its TREE_TYPE is the expected one. * cp-objcp-common.h (LANG_HOOKS_FOLD_CAST_POINTER_PLUS_P): Redefine. * cp-tree.h (cp_fold_cast_pointer_plus_p): Declare. * cp-gimplify.cc (in_cp_fold): New variable. (cp_fold_function, cp_fully_fold_init): Temporarily set in_cp_fold. (cp_fold_cast_pointer_plus_p): New function. * g++.dg/cpp0x/constexpr-static-cast1.C: New test. * g++.dg/cpp0x/constexpr-static-cast2.C: New test. * g++.dg/cpp1y/constexpr-static-cast1.C: New test. * g++.dg/cpp1y/constexpr-static-cast2.C: New test. * g++.dg/cpp1y/constexpr-static-cast3.C: New test. --- a/gcc/langhooks.h 2026-08-04 12:48:37.265620204 +0200 +++ b/gcc/langhooks.h 2026-09-08 16:13:46.824984874 +0200 @@ -190,6 +190,11 @@ struct lang_hooks_for_types i.e., type without virtual base classes or tail padding. Returns NULL_TREE otherwise. */ tree (*classtype_as_base) (const_tree); + + /* Returns true if it is ok to fold (OUTER *) (p p+ off) to + ((OUTER *) p) p+ off when p has type INNER *. C++ needs to avoid this + optimization for downcasts before cp_fold_function. */ + bool (*fold_cast_pointer_plus_p) (tree, tree); }; /* Language hooks related to decls and the symbol table. */ --- a/gcc/langhooks-def.h 2026-08-04 12:48:37.264620216 +0200 +++ b/gcc/langhooks-def.h 2026-09-08 16:13:59.879817728 +0200 @@ -225,6 +225,7 @@ extern tree lhd_unit_size_without_reusab #define LANG_HOOKS_TYPE_DWARF_ATTRIBUTE lhd_type_dwarf_attribute #define LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING lhd_unit_size_without_reusable_padding #define LANG_HOOKS_CLASSTYPE_AS_BASE hook_tree_const_tree_null +#define LANG_HOOKS_FOLD_CAST_POINTER_PLUS_P hook_bool_tree_tree_true #define LANG_HOOKS_FOR_TYPES_INITIALIZER { \ LANG_HOOKS_MAKE_TYPE, \ @@ -252,7 +253,8 @@ extern tree lhd_unit_size_without_reusab LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO, \ LANG_HOOKS_TYPE_DWARF_ATTRIBUTE, \ LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING, \ - LANG_HOOKS_CLASSTYPE_AS_BASE \ + LANG_HOOKS_CLASSTYPE_AS_BASE, \ + LANG_HOOKS_FOLD_CAST_POINTER_PLUS_P \ } /* Declaration hooks. */ --- a/gcc/fold-const.h 2026-08-05 13:17:17.223314105 +0200 +++ b/gcc/fold-const.h 2026-09-08 17:02:18.526184719 +0200 @@ -57,6 +57,7 @@ extern void shift_bytes_in_array_right ( extern tree fold (tree); extern tree fold_init (tree); +extern bool fold_cast_pointer_plus_p (tree, tree); #define fold_unary(CODE,T1,T2)\ fold_unary_loc (UNKNOWN_LOCATION, CODE, T1, T2) extern tree fold_unary_loc (location_t, enum tree_code, tree, tree); --- a/gcc/fold-const.cc 2026-08-17 10:00:21.719856296 +0200 +++ b/gcc/fold-const.cc 2026-09-08 17:03:41.385130022 +0200 @@ -9161,6 +9161,19 @@ build_fold_addr_expr_loc (location_t loc return build_fold_addr_expr_with_type_loc (loc, t, ptrtype); } +/* Returns true if it is ok to fold (OUTER *) (p p+ off) to + ((OUTER *) p) p+ off when p has type INNER *. C++ needs to avoid this + optimization for downcasts before cp_fold_function/genericization + and gimplification. */ + +bool +fold_cast_pointer_plus_p (tree outer, tree inner) +{ + if (in_gimple_form) + return true; + return lang_hooks.types.fold_cast_pointer_plus_p (outer, inner); +} + /* Fold a unary expression of code CODE and type TYPE with operand OP0. Return the folded expression if folding is successful. Otherwise, return NULL_TREE. */ @@ -9429,6 +9442,10 @@ fold_unary_loc (location_t loc, enum tre && TREE_CODE (TREE_TYPE (arg00)) != REFERENCE_TYPE) return NULL_TREE; + if (!fold_cast_pointer_plus_p (TREE_TYPE (type), + TREE_TYPE (TREE_TYPE (arg0)))) + return NULL_TREE; + arg00 = fold_convert_loc (loc, type, arg00); return fold_build_pointer_plus_loc (loc, arg00, arg01); } --- a/gcc/match.pd 2026-09-02 10:01:23.182061643 +0200 +++ b/gcc/match.pd 2026-09-08 17:08:41.409311057 +0200 @@ -3450,7 +3450,9 @@ (define_operator_list SYNC_FETCH_AND_AND #if GENERIC (simplify (pointer_plus (convert:s (pointer_plus:s @0 @1)) @3) - (pointer_plus (convert:type @0) (plus @1 @3))) + (if (fold_cast_pointer_plus_p (TREE_TYPE (type), + TREE_TYPE (TREE_TYPE (@0)))) + (pointer_plus (convert:type @0) (plus @1 @3)))) #endif /* Pattern match --- a/gcc/cp/constexpr.cc 2026-09-08 12:51:58.333351106 +0200 +++ b/gcc/cp/constexpr.cc 2026-09-08 15:53:14.529212564 +0200 @@ -10629,6 +10629,53 @@ cxx_eval_constant_expression (const cons } } + /* [expr.static.cast]/10: A prvalue of type "pointer to cv1 B", where + B is a class type, can be converted to a prvalue of type + "pointer to cv2 D", where D is a complete class derived from B, ... + If the prvalue of type "pointer to cv1 B" points to a B that is + actually a base class subobject of an object of type D, the + resulting pointer points to the enclosing object of type D. + Otherwise, the behavior is undefined. + Similarly [expr.static.cast]/2 for references. */ + if (INDIRECT_TYPE_P (type) + && INDIRECT_TYPE_P (TREE_TYPE (op)) + && COMPLETE_TYPE_P (TREE_TYPE (type)) + && !integer_zerop (op) + && is_properly_derived_from (TREE_TYPE (type), + TREE_TYPE (TREE_TYPE (op)))) + { + tree sop = tree_strip_nop_conversions (op); + if (cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop, + NULL, jump_target) == NULL_TREE) + { + tree dyntype = NULL_TREE; + if (!ctx->quiet && TREE_CODE (sop) == ADDR_EXPR) + { + sop = TREE_OPERAND (sop, 0); + while (TREE_CODE (sop) == COMPONENT_REF + && DECL_FIELD_IS_BASE (TREE_OPERAND (sop, 1))) + sop = TREE_OPERAND (sop, 0); + dyntype = strip_array_types (TREE_TYPE (sop)); + if (same_type_p (dyntype, TREE_TYPE (TREE_TYPE (op)))) + dyntype = NULL_TREE; + } + if (!ctx->quiet) + { + if (dyntype) + error_at (loc, "%qT operand (of dynamic type %qT) is " + "not a base class subobject of a %qT object", + TREE_TYPE (TREE_TYPE (op)), dyntype, + TREE_TYPE (type)); + else + error_at (loc, "%qT operand is not a base class " + "subobject of a %qT object", + TREE_TYPE (TREE_TYPE (op)), TREE_TYPE (type)); + } + *non_constant_p = true; + return t; + } + } + if (TREE_CODE (op) == PTRMEM_CST && !TYPE_PTRMEM_P (type)) { op = cplus_expand_constant (op); --- a/gcc/cp/typeck.cc 2026-08-21 14:40:07.149109601 +0200 +++ b/gcc/cp/typeck.cc 2026-09-08 15:53:14.530981828 +0200 @@ -8853,6 +8853,17 @@ build_static_cast_1 (location_t loc, tre /*nonnull=*/flag_delete_null_pointer_checks, complain); + /* When -fsanitize=null, make sure to diagnose reference binding to + NULL even when the reference is converted to pointer later on. */ + if (sanitize_flags_p (SANITIZE_NULL) + && TREE_CODE (expr) == COND_EXPR + && TREE_OPERAND (expr, 2) + && TREE_CODE (TREE_OPERAND (expr, 2)) == INTEGER_CST + && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 2))) + && (TREE_TYPE (TREE_TYPE (TREE_OPERAND (expr, 2))) + == TREE_TYPE (type))) + ubsan_maybe_instrument_reference (&TREE_OPERAND (expr, 2)); + /* Convert the pointer to a reference -- but then remember that there are no expressions with reference type in C++. @@ -8863,15 +8874,6 @@ build_static_cast_1 (location_t loc, tre the wrong answer. */ expr = cp_fold_convert (type, expr); - /* When -fsanitize=null, make sure to diagnose reference binding to - NULL even when the reference is converted to pointer later on. */ - if (sanitize_flags_p (SANITIZE_NULL) - && TREE_CODE (expr) == COND_EXPR - && TREE_OPERAND (expr, 2) - && TREE_CODE (TREE_OPERAND (expr, 2)) == INTEGER_CST - && TREE_TYPE (TREE_OPERAND (expr, 2)) == type) - ubsan_maybe_instrument_reference (&TREE_OPERAND (expr, 2)); - return convert_from_reference (rvalue (expr)); } --- a/gcc/cp/cp-objcp-common.h 2026-04-24 14:30:10.719144704 +0200 +++ b/gcc/cp/cp-objcp-common.h 2026-09-08 17:18:48.710626497 +0200 @@ -176,6 +176,8 @@ static const scoped_attribute_specs *con #define LANG_HOOKS_UNIT_SIZE_WITHOUT_REUSABLE_PADDING cp_unit_size_without_reusable_padding #undef LANG_HOOKS_CLASSTYPE_AS_BASE #define LANG_HOOKS_CLASSTYPE_AS_BASE cp_classtype_as_base +#undef LANG_HOOKS_FOLD_CAST_POINTER_PLUS_P +#define LANG_HOOKS_FOLD_CAST_POINTER_PLUS_P cp_fold_cast_pointer_plus_p #undef LANG_HOOKS_OMP_PREDETERMINED_SHARING #define LANG_HOOKS_OMP_PREDETERMINED_SHARING cxx_omp_predetermined_sharing --- a/gcc/cp/cp-tree.h 2026-09-08 12:51:58.335351080 +0200 +++ b/gcc/cp/cp-tree.h 2026-09-08 17:19:49.047863170 +0200 @@ -9235,6 +9235,7 @@ extern tree cp_fold_immediate (tree *, tree = current_function_decl); extern void process_and_check_pending_immediate_escalating_fns (); extern bool is_invisiref_parm (const_tree); +extern bool cp_fold_cast_pointer_plus_p (tree, tree); /* in name-lookup.cc */ extern tree strip_using_decl (tree); --- a/gcc/cp/cp-gimplify.cc 2026-09-08 12:51:58.334351092 +0200 +++ b/gcc/cp/cp-gimplify.cc 2026-09-08 17:35:45.000716053 +0200 @@ -95,6 +95,9 @@ struct cp_fold_data } }; +/* True if during cp_fold_function or cp_fully_fold_init. */ +static bool in_cp_fold; + /* Forward declarations. */ static tree cp_genericize_r (tree *, int *, void *); @@ -1813,7 +1816,10 @@ cp_fold_function (tree fndecl) &data, NULL); data.pset.empty (); } + bool save_in_cp_fold = in_cp_fold; + in_cp_fold = true; cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL); + in_cp_fold = save_in_cp_fold; /* This is merely an optimization: if FNDECL has no i-e expressions, we'll not save c_f_d, and we can safely say that FNDECL will not @@ -3318,7 +3324,10 @@ cp_fully_fold_init (tree x) cp_walk_tree (&x, cp_fold_immediate_r, &data, NULL); data.pset.empty (); } + bool save_in_cp_fold = in_cp_fold; + in_cp_fold = true; cp_walk_tree (&x, cp_fold_r, &data, NULL); + in_cp_fold = save_in_cp_fold; return x; } @@ -4455,4 +4464,19 @@ fold_builtin_source_location (const_tree return build_fold_addr_expr_with_type_loc (loc, var, TREE_TYPE (t)); } +/* Returns true if it is ok to fold (OUTER *) (p p+ off) to + ((OUTER *) p) p+ off when p has type INNER *. C++ needs to avoid this + optimization for downcasts before cp_fold_function. */ + +bool +cp_fold_cast_pointer_plus_p (tree outer, tree inner) +{ + if (in_cp_fold) + return true; + if (COMPLETE_TYPE_P (outer) + && is_properly_derived_from (outer, inner)) + return false; + return true; +} + #include "gt-cp-cp-gimplify.h" --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-static-cast1.C 2026-09-08 15:53:14.531845208 +0200 +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static-cast1.C 2026-09-08 15:53:14.531845208 +0200 @@ -0,0 +1,76 @@ +// { dg-do compile { target c++11 } } + +struct A { int a; }; +struct B : A { int b; }; +struct C : A { int c; }; +struct D { int d; }; +struct E { int e; }; +struct F : A, D, E { int f; }; +struct G {}; +struct H {}; +struct I {}; +struct J : G, H, I {}; +struct K { F k; J l[2]; }; +struct L : G {}; +struct M : G {}; +struct N : L, M {}; +constexpr A a = {}; +constexpr B b = {}; +constexpr C c = {}; +constexpr auto d = static_cast <const B *> (&a); // { dg-error "'const A' operand is not a base class subobject of a 'const B' object" } +constexpr auto e = static_cast <const A *> (&a); +constexpr auto f = static_cast <const A *> (&b); +constexpr auto g = static_cast <const B *> (&b); +constexpr auto h = static_cast <const B *> (f); +constexpr auto i = static_cast <const A *> (nullptr); +constexpr auto j = static_cast <const B *> (nullptr); +constexpr auto k = static_cast <const B *> (i); +constexpr auto l = static_cast <const A *> (j); +constexpr auto m = static_cast <const A *> (&c); +constexpr auto n = static_cast <const B *> (m); // { dg-error "'const A' operand \\\(of dynamic type 'const C'\\\) is not a base class subobject of a 'const B' object" } +constexpr auto o = static_cast <const C *> (m); +constexpr F p = {}; +constexpr auto q = static_cast <const A *> (&p); +constexpr auto r = static_cast <const F *> (q); +constexpr auto s = static_cast <const D *> (r); +constexpr auto t = static_cast <const F *> (s); +constexpr auto u = static_cast <const E *> (t); +constexpr auto v = static_cast <const F *> (u); +constexpr J y = {}; +constexpr auto z = static_cast <const G *> (&y); +constexpr auto aa = static_cast <const J *> (z); +constexpr auto ab = static_cast <const H *> (aa); +constexpr auto ac = static_cast <const J *> (ab); +constexpr auto ad = static_cast <const I *> (ac); +constexpr auto ae = static_cast <const J *> (ad); +constexpr H af = {}; +constexpr auto ag = static_cast <const J *> (&af); // { dg-error "'const H' operand is not a base class subobject of a 'const J' object" } +constexpr F ah[1] = {}; +constexpr auto ai = static_cast <const A *> (&ah[0]); +constexpr auto aj = static_cast <const F *> (ai); +constexpr auto ak = static_cast <const D *> (aj); +constexpr auto al = static_cast <const F *> (ak); +constexpr auto am = static_cast <const E *> (al); +constexpr auto an = static_cast <const F *> (am); +constexpr K ao = {}; +constexpr auto ap = static_cast <const A *> (&ao.k); +constexpr auto aq = static_cast <const F *> (ap); +constexpr auto ar = static_cast <const D *> (aq); +constexpr auto as = static_cast <const F *> (ar); +constexpr auto at = static_cast <const E *> (as); +constexpr auto au = static_cast <const F *> (at); +constexpr auto av = static_cast <const G *> (&ao.l[0]); +constexpr auto aw = static_cast <const J *> (av); +constexpr auto ax = static_cast <const H *> (aw); +constexpr auto ay = static_cast <const J *> (ax); +constexpr auto az = static_cast <const I *> (ay); +constexpr auto ba = static_cast <const J *> (az); +constexpr auto bb = static_cast <const G *> (&ao.l[1]); +constexpr auto bc = static_cast <const J *> (bb); +constexpr auto bd = static_cast <const H *> (bc); +constexpr auto be = static_cast <const J *> (bd); +constexpr auto bf = static_cast <const I *> (be); +constexpr auto bg = static_cast <const J *> (bf); +constexpr N bh = {}; +constexpr const G *bi = static_cast <const M *> (&bh); +constexpr auto bj = static_cast <const L *> (bi); // { dg-error "'const G' operand is not a base class subobject of a 'const L' object" } --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-static-cast2.C 2026-09-08 15:53:14.531980099 +0200 +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-static-cast2.C 2026-09-08 15:53:14.531980099 +0200 @@ -0,0 +1,72 @@ +// { dg-do compile { target c++11 } } + +struct A { int a; }; +struct B : A { int b; }; +struct C : A { int c; }; +struct D { int d; }; +struct E { int e; }; +struct F : A, D, E { int f; }; +struct G {}; +struct H {}; +struct I {}; +struct J : G, H, I {}; +struct K { F k; J l[2]; }; +struct L : G {}; +struct M : G {}; +struct N : L, M {}; +constexpr A a = {}; +constexpr B b = {}; +constexpr C c = {}; +constexpr auto &d = static_cast <const B &> (a); // { dg-error "'const A' operand is not a base class subobject of a 'const B' object" } +constexpr auto &e = static_cast <const A &> (a); +constexpr auto &f = static_cast <const A &> (b); +constexpr auto &g = static_cast <const B &> (b); +constexpr auto &h = static_cast <const B &> (f); +constexpr auto &m = static_cast <const A &> (c); +constexpr auto &n = static_cast <const B &> (m); // { dg-error "'const A' operand \\\(of dynamic type 'const C'\\\) is not a base class subobject of a 'const B' object" } +constexpr auto &o = static_cast <const C &> (m); +constexpr F p = {}; +constexpr auto &q = static_cast <const A &> (p); +constexpr auto &r = static_cast <const F &> (q); +constexpr auto &s = static_cast <const D &> (r); +constexpr auto &t = static_cast <const F &> (s); +constexpr auto &u = static_cast <const E &> (t); +constexpr auto &v = static_cast <const F &> (u); +constexpr J y = {}; +constexpr auto &z = static_cast <const G &> (y); +constexpr auto &aa = static_cast <const J &> (z); +constexpr auto &ab = static_cast <const H &> (aa); +constexpr auto &ac = static_cast <const J &> (ab); +constexpr auto &ad = static_cast <const I &> (ac); +constexpr auto &ae = static_cast <const J &> (ad); +constexpr H af = {}; +constexpr auto &ag = static_cast <const J &> (af); // { dg-error "'const H' operand is not a base class subobject of a 'const J' object" } +constexpr F ah[1] = {}; +constexpr auto &ai = static_cast <const A &> (ah[0]); +constexpr auto &aj = static_cast <const F &> (ai); +constexpr auto &ak = static_cast <const D &> (aj); +constexpr auto &al = static_cast <const F &> (ak); +constexpr auto &am = static_cast <const E &> (al); +constexpr auto &an = static_cast <const F &> (am); +constexpr K ao = {}; +constexpr auto &ap = static_cast <const A &> (ao.k); +constexpr auto &aq = static_cast <const F &> (ap); +constexpr auto &ar = static_cast <const D &> (aq); +constexpr auto &as = static_cast <const F &> (ar); +constexpr auto &at = static_cast <const E &> (as); +constexpr auto &au = static_cast <const F &> (at); +constexpr auto &av = static_cast <const G &> (ao.l[0]); +constexpr auto &aw = static_cast <const J &> (av); +constexpr auto &ax = static_cast <const H &> (aw); +constexpr auto &ay = static_cast <const J &> (ax); +constexpr auto &az = static_cast <const I &> (ay); +constexpr auto &ba = static_cast <const J &> (az); +constexpr auto &bb = static_cast <const G &> (ao.l[1]); +constexpr auto &bc = static_cast <const J &> (bb); +constexpr auto &bd = static_cast <const H &> (bc); +constexpr auto &be = static_cast <const J &> (bd); +constexpr auto &bf = static_cast <const I &> (be); +constexpr auto &bg = static_cast <const J &> (bf); +constexpr N bh = {}; +constexpr const G &bi = static_cast <const M &> (bh); +constexpr auto &bj = static_cast <const L &> (bi); // { dg-error "'const G' operand is not a base class subobject of a 'const L' object" } --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast1.C 2026-09-08 15:53:14.532160739 +0200 +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast1.C 2026-09-08 15:53:14.532160739 +0200 @@ -0,0 +1,91 @@ +// { dg-do compile { target c++14 } } + +struct A { int a; }; +struct B : A { int b; }; +struct C : A { int c; }; +struct D { int d = 42; }; +struct E { int e = 0; }; +struct F : E, D { }; +struct G : F { }; +struct H { G b; }; + +constexpr bool +foo (bool x) +{ + A a = {}; + if (x) + { + auto c = static_cast <B *> (&a); // { dg-error "'A' operand is not a base class subobject of a 'B' object" } + } + return true; +} + +constexpr bool +bar () +{ + A a = {}; + B b = {}; + auto c = static_cast <A *> (&a); + auto d = static_cast <A *> (&b); + auto e = static_cast <B *> (&b); + auto f = static_cast <B *> (d); + auto g = static_cast <A *> (nullptr); + auto h = static_cast <B *> (nullptr); + auto i = static_cast <B *> (g); + auto j = static_cast <A *> (h); + return true; +} + +constexpr bool +baz (bool x) +{ + C a = {}; + auto b = static_cast <A *> (&a); + if (x) + { + auto c = static_cast <B *> (b); // { dg-error "'A' operand \\\(of dynamic type 'C'\\\) is not a base class subobject of a 'B' object" } + } + return true; +} + +#if __cpp_constexpr_dynamic_alloc >= 201907 +constexpr bool +qux () +{ + A *a = new B {}; + auto b = static_cast <B *> (a); + auto c = static_cast <C *> (a); // { dg-error "operand \\\(of dynamic type 'B'\\\) is not a base class subobject of a 'C' object" "" { target c++20 } } + delete a; + return true; +} + +constexpr bool +corge () +{ + A *a = new B[2] {}; + auto b = static_cast <B *> (a); + auto c = static_cast <C *> (a); // { dg-error "operand \\\(of dynamic type 'B'\\\) is not a base class subobject of a 'C' object" "" { target c++20 } } + delete[] a; + return true; +} +#endif + +constexpr int +fred () +{ + H h; + auto a = static_cast <D *> (&h.b); + auto b = static_cast <G *> (a); + return b->d; +} + +static_assert (foo (false), ""); +constexpr bool a = foo (true); +static_assert (bar (), ""); +static_assert (baz (false), ""); +constexpr bool b = baz (true); +#if __cpp_constexpr_dynamic_alloc >= 201907 +constexpr bool c = qux (); +constexpr bool d = corge (); +#endif +static_assert(fred () == 42, ""); --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast2.C 2026-09-08 15:53:14.532273413 +0200 +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast2.C 2026-09-08 15:53:14.532273413 +0200 @@ -0,0 +1,87 @@ +// { dg-do compile { target c++14 } } + +struct A { int a; }; +struct B : A { int b; }; +struct C : A { int c; }; +struct D { int d = 42; }; +struct E { int e = 0; }; +struct F : E, D { }; +struct G : F { }; +struct H { G b; }; + +constexpr bool +foo (bool x) +{ + A a = {}; + if (x) + { + auto &c = static_cast <B &> (a); // { dg-error "'A' operand is not a base class subobject of a 'B' object" } + } + return true; +} + +constexpr bool +bar () +{ + A a = {}; + B b = {}; + auto &c = static_cast <A &> (a); + auto &d = static_cast <A &> (b); + auto &e = static_cast <B &> (b); + auto &f = static_cast <B &> (d); + return true; +} + +constexpr bool +baz (bool x) +{ + C a = {}; + auto &b = static_cast <A &> (a); + if (x) + { + auto &c = static_cast <B &> (b); // { dg-error "'A' operand \\\(of dynamic type 'C'\\\) is not a base class subobject of a 'B' object" } + } + return true; +} + +#if __cpp_constexpr_dynamic_alloc >= 201907 +constexpr bool +qux () +{ + A *a = new B {}; + auto &b = static_cast <B &> (*a); + auto &c = static_cast <C &> (*a); // { dg-error "'A' operand \\\(of dynamic type 'B'\\\) is not a base class subobject of a 'C' object" "" { target c++20 } } + delete a; + return true; +} + +constexpr bool +corge () +{ + A *a = new B[2] {}; + auto &b = static_cast <B &> (*a); + auto &c = static_cast <C &> (*a); // { dg-error "'A' operand \\\(of dynamic type 'B'\\\) is not a base class subobject of a 'C' object" "" { target c++20 } } + delete[] a; + return true; +} +#endif + +constexpr int +fred () +{ + H h; + auto &a = static_cast <D &> (h.b); + auto &b = static_cast <G &> (a); + return b.d; +} + +static_assert (foo (false), ""); +constexpr bool a = foo (true); +static_assert (bar (), ""); +static_assert (baz (false), ""); +constexpr bool b = baz (true); +#if __cpp_constexpr_dynamic_alloc >= 201907 +constexpr bool c = qux (); +constexpr bool d = corge (); +#endif +static_assert(fred () == 42, ""); --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast3.C 2026-09-08 15:53:14.532423479 +0200 +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-static-cast3.C 2026-09-08 15:53:14.532423479 +0200 @@ -0,0 +1,31 @@ +// { dg-do compile { target c++14 } } + +template <typename T> +struct A +{ + unsigned char a = 0; + constexpr T & + foo (const unsigned char &x) + { + a = x; + return *static_cast <T *> (this); + } +}; + +template <typename T> +struct B +{ + unsigned char b = 0; + constexpr T & + bar (const unsigned char &x) + { + b = x; + return *static_cast <T *> (this); // { dg-error "'B<D>' operand \\\(of dynamic type 'C<D>'\\\) is not a base class subobject of a 'D' object" } + } +}; + +template <typename T> +struct C : A <C <T>>, B <T> { }; +struct D : B <D> { }; + +constexpr auto c = C <D> ().foo (100).bar (10); Jakub
