https://gcc.gnu.org/g:0ffbcec62de9419812ef0017d74ce20572586c6c

commit r17-4594-g0ffbcec62de9419812ef0017d74ce20572586c6c
Author: Jakub Jelinek <[email protected]>
Date:   Wed Sep 23 09:22:07 2026 +0200

    c++: Improve printing of null pointer to data member constants [PR126599]
    
    For null pointer to data member we use INTEGER_CST with OFFSET_TYPE
    and -1 value.  When that is printed in diagnostics or in
    display_string_of, it is printed as -1, which is confusing to users
    and an implementation detail (well, part of ABI
    https://itanium-cxx-abi.github.io/cxx-abi/abi.html#data-member-pointers
    A null data member pointer is represented as an offset of -1.
    ).
    Now, as the first testcase shows, if we have a non-null pointer to data
    member constant, it is actually PTRMEM_CST and is printed in a user-friendly
    way, the following patch just arranges to print the OFFSET_TYPE -1
    INTEGER_CST similarly.
    
    2026-09-23  Jakub Jelinek  <[email protected]>
    
            PR c++/126599
            * error.cc (dump_expr): Print OFFSET_TYPE -1 differently.
    
            * g++.dg/diagnostic/ptrtomem5.C: New test.
            * g++.dg/diagnostic/ptrtomem6.C: New test.
            * g++.dg/reflect/display_string_of1.C: Add 2 new tests.
            * g++.dg/reflect/u8display_string_of1.C: Likewise.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/error.cc                                     | 17 ++++++++++++++++-
 gcc/testsuite/g++.dg/diagnostic/ptrtomem5.C         | 15 +++++++++++++++
 gcc/testsuite/g++.dg/diagnostic/ptrtomem6.C         |  7 +++++++
 gcc/testsuite/g++.dg/reflect/display_string_of1.C   |  2 ++
 gcc/testsuite/g++.dg/reflect/u8display_string_of1.C |  2 ++
 5 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 3d5a194b9bc4..47065b11e22a 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2472,8 +2472,23 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
        pp_cxx_ws_string (pp, M_("<unknown>"));
       break;
 
-    case VOID_CST:
     case INTEGER_CST:
+      if (TYPE_PTRDATAMEM_P (TREE_TYPE (t)) && integer_all_onesp (t))
+       {
+         /* OFFSET_TYPE -1 is a null pointer to member.  */
+         if (flags & TFF_EXPR_IN_PARENS)
+           pp_cxx_left_paren (pp);
+         pp_cxx_left_paren (pp);
+         dump_type (pp, TREE_TYPE (t), flags);
+         pp_cxx_right_paren (pp);
+         pp->constant (cxx_dialect < cxx11 ? null_pointer_node : nullptr_node);
+         if (flags & TFF_EXPR_IN_PARENS)
+           pp_cxx_right_paren (pp);
+         break;
+       }
+      /* FALLTHRU */
+
+    case VOID_CST:
     case REAL_CST:
     case STRING_CST:
     case COMPLEX_CST:
diff --git a/gcc/testsuite/g++.dg/diagnostic/ptrtomem5.C 
b/gcc/testsuite/g++.dg/diagnostic/ptrtomem5.C
new file mode 100644
index 000000000000..e1def87e7825
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/ptrtomem5.C
@@ -0,0 +1,15 @@
+// PR c++/126599
+// { dg-do compile { target c++17 } }
+
+struct S { int s, t, u[3][3]; };
+using A = int[3][3];
+template <auto M>
+void foo (int);
+void
+bar ()
+{
+  foo <(int S::*)nullptr> ();  // { dg-error "no matching function for call to 
'foo<\\\(\\\(int S::\\\*\\\)nullptr\\\)>\\\(\\\)'" }
+  foo <&S::s> ();              // { dg-error "no matching function for call to 
'foo<\\\&S::s>\\\(\\\)'" }
+  foo <&S::t> ();              // { dg-error "no matching function for call to 
'foo<\\\&S::t>\\\(\\\)'" }
+  foo <(A S::*)nullptr> ();    // { dg-error "no matching function for call to 
'foo<\\\(\\\(int \\\(S::\\\*\\\)\\\[3\\\]\\\[3\\\]\\\)nullptr\\\)>\\\(\\\)'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/ptrtomem6.C 
b/gcc/testsuite/g++.dg/diagnostic/ptrtomem6.C
new file mode 100644
index 000000000000..e5105ec065c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/ptrtomem6.C
@@ -0,0 +1,7 @@
+// PR c++/126599
+// { dg-do compile }
+
+struct A { int a; };
+__attribute__((assume_aligned ((int A::*) 0))) int *foo ();
+// { dg-warning "'assume_aligned' attribute argument \\\(int A::\\\*\\\)0 is 
not positive" "" { target c++98_only } .-1 }
+// { dg-warning "'assume_aligned' attribute argument \\\(int 
A::\\\*\\\)nullptr is not positive" "" { target c++11 } .-2 }
diff --git a/gcc/testsuite/g++.dg/reflect/display_string_of1.C 
b/gcc/testsuite/g++.dg/reflect/display_string_of1.C
index ae24aff1e9d2..1f1df4b978c6 100644
--- a/gcc/testsuite/g++.dg/reflect/display_string_of1.C
+++ b/gcc/testsuite/g++.dg/reflect/display_string_of1.C
@@ -135,6 +135,8 @@ foo (int a, const long b, T c, int d[4], T &e)
   static_assert (display_string_of (members_of (^^V4, ctx)[0]) == "V4& 
V4::operator+=(const V4&)");
   static_assert (display_string_of (members_of (^^V5, ctx)[0]) == 
"V5::operator int()");
   static_assert (display_string_of (^^operator""_a) == "int 
operator\"\"_a(const char*)");
+  static_assert (display_string_of (reflect_constant ((int U::*)nullptr)) == 
"(int U::*)nullptr");
+  static_assert (display_string_of (reflect_constant (&U::u)) == "&U::u");
 }
 
 namespace NS5 {
diff --git a/gcc/testsuite/g++.dg/reflect/u8display_string_of1.C 
b/gcc/testsuite/g++.dg/reflect/u8display_string_of1.C
index 0ff9cc7c992d..e0fb2e3b0a4a 100644
--- a/gcc/testsuite/g++.dg/reflect/u8display_string_of1.C
+++ b/gcc/testsuite/g++.dg/reflect/u8display_string_of1.C
@@ -135,6 +135,8 @@ foo (int a, const long b, T c, int d[4], T &e)
   static_assert (u8display_string_of (members_of (^^V4, ctx)[0]) == u8"V4& 
V4::operator+=(const V4&)");
   static_assert (u8display_string_of (members_of (^^V5, ctx)[0]) == 
u8"V5::operator int()");
   static_assert (u8display_string_of (^^operator""_a) == u8"int 
operator\"\"_a(const char*)");
+  static_assert (u8display_string_of (reflect_constant ((int U::*)nullptr)) == 
u8"(int U::*)nullptr");
+  static_assert (u8display_string_of (reflect_constant (&U::u)) == u8"&U::u");
 }
 
 namespace NS5 {

Reply via email to