Re: [PATCH] c++, v2: Reject UB in static_cast downcasts during constant evaluation
Jakub Jelinek <[email protected]> Thu, 6 Aug 2026 17:31:50 +0200
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <anSo5jbYuRtEbfcI@tucnak> |
On Wed, Aug 05, 2026 at 03:49:18PM -0400, Jason Merrill wrote:
> > --- 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.
>
> Yes, that's what I was thinking about.
>
> > 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.
> Another possible approach to this issue would be for
> cxx_fold_pointer_plus_expression to handle the case of a NOP_EXPR on the lhs
> specially so it isn't exposed to the usual NOP_EXPR handling?
I'd be afraid that any extra folding before cp_fold can turn into
accepts-invalid if the casts are completely optimized away.
Though, so far I've been unable to construct a testcase that would
trigger it. E.g. on the following in the first case the thing is
that the casts are added by build_static_cast_1 -> build_base_path
which does e.g.
expr = build1 (NOP_EXPR, ptr_target_type, expr);
rathern than cp_fold_convert, and then the cp_fold_convert does nothing.
And in the bar case below the POINTER_PLUS_EXPR in there is conditional
on the pointer not being null and also isn't being optimized out.
Guess I'll try the cxx_fold_pointer_plus_expression change you're proposing.
struct A {};
struct B : A {};
struct C { int c; };
struct D { int d; };
struct E : C, D { int e; };
constexpr bool
foo (bool x)
{
struct A a = {};
A *b = &a;
A *c = nullptr;
if (x)
{
c = static_cast <A *> (static_cast <B *> (static_cast <A *> (static_cast <B *> (b))));
}
return true;
}
constexpr bool a = foo (true);
constexpr bool
bar (bool x)
{
struct D a = { 42 };
D *b = &a;
D *c = nullptr;
if (x)
{
c = static_cast <D *> (static_cast <E *> (static_cast <D *> (static_cast <E *> (b))));
}
return true;
}
constexpr bool b = bar (true);
Jakub