On 9/11/26 8:55 AM, Jakub Jelinek wrote:
On Wed, Sep 09, 2026 at 10:11:38AM -0400, Jason Merrill wrote:
On 9/9/26 3:41 AM, Jakub Jelinek wrote:
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.

Yes?  I was thinking that when cxx_fold_pointer_plus_expression sees
(pointer_plus (convert @0) @1) it can evaluate (pointer_plus @0 @1) instead
and then convert the result.

Ah, I forgot the details.
I still think it is unsafe compared to the patch with the langhook I've
posted, e.g. it makes us reject
struct A { int a; };
struct B : A {};
struct C { B a; A b; A c; };

constexpr int
foo ()
{
   C d {};
   A *p = static_cast <A *> (&d.a);
   B *q = static_cast <B *> (p) + 2;
   A *r = static_cast <A *> (q);
   return r->a;
}

static_assert (foo () == 0);
which we accepted before (though clang++ rejects it, but for a different
reason - that the pointer addition is not valid because d.a is not an array
element).

Yes, under https://eel.is/c++draft/expr#add-4.3 , which we don't currently check.

So, now it is rejected for a wrong reason with this patch.
I don't know if it is possible to construct a valid testcase where this
problem occurs.

As in my CWG mail, let's consider

struct A { int a; };
struct B : A {};
struct C { B a[3]; };

constexpr int
foo ()
{
  C d {};
  A *p = d.a;
  B *q = static_cast <B *> (p) /* +2 */;
  A *r = q + 2;
  return r->a;
}

static_assert (foo () == 0);

I think it's uncontroversial that p does not "point to an array element", so p+2 is UB. The question is whether q does, after the round trip, and so whether q+2 is OK. Currently MSVC thinks it's OK, EDG thinks it's UB, Clang curiously treats it differently if the addition is done before or after initializing q.

Which sounds like exactly this POINTER_PLUS_EXPR situation: if we do the addition in A* and then convert to B*, the addition is UB because the A base subobject is not an array element.

For now I think this patch is OK, we can refine this case later when implementing this check.

Or if you'd rather pursue the langhook now let's try to make it more generic, something to do with type-safe folding rather than specifically about pointer_plus_expr?

Jason

Reply via email to