Author: Kuo, Mei-Chun
Date: 2025-03-13T05:52:42+01:00
New Revision: fe0d3e3764961b62f43f1b129f30aaec5f30bc16

URL: 
https://github.com/llvm/llvm-project/commit/fe0d3e3764961b62f43f1b129f30aaec5f30bc16
DIFF: 
https://github.com/llvm/llvm-project/commit/fe0d3e3764961b62f43f1b129f30aaec5f30bc16.diff

LOG: [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.

Added: 
    clang/test/SemaCXX/copy-ctor-template.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/SemaTemplate/constructor-template.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e16cfc69590fa..36215af5bc7b0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -292,6 +292,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.

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..c58bd7c0c5e10
--- /dev/null
+++ b/clang/test/SemaCXX/copy-ctor-template.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+template<class T, class V>
+struct 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, 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>
+struct B{
+    B();
+    template<class U> B(U); // No error (templated constructor)
+};
+
+void h() {
+    B<int, int> b = B<int, int>(); // should use implicit copy constructor
+}

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);
   }
 }


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to