[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