[clang] [Clang] Implement CWG 2282 (PR #215157)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 9 15:44:41 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Igor Kudrin (igorkudrin)
<details>
<summary>Changes</summary>
Link: https://wg21.link/cwg2282
For non-over-aligned types, overload resolution now falls back to aligned allocation functions.
---
Full diff: https://github.com/llvm/llvm-project/pull/215157.diff
8 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+3)
- (modified) clang/lib/Sema/SemaExprCXX.cpp (+22-9)
- (modified) clang/test/CXX/drs/cwg22xx.cpp (+26)
- (modified) clang/test/CXX/drs/cwg5xx.cpp (+1-1)
- (modified) clang/test/CXX/expr/expr.unary/expr.new/p14.cpp (+4-4)
- (modified) clang/test/SemaCXX/new-delete.cpp (+1-1)
- (modified) clang/test/SemaCXX/std-align-val-t-in-operator-new.cpp (+12-2)
- (modified) clang/www/cxx_dr_status.html (+1-1)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index fc947d05fad83..197e8e79da85b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -135,6 +135,9 @@ features cannot lower the translation-unit ABI level;
#### Resolutions to C++ Defect Reports
+- Clang now falls back to alignment-aware allocation functions for
+ non-overaligned types, implementing [CWG2282](https://wg21.link/cwg2282).
+
### C Language Changes
#### C2y Feature Support
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index a76146a8d914f..5709957b14439 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -2728,7 +2728,7 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
static void diagnoseNoViableFunctionForAllocationOverloadResolution(
Sema &S, const LookupResult &R, SourceRange Range, ArrayRef<Expr *> Args,
OverloadCandidateSet &Candidates, OverloadCandidateSet *AlignedCandidates,
- Expr *AlignArg, bool IncludedMSVCFallback) {
+ Expr *AlignArg, bool IncludedMSVCFallback, bool AlignedBeforeUnaligned) {
// If this is an allocation of the form 'new (p) X' for some object
// pointer p (or an expression that will decay to such a pointer),
// diagnose the reason for the error.
@@ -2784,10 +2784,13 @@ static void diagnoseNoViableFunctionForAllocationOverloadResolution(
S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call)
<< R.getLookupName() << Range;
- if (AlignedCandidates)
+ if (AlignedCandidates && AlignedBeforeUnaligned)
AlignedCandidates->NoteCandidates(S, AlignedArgs, AlignedCands, "",
R.getNameLoc());
Candidates.NoteCandidates(S, Args, Cands, "", R.getNameLoc());
+ if (AlignedCandidates && !AlignedBeforeUnaligned)
+ AlignedCandidates->NoteCandidates(S, AlignedArgs, AlignedCands, "",
+ R.getNameLoc());
if (IncludedMSVCFallback)
S.Diag(R.getNameLoc(), diag::note_ovl_ms_allocation_fallback_failed)
<< Range;
@@ -2896,6 +2899,7 @@ DiagnoseAllocationLookupFailure(Sema &SemaRef, const LookupResult &R,
ImplicitAllocationArguments *UnalignedArgumentList = nullptr;
ImplicitAllocationArguments *AlignedArgumentList = nullptr;
bool IncludedMSVCFallback = false;
+ bool AlignedBeforeUnaligned = true;
for (ImplicitAllocationArguments &AllocationArguments : ArgumentCandidates) {
if (AllocationArguments.IsMSVCCompatibilityFallback) {
IncludedMSVCFallback = true;
@@ -2903,10 +2907,12 @@ DiagnoseAllocationLookupFailure(Sema &SemaRef, const LookupResult &R,
}
if (AllocationArguments.PassTypeIdentity == TypeAwareAllocationMode::Yes)
continue;
- if (AllocationArguments.PassAlignment == AlignedAllocationMode::Yes)
+ if (AllocationArguments.PassAlignment == AlignedAllocationMode::Yes) {
AlignedArgumentList = &AllocationArguments;
- else
+ AlignedBeforeUnaligned = !UnalignedArgumentList;
+ } else {
UnalignedArgumentList = &AllocationArguments;
+ }
}
if (!UnalignedArgumentList)
return;
@@ -2939,7 +2945,7 @@ DiagnoseAllocationLookupFailure(Sema &SemaRef, const LookupResult &R,
diagnoseNoViableFunctionForAllocationOverloadResolution(
SemaRef, R, Range, UnalignedArgs, UnalignedCandidates,
AlignedCandidates ? &*AlignedCandidates : nullptr, AlignArg,
- IncludedMSVCFallback);
+ IncludedMSVCFallback, AlignedBeforeUnaligned);
}
Expr *Sema::tryGetTypeIdentityArgument(QualType Type, SourceLocation Loc) {
@@ -3045,13 +3051,20 @@ Sema::resolveAllocationArguments(LookupResult &R,
*this, /*TypeIdentityArg=*/nullptr, AllocationSizeExpr,
AllocationAlignmentExpr, /*IsMSVCCompatibilityFallback=*/false);
- // C++17 [expr.new]p13:
- // If no matching function is found and the allocated object type has
- // new-extended alignment, the alignment argument is removed from the
- // argument list, and overload resolution is performed again.
+ // C++20 [expr.new]p18:
+ // If no matching function is found then
+ // — if the allocated object type has new-extended alignment, the
+ // alignment argument is removed from the argument list;
+ // — otherwise, an argument that is the type’s alignment and has type
+ // std::align_val_t is added into the argument list immediately after
+ // the first argument;
+ // and then overload resolution is performed again.
if (IAP.PassAlignment == AlignedAllocationMode::Yes)
FoundArguments.push_back(AlignedArguments);
FoundArguments.push_back(UnalignedArguments);
+ if (IAP.PassAlignment == AlignedAllocationMode::No &&
+ AllocationAlignmentExpr && getLangOpts().AlignedAllocation)
+ FoundArguments.push_back(AlignedArguments);
// The MSVC global fallback path
if (getLangOpts().MSVCCompat &&
diff --git a/clang/test/CXX/drs/cwg22xx.cpp b/clang/test/CXX/drs/cwg22xx.cpp
index 6d51afcdda743..c04e81d8dee4e 100644
--- a/clang/test/CXX/drs/cwg22xx.cpp
+++ b/clang/test/CXX/drs/cwg22xx.cpp
@@ -6,6 +6,12 @@
// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,since-cxx11,since-cxx17
// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -fexceptions -fcxx-exceptions -pedantic-errors -verify-directives -verify=expected,since-cxx11,since-cxx17
+__extension__ typedef __SIZE_TYPE__ size_t;
+#if __cplusplus >= 201703L
+namespace std {
+ enum class align_val_t : size_t {};
+} // namespace std
+#endif
namespace cwg2211 { // cwg2211: 8
#if __cplusplus >= 201103L
@@ -196,6 +202,26 @@ void g() {
#endif
} // namespace cwg2277
+namespace cwg2282 { // cwg2282: 24
+#if __cplusplus >= 201703L
+struct A {
+ void *operator new(size_t, std::align_val_t) = delete; // #cwg2282-new-align
+ void *operator new(size_t, std::align_val_t, double) = delete; // #cwg2282-new-align-placement
+};
+
+void f() {
+ (void)new A;
+ // since-cxx17-error at -1 {{call to deleted function 'operator new'}}
+ // since-cxx17-note@#cwg2282-new-align {{candidate function has been explicitly deleted}}
+ // since-cxx17-note@#cwg2282-new-align-placement {{candidate function not viable: requires 3 arguments, but 2 were provided}}
+ (void)new (1.5) A;
+ // since-cxx17-error at -1 {{call to deleted function 'operator new'}}
+ // since-cxx17-note@#cwg2282-new-align-placement {{candidate function has been explicitly deleted}}
+ // since-cxx17-note@#cwg2282-new-align {{candidate function not viable: requires 2 arguments, but 3 were provided}}
+}
+#endif
+} // namespace cwg2282
+
namespace cwg2285 { // cwg2285: 4
// Note: Clang 4 implements this DR but it set a wrong value of `__cplusplus`
#if __cplusplus >= 201703L
diff --git a/clang/test/CXX/drs/cwg5xx.cpp b/clang/test/CXX/drs/cwg5xx.cpp
index 74e2464f60885..374ebf09baf34 100644
--- a/clang/test/CXX/drs/cwg5xx.cpp
+++ b/clang/test/CXX/drs/cwg5xx.cpp
@@ -679,8 +679,8 @@ namespace cwg553 {
// "is looked up in global scope", where it is not visible.
void *p = new (c) int;
// expected-error at -1 {{no matching function for call to 'operator new'}}
- // since-cxx17-note@#cwg5xx-global-operator-new-aligned {{candidate function not viable: no known conversion from 'cwg553_class' to 'std::align_val_t' for 2nd argument}}
// expected-note@#cwg5xx-global-operator-new {{candidate function not viable: requires 1 argument, but 2 were provided}}
+ // since-cxx17-note@#cwg5xx-global-operator-new-aligned {{candidate function not viable: requires 2 arguments, but 3 were provided}}
struct namespace_scope {
friend void *operator new(size_t, namespace_scope);
diff --git a/clang/test/CXX/expr/expr.unary/expr.new/p14.cpp b/clang/test/CXX/expr/expr.unary/expr.new/p14.cpp
index d0b24c8fe47b7..2f8404a83c00d 100644
--- a/clang/test/CXX/expr/expr.unary/expr.new/p14.cpp
+++ b/clang/test/CXX/expr/expr.unary/expr.new/p14.cpp
@@ -6,7 +6,7 @@ namespace std { enum class align_val_t : size_t {}; }
struct Arg {} arg;
// If the type is aligned, first try with an alignment argument and then
-// without. If not, never consider supplying an alignment.
+// without. If not, try in the reverse order.
template<unsigned Align, typename ...Ts>
struct alignas(Align) Unaligned {
@@ -19,11 +19,11 @@ auto *ubp = new (arg) Unaligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; // e
template<unsigned Align, typename ...Ts>
struct alignas(Align) Aligned {
- void *operator new(size_t, std::align_val_t, Ts...) = delete; // expected-note 2{{deleted}} expected-note 2{{not viable}}
+ void *operator new(size_t, std::align_val_t, Ts...) = delete; // expected-note 4{{deleted}}
};
-auto *aa = new Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; // expected-error {{no matching}}
+auto *aa = new Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__>; // expected-error {{deleted}}
auto *ab = new Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2>; // expected-error {{deleted}}
-auto *aap = new (arg) Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; // expected-error {{no matching}}
+auto *aap = new (arg) Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__, Arg>; // expected-error {{deleted}}
auto *abp = new (arg) Aligned<__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2, Arg>; // expected-error {{deleted}}
// If both are available, we prefer the aligned version for an overaligned
diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp
index 2a2f91186871e..f8a1743521415 100644
--- a/clang/test/SemaCXX/new-delete.cpp
+++ b/clang/test/SemaCXX/new-delete.cpp
@@ -15,7 +15,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify=expected,since-cxx26,cxx17,cxx20 %s -triple=i686-pc-linux-gnu -Wno-new-returns-null -std=c++2c -fexperimental-new-constant-interpreter -DNEW_INTERP
// FIXME Location is (frontend)
-// cxx17-note@*:* {{candidate function not viable: requires 2 arguments, but 3 were provided}}
+// cxx17-note@*:* {{candidate function not viable: requires 2 arguments, but 4 were provided}}
#include <stddef.h>
diff --git a/clang/test/SemaCXX/std-align-val-t-in-operator-new.cpp b/clang/test/SemaCXX/std-align-val-t-in-operator-new.cpp
index 9c34cb8e0d508..bb8333e38d1dc 100644
--- a/clang/test/SemaCXX/std-align-val-t-in-operator-new.cpp
+++ b/clang/test/SemaCXX/std-align-val-t-in-operator-new.cpp
@@ -62,9 +62,14 @@ void *operator new(std::size_t, std::align_val_t, X); // #3
// FIXME: Consider improving notes 1 and 3 here to say that these are aligned
// allocation functions and the type is not over-aligned.
X *p = new (123) X; // expected-error {{no matching function}}
+#if __cpp_aligned_new
+// expected-note@#1 {{requires 2 arguments, but 3 were provided}}
+// expected-note@#3 {{no known conversion from 'int' to 'X' for 3rd argument}}
+#else
// expected-note@#1 {{no known conversion from 'int' to 'std::align_val_t' for 2nd argument}}
-// expected-note@#2 {{no known conversion from 'int' to 'X' for 2nd argument}}
// expected-note@#3 {{requires 3 arguments}}
+#endif
+// expected-note@#2 {{no known conversion from 'int' to 'X' for 2nd argument}}
// expected-note@* {{requires 1 argument, but 2 were provided}} (builtin)
#ifdef __cpp_aligned_new
@@ -77,7 +82,12 @@ Y *q = new (123) Y; // expected-error {{no matching function}}
#endif
X *r = new (std::align_val_t(32), 123) X; // expected-error {{no matching function}}
+#ifdef __cpp_aligned_new
+// expected-note@#1 {{requires 2 arguments, but 4 were provided}}
+// expected-note@#3 {{requires 3 arguments, but 4 were provided}}
+#else
// expected-note@#1 {{requires 2 arguments, but 3 were provided}}
-// expected-note@#2 {{requires 2 arguments, but 3 were provided}}
// expected-note@#3 {{no known conversion from 'int' to 'X' for 3rd argument}}
+#endif
+// expected-note@#2 {{requires 2 arguments, but 3 were provided}}
// expected-note@* {{requires 1 argument, but 3 were provided}} (builtin)
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 4d29e6b4b32f4..a2a96f470d94a 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -15763,7 +15763,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>[<a href="https://wg21.link/expr.new">expr.new</a>]</td>
<td>C++20</td>
<td>Consistency with mismatched aligned/non-over-aligned allocation/deallocation functions</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 24</td>
</tr>
<tr id="2283">
<td><a href="https://cplusplus.github.io/CWG/issues/2283.html">2283</a></td>
``````````
</details>
https://github.com/llvm/llvm-project/pull/215157
More information about the cfe-commits
mailing list