Re: [PATCH] c++, v2: Reject UB in static_cast downcasts during constant evaluation
Jakub Jelinek <[email protected]> Wed, 5 Aug 2026 15:58:43 +0200
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <anNBkwbbsvnAkEmO@tucnak> |
On Thu, Jul 30, 2026 at 09:04:59PM -0400, Jason Merrill wrote:
> > Here is an updated patch, which
> > 1) has slightly different wording mostly as you suggested,
> > I have just used the pointed or referenced type rather than a pointer
> > (what would be printed in the reference case anyway?)
> > 2) in cp_fold_convert I've stopped using fold_convert for the
> > INDIRECT_TYPE_P to INDIRECT_TYPE_P casts so that we avoid the
> > Convert (T1)(X p+ Y) into ((T1)X p+ Y), for pointer type, when ...
> > problematic fold-const.cc optimization, instead it optimizes just
> > some simple cases
> > 3) had to move -fsanitize=null checking for references on nullptr in order
> > not to regress one ubsan testcase
> >
> > Bootstrapped successfull on both x86_64-linux and i686-linux, but has one
> > regression on both:
> > FAIL: std/ranges/adaptors/slide/1.cc -std=gnu++23 (test for excess errors)
> > Excess errors:
> > /home/jakub/src/gcc/libstdc++-v3/testsuite/std/ranges/adaptors/slide/1.cc:110: error: non-constant condition for static assertion
> > /home/jakub/src/gcc/obj74/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ranges_base.h:1016: error: '(((((int*)(& x)) + 12) - (((int*)(& x)) + 4)) / 4)' is not a constant expression
> > and ditto with -std=gnu++29. I'm afraid something relies on
> > some of the fold_convert optimizations, dunno if we should try harder
> > during POINTER_DIFF_EXPR folding or what.
> What if we disable the fold_unary_loc transformation if the pointers are to
> different RECORD_TYPEs? There are already a couple other exceptions.
Note, with the r17-2979 commit in, the patch as posted doesn't cause
regressions. It can be also narrowed, so in cvt.cc do fold_convert
also if not
+ && CLASS_TYPE_P (TREE_TYPE (type))
+ && CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (expr)))
(but TYPE_MAIN_VARIANT check in that case doesn't really work,
we have there cases we don't want to optimize where
TREE_TYPE (type) and TREE_TYPE (TREE_TYPE (expr)) is the same type,
but after stripping nops from expr we end up with some problematic type.
Regarding the suggested fold-const.cc change, I thought
"Convert (T1)(X p+ Y) into ((T1)X p+ Y), for pointer type, when the new"
is a pretty important optimization that allows moving around
POINTER_PLUS_EXPRs so that they can be optimized together.
If you mean
--- gcc/fold-const.cc.jj 2026-08-04 17:00:02.424807528 +0200
+++ gcc/fold-const.cc 2026-08-05 14:53:17.951727844 +0200
@@ -9429,6 +9429,14 @@ fold_unary_loc (location_t loc, enum tre
&& TREE_CODE (TREE_TYPE (arg00)) != REFERENCE_TYPE)
return NULL_TREE;
+ if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (type))
+ && RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (arg0)))
+ && (TYPE_MAIN_VARIANT (TREE_TYPE (type))
+ != TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (arg0))))
+ && !in_gimple_form
+ && lang_GNU_CXX ())
+ return NULL_TREE;
+
arg00 = fold_convert_loc (loc, type, arg00);
return fold_build_pointer_plus_loc (loc, arg00, arg01);
}
with a comment, then the disadvantage is that it will not optimize even when
doing cp_fold* or during cp_gimplify_expr.
That said, I've been wondering if such change would prevent optimization of
say:
struct A { int a; };
#ifdef __cplusplus
struct B : A { int b; };
struct C : B { int c; };
#else
struct C { int c; };
#endif
A *
foo (A *p)
{
return ((A *) (((C *) ((A *) (((C *) ((A *) (((C *) ((A *) (((C *) p) + 1))) + 1))) + 1))) + 1));
}
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
optimization where the GENERIC only simplification clearly
looks also inapproprite for C++ constant evaluation. But surprisingly
commenting that out doesn't help, the first one triggers there too,
so wonder what else gets rid of the casts in there.
Jakub