Author: Sirraide Date: 2024-07-16T01:55:11+02:00 New Revision: 864478cc74f5e258f86014886df16aa8d393bcc6
URL: https://github.com/llvm/llvm-project/commit/864478cc74f5e258f86014886df16aa8d393bcc6 DIFF: https://github.com/llvm/llvm-project/commit/864478cc74f5e258f86014886df16aa8d393bcc6.diff LOG: [Clang] Disallow explicit object parameters in more contexts (#89078) This diagnoses explicit object parameters in more contexts where they aren’t supposed to appear in (e.g. function pointer types, non-function member decls, etc.) [dcl.fct] This fixes #85992. Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaDeclCXX.cpp clang/lib/Sema/SemaType.cpp clang/test/SemaCXX/cxx2b-deducing-this.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a34f109ba21ce..969856a8f978c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1038,6 +1038,8 @@ Bug Fixes to C++ Support - Fix a crash when parsing an invalid type-requirement in a requires expression. (#GH51868) - Fix parsing of built-in type-traits such as ``__is_pointer`` in libstdc++ headers. (#GH95598) - Fixed failed assertion when resolving context of defaulted comparison method outside of struct. (#GH96043). +- Clang now diagnoses explicit object parameters in member pointers and other contexts where they should not appear. + Fixes (#GH85992). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0ea3677355169..52ff4b026a60e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7577,6 +7577,8 @@ def err_explicit_object_lambda_ambiguous_base : Error< def err_explicit_object_lambda_inaccessible_base : Error< "invalid explicit object parameter type %0 in lambda with capture; " "the type must derive publicly from the lambda">; +def err_explicit_object_parameter_invalid: Error< + "an explicit object parameter can only appear as the first parameter of a member function">; def err_ref_qualifier_overload : Error< "cannot overload a member function %select{without a ref-qualifier|with " diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 17c4ba99c03f5..f24912cde275a 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11258,6 +11258,34 @@ void Sema::CheckExplicitObjectMemberFunction(Declarator &D, D.setInvalidType(); } + // Friend declarations require some care. Consider: + // + // namespace N { + // struct A{}; + // int f(A); + // } + // + // struct S { + // struct T { + // int f(this T); + // }; + // + // friend int T::f(this T); // Allow this. + // friend int f(this S); // But disallow this. + // friend int N::f(this A); // And disallow this. + // }; + // + // Here, it seems to suffice to check whether the scope + // specifier designates a class type. + if (D.getDeclSpec().isFriendSpecified() && + !isa_and_present<CXXRecordDecl>( + computeDeclContext(D.getCXXScopeSpec()))) { + Diag(ExplicitObjectParam->getBeginLoc(), + diag::err_explicit_object_parameter_nonmember) + << D.getSourceRange() << /*non-member=*/2 << IsLambda; + D.setInvalidType(); + } + if (IsLambda && FTI.hasMutableQualifier()) { Diag(ExplicitObjectParam->getBeginLoc(), diag::err_explicit_object_parameter_mutable) @@ -11268,10 +11296,8 @@ void Sema::CheckExplicitObjectMemberFunction(Declarator &D, return; if (!DC || !DC->isRecord()) { - Diag(ExplicitObjectParam->getLocation(), - diag::err_explicit_object_parameter_nonmember) - << D.getSourceRange() << /*non-member=*/2 << IsLambda; - D.setInvalidType(); + assert(D.isInvalidType() && "Explicit object parameter in non-member " + "should have been diagnosed already"); return; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 46166e44d17bd..baac1fe4f2407 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -4762,6 +4762,61 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // Check for auto functions and trailing return type and adjust the // return type accordingly. if (!D.isInvalidType()) { + auto IsClassType = [&](CXXScopeSpec &SS) { + // If there already was an problem with the scope, don’t issue another + // error about the explicit object parameter. + return SS.isInvalid() || + isa_and_present<CXXRecordDecl>(S.computeDeclContext(SS)); + }; + + // C++23 [dcl.fct]p6: + // + // An explicit-object-parameter-declaration is a parameter-declaration + // with a this specifier. An explicit-object-parameter-declaration shall + // appear only as the first parameter-declaration of a + // parameter-declaration-list of one of: + // + // - a declaration of a member function or member function template + // ([class.mem]), or + // + // - an explicit instantiation ([temp.explicit]) or explicit + // specialization ([temp.expl.spec]) of a templated member function, + // or + // + // - a lambda-declarator [expr.prim.lambda]. + DeclaratorContext C = D.getContext(); + ParmVarDecl *First = + FTI.NumParams + ? dyn_cast_if_present<ParmVarDecl>(FTI.Params[0].Param) + : nullptr; + + bool IsFunctionDecl = D.getInnermostNonParenChunk() == &DeclType; + if (First && First->isExplicitObjectParameter() && + C != DeclaratorContext::LambdaExpr && + + // Either not a member or nested declarator in a member. + // + // Note that e.g. 'static' or 'friend' declarations are accepted + // here; we diagnose them later when we build the member function + // because it's easier that way. + (C != DeclaratorContext::Member || !IsFunctionDecl) && + + // Allow out-of-line definitions of member functions. + !IsClassType(D.getCXXScopeSpec())) { + if (IsFunctionDecl) + S.Diag(First->getBeginLoc(), + diag::err_explicit_object_parameter_nonmember) + << /*non-member*/ 2 << /*function*/ 0 + << First->getSourceRange(); + else + S.Diag(First->getBeginLoc(), + diag::err_explicit_object_parameter_invalid) + << First->getSourceRange(); + + D.setInvalidType(); + AreDeclaratorChunksValid = false; + } + // trailing-return-type is only required if we're declaring a function, // and not, for instance, a pointer to a function. if (D.getDeclSpec().hasAutoTypeSpec() && diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp index 2c19b091fabad..5cbc1f735383b 100644 --- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp +++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp @@ -918,3 +918,44 @@ struct C { } }; } + +namespace GH85992 { +namespace N { +struct A { + int f(this A); +}; + +int f(A); +} + +struct S { + int (S::*x)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}} + int (*y)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}} + int (***z)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}} + + int f(this S); + int ((g))(this S); + friend int h(this S); // expected-error {{an explicit object parameter cannot appear in a non-member function}} + int h(int x, int (*)(this S)); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}} + + struct T { + int f(this T); + }; + + friend int T::f(this T); + friend int N::A::f(this N::A); + friend int N::f(this N::A); // expected-error {{an explicit object parameter cannot appear in a non-member function}} + int friend func(this T); // expected-error {{an explicit object parameter cannot appear in a non-member function}} +}; + +using T = int (*)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}} +using U = int (S::*)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}} +int h(this int); // expected-error {{an explicit object parameter cannot appear in a non-member function}} + +int S::f(this S) { return 1; } + +namespace a { +void f(); +}; +void a::f(this auto) {} // expected-error {{an explicit object parameter cannot appear in a non-member function}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits