Skip to content

[Clang] Fix crash on template-specialization #142338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ Bug Fixes to C++ Support
- Clang modules now allow a module and its user to differ on TrivialAutoVarInit*
- Fixed an access checking bug when initializing non-aggregates in default arguments (#GH62444), (#GH83608)
- Fixed a pack substitution bug in deducing class template partial specializations. (#GH53609)
- Fixed a crash when specializing a template function whose primary template has a default argument (#GH54279) (#GH95420)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "clang/Sema/SemaSwift.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateInstCallback.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/TimeProfiler.h"
#include <optional>

Expand Down Expand Up @@ -5120,6 +5121,10 @@ bool Sema::addInstantiatedParametersToScope(
// Simple case: not a parameter pack.
assert(FParamIdx < Function->getNumParams());
ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx);
DeclarationName Name = FunctionParam->getDeclName();
auto _ = llvm::make_scope_exit([&]() {
FunctionParam->setDeclName(Name);
});
Comment on lines +5124 to +5127
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can always init-capture it

Suggested change
DeclarationName Name = FunctionParam->getDeclName();
auto _ = llvm::make_scope_exit([&]() {
FunctionParam->setDeclName(Name);
});
auto _ = llvm::make_scope_exit([&, Name(FunctionParam->getDeclName())] {
FunctionParam->setDeclName(Name);
});

FunctionParam->setDeclName(PatternParam->getDeclName());
// If the parameter's type is not dependent, update it to match the type
// in the pattern. They can differ in top-level cv-qualifiers, and we want
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenCXX/constructor-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ template<typename T> struct X;

// Make sure that the instantiated constructor initializes start and
// end properly.
// CHECK-LABEL: define linkonce_odr void @_ZN1XIiEC2ERKS0_(ptr {{[^,]*}} %this, ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %other) unnamed_addr
// CHECK-LABEL: define linkonce_odr void @_ZN1XIiEC2ERKS0_(ptr {{[^,]*}} %this, ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %0) unnamed_addr

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The restoration of the name changes the param name in the AST and causes this test to fail.
It seems possible to avoid it, but I'm not sure the current solution is bad on the case for the default argument.

There seem to be no AST tests that validate this requirement.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What name does it change it to? Is this expected?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It changed the emitted definition from X<int>::X(const X &other) to X<int>::X(const X&) IIUC. I'm not sure if this is a benign change or a regression - do we need the parameter name in the middle-end?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels weird but not really sure. The change seemed to be about restoring names and so I find the result puzzling.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It changes to the name used in the declaration. In this case the declaration in the class is X(const X &); and the argument has no name. In this case it uses %0. Would the declaration be X(const X & x); the name would change to %x.

I noticed only one test fails due to the change and it does not look that the name of the argument is specifically part of the test. There seem to be no other tests that are affected. So I'm not sure whether this specific change in behaviour is wanted or important. I'd be happy to look into not changing it in this case.

// CHECK: {{store.*null}}
// CHECK: {{store.*null}}
// CHECK: ret
Expand Down
49 changes: 49 additions & 0 deletions clang/test/SemaTemplate/default-arguments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,52 @@ static_assert(S<short *>().SizeOfT<char>() == sizeof(short *), "");
} // namespace GH68490

#endif

namespace PR54279 {
// Using a different name for the argument when there is a default argument
// caused a crash.
template <typename T> void f(const T &a, int c = 0);
template <> void f(const int &unused, int) {
f(42);
}
}

namespace PR54279 {
template <int> struct a {};
namespace b {
template <int c>
void e(a<c> &, const int &center, double, double, unsigned, bool = 0);
template <int c> void d(a<c> &, double, double, double, unsigned, bool);
} // namespace b
struct g {
g(int center = 0);
int center;
};
namespace b {
template <> void e(a<3> &, const int &f, double, double, unsigned, bool) {
a<3> h;
e(h, 0, 0, 0, 0);
}
template <> void d(a<0> &, double, double, double, unsigned, bool);
} // namespace b
}

namespace PR95420 {
template <typename _Container>
constexpr auto
size(const _Container &__cont) noexcept -> decltype(__cont.size0);

struct A {
A* data[2];
};

template <typename T>
void f1(unsigned long, const A& f, const A& b = {});

template <>
void f1<A>(unsigned long size, const A& f, const A& b) { // We also need b
f1<A>(0, *f.data[0]); // We still need this line

size; // Changing the name of size prevents crash
}
}