[clang] [clang-tools-extra] [Clang] Still track rewrite operator== from the spaceship operator (PR #230062)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 8 04:20:32 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tools-extra
Author: Younan Zhang (zyn0217)
<details>
<summary>Changes</summary>
This reverts the behavior since d4cf20ca37160cb062a9db773d0e6255d6bbc31a
We still need to track the instantiated-from decl for rewritten operators, in order to suppress spurious warnings that suggest the spaceship was not used.
Also this fixes regressions introduced from that commit.
Fixes https://github.com/llvm/llvm-project/issues/125233
Fixes https://github.com/llvm/llvm-project/issues/104720
---
Full diff: https://github.com/llvm/llvm-project/pull/230062.diff
7 Files Affected:
- (modified) clang-tools-extra/clangd/unittests/FindTargetTests.cpp (+4-1)
- (modified) clang-tools-extra/clangd/unittests/HoverTests.cpp (+2-2)
- (modified) clang/docs/ReleaseNotes.md (+4)
- (modified) clang/lib/Sema/SemaConcept.cpp (+4-1)
- (modified) clang/lib/Sema/SemaOverload.cpp (+4)
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+11-13)
- (modified) clang/test/CXX/class/class.compare/class.compare.default/p4.cpp (+52-5)
``````````diff
diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index 87febfef753a1..b6aa62ce0d422 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -748,7 +748,10 @@ TEST_F(TargetDeclTest, RewrittenBinaryOperator) {
bool x = (Foo(1) [[!=]] Foo(2));
)cpp";
EXPECT_DECLS("CXXRewrittenBinaryOperator",
- {"bool operator==(const Foo &) const noexcept = default"});
+ {"std::strong_ordering operator<=>(const Foo &) const = default",
+ Rel::TemplatePattern},
+ {"bool operator==(const Foo &) const noexcept = default",
+ Rel::TemplateInstantiation});
}
TEST_F(TargetDeclTest, FunctionTemplate) {
diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp
index d355118f30761..28ff463008fc3 100644
--- a/clang-tools-extra/clangd/unittests/HoverTests.cpp
+++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp
@@ -3322,7 +3322,7 @@ TEST(Hover, All) {
HI.NamespaceScope = "";
HI.Definition =
"bool operator==(const Foo &) const noexcept = default";
- HI.Documentation = "";
+ HI.Documentation = "Foo spaceship";
}},
};
@@ -4743,7 +4743,7 @@ TEST(Hover, SpaceshipTemplateNoCrash) {
TU.ExtraArgs.push_back("-std=c++20");
auto AST = TU.build();
auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
- EXPECT_EQ(HI->Documentation, "");
+ EXPECT_EQ(HI->Documentation, "Foo bar baz");
}
TEST(Hover, ForwardStructNoCrash) {
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 756722bcd2d1b..f1ce646249902 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -620,6 +620,10 @@ features cannot lower the translation-unit ABI level;
parsed where a nested-name-specifier could appear (e.g. ``int decltype = 0;``).
Clang now diagnoses the error instead of asserting. (#GH211207)
+- Fixed a bogus unused function warning when using `operator<=>` within an anonymous namespace. (#GH125233)
+
+- Fixed a regression where the rewritten comparison operator was not instantiated properly. (#GH104720)
+
- Fixed an assertion failure when a parenthesized structured binding declarator
was followed by a function declarator and body (e.g. ``([a, b])() {}``).
(#GH218144, #GH193687)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index fb66bdd6d2bd0..ad49a2ec94a04 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2586,7 +2586,10 @@ bool Sema::IsAtLeastAsConstrained(const NamedDecl *D1,
auto IsExpectedEntity = [](const FunctionDecl *FD) {
FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind();
return Kind == FunctionDecl::TK_NonTemplate ||
- Kind == FunctionDecl::TK_FunctionTemplate;
+ Kind == FunctionDecl::TK_FunctionTemplate ||
+ (Kind == FunctionDecl::TK_MemberSpecialization &&
+ FD->getInstantiatedFromMemberFunction()
+ ->getOverloadedOperator() == OO_Spaceship);
};
const auto *FD2 = dyn_cast<FunctionDecl>(D2);
assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) &&
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 963f0e93bd651..74bd66a94aec9 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -15806,6 +15806,10 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
if (FnExpr.isInvalid())
return ExprError();
+ if (auto *Pattern = FnDecl->getInstantiatedFromMemberFunction();
+ Pattern && Pattern->getOverloadedOperator() == OO_Spaceship)
+ Pattern->setIsUsed();
+
// Determine the result type.
QualType ResultTy = FnDecl->getReturnType();
ExprValueKind VK = Expr::getValueKindForType(ResultTy);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 7668b75e836e4..0d027113f8d50 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3156,18 +3156,16 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
FunctionTemplate,
TemplateArgumentList::CreateCopy(SemaRef.Context, Innermost),
/*InsertToken=*/{});
- } else if (FunctionRewriteKind == RewriteKind::None) {
- if (isFriend && D->isThisDeclarationADefinition()) {
- // Do not connect the friend to the template unless it's actually a
- // definition. We don't want non-template functions to be marked as being
- // template instantiations.
- Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
- } else if (!isFriend) {
- // If this is not a function template, and this is not a friend (that is,
- // this is a locally declared function), save the instantiation
- // relationship for the purposes of constraint instantiation.
- Function->setInstantiatedFromDecl(D);
- }
+ } else if (isFriend && D->isThisDeclarationADefinition()) {
+ // Do not connect the friend to the template unless it's actually a
+ // definition. We don't want non-template functions to be marked as being
+ // template instantiations.
+ Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
+ } else if (!isFriend) {
+ // If this is not a function template, and this is not a friend (that is,
+ // this is a locally declared function), save the instantiation
+ // relationship for the purposes of constraint instantiation.
+ Function->setInstantiatedFromDecl(D);
}
if (isFriend) {
@@ -3566,7 +3564,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
FunctionTemplate,
TemplateArgumentList::CreateCopy(SemaRef.Context, Innermost),
/*InsertToken=*/{});
- } else if (!isFriend && FunctionRewriteKind == RewriteKind::None) {
+ } else if (!isFriend) {
// Record that this is an instantiation of a member function.
Method->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
}
diff --git a/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp b/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
index 53a8bfc9a4f4a..ac16aa64111fc 100644
--- a/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
+++ b/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx2a %s
-// RUN: %clang_cc1 -std=c++23 -verify=expected %s
+// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx2a -Wunused-function %s
+// RUN: %clang_cc1 -std=c++23 -verify=expected -Wunused-function %s
// This test is for [class.compare.default]p3 as modified and renumbered to p4
// by P2002R0.
@@ -18,7 +18,7 @@ namespace std {
namespace N {
struct A {
- friend constexpr std::strong_ordering operator<=>(const A&, const A&) = default; // expected-note 2{{declared here}}
+ friend constexpr std::strong_ordering operator<=>(const A&, const A&) = default; // expected-note {{declared here}}
};
constexpr std::strong_ordering (*test_a_threeway_not_found)(const A&, const A&) = &operator<=>; // expected-error {{undeclared}}
@@ -32,8 +32,7 @@ namespace N {
constexpr bool operator==(const A&, const A&) noexcept;
constexpr bool (*test_a_equal)(const A&, const A&) noexcept = &operator==;
- static_assert((*test_a_equal)(A(), A())); // expected-error {{static assertion expression is not an integral constant expression}}
- // expected-note at -1 {{undefined function 'operator==' cannot be used in a constant expression}}
+ static_assert((*test_a_equal)(A(), A()));
}
struct B1 {
@@ -180,3 +179,51 @@ namespace Constrained {
return a != A<int>();
}
}
+
+namespace GH104720 {
+
+struct A {
+ friend constexpr auto operator<=>(const A &, const A &) = default;
+};
+
+constexpr bool operator ==(const A &, const A &) noexcept;
+
+static_assert( ((bool(*)(const A &, const A &))&operator==)( A{}, A{} ) );
+
+}
+
+namespace GH104720_2 {
+
+struct A {
+ friend constexpr bool operator==(const A &, const A &) = default; // #GH104720_2_A
+};
+
+// FIXME: This should probably be accepted - we only look at the recent declaration when defining a default function.
+//
+// bool foo() {
+// return A() == A();
+// }
+//
+// ^^^ activating this function would make the assertion valid!
+
+constexpr bool operator ==(const A &, const A &) noexcept;
+static_assert( ((bool(*)(const A &, const A &))&operator==)( A{}, A{} ) );
+// expected-error at -1 {{not an integral}}
+// expected-note at -2 {{undefined function}} expected-note@#GH104720_2_A {{here}}
+
+}
+
+namespace GH125233 {
+
+namespace {
+
+struct S {
+ friend auto operator<=>(const S&, const S&) = default;
+};
+
+[[maybe_unused]] bool awoo(S a, S b) { return a == b; }
+
+}
+
+}
+
``````````
</details>
https://github.com/llvm/llvm-project/pull/230062
More information about the cfe-commits
mailing list