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