[clang] 6dc1636 - [Clang] Check for abstract parameters only when functions are defined.

Corentin Jabot via cfe-commits cfe-commits at lists.llvm.org
Wed Jun 7 12:44:21 PDT 2023


Author: Corentin Jabot
Date: 2023-06-07T21:44:14+02:00
New Revision: 6dc1636815cb9321657f1cb7ac87a46553870dc7

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

LOG: [Clang] Check for abstract parameters only when functions are defined.

The C++ standard allows abstract parameters in deleted functions
and in function declarations

> The type of a parameter or the return type for a function definition
> shall not be a (possibly cv-qualified) class type that is
> incomplete or abstract within the function body
> unless the function is deleted.

Fixes https://github.com/llvm/llvm-project/issues/63012

Reviewed By: #clang-language-wg, aaron.ballman

Differential Revision: https://reviews.llvm.org/D152096

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaChecking.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/CXX/class.derived/class.abstract/p3.cpp
    clang/test/CXX/drs/dr6xx.cpp
    clang/test/SemaCXX/abstract.cpp
    clang/test/SemaCXX/auto-type-from-cxx.cpp
    clang/test/SemaObjCXX/parameters.mm

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ad8d1aa3c9c3d..e24b1cda53711 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -549,6 +549,9 @@ Bug Fixes to C++ Support
 - Fix access of a friend class declared in a local class. Clang previously
   emitted an error when a friend of a local class tried to access it's
   private data members.
+- Allow abstract parameter and return types in functions that are
+  either deleted or not defined.
+  (`#63012 <https://github.com/llvm/llvm-project/issues/63012>`_)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index c4544f1a07b8b..3d72c9f359ea9 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -15994,10 +15994,16 @@ bool Sema::CheckParmsForFunctionDef(ArrayRef<ParmVarDecl *> Parameters,
     // function declarator that is part of a function definition of
     // that function shall not have incomplete type.
     //
-    // This is also C++ [dcl.fct]p6.
+    // C++23 [dcl.fct.def.general]/p2
+    // The type of a parameter [...] for a function definition
+    // shall not be a (possibly cv-qualified) class type that is incomplete
+    // or abstract within the function body unless the function is deleted.
     if (!Param->isInvalidDecl() &&
-        RequireCompleteType(Param->getLocation(), Param->getType(),
-                            diag::err_typecheck_decl_incomplete_type)) {
+        (RequireCompleteType(Param->getLocation(), Param->getType(),
+                             diag::err_typecheck_decl_incomplete_type) ||
+         RequireNonAbstractType(Param->getBeginLoc(), Param->getOriginalType(),
+                                diag::err_abstract_type_in_decl,
+                                AbstractParamType))) {
       Param->setInvalidDecl();
       HasInvalidParm = true;
     }

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e90a1540f6d93..4d4be343b9826 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9124,15 +9124,6 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
   }
   Expr *TrailingRequiresClause = D.getTrailingRequiresClause();
 
-  // Check that the return type is not an abstract class type.
-  // For record types, this is done by the AbstractClassUsageDiagnoser once
-  // the class has been completely parsed.
-  if (!DC->isRecord() &&
-      SemaRef.RequireNonAbstractType(
-          D.getIdentifierLoc(), R->castAs<FunctionType>()->getReturnType(),
-          diag::err_abstract_type_in_decl, SemaRef.AbstractReturnType))
-    D.setInvalidType();
-
   if (Name.getNameKind() == DeclarationName::CXXConstructorName) {
     // This is a C++ constructor declaration.
     assert(DC->isRecord() &&
@@ -14863,14 +14854,6 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
     checkNonTrivialCUnion(New->getType(), New->getLocation(),
                           NTCUC_FunctionParam, NTCUK_Destruct|NTCUK_Copy);
 
-  // Parameters can not be abstract class types.
-  // For record types, this is done by the AbstractClassUsageDiagnoser once
-  // the class has been completely parsed.
-  if (!CurContext->isRecord() &&
-      RequireNonAbstractType(NameLoc, T, diag::err_abstract_type_in_decl,
-                             AbstractParamType))
-    New->setInvalidDecl();
-
   // Parameter declarators cannot be interface types. All ObjC objects are
   // passed by reference.
   if (T->isObjCObjectType()) {
@@ -15277,13 +15260,19 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
     }
   }
 
-  // The return type of a function definition must be complete (C99 6.9.1p3),
-  // unless the function is deleted (C++ specifc, C++ [dcl.fct.def.general]p2)
+  // The return type of a function definition must be complete (C99 6.9.1p3).
+  // C++23 [dcl.fct.def.general]/p2
+  // The type of [...] the return for a function definition
+  // shall not be a (possibly cv-qualified) class type that is incomplete
+  // or abstract within the function body unless the function is deleted.
   QualType ResultType = FD->getReturnType();
   if (!ResultType->isDependentType() && !ResultType->isVoidType() &&
       !FD->isInvalidDecl() && BodyKind != FnBodyKind::Delete &&
-      RequireCompleteType(FD->getLocation(), ResultType,
-                          diag::err_func_def_incomplete_result))
+      (RequireCompleteType(FD->getLocation(), ResultType,
+                           diag::err_func_def_incomplete_result) ||
+       RequireNonAbstractType(FD->getLocation(), FD->getReturnType(),
+                              diag::err_abstract_type_in_decl,
+                              AbstractReturnType)))
     FD->setInvalidDecl();
 
   if (FnBodyScope)

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 6496c4299cf81..566b0e67f4098 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -6084,9 +6084,9 @@ void AbstractUsageInfo::CheckType(const NamedDecl *D, TypeLoc TL,
 /// Check for invalid uses of an abstract type in a function declaration.
 static void CheckAbstractClassUsage(AbstractUsageInfo &Info,
                                     FunctionDecl *FD) {
-  // No need to do the check on definitions, which require that
-  // the return/param types be complete.
-  if (FD->doesThisDeclarationHaveABody())
+  // Only definitions are required to refer to complete and
+  // non-abstract types.
+  if (!FD->doesThisDeclarationHaveABody())
     return;
 
   // For safety's sake, just ignore it if we don't have type source

diff  --git a/clang/test/CXX/class.derived/class.abstract/p3.cpp b/clang/test/CXX/class.derived/class.abstract/p3.cpp
index ad5b874788359..cf973d2567294 100644
--- a/clang/test/CXX/class.derived/class.abstract/p3.cpp
+++ b/clang/test/CXX/class.derived/class.abstract/p3.cpp
@@ -19,7 +19,7 @@ using D = SecretlyAbstract<char>[1];
 B b; // expected-error {{abstract class}}
 D d; // expected-error {{abstract class}}
 
-template<int> struct N;
+template<int> struct N{};
 
 // Note: C is not instantiated anywhere in this file, so we never discover that
 // it is in fact abstract. The C++ standard suggests that we need to
@@ -35,21 +35,21 @@ template<int> struct N;
 
 //  - as a parameter type
 void f(A&);
-void f(A); // expected-error {{abstract class}}
-void f(A[1]); // expected-error {{abstract class}}
-void f(B); // expected-error {{abstract class}}
-void f(B[1]); // expected-error {{abstract class}}
+void f(A){} // expected-error {{abstract class}}
+void f(A[1]){} // expected-error {{abstract class}}
+void f(B){} // expected-error {{abstract class}}
+void f(B[1]){} // expected-error {{abstract class}}
 void f(C);
 void f(C[1]);
-void f(D); // expected-error {{abstract class}}
-void f(D[1]); // expected-error {{abstract class}}
+void f(D){} // expected-error {{abstract class}}
+void f(D[1]){} // expected-error {{abstract class}}
 
 //  - as a function return type
 A &f(N<0>);
 A *f(N<1>);
-A f(N<2>); // expected-error {{abstract class}}
+A f(N<2>){} // expected-error {{abstract class}}
 A (&f(N<3>))[2]; // expected-error {{abstract class}}
-B f(N<4>); // expected-error {{abstract class}}
+B f(N<4>){} // expected-error {{abstract class}}
 B (&f(N<5>))[2]; // expected-error {{abstract class}}
 C f(N<6>);
 C (&f(N<7>))[2];
@@ -72,13 +72,10 @@ void h() {
   (D){0}; // expected-error {{abstract class}}
 }
 
-template<typename T> void t(T); // expected-note 2{{abstract class}}
+template<typename T> void t(T);
 void i(A &a, B &b, C &c, D &d) {
-  // FIXME: These should be handled consistently. We currently reject the first
-  // two early because we (probably incorrectly, depending on dr1640) take
-  // abstractness into account in forming implicit conversion sequences.
-  t(a); // expected-error {{no matching function}}
-  t(b); // expected-error {{no matching function}}
+  t(a); // expected-error {{allocating an object of abstract class type 'A'}}
+  t(b); // expected-error {{allocating an object of abstract class type 'SecretlyAbstract<int>'}}
   t(c); // expected-error {{allocating an object of abstract class type}}
   t(d); // ok, decays to pointer
 }

diff  --git a/clang/test/CXX/drs/dr6xx.cpp b/clang/test/CXX/drs/dr6xx.cpp
index a08f55dd562a4..557663f2ca6a9 100644
--- a/clang/test/CXX/drs/dr6xx.cpp
+++ b/clang/test/CXX/drs/dr6xx.cpp
@@ -691,7 +691,7 @@ namespace dr656 { // dr656: yes
 }
 
 namespace dr657 { // dr657: partial
-  struct Abs { virtual void x() = 0; };
+  struct Abs { virtual void x() = 0; }; // expected-note {{unimplemented pure virtual method 'x' in 'Abs'}}
   struct Der : public Abs { virtual void x(); };
 
   struct Cnvt { template<typename F> Cnvt(F); };
@@ -707,10 +707,8 @@ namespace dr657 { // dr657: partial
   // FIXME: The following examples demonstrate that we might be accepting the
   // above cases for the wrong reason.
 
-  // FIXME: We should reject this.
-  struct C { C(Abs) {} };
-  // FIXME: We should reject this.
-  struct Q { operator Abs() { __builtin_unreachable(); } } q;
+  struct C { C(Abs) {} }; // expected-error {{parameter type 'Abs' is an abstract class}}
+  struct Q { operator Abs() { __builtin_unreachable(); } } q; // expected-error {{return type 'Abs' is an abstract class}}
 #if __cplusplus >= 201703L
   // FIXME: We should *definitely* reject this.
   C c = Q().operator Abs();

diff  --git a/clang/test/SemaCXX/abstract.cpp b/clang/test/SemaCXX/abstract.cpp
index cdffa09fb3c9c..2215560a34172 100644
--- a/clang/test/SemaCXX/abstract.cpp
+++ b/clang/test/SemaCXX/abstract.cpp
@@ -32,42 +32,48 @@ static_assert(!__is_abstract(E), "E inherits from an abstract class but implemen
 C *d = new C; // expected-error {{allocating an object of abstract class type 'C'}}
 
 C c; // expected-error {{variable type 'C' is an abstract class}}
-void t1(C c); // expected-error {{parameter type 'C' is an abstract class}}
-void t2(C); // expected-error {{parameter type 'C' is an abstract class}}
+void t1(C c);
+void t2(C);
+void t3(C c){} // expected-error {{parameter type 'C' is an abstract class}}
+void t4(C){} // expected-error {{parameter type 'C' is an abstract class}}
 
 struct S {
   C c; // expected-error {{field type 'C' is an abstract class}}
 };
 
-void t3(const C&);
+void t5(const C&);
 
 void f() {
   C(); // expected-error {{allocating an object of abstract class type 'C'}}
-  t3(C()); // expected-error {{allocating an object of abstract class type 'C'}}
+  t5(C()); // expected-error {{allocating an object of abstract class type 'C'}}
 }
 
 C e1[2]; // expected-error {{array of abstract class type 'C'}}
 C (*e2)[2]; // expected-error {{array of abstract class type 'C'}}
 C (**e3)[2]; // expected-error {{array of abstract class type 'C'}}
 
-void t4(C c[2]); // expected-error {{array of abstract class type 'C'}}
+void t6(C c[2]); // expected-error {{array of abstract class type 'C'}}
 
-void t5(void (*)(C)); // expected-error {{parameter type 'C' is an abstract class}}
+void t7(void (*)(C));
 
-typedef void (*Func)(C); // expected-error {{parameter type 'C' is an abstract class}}
-void t6(Func);
+typedef void (*Func)(C);
+void t8(Func);
 
 class F {
   F a() { while (1) {} } // expected-error {{return type 'F' is an abstract class}}
-    
+
   class D {
-    void f(F c); // expected-error {{parameter type 'F' is an abstract class}}
+    void f(F c){}  // expected-error {{parameter type 'F' is an abstract class}}
+    void g(F c);
+    void h(F c) = delete;
   };
 
   union U {
-    void u(F c); // expected-error {{parameter type 'F' is an abstract class}}
+    void u(F c){} // expected-error {{parameter type 'F' is an abstract class}}
+    void v(F c);
+    void w(F c) = delete;
   };
-    
+
   virtual void f() = 0; // expected-note {{unimplemented pure virtual method 'f'}}
 };
 
@@ -76,9 +82,9 @@ class F {
 
 class Abstract;
 
-void t7(Abstract a);
+void t8(Abstract a);
 
-void t8() {
+void t9() {
   void h(Abstract a);
 }
 
@@ -143,7 +149,7 @@ namespace PR5222 {
     virtual C *clone();
   };
 
-  C c;  
+  C c;
 }
 
 // PR5550 - instantiating template didn't track overridden methods
@@ -159,7 +165,7 @@ namespace PR5550 {
   struct C : public B<int> {
     virtual void a();
     virtual void c();
-  }; 
+  };
   C x;
 }
 
@@ -198,15 +204,15 @@ namespace test1 {
 namespace test2 {
   struct X1 {
     virtual void xfunc(void) = 0;  // expected-note {{unimplemented pure virtual method}}
-    void g(X1 parm7);        // expected-error {{parameter type 'test2::X1' is an abstract class}}
-    void g(X1 parm8[2]);     // expected-error {{array of abstract class type 'test2::X1'}}
+    void g(X1 parm7){}        // expected-error {{parameter type 'X1' is an abstract class}}
+    void g(X1 parm8[2]){}     // expected-error {{parameter type 'X1' is an abstract class}}
   };
 
   template <int N>
   struct X2 {
     virtual void xfunc(void) = 0;  // expected-note {{unimplemented pure virtual method}}
-    void g(X2 parm10);        // expected-error {{parameter type 'X2<N>' is an abstract class}}
-    void g(X2 parm11[2]);     // expected-error {{array of abstract class type 'X2<N>'}}
+    void g(X2 parm10){}        // expected-error {{parameter type 'X2<N>' is an abstract class}}
+    void g(X2 parm11[2]) {}     // expected-error {{parameter type 'X2<N>' is an abstract class}}
   };
 }
 
@@ -331,8 +337,8 @@ struct var_template_def { // expected-note {{until the closing '}'}}
 };
 
 struct friend_fn {
-  friend void g(friend_fn); // expected-error {{abstract class}}
-  virtual void f() = 0; // expected-note {{unimplemented}}
+  friend void g(friend_fn);
+  virtual void f() = 0;
 };
 
 struct friend_fn_def {
@@ -342,8 +348,8 @@ struct friend_fn_def {
 
 struct friend_template {
   template<typename T>
-  friend void g(friend_template); // expected-error {{abstract class}}
-  virtual void f() = 0; // expected-note {{unimplemented}}
+  friend void g(friend_template);
+  virtual void f() = 0;
 };
 
 struct friend_template_def {
@@ -351,3 +357,27 @@ struct friend_template_def {
   friend void g(friend_template_def) {} // expected-error {{abstract class}}
   virtual void f() = 0; // expected-note {{unimplemented}}
 };
+
+namespace GH63012 {
+struct foo {
+    virtual ~foo() = 0;
+};
+void f(foo) = delete;
+foo  i() = delete;
+void h(foo);
+foo  g();
+
+struct S {
+  virtual void func() = 0; // expected-note {{unimplemented pure virtual method 'func' in 'S'}}
+};
+void S::func() {}
+
+static_assert(__is_abstract(S), "");
+
+struct T {
+  void func(S) = delete;
+  void other(S);
+  void yet_another(S) {} // expected-error{{parameter type 'S' is an abstract class}}
+};
+
+}

diff  --git a/clang/test/SemaCXX/auto-type-from-cxx.cpp b/clang/test/SemaCXX/auto-type-from-cxx.cpp
index 5cd48991ffb7a..27c481a78a8c0 100644
--- a/clang/test/SemaCXX/auto-type-from-cxx.cpp
+++ b/clang/test/SemaCXX/auto-type-from-cxx.cpp
@@ -21,18 +21,18 @@ int d() {
 namespace TestDeductionFail {
 
 template<typename T>
-void caller(T x) {x.fun();} // expected-note {{candidate template ignored: substitution failure [with T = TestDeductionFail::Abstract]: parameter type 'TestDeductionFail::Abstract' is an abstract class}}
+void caller(T x) {x.fun();} // expected-error {{parameter type 'TestDeductionFail::Abstract' is an abstract class}}
 
 template<typename T>
 auto getCaller(){
-  return caller<T>; // expected-error {{cannot deduce return type 'auto' from returned value of type '<overloaded function type>'}}
+  return caller<T>; // expected-note {{in instantiation of function template specialization 'TestDeductionFail::caller<TestDeductionFail::Abstract>' requested here}}
 }
 
 class Abstract{
   public:
     void fun();
-    virtual void vfun()=0;
-    void call(){getCaller<Abstract>()(*this);} // expected-note {{in instantiation of function template specialization 'TestDeductionFail::getCaller<TestDeductionFail::Abstract>' requested here}}
+    virtual void vfun()=0; // expected-note {{unimplemented pure virtual method 'vfun' in 'Abstract'}}
+    void call(){getCaller<Abstract>()(*this);} // expected-error {{allocating an object of abstract class type 'TestDeductionFail::Abstract'}}
 };
 
 }

diff  --git a/clang/test/SemaObjCXX/parameters.mm b/clang/test/SemaObjCXX/parameters.mm
index 363675a0ddf78..a18701c90ac12 100644
--- a/clang/test/SemaObjCXX/parameters.mm
+++ b/clang/test/SemaObjCXX/parameters.mm
@@ -11,9 +11,9 @@ @interface A
 X0<A> x0a; // expected-note{{instantiation}}
 
 
-struct test2 { virtual void foo() = 0; }; // expected-note {{unimplemented}}
+struct test2 { virtual void foo() = 0; };
 @interface Test2
-- (void) foo: (test2) foo; // expected-error {{parameter type 'test2' is an abstract class}}
+- (void) foo: (test2) foo ;
 @end
 
 template<typename T> void r1(__restrict T);


        


More information about the cfe-commits mailing list