[PATCH RFA (construct_at)] c++: new-expr clobber of constant-size array

Jason Merrill jason@redhat.com
Sat Oct 4 17:15:49 GMT 2025


Tested x86_64-pc-linux-gnu.  Is the library change OK for trunk?

The library patch checks a couple of macros, but I'm not sure that's necessary;
can we just assume std::launder in construct_at?

-- 8< --

I previously tried to clobber an array as a whole, but fell back on a loop
due to issues with std::construct_at following the resolution of LWG3436.
But the loop seems to make life hard for the optimizers and there was
support for reconsidering LWG3436 when I raised the issue on the reflector,
so let's pursue that direction.

This also fixes some xfails in Warray-bounds-20.C.

libstdc++-v3/ChangeLog:

	* include/bits/stl_construct.h (construct_at): Launder instead of
	adding an extra array bound.

gcc/cp/ChangeLog:

	* init.cc (build_new_1): Clobber a constant-bound array as a whole.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/constexpr-new23.C: Launder instead of
	adding an extra array bound.
	* g++.dg/warn/Warray-bounds-20.C: Remove xfails, add diags.
---
 libstdc++-v3/include/bits/stl_construct.h    |  9 +++++++-
 gcc/cp/init.cc                               | 22 ++++++++++++++++----
 gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C |  5 ++++-
 gcc/testsuite/g++.dg/warn/Warray-bounds-20.C | 10 ++++-----
 4 files changed, 35 insertions(+), 11 deletions(-)

diff --git a/libstdc++-v3/include/bits/stl_construct.h b/libstdc++-v3/include/bits/stl_construct.h
index 98b403c4c71..550e3c7ea3f 100644
--- a/libstdc++-v3/include/bits/stl_construct.h
+++ b/libstdc++-v3/include/bits/stl_construct.h
@@ -104,7 +104,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  static_assert(sizeof...(_Args) == 0, "std::construct_at for array "
 		       "types must not use any arguments to initialize the "
 		       "array");
-	  return ::new(__loc) _Tp[1]();
+	  __loc = ::new(__loc) _Tp();
+#if __cpp_lib_launder
+	  return std::launder(__location);
+#elif _GLIBCXX_HAVE_BUILTIN_LAUNDER
+	  return __builtin_launder(__location);
+#else
+	  return static_cast<_Tp*>(__loc);
+#endif
 	}
       else
 	return ::new(__loc) _Tp(std::forward<_Args>(__args)...);
diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc
index 3fe476d7eec..1e175ae7670 100644
--- a/gcc/cp/init.cc
+++ b/gcc/cp/init.cc
@@ -3662,11 +3662,11 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
   tree clobber_expr = NULL_TREE;
   if (do_clobber)
     {
-      tree clobber = build_clobber (elt_type, CLOBBER_OBJECT_BEGIN);
-      CONSTRUCTOR_IS_DIRECT_INIT (clobber) = true;
-      if (array_p)
+      if (array_p && TREE_CODE (cst_outer_nelts) != INTEGER_CST)
 	{
 	  /* Clobber each element rather than the array at once.  */
+	  tree clobber = build_clobber (elt_type, CLOBBER_OBJECT_BEGIN);
+	  CONSTRUCTOR_IS_DIRECT_INIT (clobber) = true;
 	  tree maxindex = cp_build_binary_op (input_location,
 					      MINUS_EXPR, outer_nelts,
 					      integer_one_node,
@@ -3677,7 +3677,21 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 	}
       else
 	{
-	  tree targ = cp_build_fold_indirect_ref (data_addr);
+	  tree targ = data_addr;
+	  tree ttype = type;
+	  if (array_p)
+	    {
+	      tree dom
+		= compute_array_index_type (NULL_TREE,
+					    CONST_CAST_TREE (cst_outer_nelts),
+					    complain);
+	      ttype = build_cplus_array_type (type, dom);
+	      tree ptype = build_pointer_type (ttype);
+	      targ = fold_convert (ptype, targ);
+	    }
+	  targ = cp_build_fold_indirect_ref (targ);
+	  tree clobber = build_clobber (ttype, CLOBBER_OBJECT_BEGIN);
+	  CONSTRUCTOR_IS_DIRECT_INIT (clobber) = true;
 	  clobber_expr = cp_build_init_expr (targ, clobber);
 	}
     }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C
index 1abbef18fae..dc406985f26 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new23.C
@@ -13,7 +13,10 @@ namespace std {
     constexpr T* construct_at(T* p)
     {
       if constexpr (__is_array(T))
-        return ::new((void*)p) T[1]();
+	{
+	  (void) ::new((void*)p) T();
+	  return __builtin_launder (p);
+	}
       else
         return ::new((void*)p) T();
     }
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-20.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-20.C
index 36f8046fed7..ec91e44f988 100644
--- a/gcc/testsuite/g++.dg/warn/Warray-bounds-20.C
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-20.C
@@ -53,18 +53,18 @@ void warn_derived_ctor_access_new_alloc ()
 
 void warn_derived_ctor_access_new_array_decl ()
 {
-  char b[sizeof (D1) * 2];    // { dg-message "at offset \\d+ into object 'b' of size 80" "LP64 note" { target { lp64 } xfail { lp64 } } }
-                              // { dg-message "at offset \\d+ into object 'b' of size 40" "LP64 note" { target { ilp32 } xfail { ilp32 } } .-1 }
+  char b[sizeof (D1) * 2];    // { dg-message "at offset \\d+ into object 'b' of size 80" "LP64 note" { target { lp64 } } }
+                              // { dg-message "at offset \\d+ into object 'b' of size 40" "LP64 note" { target { ilp32 } } .-1 }
   char *p = b;
   ++p;
-  D1 *q = new (p) D1[2];
+  D1 *q = new (p) D1[2];	// { dg-message "partly outside array bounds" }
   sink (q);
 }
 
 void warn_derived_ctor_access_new_array_alloc ()
 {
-  char *p = new char[sizeof (D1) * 2];            // { dg-message "at offset \\d+ into object of size \\d+ allocated by '\[^\n\r]*operator new\[^\n\r]*" "note" { xfail *-*-* } }
+  char *p = new char[sizeof (D1) * 2];            // { dg-message "at offset \\d+ into object of size \\d+ allocated by '\[^\n\r]*operator new\[^\n\r]*" "note" }
   ++p;
-  D1 *q = new (p) D1[2];
+  D1 *q = new (p) D1[2];	// { dg-message "partly outside array bounds" }
   sink (q);
 }

base-commit: 5ee5a05da06078975a933be849431eee1d4c6989
-- 
2.51.0



More information about the Libstdc++ mailing list