https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/230062
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 >From 7474cb2c6085a36ff0663844562a80c4adaa7b7e Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Thu, 8 Oct 2026 19:14:40 +0800 Subject: [PATCH] [Clang] Still track rewrite operator== from the spaceship operator 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. --- .../clangd/unittests/FindTargetTests.cpp | 5 +- .../clangd/unittests/HoverTests.cpp | 4 +- clang/docs/ReleaseNotes.md | 4 ++ clang/lib/Sema/SemaConcept.cpp | 5 +- clang/lib/Sema/SemaOverload.cpp | 4 ++ .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 24 ++++---- .../class.compare.default/p4.cpp | 57 +++++++++++++++++-- 7 files changed, 81 insertions(+), 22 deletions(-) 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@-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@-1 {{not an integral}} +// expected-note@-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; } + +} + +} + _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
