[clang] [Sema] Diagnose by-value copy constructors in template instantiations (PR #130866)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 12 14:15:39 PDT 2025
https://github.com/Megan0704-1 updated https://github.com/llvm/llvm-project/pull/130866
>From 80e764fcfa1912e9d3771f4edb354569741010b7 Mon Sep 17 00:00:00 2001
From: Megan <megankuo at asu.edu>
Date: Tue, 11 Mar 2025 17:09:04 -0700
Subject: [PATCH 1/4] [Sema] Diagnose by-value copy constructors in template
instantiations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes #80963
Previously, Clang skipped diagnosing a constructor if it was implicitly
instantiated from a template class (TSK_ImplicitInstantiation). This allowed
ill-formed “copy” constructors taking the class by value (e.g. A(A)) to slip
through without a diagnostic.
However, the C++ standard mandates that copy constructors must take their
class type parameter by reference (e.g., A(const A&)). Furthermore, a
constructor template that *would* form a copy-by-value signature is not
treated as a copy constructor and should never be chosen for copying.
This patch replaces the check on TSK_ImplicitInstantiation with a check
to see if the constructor is a function template specialization (i.e.,
isFunctionTemplateSpecialization()). That ensures proper diagnosis of
non-template copy-by-value constructors, while still allowing valid
template constructors that might appear to have a copy-like signature
but should be SFINAEd out or simply not selected as a copy constructor.
---
clang/lib/Sema/SemaDeclCXX.cpp | 4 ++--
clang/test/SemaCXX/copy-ctor-template.cpp | 22 ++++++++++++++++++++++
2 files changed, 24 insertions(+), 2 deletions(-)
create mode 100644 clang/test/SemaCXX/copy-ctor-template.cpp
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 96aac7871db1e..1c62a551ee732 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -10921,8 +10921,8 @@ void Sema::CheckConstructor(CXXConstructorDecl *Constructor) {
// parameters have default arguments.
if (!Constructor->isInvalidDecl() &&
Constructor->hasOneParamOrDefaultArgs() &&
- Constructor->getTemplateSpecializationKind() !=
- TSK_ImplicitInstantiation) {
+ !Constructor->isFunctionTemplateSpecialization()
+ ) {
QualType ParamType = Constructor->getParamDecl(0)->getType();
QualType ClassTy = Context.getTagDeclType(ClassDecl);
if (Context.getCanonicalType(ParamType).getUnqualifiedType() == ClassTy) {
diff --git a/clang/test/SemaCXX/copy-ctor-template.cpp b/clang/test/SemaCXX/copy-ctor-template.cpp
new file mode 100644
index 0000000000000..a46a167038cf7
--- /dev/null
+++ b/clang/test/SemaCXX/copy-ctor-template.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template<class T, class V>
+struct A{
+ A();
+ A(A&);
+ A(A<V, T>); // expected-error{{copy constructor must pass its first argument by reference}}
+};
+
+void f() {
+ A<int, int> a = A<int, int>(); // expected-note{{in instantiation of template class 'A<int, int>'}}
+}
+
+template<class T, class V>
+struct B{
+ B();
+ template<class U> B(U); // No error (templated constructor)
+};
+
+void g() {
+ B<int, int> b = B<int, int>(); // should use implicit copy constructor
+}
>From 4fe1d934e7a688c346d5218e213decc67b261d70 Mon Sep 17 00:00:00 2001
From: Megan <megankuo at asu.edu>
Date: Wed, 12 Mar 2025 00:47:56 -0700
Subject: [PATCH 2/4] [Docs] Add release note for #80963 fix
---
clang/docs/ReleaseNotes.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 004f78f22ac36..8bae0d6e99ae0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -293,6 +293,7 @@ Bug Fixes to Attribute Support
Bug Fixes to C++ Support
^^^^^^^^^^^^^^^^^^^^^^^^
+- Clang now diagnoses copy constructors taking the class by value in template instantiations. (#GH130866)
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
- Clang now prints the correct instantiation context for diagnostics suppressed
by template argument deduction.
>From 12d9237d7a0650328f6167b1951195cd851f6234 Mon Sep 17 00:00:00 2001
From: Megan <megankuo at asu.edu>
Date: Wed, 12 Mar 2025 01:24:35 -0700
Subject: [PATCH 3/4] [Sema] Fix test diagnostics for copy-ctor-template
---
clang/test/SemaCXX/copy-ctor-template.cpp | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/clang/test/SemaCXX/copy-ctor-template.cpp b/clang/test/SemaCXX/copy-ctor-template.cpp
index a46a167038cf7..c58bd7c0c5e10 100644
--- a/clang/test/SemaCXX/copy-ctor-template.cpp
+++ b/clang/test/SemaCXX/copy-ctor-template.cpp
@@ -2,13 +2,20 @@
template<class T, class V>
struct A{
- A();
- A(A&);
+ A(); // expected-note{{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
+ A(A&); // expected-note{{candidate constructor not viable: expects an lvalue for 1st argument}}
A(A<V, T>); // expected-error{{copy constructor must pass its first argument by reference}}
};
void f() {
- A<int, int> a = A<int, int>(); // expected-note{{in instantiation of template class 'A<int, int>'}}
+ A<int, int> a = A<int, int>(); // expected-note{{in instantiation of template class 'A<int, int>'}}
+ A<int, double> a1 = A<double, int>(); // No error (not a copy constructor)
+}
+
+// Test rvalue-to-lvalue conversion in copy constructor
+A<int, int> &&a(void);
+void g() {
+ A<int, int> a2 = a(); // expected-error{{no matching constructor}}
}
template<class T, class V>
@@ -17,6 +24,6 @@ struct B{
template<class U> B(U); // No error (templated constructor)
};
-void g() {
+void h() {
B<int, int> b = B<int, int>(); // should use implicit copy constructor
}
>From 790d151975c9ce4f5f823484d100d9460077b971 Mon Sep 17 00:00:00 2001
From: Megan <megankuo at asu.edu>
Date: Wed, 12 Mar 2025 14:14:31 -0700
Subject: [PATCH 4/4] [SemaCXX] Update constructor-template.cpp test for #80963
Adjust the test to account for new diagnostics emitted when a class template constructor takes the class by value after instantiation.
- Add expected-error annotations to lines 142, 159, 172 (invalid by-value constructors when T = U).
- Add expected-note annotations to lines 147, 164, 176 (template instantiation points).
- Remove expected-error from line 76 (valid template specialization).
---
clang/test/SemaTemplate/constructor-template.cpp | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/clang/test/SemaTemplate/constructor-template.cpp b/clang/test/SemaTemplate/constructor-template.cpp
index a89dc60cfa347..13f00beb1ffc5 100644
--- a/clang/test/SemaTemplate/constructor-template.cpp
+++ b/clang/test/SemaTemplate/constructor-template.cpp
@@ -73,7 +73,7 @@ struct X3 {
template<typename T> X3(T);
};
-template<> X3::X3(X3); // expected-error{{must pass its first argument by reference}}
+template<> X3::X3(X3); // No error (template constructor)
struct X4 {
X4();
@@ -139,12 +139,12 @@ namespace self_by_value {
template <class T, class U> struct A {
A() {}
A(const A<T,U> &o) {}
- A(A<T,T> o) {}
+ A(A<T,T> o) {} // expected-error{{copy constructor must pass its first argument by reference}}
};
void helper(A<int,float>);
- void test1(A<int,int> a) {
+ void test1(A<int,int> a) { // expected-note{{in instantiation of template class 'self_by_value::A<int, int>'}}
helper(a);
}
void test2() {
@@ -156,12 +156,13 @@ namespace self_by_value_2 {
template <class T, class U> struct A {
A() {} // precxx17-note {{not viable: requires 0 arguments}}
A(A<T,U> &o) {} // precxx17-note {{not viable: expects an lvalue}}
- A(A<T,T> o) {} // precxx17-note {{ignored: instantiation takes its own class type by value}}
+ A(A<T,T> o) {} // expected-error{{copy constructor must pass its first argument by reference}}
};
void helper_A(A<int,int>); // precxx17-note {{passing argument to parameter here}}
void test_A() {
- helper_A(A<int,int>()); // precxx17-error {{no matching constructor}}
+ helper_A(A<int,int>()); // precxx17-error {{no matching constructor}} \
+ // expected-note{{in instantiation of template class 'self_by_value_2::A<int, int>'}}
}
}
@@ -169,11 +170,11 @@ namespace self_by_value_3 {
template <class T, class U> struct A {
A() {}
A(A<T,U> &o) {}
- A(A<T,T> o) {}
+ A(A<T,T> o) {} // expected-error{{copy constructor must pass its first argument by reference}}
};
void helper_A(A<int,int>);
- void test_A(A<int,int> b) {
+ void test_A(A<int,int> b) { // expected-note{{in instantiation of template class 'self_by_value_3::A<int, int>'}}
helper_A(b);
}
}
More information about the cfe-commits
mailing list