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

Reply via email to