https://github.com/akashagrwl updated https://github.com/llvm/llvm-project/pull/210254
>From c2f6ad63e6f8937494b6233891fa38996d8b5b47 Mon Sep 17 00:00:00 2001 From: Akash Agrawal <[email protected]> Date: Thu, 16 Jul 2026 23:06:52 -0700 Subject: [PATCH 1/4] [Clang][Sema] Don't delay the access check when computing implicit deletion Sema::isMemberAccessibleForDeletion treats AR_delayed as unreachable, but CheckAccess returns AR_delayed whenever it runs inside an enclosing delayed-diagnostics scope. That happens when deletion checking runs synchronously while parsing a later declaration -- e.g. while explaining why a defaulted operator<=> is deleted for an expression in that declaration's initializer. The caller cannot consume a delayed diagnostic, so letting CheckAccess delay always hits llvm_unreachable("cannot delay =delete computation") and crashes. Force an immediate answer by wrapping the CheckAccess call in DelayedDiagnostics.pushUndelayed()/popUndelayed() via llvm::scope_exit, mirroring the existing Sema::CheckEnableIf pattern in SemaOverload.cpp. --- clang/lib/Sema/SemaAccess.cpp | 11 +++++++++++ clang/test/SemaCXX/cxx20-default-compare.cpp | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index 17415b4185eff..f2e19488a6ee2 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -21,6 +21,7 @@ #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" +#include "llvm/ADT/ScopeExit.h" using namespace clang; using namespace sema; @@ -1612,6 +1613,16 @@ bool Sema::isMemberAccessibleForDeletion(CXXRecordDecl *NamingClass, // Suppress diagnostics. Entity.setDiag(Diag); + // Deletion checking can run while we are inside an enclosing + // delayed-diagnostics scope (e.g. when parsing a later declaration whose + // initializer requires explaining why a defaulted comparison operator is + // deleted). CheckAccess would then return AR_delayed, but the result must be + // known immediately here. Force an undelayed check, mirroring CheckEnableIf. + llvm::scope_exit UndelayDiags( + [&, CurrentState(DelayedDiagnostics.pushUndelayed())] { + DelayedDiagnostics.popUndelayed(CurrentState); + }); + switch (CheckAccess(*this, Loc, Entity)) { case AR_accessible: return true; case AR_inaccessible: return false; diff --git a/clang/test/SemaCXX/cxx20-default-compare.cpp b/clang/test/SemaCXX/cxx20-default-compare.cpp index 3e4673c31e489..cb2886608d6f6 100644 --- a/clang/test/SemaCXX/cxx20-default-compare.cpp +++ b/clang/test/SemaCXX/cxx20-default-compare.cpp @@ -65,3 +65,21 @@ struct MutCheck : MutOnly { // bool operator==(this MutCheck, MutCheck) = default; }; } + +namespace delayed_deletion_check { +// Explaining why a defaulted comparison operator is deleted can run while we +// are parsing a later declaration, i.e. inside an enclosing delayed-diagnostics +// scope. The access check for the deleted-ness computation must produce an +// immediate answer rather than being delayed. Previously this crashed. +struct HasPrivateSpaceship { +private: + std::strong_ordering operator<=>(const HasPrivateSpaceship &) const; // expected-note 2 {{declared private here}} +}; + +struct S { + HasPrivateSpaceship member; // expected-note 2 {{because it would invoke a private 'operator<=>' member of 'delayed_deletion_check::HasPrivateSpaceship' to compare member 'member'}} + auto operator<=>(const S &) const = default; // expected-warning {{explicitly defaulted three-way comparison operator is implicitly deleted}} expected-note {{replace 'default' with 'delete'}} expected-note {{explicitly defaulted function was implicitly deleted here}} +}; + +bool b = (S{} < S{}); // expected-error {{object of type 'S' cannot be compared because its 'operator<=>' is implicitly deleted}} +} >From 62811efb260c7d6c7cd3b889f23af649b5905f57 Mon Sep 17 00:00:00 2001 From: Akash Agrawal <[email protected]> Date: Wed, 22 Jul 2026 05:57:08 -0700 Subject: [PATCH 2/4] renaming delayed_deletion_check to immediate_deletion_check --- clang/test/SemaCXX/cxx20-default-compare.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/SemaCXX/cxx20-default-compare.cpp b/clang/test/SemaCXX/cxx20-default-compare.cpp index cb2886608d6f6..04e95cede930e 100644 --- a/clang/test/SemaCXX/cxx20-default-compare.cpp +++ b/clang/test/SemaCXX/cxx20-default-compare.cpp @@ -66,7 +66,7 @@ struct MutCheck : MutOnly { }; } -namespace delayed_deletion_check { +namespace immediate_deletion_check { // Explaining why a defaulted comparison operator is deleted can run while we // are parsing a later declaration, i.e. inside an enclosing delayed-diagnostics // scope. The access check for the deleted-ness computation must produce an @@ -77,7 +77,7 @@ struct HasPrivateSpaceship { }; struct S { - HasPrivateSpaceship member; // expected-note 2 {{because it would invoke a private 'operator<=>' member of 'delayed_deletion_check::HasPrivateSpaceship' to compare member 'member'}} + HasPrivateSpaceship member; // expected-note 2 {{because it would invoke a private 'operator<=>' member of 'immediate_deletion_check ::HasPrivateSpaceship' to compare member 'member'}} auto operator<=>(const S &) const = default; // expected-warning {{explicitly defaulted three-way comparison operator is implicitly deleted}} expected-note {{replace 'default' with 'delete'}} expected-note {{explicitly defaulted function was implicitly deleted here}} }; >From debd6d7a27705237d900725406d9c602e8496a63 Mon Sep 17 00:00:00 2001 From: Akash Agrawal <[email protected]> Date: Wed, 22 Jul 2026 18:40:01 +0530 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Younan Zhang <[email protected]> --- clang/lib/Sema/SemaAccess.cpp | 5 ++--- clang/test/SemaCXX/cxx20-default-compare.cpp | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index f2e19488a6ee2..9205ab1f283b7 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -1613,11 +1613,10 @@ bool Sema::isMemberAccessibleForDeletion(CXXRecordDecl *NamingClass, // Suppress diagnostics. Entity.setDiag(Diag); - // Deletion checking can run while we are inside an enclosing + // We don't want to delay access checking even we are inside an enclosing // delayed-diagnostics scope (e.g. when parsing a later declaration whose // initializer requires explaining why a defaulted comparison operator is - // deleted). CheckAccess would then return AR_delayed, but the result must be - // known immediately here. Force an undelayed check, mirroring CheckEnableIf. + // deleted) llvm::scope_exit UndelayDiags( [&, CurrentState(DelayedDiagnostics.pushUndelayed())] { DelayedDiagnostics.popUndelayed(CurrentState); diff --git a/clang/test/SemaCXX/cxx20-default-compare.cpp b/clang/test/SemaCXX/cxx20-default-compare.cpp index 04e95cede930e..87a6cfd09e186 100644 --- a/clang/test/SemaCXX/cxx20-default-compare.cpp +++ b/clang/test/SemaCXX/cxx20-default-compare.cpp @@ -67,10 +67,6 @@ struct MutCheck : MutOnly { } namespace immediate_deletion_check { -// Explaining why a defaulted comparison operator is deleted can run while we -// are parsing a later declaration, i.e. inside an enclosing delayed-diagnostics -// scope. The access check for the deleted-ness computation must produce an -// immediate answer rather than being delayed. Previously this crashed. struct HasPrivateSpaceship { private: std::strong_ordering operator<=>(const HasPrivateSpaceship &) const; // expected-note 2 {{declared private here}} >From ae3dac8127875e97571f6e09b13250757abd6cf3 Mon Sep 17 00:00:00 2001 From: Akash Agrawal <[email protected]> Date: Wed, 22 Jul 2026 22:53:58 -0700 Subject: [PATCH 4/4] nit: typo fix removed extra space --- clang/test/SemaCXX/cxx20-default-compare.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/SemaCXX/cxx20-default-compare.cpp b/clang/test/SemaCXX/cxx20-default-compare.cpp index 87a6cfd09e186..c569e9d866970 100644 --- a/clang/test/SemaCXX/cxx20-default-compare.cpp +++ b/clang/test/SemaCXX/cxx20-default-compare.cpp @@ -66,14 +66,14 @@ struct MutCheck : MutOnly { }; } -namespace immediate_deletion_check { +namespace immediate_deletion_check { struct HasPrivateSpaceship { private: std::strong_ordering operator<=>(const HasPrivateSpaceship &) const; // expected-note 2 {{declared private here}} }; struct S { - HasPrivateSpaceship member; // expected-note 2 {{because it would invoke a private 'operator<=>' member of 'immediate_deletion_check ::HasPrivateSpaceship' to compare member 'member'}} + HasPrivateSpaceship member; // expected-note 2 {{because it would invoke a private 'operator<=>' member of 'immediate_deletion_check::HasPrivateSpaceship' to compare member 'member'}} auto operator<=>(const S &) const = default; // expected-warning {{explicitly defaulted three-way comparison operator is implicitly deleted}} expected-note {{replace 'default' with 'delete'}} expected-note {{explicitly defaulted function was implicitly deleted here}} }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
