[clang] [clang][P2719] Relax requirements for matching operator new and delete (PR #195779)
Oliver Hunt via cfe-commits
cfe-commits at lists.llvm.org
Tue May 5 01:42:15 PDT 2026
https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/195779
>From c0a8d9f031cab7d07504475e99023be3ef25bc08 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Mon, 4 May 2026 19:23:45 -0700
Subject: [PATCH 1/3] [clang][P2719] Relax requirements for matching operator
new and delete
The most recent revision of P2719 introduced very strict rules about
matching parameter sets between type aware operators new and delete.
The intention was to resolve the classic "no matching operator delete
has been found so the object will silently leak" problem. The strict
rules however made deleting objects that had a placement new
"impossible".
I missed this however as all of our large scale tests involved
projects that were already using manually implemented allocators
(often trying to support type isolation). The problem with this from
a validation point of view is simple: all of these projects had
existing class scoped operators, and the untyped delete was silently
selected, avoiding the need for a non-placement type-aware delete
that would conflict with the placement cleanup delete.
The next revision of P2719 resolves this by removing the exact type
matching requirements and simply enforcing the intended semantics
directly: if a type aware allocation function is called and there
is a need for exception cleanup, it is an error if an appropriate
operator delete cannot be found.
This PR implements that behavior.
---
.../clang/Basic/DiagnosticSemaKinds.td | 11 +-
clang/lib/Sema/SemaCoroutine.cpp | 2 +-
clang/lib/Sema/SemaDeclCXX.cpp | 65 ++++++-----
clang/lib/Sema/SemaExprCXX.cpp | 102 ++++++++++--------
...re-class-scoped-mismatched-constraints.cpp | 85 +++++++++------
clang/test/SemaCXX/type-aware-coroutines.cpp | 6 --
...type-aware-new-delete-basic-resolution.cpp | 76 ++++++++++---
.../type-aware-placement-operators.cpp | 2 +-
8 files changed, 218 insertions(+), 131 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e059260778631..4ef8441bd9747 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10396,14 +10396,17 @@ def err_type_aware_destroying_operator_delete : Error<
def warn_ext_type_aware_allocators : ExtWarn<
"type aware allocators are a Clang extension">, InGroup<DiagGroup<"ext-cxx-type-aware-allocators">>;
def err_type_aware_allocator_missing_matching_operator : Error<
- "declaration of type aware %0 in %1 must have matching type aware %2"
+ "declaration of type aware %0 in %1 must have an %2 in the same scope"
>;
def note_unmatched_type_aware_allocator_declared : Note<
"unmatched type aware %0 declared here">;
-def err_mismatching_type_aware_cleanup_deallocator : Error<
- "type aware %0 requires a matching type aware %select{|placement }1%2 to be declared in the same scope">;
+def err_type_aware_new_missing_cleanup_operator : Error<
+ "type aware %0 requires %select{a matching|an unambiguous|the}1 %select{|placement }2cleanup %3%select{|| to be declared in the same scope}1">;
+def note_type_aware_ambiguous_delete : Error<
+ "cleanup candidate %0 declared here"
+>;
def note_type_aware_operator_declared : Note<
- "%select{non-|}0type aware %1 declared here in %2">;
+ "type aware %0 declared here in %1">;
def note_implicit_delete_this_in_destructor_here : Note<
"while checking implicit 'delete this' for virtual destructor">;
def err_builtin_operator_new_delete_not_usual : Error<
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 7f9b1d642cf9d..679decd7e9752 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1120,7 +1120,7 @@ static bool DiagnoseTypeAwareAllocators(Sema &S, SourceLocation Loc,
HaveIssuedWarning = true;
}
S.Diag(Decl->getLocation(), diag::note_type_aware_operator_declared)
- << /* isTypeAware=*/1 << Decl << Decl->getDeclContext();
+ << Decl << Decl->getDeclContext();
}
R.suppressDiagnostics();
return HaveIssuedWarning;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1e339fee29ab8..1791680de8359 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7448,42 +7448,53 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
llvm::SmallDenseMap<OverloadedOperatorKind,
llvm::SmallVector<const FunctionDecl *, 2>, 4>
- TypeAwareDecls{{OO_New, {}},
- {OO_Array_New, {}},
- {OO_Delete, {}},
- {OO_Array_New, {}}};
+ TypeAwareAllocatorDeclKinds;
+ llvm::SmallDenseSet<OverloadedOperatorKind, 4> AllocatorDeclsKinds;
for (auto *D : Record->decls()) {
const FunctionDecl *FnDecl = D->getAsFunction();
- if (!FnDecl || !FnDecl->isTypeAwareOperatorNewOrDelete())
+ if (!FnDecl)
continue;
- assert(FnDecl->getDeclName().isAnyOperatorNewOrDelete());
- TypeAwareDecls[FnDecl->getOverloadedOperator()].push_back(FnDecl);
+ DeclarationName FnName = FnDecl->getDeclName();
+ if (!FnName.isAnyOperatorNewOrDelete())
+ continue;
+ OverloadedOperatorKind OperatorKind = FnDecl->getOverloadedOperator();
+ if (FnDecl->isTypeAwareOperatorNewOrDelete())
+ TypeAwareAllocatorDeclKinds[OperatorKind].push_back(FnDecl);
+ AllocatorDeclsKinds.insert(OperatorKind);
}
+
auto CheckMismatchedTypeAwareAllocators =
- [this, &TypeAwareDecls, Record](OverloadedOperatorKind NewKind,
- OverloadedOperatorKind DeleteKind) {
- auto &NewDecls = TypeAwareDecls[NewKind];
- auto &DeleteDecls = TypeAwareDecls[DeleteKind];
- if (NewDecls.empty() == DeleteDecls.empty())
+ [&](OverloadedOperatorKind NewKind, OverloadedOperatorKind DeleteKind) {
+ if (!TypeAwareAllocatorDeclKinds.contains(NewKind) &&
+ !TypeAwareAllocatorDeclKinds.contains(DeleteKind))
+ return;
+ if (TypeAwareAllocatorDeclKinds.contains(NewKind) &&
+ AllocatorDeclsKinds.contains(DeleteKind))
+ return;
+ if (TypeAwareAllocatorDeclKinds.contains(DeleteKind) &&
+ AllocatorDeclsKinds.contains(NewKind))
return;
- DeclarationName FoundOperator =
- Context.DeclarationNames.getCXXOperatorName(
- NewDecls.empty() ? DeleteKind : NewKind);
- DeclarationName MissingOperator =
- Context.DeclarationNames.getCXXOperatorName(
- NewDecls.empty() ? NewKind : DeleteKind);
+
+ bool TypeAwareOperatorIsNew =
+ TypeAwareAllocatorDeclKinds.contains(NewKind);
+ OverloadedOperatorKind TypedAllocatorKind =
+ TypeAwareOperatorIsNew ? NewKind : DeleteKind;
+ OverloadedOperatorKind UntypedAllocatorKind =
+ TypeAwareOperatorIsNew ? DeleteKind : NewKind;
+ DeclarationName TypedOperator =
+ Context.DeclarationNames.getCXXOperatorName(TypedAllocatorKind);
+ DeclarationName UntypedOperator =
+ Context.DeclarationNames.getCXXOperatorName(UntypedAllocatorKind);
+
Diag(Record->getLocation(),
diag::err_type_aware_allocator_missing_matching_operator)
- << FoundOperator << Context.getCanonicalTagType(Record)
- << MissingOperator;
- for (auto MD : NewDecls)
- Diag(MD->getLocation(),
- diag::note_unmatched_type_aware_allocator_declared)
- << MD;
- for (auto MD : DeleteDecls)
- Diag(MD->getLocation(),
+ << TypedOperator << Context.getCanonicalTagType(Record)
+ << UntypedOperator;
+ for (const FunctionDecl *UnmatchedTypeAwareDecl :
+ TypeAwareAllocatorDeclKinds[TypedAllocatorKind])
+ Diag(UnmatchedTypeAwareDecl->getLocation(),
diag::note_unmatched_type_aware_allocator_declared)
- << MD;
+ << UnmatchedTypeAwareDecl;
};
CheckMismatchedTypeAwareAllocators(OO_New, OO_Delete);
CheckMismatchedTypeAwareAllocators(OO_Array_New, OO_Array_Delete);
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index e60d5d7748b44..6004781f9806e 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3133,20 +3133,38 @@ bool Sema::FindAllocationFunctions(
bool IsClassScopedTypeAwareNew =
isTypeAwareAllocation(IAP.PassTypeIdentity) &&
OperatorNewContext->isRecord();
- auto DiagnoseMissingTypeAwareCleanupOperator = [&](bool IsPlacementOperator) {
- assert(isTypeAwareAllocation(IAP.PassTypeIdentity));
- if (Diagnose) {
- Diag(StartLoc, diag::err_mismatching_type_aware_cleanup_deallocator)
- << OperatorNew->getDeclName() << IsPlacementOperator << DeleteName;
- Diag(OperatorNew->getLocation(), diag::note_type_aware_operator_declared)
- << OperatorNew->isTypeAwareOperatorNewOrDelete()
- << OperatorNew->getDeclName() << OperatorNewContext;
- }
+ enum class TypeAwareCleanupOperatorError {
+ Missing,
+ Ambiguous,
+ MismatchedScope
};
+ auto DiagnoseMissingTypeAwareCleanupOperator =
+ [&](bool IsPlacementOperator, TypeAwareCleanupOperatorError Error,
+ FunctionDecl *OperatorDelete = 0) {
+ assert(isTypeAwareAllocation(IAP.PassTypeIdentity));
+ if (Diagnose) {
+ Diag(StartLoc, diag::err_type_aware_new_missing_cleanup_operator)
+ << OperatorNew->getDeclName() << Error << IsPlacementOperator
+ << DeleteName;
+ Diag(OperatorNew->getLocation(),
+ diag::note_type_aware_operator_declared)
+ << OperatorNew->getDeclName() << OperatorNewContext;
+ if (Error == TypeAwareCleanupOperatorError::MismatchedScope) {
+ DeclContext *OperatorDeleteContext =
+ GetRedeclContext(OperatorDelete);
+ Diag(OperatorDelete->getLocation(),
+ diag::note_type_aware_operator_declared)
+ << OperatorDelete->getDeclName() << OperatorDeleteContext;
+ }
+ }
+ };
+
if (IsClassScopedTypeAwareNew && FoundDelete.empty()) {
- DiagnoseMissingTypeAwareCleanupOperator(/*isPlacementNew=*/false);
+ DiagnoseMissingTypeAwareCleanupOperator(
+ /*isPlacementNew=*/false, TypeAwareCleanupOperatorError::Missing);
return true;
}
+
if (FoundDelete.empty()) {
FoundDelete.clear(LookupOrdinaryName);
@@ -3163,6 +3181,12 @@ bool Sema::FindAllocationFunctions(
FoundDelete.suppressDiagnostics();
+ if (OperatorNew->isTypeAwareOperatorNewOrDelete() && FoundDelete.empty()) {
+ DiagnoseMissingTypeAwareCleanupOperator(
+ /*isPlacementNew=*/false, TypeAwareCleanupOperatorError::Missing);
+ return true;
+ }
+
SmallVector<std::pair<DeclAccessPair,FunctionDecl*>, 2> Matches;
// Whether we're looking for a placement operator delete is dictated
@@ -3247,10 +3271,6 @@ bool Sema::FindAllocationFunctions(
if (getLangOpts().CUDA)
CUDA().EraseUnwantedMatches(getCurFunctionDecl(/*AllowLambda=*/true),
Matches);
- if (Matches.empty() && isTypeAwareAllocation(IAP.PassTypeIdentity)) {
- DiagnoseMissingTypeAwareCleanupOperator(isPlacementNew);
- return true;
- }
} else {
// C++1y [expr.new]p22:
// For a non-placement allocation function, the normal deallocation
@@ -3277,40 +3297,38 @@ bool Sema::FindAllocationFunctions(
}
}
+ if (OperatorNew->isTypeAwareOperatorNewOrDelete()) {
+ if (Matches.size() == 0) {
+ DiagnoseMissingTypeAwareCleanupOperator(
+ isPlacementNew, TypeAwareCleanupOperatorError::Missing);
+ return true;
+ }
+ if (Matches.size() > 1) {
+ DiagnoseMissingTypeAwareCleanupOperator(
+ isPlacementNew, TypeAwareCleanupOperatorError::Ambiguous);
+ for (auto &Match : Matches)
+ Diag(Match.second->getLocation(),
+ diag::note_type_aware_ambiguous_delete)
+ << DeleteName;
+ return true;
+ }
+ FunctionDecl *OperatorDelete = Matches[0].second;
+ DeclContext *OperatorNewContext = GetRedeclContext(OperatorNew);
+ DeclContext *OperatorDeleteContext = GetRedeclContext(OperatorDelete);
+ if (OperatorDeleteContext != OperatorNewContext) {
+ DiagnoseMissingTypeAwareCleanupOperator(
+ isPlacementNew, TypeAwareCleanupOperatorError::MismatchedScope,
+ OperatorDelete);
+ return true;
+ }
+ }
+
// C++ [expr.new]p20:
// [...] If the lookup finds a single matching deallocation
// function, that function will be called; otherwise, no
// deallocation function will be called.
if (Matches.size() == 1) {
OperatorDelete = Matches[0].second;
- DeclContext *OperatorDeleteContext = GetRedeclContext(OperatorDelete);
- bool FoundTypeAwareOperator =
- OperatorDelete->isTypeAwareOperatorNewOrDelete() ||
- OperatorNew->isTypeAwareOperatorNewOrDelete();
- if (Diagnose && FoundTypeAwareOperator) {
- bool MismatchedTypeAwareness =
- OperatorDelete->isTypeAwareOperatorNewOrDelete() !=
- OperatorNew->isTypeAwareOperatorNewOrDelete();
- bool MismatchedContext = OperatorDeleteContext != OperatorNewContext;
- if (MismatchedTypeAwareness || MismatchedContext) {
- FunctionDecl *Operators[] = {OperatorDelete, OperatorNew};
- bool TypeAwareOperatorIndex =
- OperatorNew->isTypeAwareOperatorNewOrDelete();
- Diag(StartLoc, diag::err_mismatching_type_aware_cleanup_deallocator)
- << Operators[TypeAwareOperatorIndex]->getDeclName()
- << isPlacementNew
- << Operators[!TypeAwareOperatorIndex]->getDeclName()
- << GetRedeclContext(Operators[TypeAwareOperatorIndex]);
- Diag(OperatorNew->getLocation(),
- diag::note_type_aware_operator_declared)
- << OperatorNew->isTypeAwareOperatorNewOrDelete()
- << OperatorNew->getDeclName() << OperatorNewContext;
- Diag(OperatorDelete->getLocation(),
- diag::note_type_aware_operator_declared)
- << OperatorDelete->isTypeAwareOperatorNewOrDelete()
- << OperatorDelete->getDeclName() << OperatorDeleteContext;
- }
- }
// C++1z [expr.new]p23:
// If the lookup finds a usual deallocation function (3.7.4.2)
diff --git a/clang/test/SemaCXX/type-aware-class-scoped-mismatched-constraints.cpp b/clang/test/SemaCXX/type-aware-class-scoped-mismatched-constraints.cpp
index 4ccc256d63629..2495b5022a390 100644
--- a/clang/test/SemaCXX/type-aware-class-scoped-mismatched-constraints.cpp
+++ b/clang/test/SemaCXX/type-aware-class-scoped-mismatched-constraints.cpp
@@ -25,42 +25,42 @@ void operator delete(void*, size_t, std::align_val_t) noexcept; // #default_oper
#endif
struct Invalid1 {
- // expected-error at -1 {{declaration of type aware 'operator new' in 'Invalid1' must have matching type aware 'operator delete'}}
+ // expected-error at -1 {{declaration of type aware 'operator new' in 'Invalid1' must have an 'operator delete'}}
void *operator new(std::type_identity<Invalid1>, size_t, std::align_val_t);
// expected-note at -1 {{unmatched type aware 'operator new' declared here}}
};
struct Invalid2 {
- // expected-error at -1 {{declaration of type aware 'operator new' in 'Invalid2' must have matching type aware 'operator delete'}}
+ // expected-error at -1 {{declaration of type aware 'operator new' in 'Invalid2' must have an 'operator delete'}}
template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #Invalid2_new
// expected-note at -1 {{unmatched type aware 'operator new' declared here}}
};
struct Invalid3 {
- // expected-error at -1 {{declaration of type aware 'operator delete' in 'Invalid3' must have matching type aware 'operator new'}}
+ // expected-error at -1 {{declaration of type aware 'operator delete' in 'Invalid3' must have an 'operator new'}}
void operator delete(std::type_identity<Invalid3>, void*, size_t, std::align_val_t);
// expected-note at -1 {{unmatched type aware 'operator delete' declared here}}
};
struct Invalid4 {
- // expected-error at -1 {{declaration of type aware 'operator delete' in 'Invalid4' must have matching type aware 'operator new'}}
+ // expected-error at -1 {{declaration of type aware 'operator delete' in 'Invalid4' must have an 'operator new'}}
template <class T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #Invalid4_delete
// expected-note at -1 {{unmatched type aware 'operator delete' declared here}}
};
struct Invalid5 {
- // expected-error at -1 {{declaration of type aware 'operator new[]' in 'Invalid5' must have matching type aware 'operator delete[]'}}
+ // expected-error at -1 {{declaration of type aware 'operator new[]' in 'Invalid5' must have an 'operator delete[]'}}
void *operator new[](std::type_identity<Invalid5>, size_t, std::align_val_t);
// expected-note at -1 {{unmatched type aware 'operator new[]' declared here}}
};
struct Invalid6 {
- // expected-error at -1 {{declaration of type aware 'operator new[]' in 'Invalid6' must have matching type aware 'operator delete[]'}}
+ // expected-error at -1 {{declaration of type aware 'operator new[]' in 'Invalid6' must have an 'operator delete[]'}}
template <class T> void *operator new[](std::type_identity<T>, size_t, std::align_val_t);
// expected-note at -1 {{unmatched type aware 'operator new[]' declared here}}
};
struct Invalid7 {
- // expected-error at -1 {{declaration of type aware 'operator delete[]' in 'Invalid7' must have matching type aware 'operator new[]'}}
+ // expected-error at -1 {{declaration of type aware 'operator delete[]' in 'Invalid7' must have an 'operator new[]'}}
void operator delete[](std::type_identity<Invalid7>, void*, size_t, std::align_val_t);
// expected-note at -1 {{unmatched type aware 'operator delete[]' declared here}}
};
struct Invalid8 {
- // expected-error at -1 {{declaration of type aware 'operator delete[]' in 'Invalid8' must have matching type aware 'operator new[]'}}
+ // expected-error at -1 {{declaration of type aware 'operator delete[]' in 'Invalid8' must have an 'operator new[]'}}
template <class T> void operator delete[](std::type_identity<T>, void*, size_t, std::align_val_t);
// expected-note at -1 {{unmatched type aware 'operator delete[]' declared here}}
};
@@ -89,6 +89,8 @@ struct TestClass2 {
void basic_tests() {
TestClass1 * tc1 = new TestClass1;
+ // expected-error at -1 {{type aware 'operator new' requires a matching cleanup 'operator delete}}
+ // expected-note@#TestClass1_new {{type aware 'operator new' declared here in 'TestClass1'}}
delete tc1;
// expected-error at -1 {{no suitable member 'operator delete' in 'TestClass1'}}
// expected-note@#TestClass1_delete {{member 'operator delete' declared here}}
@@ -96,23 +98,20 @@ void basic_tests() {
// expected-error at -1 {{no matching function for call to 'operator new'}}
delete tc2;
Invalid9 * i9 = new Invalid9;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires a matching cleanup 'operator delete'}}
// expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
delete i9;
Invalid10 * i10 = new Invalid10;
- // expected-error at -1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
// expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
- // expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
delete i10;
Invalid11 * i11 = new Invalid11;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
- // expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
+ // expected-error at -1 {{type aware 'operator new' requires the cleanup 'operator delete' to be declared in the same scope}}
+ // expected-note@#Invalid2_new {{type aware 'operator new' declared here}}
// expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
delete i11;
Invalid12 * i12 = new Invalid12;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires the cleanup 'operator delete' to be declared in the same scope}}
// expected-note@#Invalid2_new {{type aware 'operator new' declared here in 'Invalid2'}}
- // expected-note@#Invalid4_delete {{type aware 'operator delete' declared here in 'Invalid4'}}
delete i12;
}
@@ -126,7 +125,7 @@ struct Subclass1 : Baseclass1 {
};
struct Baseclass2 {
- template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+ template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #Baseclass2_new
void operator delete(std::type_identity<Baseclass2>, void *, size_t, std::align_val_t); // #Baseclass2_delete
};
@@ -147,7 +146,7 @@ struct Subclass3_1 : Baseclass3 {
struct Subclass3_2 : Baseclass3 {
Subclass3_2();
- template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
+ template <class T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #Subclass3_2_new
void operator delete(std::type_identity<int>, void *, size_t, std::align_val_t); // #Subclass3_2_delete
};
@@ -159,6 +158,8 @@ void test_subclasses() {
// expected-error at -1 {{no suitable member 'operator delete' in 'Subclass1'}}
// expected-note@#Baseclass1_delete {{member 'operator delete' declared here}}
Subclass2 * sc2 = new Subclass2;
+ // expected-error at -1 {{type aware 'operator new' requires a matching cleanup 'operator delete'}}
+ // expected-note@#Baseclass2_new {{type aware 'operator new' declared here in 'Baseclass2'}}
delete sc2;
// expected-error at -1 {{no suitable member 'operator delete' in 'Subclass2'}}
// expected-note@#Baseclass2_delete {{member 'operator delete' declared here}}
@@ -166,6 +167,8 @@ void test_subclasses() {
// expected-error at -1 {{no matching function for call to 'operator new'}}
delete sc3_1;
Subclass3_2 * sc3_2 = new Subclass3_2;
+ // expected-error at -1 {{type aware 'operator new' requires a matching cleanup 'operator delete'}}
+ // expected-note@#Subclass3_2_new {{type aware 'operator new' declared here in 'Subclass3_2'}}
delete sc3_2;
// expected-error at -1 {{no suitable member 'operator delete' in 'Subclass3_2'}}
// expected-note@#Subclass3_2_delete {{member 'operator delete' declared here}}
@@ -175,7 +178,7 @@ template <class A, class B> constexpr bool same_type_v = false;
template <class A> constexpr bool same_type_v<A, A> = true;
template <class T> struct InvalidConstrainedOperator {
- template <class U> void *operator new(std::type_identity<U>, size_t, std::align_val_t);
+ template <class U> void *operator new(std::type_identity<U>, size_t, std::align_val_t); // #InvalidConstrainedOperator_new
template <class U> requires (same_type_v<T, int>) void operator delete(std::type_identity<U>, void *, size_t, std::align_val_t); // #InvalidConstrainedOperator_delete
};
@@ -190,13 +193,15 @@ void test_incompatible_constrained_operators(Context &Ctx) {
InvalidConstrainedOperator<int> *ico1 = new InvalidConstrainedOperator<int>;
delete ico1;
InvalidConstrainedOperator<float> *ico2 = new InvalidConstrainedOperator<float>;
+ // expected-error at -1 {{type aware 'operator new' requires a matching cleanup 'operator delete'}}
+ // expected-note@#InvalidConstrainedOperator_new {{type aware 'operator new' declared here in 'InvalidConstrainedOperator<float>'}}
delete ico2;
// expected-error at -1 {{no suitable member 'operator delete' in 'InvalidConstrainedOperator<float>'}}
// expected-note@#InvalidConstrainedOperator_delete {{member 'operator delete' declared here}}
InvalidConstrainedCleanup<int> *icc1 = new (Ctx) InvalidConstrainedCleanup<int>;
delete icc1;
InvalidConstrainedCleanup<float> *icc2 = new (Ctx) InvalidConstrainedCleanup<float>;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires a matching placement cleanup 'operator delete'}}
// expected-note@#InvalidConstrainedCleanup_placement_new {{type aware 'operator new' declared here in 'InvalidConstrainedCleanup<float>'}}
delete icc2;
}
@@ -233,31 +238,45 @@ void *operator new(std::type_identity<AnonymousClass6>, size_t, std::align_val_t
void operator delete(std::type_identity<AnonymousClass6>, void*, size_t, std::align_val_t);
void test_anonymous_types(Context &Ctx) {
AnonymousClass1 *ac1 = new AnonymousClass1;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires a matching cleanup 'operator delete'}}
// expected-note@#AnonymousClass1_new {{type aware 'operator new' declared here in 'AnonymousClass1'}}
delete ac1;
AnonymousClass2 *ac2 = new AnonymousClass2;
- // expected-error at -1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
- // expected-note@#AnonymousClass2_delete {{unmatched type aware 'operator delete' declared here}}
- // expected-note@#AnonymousClass2_delete {{type aware 'operator delete' declared here in 'AnonymousClass2'}}
- // expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
-
delete ac2;
AnonymousClass3 *ac3 = new AnonymousClass3;
delete ac3;
AnonymousClass4 *ac4 = new AnonymousClass4;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
- // expected-note@#AnonymousClass4_new {{type aware 'operator new' declared here in the global namespace}}
delete ac4;
AnonymousClass5 *ac5 = new AnonymousClass5;
- // expected-error at -1 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
- // expected-note@#AnonymousClass5_delete {{type aware 'operator delete' declared here}}
- // expected-note@#default_operator_new {{non-type aware 'operator new' declared here in the global namespace}}
delete ac5;
AnonymousClass6 *ac6 = new (Ctx) AnonymousClass6;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires a matching placement cleanup 'operator delete'}}
// expected-note@#AnonymousClass6_placement_new {{type aware 'operator new' declared here in the global namespace}}
- // expected-note@#default_operator_delete {{non-type aware 'operator delete' declared here in the global namespace}}
+ // expected-note@#AnonymousClass2_delete {{unmatched type aware 'operator delete' declared here}}
delete ac6;
-}
\ No newline at end of file
+}
+struct AllocatorContext;
+struct NonPlacementTypedDeleteIsAnOption {
+ NonPlacementTypedDeleteIsAnOption();
+ void *operator new(std::type_identity<NonPlacementTypedDeleteIsAnOption>, size_t, std::align_val_t, AllocatorContext *);
+ void operator delete(std::type_identity<NonPlacementTypedDeleteIsAnOption>, void *, size_t, std::align_val_t, AllocatorContext *);
+ void operator delete(std::type_identity<NonPlacementTypedDeleteIsAnOption>, void *, size_t, std::align_val_t);
+};
+
+void typeAwarePlacementDelete(AllocatorContext *Ctx) {
+ NonPlacementTypedDeleteIsAnOption *Obj = new (Ctx) NonPlacementTypedDeleteIsAnOption;
+ delete Obj;
+}
+
+struct UntypedDeleteIsAnOption {
+ UntypedDeleteIsAnOption();
+ void *operator new(std::type_identity<UntypedDeleteIsAnOption>, size_t, std::align_val_t, AllocatorContext *);
+ void operator delete(std::type_identity<UntypedDeleteIsAnOption>, void *, size_t, std::align_val_t, AllocatorContext *);
+ void operator delete(void *);
+};
+
+void nontypeAwareDelete(AllocatorContext *Ctx) {
+ UntypedDeleteIsAnOption *Obj = new (Ctx) UntypedDeleteIsAnOption;
+ delete Obj;
+}
diff --git a/clang/test/SemaCXX/type-aware-coroutines.cpp b/clang/test/SemaCXX/type-aware-coroutines.cpp
index e41d07b9bccf5..2fefa3542ce76 100644
--- a/clang/test/SemaCXX/type-aware-coroutines.cpp
+++ b/clang/test/SemaCXX/type-aware-coroutines.cpp
@@ -46,8 +46,6 @@ struct resumable2 {
struct resumable3 {
struct promise_type {
- // expected-error at -1 {{declaration of type aware 'operator new' in 'resumable3::promise_type' must have matching type aware 'operator delete'}}
- // expected-note@#resumable3_tan {{unmatched type aware 'operator new' declared here}}
void *operator new(std::size_t sz, float);
void *operator new(std::type_identity<promise_type>, std::size_t sz, std::align_val_t, float); // #resumable3_tan
void operator delete(void *);
@@ -62,8 +60,6 @@ struct resumable3 {
};
struct resumable4 {
struct promise_type {
- // expected-error at -1 {{declaration of type aware 'operator delete' in 'resumable4::promise_type' must have matching type aware 'operator new'}}
- // expected-note@#resumable4_tad {{unmatched type aware 'operator delete' declared here}}
void *operator new(std::size_t sz, float);
template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t, std::align_val_t); // #resumable4_tad
@@ -77,8 +73,6 @@ struct resumable4 {
};
struct resumable5 {
struct promise_type {
- // expected-error at -1 {{declaration of type aware 'operator delete' in 'resumable5::promise_type' must have matching type aware 'operator new'}}
- // expected-note@#resumable5_tad {{unmatched type aware 'operator delete' declared here}}
void *operator new(std::size_t sz, float);
void operator delete(void *);
template <typename T> void operator delete(std::type_identity<T>, void *, std::size_t, std::align_val_t); // #resumable5_tad
diff --git a/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
index 978f6ee5cf6c6..9f8bb6f57f846 100644
--- a/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
+++ b/clang/test/SemaCXX/type-aware-new-delete-basic-resolution.cpp
@@ -83,8 +83,6 @@ struct InclassNew5 {
};
struct InclassNew6 {
- // expected-error at -1 {{declaration of type aware 'operator delete' in 'InclassNew6' must have matching type aware 'operator new'}}
- // expected-note@#36 {{unmatched type aware 'operator delete' declared here}}
InclassNew6();
void *operator new(size_t); // #34
void operator delete(void *) = delete; // #35
@@ -99,15 +97,13 @@ struct InclassNew7 {
};
struct InclassNew8 {
- // expected-error at -1 {{declaration of type aware 'operator new' in 'InclassNew8' must have matching type aware 'operator delete'}}
- // expected-note@#40 {{unmatched type aware 'operator new' declared here}}
InclassNew8();
void *operator new(std::type_identity<InclassNew8>, size_t, std::align_val_t); // #40
void operator delete(void*); // #41
};
struct InclassNew9 {
- // expected-error at -1 {{declaration of type aware 'operator new' in 'InclassNew9' must have matching type aware 'operator delete'}}
+ // expected-error at -1 {{declaration of type aware 'operator new' in 'InclassNew9' must have an 'operator delete' in the same scope}}
// expected-note@#42 {{unmatched type aware 'operator new' declared here}}
InclassNew9();
void *operator new(std::type_identity<InclassNew9>, size_t, std::align_val_t); // #42
@@ -222,13 +218,13 @@ BaseClass6::~BaseClass6(){
}
struct SubClass6_1 : BaseClass6 {
- // expected-error at -1 {{declaration of type aware 'operator new' in 'SubClass6_1' must have matching type aware 'operator delete'}}
+ // expected-error at -1 {{declaration of type aware 'operator new' in 'SubClass6_1' must have an 'operator delete' in the same scope}}
// expected-note@#62 {{unmatched type aware 'operator new' declared here}}
template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #62
SubClass6_1();
};
struct SubClass6_2 : BaseClass6 {
- // expected-error at -1 {{declaration of type aware 'operator delete' in 'SubClass6_2' must have matching type aware 'operator new'}}
+ // expected-error at -1 {{declaration of type aware 'operator delete' in 'SubClass6_2' must have an 'operator new'}}
// expected-note@#63 {{unmatched type aware 'operator delete' declared here}}
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t); // #63
SubClass6_2();
@@ -256,10 +252,37 @@ struct MultiDimensionArrayTest3 {
};
struct ClassScopedTemplatePackStruct {
- template <class T, class... Pack> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Pack...);
+ template <class T, class... Pack> void *operator new(std::type_identity<T>, size_t, std::align_val_t, Pack...); // #ClassScopedTemplatePackStruct_new
template <class T, class... Pack> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, Pack...); // #70
};
+struct UntypedDelete1 {
+ UntypedDelete1() noexcept(false);
+ template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t); // #71
+ void operator delete(void*); // #72
+};
+
+struct UntypedDeleteContext;
+struct UntypedDelete2 {
+ UntypedDelete2();
+ template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, UntypedDeleteContext*); // #73
+ template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t, UntypedDeleteContext*); // #74
+ void operator delete(void*); // #75
+};
+
+struct UntypedDelete3 {
+ UntypedDelete3() noexcept(false);
+ template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, UntypedDeleteContext*); // #76
+ void operator delete(void*); // #77
+};
+
+struct UntypedDelete4 {
+ UntypedDelete4() noexcept(true);
+ template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t, UntypedDeleteContext*); // #78
+ void operator delete(void*); // #79
+};
+
+
void test() {
// untyped in class declaration wins
@@ -337,9 +360,6 @@ void test() {
InclassNew6 *O10 = new InclassNew6;
// expected-error at -1 {{attempt to use a deleted function}}
// expected-note@#36 {{'operator delete' has been explicitly marked deleted here}}
- // expected-error at -3 {{type aware 'operator delete' requires a matching type aware 'operator new' to be declared in the same scope}}
- // expected-note@#34 {{non-type aware 'operator new' declared here in 'InclassNew6'}}
- // expected-note@#36 {{type aware 'operator delete' declared here}}
delete O10;
// expected-error at -1 {{attempt to use a deleted function}}
// expected-note@#36 {{'operator delete' has been explicitly marked deleted here}}
@@ -351,13 +371,10 @@ void test() {
// expected-note@#39 {{'operator delete' has been explicitly marked deleted here}}
InclassNew8 *O12 = new InclassNew8;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
- // expected-note@#40 {{type aware 'operator new' declared here in 'InclassNew8'}}
- // expected-note@#41 {{non-type aware 'operator delete' declared here}}
delete O12;
InclassNew9 *O13 = new InclassNew9;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires a matching cleanup 'operator delete'}}
// expected-note@#42 {{type aware 'operator new' declared here in 'InclassNew9'}}
delete O13;
@@ -408,13 +425,13 @@ void test() {
// expected-note@#59 {{member 'operator delete' declared here}}
SubClass6_1 *O22 = new SubClass6_1;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires the cleanup 'operator delete' to be declared in the same scope}}
// expected-note@#62 {{type aware 'operator new' declared here in 'SubClass6_1'}}
// expected-note@#61 {{type aware 'operator delete' declared here in 'BaseClass6'}}
delete O22;
SubClass6_2 *O23 = new SubClass6_2;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires the cleanup 'operator delete' to be declared in the same scope}}
// expected-note@#60 {{type aware 'operator new' declared here in 'BaseClass6'}}
// expected-note@#63 {{type aware 'operator delete' declared here in 'SubClass6_2'}}
delete O23;
@@ -465,8 +482,33 @@ void test() {
}
{
ClassScopedTemplatePackStruct *O30 = new ClassScopedTemplatePackStruct;
+ // expected-error at -1 {{type aware 'operator new' requires a matching cleanup 'operator delete'}}
+ // expected-note@#ClassScopedTemplatePackStruct_new {{type aware 'operator new' declared here in 'ClassScopedTemplatePackStruct'}}
delete O30;
// expected-error at -1 {{no suitable member 'operator delete' in 'ClassScopedTemplatePackStruct'}}
// expected-note@#70 {{member 'operator delete' declared here}}
}
+ {
+ UntypedDelete1 *UD1 = new UntypedDelete1;
+ delete UD1;
+ }
+ {
+ UntypedDeleteContext *Ctx = 0;
+ UntypedDelete2 *UD2 = new (Ctx) UntypedDelete2;
+ delete UD2;
+ }
+ {
+ UntypedDeleteContext *Ctx = 0;
+ UntypedDelete3 *UD3 = new (Ctx) UntypedDelete3;
+ // expected-error at -1 {{type aware 'operator new' requires a matching placement cleanup 'operator delete'}}
+ // expected-note@#76 {{type aware 'operator new' declared here in 'UntypedDelete3'}}
+ delete UD3;
+ }
+ {
+ UntypedDeleteContext *Ctx = 0;
+ UntypedDelete4 *UD4 = new (Ctx) UntypedDelete4;
+ // expected-error at -1 {{'operator new' requires a matching placement cleanup 'operator delete'}}
+ // expected-note@#78 {{type aware 'operator new' declared here in 'UntypedDelete4'}}
+ delete UD4;
+ }
}
diff --git a/clang/test/SemaCXX/type-aware-placement-operators.cpp b/clang/test/SemaCXX/type-aware-placement-operators.cpp
index 3ba5c4de4a54f..1f4eb37d47695 100644
--- a/clang/test/SemaCXX/type-aware-placement-operators.cpp
+++ b/clang/test/SemaCXX/type-aware-placement-operators.cpp
@@ -67,7 +67,7 @@ void test(Context& Ctx) {
// expected-note@#4 {{'operator delete<S3>' has been explicitly marked deleted here}}
S4 *s4_1 = new (Ctx) S4;
- // expected-error at -1 {{type aware 'operator new' requires a matching type aware placement 'operator delete' to be declared in the same scope}}
+ // expected-error at -1 {{type aware 'operator new' requires a matching placement cleanup 'operator delete'}}
// expected-note@#S4_new {{type aware 'operator new' declared here in 'S4'}}
delete s4_1;
}
>From 45a4a26419e765db9998c881a2f80c8c416f3581 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Tue, 5 May 2026 01:30:12 -0700
Subject: [PATCH 2/3] Clean up some of the logic
---
clang/lib/Sema/SemaDeclCXX.cpp | 22 +++++++++++++++-------
clang/lib/Sema/SemaExprCXX.cpp | 29 +++++++++++++++--------------
2 files changed, 30 insertions(+), 21 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1791680de8359..3961d29c11b67 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7465,14 +7465,22 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
auto CheckMismatchedTypeAwareAllocators =
[&](OverloadedOperatorKind NewKind, OverloadedOperatorKind DeleteKind) {
- if (!TypeAwareAllocatorDeclKinds.contains(NewKind) &&
- !TypeAwareAllocatorDeclKinds.contains(DeleteKind))
+ bool HasTypeAwareNew =
+ TypeAwareAllocatorDeclKinds.contains(NewKind);
+ bool HasClassScopedNew =
+ AllocatorDeclsKinds.contains(NewKind);
+ bool HasTypeAwareDelete =
+ TypeAwareAllocatorDeclKinds.contains(DeleteKind);
+ bool HasClassScopedDelete =
+ AllocatorDeclsKinds.contains(DeleteKind);
+
+ // No type aware allocators so nothing to do.
+ if (!HasTypeAwareNew && !HasTypeAwareDelete)
return;
- if (TypeAwareAllocatorDeclKinds.contains(NewKind) &&
- AllocatorDeclsKinds.contains(DeleteKind))
- return;
- if (TypeAwareAllocatorDeclKinds.contains(DeleteKind) &&
- AllocatorDeclsKinds.contains(NewKind))
+
+ // There is at least one type aware allocation function, so we must
+ // have both operators new and delete declared in the class.
+ if (HasClassScopedNew && HasClassScopedDelete)
return;
bool TypeAwareOperatorIsNew =
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 6004781f9806e..c4f4b73231584 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3142,20 +3142,21 @@ bool Sema::FindAllocationFunctions(
[&](bool IsPlacementOperator, TypeAwareCleanupOperatorError Error,
FunctionDecl *OperatorDelete = 0) {
assert(isTypeAwareAllocation(IAP.PassTypeIdentity));
- if (Diagnose) {
- Diag(StartLoc, diag::err_type_aware_new_missing_cleanup_operator)
- << OperatorNew->getDeclName() << Error << IsPlacementOperator
- << DeleteName;
- Diag(OperatorNew->getLocation(),
- diag::note_type_aware_operator_declared)
- << OperatorNew->getDeclName() << OperatorNewContext;
- if (Error == TypeAwareCleanupOperatorError::MismatchedScope) {
- DeclContext *OperatorDeleteContext =
- GetRedeclContext(OperatorDelete);
- Diag(OperatorDelete->getLocation(),
- diag::note_type_aware_operator_declared)
- << OperatorDelete->getDeclName() << OperatorDeleteContext;
- }
+ if (!Diagnose)
+ return;
+
+ Diag(StartLoc, diag::err_type_aware_new_missing_cleanup_operator)
+ << OperatorNew->getDeclName() << Error << IsPlacementOperator
+ << DeleteName;
+ Diag(OperatorNew->getLocation(),
+ diag::note_type_aware_operator_declared)
+ << OperatorNew->getDeclName() << OperatorNewContext;
+
+ if (Error == TypeAwareCleanupOperatorError::MismatchedScope) {
+ DeclContext *OperatorDeleteContext = GetRedeclContext(OperatorDelete);
+ Diag(OperatorDelete->getLocation(),
+ diag::note_type_aware_operator_declared)
+ << OperatorDelete->getDeclName() << OperatorDeleteContext;
}
};
>From 8d06a65f95c1f5f21f23909566a402a5a2f94f77 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Tue, 5 May 2026 01:41:51 -0700
Subject: [PATCH 3/3] Sigh formatting is the bane of my existence
---
clang/lib/Sema/SemaDeclCXX.cpp | 11 ++++-------
clang/lib/Sema/SemaExprCXX.cpp | 4 ++--
2 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3961d29c11b67..be87b9ff4090d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7465,14 +7465,11 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
auto CheckMismatchedTypeAwareAllocators =
[&](OverloadedOperatorKind NewKind, OverloadedOperatorKind DeleteKind) {
- bool HasTypeAwareNew =
- TypeAwareAllocatorDeclKinds.contains(NewKind);
- bool HasClassScopedNew =
- AllocatorDeclsKinds.contains(NewKind);
+ bool HasTypeAwareNew = TypeAwareAllocatorDeclKinds.contains(NewKind);
+ bool HasClassScopedNew = AllocatorDeclsKinds.contains(NewKind);
bool HasTypeAwareDelete =
- TypeAwareAllocatorDeclKinds.contains(DeleteKind);
- bool HasClassScopedDelete =
- AllocatorDeclsKinds.contains(DeleteKind);
+ TypeAwareAllocatorDeclKinds.contains(DeleteKind);
+ bool HasClassScopedDelete = AllocatorDeclsKinds.contains(DeleteKind);
// No type aware allocators so nothing to do.
if (!HasTypeAwareNew && !HasTypeAwareDelete)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c4f4b73231584..c8b7b6debddef 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3149,13 +3149,13 @@ bool Sema::FindAllocationFunctions(
<< OperatorNew->getDeclName() << Error << IsPlacementOperator
<< DeleteName;
Diag(OperatorNew->getLocation(),
- diag::note_type_aware_operator_declared)
+ diag::note_type_aware_operator_declared)
<< OperatorNew->getDeclName() << OperatorNewContext;
if (Error == TypeAwareCleanupOperatorError::MismatchedScope) {
DeclContext *OperatorDeleteContext = GetRedeclContext(OperatorDelete);
Diag(OperatorDelete->getLocation(),
- diag::note_type_aware_operator_declared)
+ diag::note_type_aware_operator_declared)
<< OperatorDelete->getDeclName() << OperatorDeleteContext;
}
};
More information about the cfe-commits
mailing list