Skip to content

Commit fe0d3e3

Browse files
authored
[Sema] Diagnose by-value copy constructors in template instantiations (#130866)
Fixes #80963 This PR ensures Clang diagnoses by-value copy constructors in implicitly instantiated class templates (e.g., `A<int, int>(A<int, int>)`), per [class.copy.ctor]. Changes: - Remove `TSK_ImplicitInstantiation` check in `SemaDeclCXX.cpp`. - Add `!isFunctionTemplateSpecialization()` to skip templated constructors. - Add regression tests.
1 parent 646a6e7 commit fe0d3e3

File tree

4 files changed

+40
-9
lines changed

4 files changed

+40
-9
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ Bug Fixes to Attribute Support
292292
Bug Fixes to C++ Support
293293
^^^^^^^^^^^^^^^^^^^^^^^^
294294

295+
- Clang now diagnoses copy constructors taking the class by value in template instantiations. (#GH130866)
295296
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
296297
- Clang now prints the correct instantiation context for diagnostics suppressed
297298
by template argument deduction.

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10921,8 +10921,8 @@ void Sema::CheckConstructor(CXXConstructorDecl *Constructor) {
1092110921
// parameters have default arguments.
1092210922
if (!Constructor->isInvalidDecl() &&
1092310923
Constructor->hasOneParamOrDefaultArgs() &&
10924-
Constructor->getTemplateSpecializationKind() !=
10925-
TSK_ImplicitInstantiation) {
10924+
!Constructor->isFunctionTemplateSpecialization()
10925+
) {
1092610926
QualType ParamType = Constructor->getParamDecl(0)->getType();
1092710927
QualType ClassTy = Context.getTagDeclType(ClassDecl);
1092810928
if (Context.getCanonicalType(ParamType).getUnqualifiedType() == ClassTy) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
template<class T, class V>
4+
struct A{
5+
A(); // expected-note{{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
6+
A(A&); // expected-note{{candidate constructor not viable: expects an lvalue for 1st argument}}
7+
A(A<V, T>); // expected-error{{copy constructor must pass its first argument by reference}}
8+
};
9+
10+
void f() {
11+
A<int, int> a = A<int, int>(); // expected-note{{in instantiation of template class 'A<int, int>'}}
12+
A<int, double> a1 = A<double, int>(); // No error (not a copy constructor)
13+
}
14+
15+
// Test rvalue-to-lvalue conversion in copy constructor
16+
A<int, int> &&a(void);
17+
void g() {
18+
A<int, int> a2 = a(); // expected-error{{no matching constructor}}
19+
}
20+
21+
template<class T, class V>
22+
struct B{
23+
B();
24+
template<class U> B(U); // No error (templated constructor)
25+
};
26+
27+
void h() {
28+
B<int, int> b = B<int, int>(); // should use implicit copy constructor
29+
}

clang/test/SemaTemplate/constructor-template.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ struct X3 {
7373
template<typename T> X3(T);
7474
};
7575

76-
template<> X3::X3(X3); // expected-error{{must pass its first argument by reference}}
76+
template<> X3::X3(X3); // No error (template constructor)
7777

7878
struct X4 {
7979
X4();
@@ -139,12 +139,12 @@ namespace self_by_value {
139139
template <class T, class U> struct A {
140140
A() {}
141141
A(const A<T,U> &o) {}
142-
A(A<T,T> o) {}
142+
A(A<T,T> o) {} // expected-error{{copy constructor must pass its first argument by reference}}
143143
};
144144

145145
void helper(A<int,float>);
146146

147-
void test1(A<int,int> a) {
147+
void test1(A<int,int> a) { // expected-note{{in instantiation of template class 'self_by_value::A<int, int>'}}
148148
helper(a);
149149
}
150150
void test2() {
@@ -156,24 +156,25 @@ namespace self_by_value_2 {
156156
template <class T, class U> struct A {
157157
A() {} // precxx17-note {{not viable: requires 0 arguments}}
158158
A(A<T,U> &o) {} // precxx17-note {{not viable: expects an lvalue}}
159-
A(A<T,T> o) {} // precxx17-note {{ignored: instantiation takes its own class type by value}}
159+
A(A<T,T> o) {} // expected-error{{copy constructor must pass its first argument by reference}}
160160
};
161161

162162
void helper_A(A<int,int>); // precxx17-note {{passing argument to parameter here}}
163163
void test_A() {
164-
helper_A(A<int,int>()); // precxx17-error {{no matching constructor}}
164+
helper_A(A<int,int>()); // precxx17-error {{no matching constructor}} \
165+
// expected-note{{in instantiation of template class 'self_by_value_2::A<int, int>'}}
165166
}
166167
}
167168

168169
namespace self_by_value_3 {
169170
template <class T, class U> struct A {
170171
A() {}
171172
A(A<T,U> &o) {}
172-
A(A<T,T> o) {}
173+
A(A<T,T> o) {} // expected-error{{copy constructor must pass its first argument by reference}}
173174
};
174175

175176
void helper_A(A<int,int>);
176-
void test_A(A<int,int> b) {
177+
void test_A(A<int,int> b) { // expected-note{{in instantiation of template class 'self_by_value_3::A<int, int>'}}
177178
helper_A(b);
178179
}
179180
}

0 commit comments

Comments
 (0)