Skip to content

Commit

Permalink
[Clang][Sema] Don't set instantiated from function when rewriting ope…
Browse files Browse the repository at this point in the history
…rator<=> (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.
  • Loading branch information
sdkrystian authored May 8, 2024
1 parent 77c5cea commit d4cf20c
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 21 deletions.
5 changes: 1 addition & 4 deletions clang-tools-extra/clangd/unittests/FindTargetTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,10 +642,7 @@ TEST_F(TargetDeclTest, RewrittenBinaryOperator) {
bool x = (Foo(1) [[!=]] Foo(2));
)cpp";
EXPECT_DECLS("CXXRewrittenBinaryOperator",
{"std::strong_ordering operator<=>(const Foo &) const = default",
Rel::TemplatePattern},
{"bool operator==(const Foo &) const noexcept = default",
Rel::TemplateInstantiation});
{"bool operator==(const Foo &) const noexcept = default"});
}

TEST_F(TargetDeclTest, FunctionTemplate) {
Expand Down
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/unittests/HoverTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3091,7 +3091,7 @@ TEST(Hover, All) {
HI.NamespaceScope = "";
HI.Definition =
"bool operator==(const Foo &) const noexcept = default";
HI.Documentation = "Foo spaceship";
HI.Documentation = "";
}},
};

Expand Down Expand Up @@ -3894,7 +3894,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, "Foo bar baz");
EXPECT_EQ(HI->Documentation, "");
}

TEST(Hover, ForwardStructNoCrash) {
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,8 @@ Bug Fixes to C++ Support
and (#GH88832).
- Clang now defers all substitution into the exception specification of a function template specialization
until the noexcept-specifier is instantiated.
- Fix a crash when an implicitly declared ``operator==`` function with a trailing requires-clause has its
constraints compared to that of another declaration.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
24 changes: 13 additions & 11 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2269,16 +2269,18 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
TemplateArgumentList::CreateCopy(SemaRef.Context,
Innermost),
/*InsertPos=*/nullptr);
} 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);
} 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);
}
}

if (isFriend) {
Expand Down Expand Up @@ -2669,7 +2671,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
TemplateArgumentList::CreateCopy(SemaRef.Context,
Innermost),
/*InsertPos=*/nullptr);
} else if (!isFriend) {
} else if (!isFriend && FunctionRewriteKind == RewriteKind::None) {
// Record that this is an instantiation of a member function.
Method->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
}
Expand Down
27 changes: 23 additions & 4 deletions clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,22 @@ namespace std {

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

constexpr bool (*test_a_not_found)(const A&, const A&) = &operator==; // expected-error {{undeclared}}
constexpr std::strong_ordering (*test_a_threeway_not_found)(const A&, const A&) = &operator<=>; // expected-error {{undeclared}}

constexpr std::strong_ordering operator<=>(const A&, const A&) noexcept;
constexpr std::strong_ordering (*test_a_threeway)(const A&, const A&) = &operator<=>;
static_assert(!(*test_a_threeway)(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}}

constexpr bool (*test_a_equal_not_found)(const A&, const A&) = &operator==; // expected-error {{undeclared}}

constexpr bool operator==(const A&, const A&) noexcept;
constexpr bool (*test_a)(const A&, const A&) noexcept = &operator==;
static_assert((*test_a)(A(), A()));
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}}
}

struct B1 {
Expand Down Expand Up @@ -161,3 +169,14 @@ struct non_constexpr_type {

my_struct<non_constexpr_type> obj; // cxx2a-note {{in instantiation of template class 'GH61238::my_struct<GH61238::non_constexpr_type>' requested here}}
}

namespace Constrained {
template<typename T>
struct A {
std::strong_ordering operator<=>(const A&) const requires true = default;
};

bool f(A<int> a) {
return a != A<int>();
}
}

0 comments on commit d4cf20c

Please sign in to comment.