Skip to content

Commit d4cf20c

Browse files
authored
[Clang][Sema] Don't set instantiated from function when rewriting operator<=> (llvm#91339)
The following snippet causes a crash: ``` template<typename T> struct A { bool operator<=>(const A&) const requires true = default; }; bool f(A<int> a) { return a != A<int>(); } ``` This occurs because during the rewrite from `operator<=>` to `operator==`, the "pattern" `operator<=>` function is set as the instantiated from function for the newly created `operator==` function. This is obviously incorrect, and this patch fixes it.
1 parent 77c5cea commit d4cf20c

File tree

5 files changed

+41
-21
lines changed

5 files changed

+41
-21
lines changed

clang-tools-extra/clangd/unittests/FindTargetTests.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -642,10 +642,7 @@ TEST_F(TargetDeclTest, RewrittenBinaryOperator) {
642642
bool x = (Foo(1) [[!=]] Foo(2));
643643
)cpp";
644644
EXPECT_DECLS("CXXRewrittenBinaryOperator",
645-
{"std::strong_ordering operator<=>(const Foo &) const = default",
646-
Rel::TemplatePattern},
647-
{"bool operator==(const Foo &) const noexcept = default",
648-
Rel::TemplateInstantiation});
645+
{"bool operator==(const Foo &) const noexcept = default"});
649646
}
650647

651648
TEST_F(TargetDeclTest, FunctionTemplate) {

clang-tools-extra/clangd/unittests/HoverTests.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -3091,7 +3091,7 @@ TEST(Hover, All) {
30913091
HI.NamespaceScope = "";
30923092
HI.Definition =
30933093
"bool operator==(const Foo &) const noexcept = default";
3094-
HI.Documentation = "Foo spaceship";
3094+
HI.Documentation = "";
30953095
}},
30963096
};
30973097

@@ -3894,7 +3894,7 @@ TEST(Hover, SpaceshipTemplateNoCrash) {
38943894
TU.ExtraArgs.push_back("-std=c++20");
38953895
auto AST = TU.build();
38963896
auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
3897-
EXPECT_EQ(HI->Documentation, "Foo bar baz");
3897+
EXPECT_EQ(HI->Documentation, "");
38983898
}
38993899

39003900
TEST(Hover, ForwardStructNoCrash) {

clang/docs/ReleaseNotes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,8 @@ Bug Fixes to C++ Support
693693
and (#GH88832).
694694
- Clang now defers all substitution into the exception specification of a function template specialization
695695
until the noexcept-specifier is instantiated.
696+
- Fix a crash when an implicitly declared ``operator==`` function with a trailing requires-clause has its
697+
constraints compared to that of another declaration.
696698

697699
Bug Fixes to AST Handling
698700
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

+13-11
Original file line numberDiff line numberDiff line change
@@ -2269,16 +2269,18 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
22692269
TemplateArgumentList::CreateCopy(SemaRef.Context,
22702270
Innermost),
22712271
/*InsertPos=*/nullptr);
2272-
} else if (isFriend && D->isThisDeclarationADefinition()) {
2273-
// Do not connect the friend to the template unless it's actually a
2274-
// definition. We don't want non-template functions to be marked as being
2275-
// template instantiations.
2276-
Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
2277-
} else if (!isFriend) {
2278-
// If this is not a function template, and this is not a friend (that is,
2279-
// this is a locally declared function), save the instantiation relationship
2280-
// for the purposes of constraint instantiation.
2281-
Function->setInstantiatedFromDecl(D);
2272+
} else if (FunctionRewriteKind == RewriteKind::None) {
2273+
if (isFriend && D->isThisDeclarationADefinition()) {
2274+
// Do not connect the friend to the template unless it's actually a
2275+
// definition. We don't want non-template functions to be marked as being
2276+
// template instantiations.
2277+
Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
2278+
} else if (!isFriend) {
2279+
// If this is not a function template, and this is not a friend (that is,
2280+
// this is a locally declared function), save the instantiation
2281+
// relationship for the purposes of constraint instantiation.
2282+
Function->setInstantiatedFromDecl(D);
2283+
}
22822284
}
22832285

22842286
if (isFriend) {
@@ -2669,7 +2671,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
26692671
TemplateArgumentList::CreateCopy(SemaRef.Context,
26702672
Innermost),
26712673
/*InsertPos=*/nullptr);
2672-
} else if (!isFriend) {
2674+
} else if (!isFriend && FunctionRewriteKind == RewriteKind::None) {
26732675
// Record that this is an instantiation of a member function.
26742676
Method->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
26752677
}

clang/test/CXX/class/class.compare/class.compare.default/p4.cpp

+23-4
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,22 @@ namespace std {
1818

1919
namespace N {
2020
struct A {
21-
friend constexpr std::strong_ordering operator<=>(const A&, const A&) = default;
21+
friend constexpr std::strong_ordering operator<=>(const A&, const A&) = default; // expected-note 2{{declared here}}
2222
};
2323

24-
constexpr bool (*test_a_not_found)(const A&, const A&) = &operator==; // expected-error {{undeclared}}
24+
constexpr std::strong_ordering (*test_a_threeway_not_found)(const A&, const A&) = &operator<=>; // expected-error {{undeclared}}
25+
26+
constexpr std::strong_ordering operator<=>(const A&, const A&) noexcept;
27+
constexpr std::strong_ordering (*test_a_threeway)(const A&, const A&) = &operator<=>;
28+
static_assert(!(*test_a_threeway)(A(), A())); // expected-error {{static assertion expression is not an integral constant expression}}
29+
// expected-note@-1 {{undefined function 'operator<=>' cannot be used in a constant expression}}
30+
31+
constexpr bool (*test_a_equal_not_found)(const A&, const A&) = &operator==; // expected-error {{undeclared}}
2532

2633
constexpr bool operator==(const A&, const A&) noexcept;
27-
constexpr bool (*test_a)(const A&, const A&) noexcept = &operator==;
28-
static_assert((*test_a)(A(), A()));
34+
constexpr bool (*test_a_equal)(const A&, const A&) noexcept = &operator==;
35+
static_assert((*test_a_equal)(A(), A())); // expected-error {{static assertion expression is not an integral constant expression}}
36+
// expected-note@-1 {{undefined function 'operator==' cannot be used in a constant expression}}
2937
}
3038

3139
struct B1 {
@@ -161,3 +169,14 @@ struct non_constexpr_type {
161169

162170
my_struct<non_constexpr_type> obj; // cxx2a-note {{in instantiation of template class 'GH61238::my_struct<GH61238::non_constexpr_type>' requested here}}
163171
}
172+
173+
namespace Constrained {
174+
template<typename T>
175+
struct A {
176+
std::strong_ordering operator<=>(const A&) const requires true = default;
177+
};
178+
179+
bool f(A<int> a) {
180+
return a != A<int>();
181+
}
182+
}

0 commit comments

Comments
 (0)