cor3ntin created this revision.
Herald added a project: All.
cor3ntin requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

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


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D152096

Files:
  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

Index: clang/test/SemaObjCXX/parameters.mm
===================================================================
--- clang/test/SemaObjCXX/parameters.mm
+++ clang/test/SemaObjCXX/parameters.mm
@@ -11,9 +11,9 @@
 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);
Index: clang/test/SemaCXX/auto-type-from-cxx.cpp
===================================================================
--- clang/test/SemaCXX/auto-type-from-cxx.cpp
+++ clang/test/SemaCXX/auto-type-from-cxx.cpp
@@ -21,18 +21,18 @@
 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'}}
 };
 
 }
Index: clang/test/SemaCXX/abstract.cpp
===================================================================
--- clang/test/SemaCXX/abstract.cpp
+++ clang/test/SemaCXX/abstract.cpp
@@ -32,42 +32,48 @@
 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 Abstract;
 
-void t7(Abstract a);
+void t8(Abstract a);
 
-void t8() {
+void t9() {
   void h(Abstract a);
 }
 
@@ -198,15 +204,15 @@
 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 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_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,13 @@
   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();
+}
Index: clang/test/CXX/drs/dr6xx.cpp
===================================================================
--- clang/test/CXX/drs/dr6xx.cpp
+++ clang/test/CXX/drs/dr6xx.cpp
@@ -691,7 +691,7 @@
 }
 
 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 @@
   // 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();
Index: clang/test/CXX/class.derived/class.abstract/p3.cpp
===================================================================
--- clang/test/CXX/class.derived/class.abstract/p3.cpp
+++ clang/test/CXX/class.derived/class.abstract/p3.cpp
@@ -19,7 +19,7 @@
 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 @@
 
 //  - 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,13 @@
   (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
 }
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -6002,9 +6002,9 @@
 /// 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->hasSkippedBody())
     return;
 
   // For safety's sake, just ignore it if we don't have type source
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -9123,15 +9123,6 @@
   }
   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() &&
@@ -14862,14 +14853,6 @@
     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()) {
@@ -15274,13 +15257,19 @@
     }
   }
 
-  // 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)
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -15994,10 +15994,16 @@
     // 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;
     }
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -521,6 +521,9 @@
   (`#62494 <https://github.com/llvm/llvm-project/issues/62494>`_)
 - Fix handling of generic lambda used as template arguments.
   (`#62611 <https://github.com/llvm/llvm-project/issues/62611>`_)
+- 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
 ^^^^^^^^^^^^^^^^^^^^^^^^^
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to