Skip to content

Commit 864478c

Browse files
authored
[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.
1 parent 34e06dc commit 864478c

File tree

5 files changed

+130
-4
lines changed

5 files changed

+130
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,8 @@ Bug Fixes to C++ Support
10381038
- Fix a crash when parsing an invalid type-requirement in a requires expression. (#GH51868)
10391039
- Fix parsing of built-in type-traits such as ``__is_pointer`` in libstdc++ headers. (#GH95598)
10401040
- Fixed failed assertion when resolving context of defaulted comparison method outside of struct. (#GH96043).
1041+
- Clang now diagnoses explicit object parameters in member pointers and other contexts where they should not appear.
1042+
Fixes (#GH85992).
10411043

10421044
Bug Fixes to AST Handling
10431045
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7577,6 +7577,8 @@ def err_explicit_object_lambda_ambiguous_base : Error<
75777577
def err_explicit_object_lambda_inaccessible_base : Error<
75787578
"invalid explicit object parameter type %0 in lambda with capture; "
75797579
"the type must derive publicly from the lambda">;
7580+
def err_explicit_object_parameter_invalid: Error<
7581+
"an explicit object parameter can only appear as the first parameter of a member function">;
75807582

75817583
def err_ref_qualifier_overload : Error<
75827584
"cannot overload a member function %select{without a ref-qualifier|with "

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11258,6 +11258,34 @@ void Sema::CheckExplicitObjectMemberFunction(Declarator &D,
1125811258
D.setInvalidType();
1125911259
}
1126011260

11261+
// Friend declarations require some care. Consider:
11262+
//
11263+
// namespace N {
11264+
// struct A{};
11265+
// int f(A);
11266+
// }
11267+
//
11268+
// struct S {
11269+
// struct T {
11270+
// int f(this T);
11271+
// };
11272+
//
11273+
// friend int T::f(this T); // Allow this.
11274+
// friend int f(this S); // But disallow this.
11275+
// friend int N::f(this A); // And disallow this.
11276+
// };
11277+
//
11278+
// Here, it seems to suffice to check whether the scope
11279+
// specifier designates a class type.
11280+
if (D.getDeclSpec().isFriendSpecified() &&
11281+
!isa_and_present<CXXRecordDecl>(
11282+
computeDeclContext(D.getCXXScopeSpec()))) {
11283+
Diag(ExplicitObjectParam->getBeginLoc(),
11284+
diag::err_explicit_object_parameter_nonmember)
11285+
<< D.getSourceRange() << /*non-member=*/2 << IsLambda;
11286+
D.setInvalidType();
11287+
}
11288+
1126111289
if (IsLambda && FTI.hasMutableQualifier()) {
1126211290
Diag(ExplicitObjectParam->getBeginLoc(),
1126311291
diag::err_explicit_object_parameter_mutable)
@@ -11268,10 +11296,8 @@ void Sema::CheckExplicitObjectMemberFunction(Declarator &D,
1126811296
return;
1126911297

1127011298
if (!DC || !DC->isRecord()) {
11271-
Diag(ExplicitObjectParam->getLocation(),
11272-
diag::err_explicit_object_parameter_nonmember)
11273-
<< D.getSourceRange() << /*non-member=*/2 << IsLambda;
11274-
D.setInvalidType();
11299+
assert(D.isInvalidType() && "Explicit object parameter in non-member "
11300+
"should have been diagnosed already");
1127511301
return;
1127611302
}
1127711303

clang/lib/Sema/SemaType.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4762,6 +4762,61 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
47624762
// Check for auto functions and trailing return type and adjust the
47634763
// return type accordingly.
47644764
if (!D.isInvalidType()) {
4765+
auto IsClassType = [&](CXXScopeSpec &SS) {
4766+
// If there already was an problem with the scope, don’t issue another
4767+
// error about the explicit object parameter.
4768+
return SS.isInvalid() ||
4769+
isa_and_present<CXXRecordDecl>(S.computeDeclContext(SS));
4770+
};
4771+
4772+
// C++23 [dcl.fct]p6:
4773+
//
4774+
// An explicit-object-parameter-declaration is a parameter-declaration
4775+
// with a this specifier. An explicit-object-parameter-declaration shall
4776+
// appear only as the first parameter-declaration of a
4777+
// parameter-declaration-list of one of:
4778+
//
4779+
// - a declaration of a member function or member function template
4780+
// ([class.mem]), or
4781+
//
4782+
// - an explicit instantiation ([temp.explicit]) or explicit
4783+
// specialization ([temp.expl.spec]) of a templated member function,
4784+
// or
4785+
//
4786+
// - a lambda-declarator [expr.prim.lambda].
4787+
DeclaratorContext C = D.getContext();
4788+
ParmVarDecl *First =
4789+
FTI.NumParams
4790+
? dyn_cast_if_present<ParmVarDecl>(FTI.Params[0].Param)
4791+
: nullptr;
4792+
4793+
bool IsFunctionDecl = D.getInnermostNonParenChunk() == &DeclType;
4794+
if (First && First->isExplicitObjectParameter() &&
4795+
C != DeclaratorContext::LambdaExpr &&
4796+
4797+
// Either not a member or nested declarator in a member.
4798+
//
4799+
// Note that e.g. 'static' or 'friend' declarations are accepted
4800+
// here; we diagnose them later when we build the member function
4801+
// because it's easier that way.
4802+
(C != DeclaratorContext::Member || !IsFunctionDecl) &&
4803+
4804+
// Allow out-of-line definitions of member functions.
4805+
!IsClassType(D.getCXXScopeSpec())) {
4806+
if (IsFunctionDecl)
4807+
S.Diag(First->getBeginLoc(),
4808+
diag::err_explicit_object_parameter_nonmember)
4809+
<< /*non-member*/ 2 << /*function*/ 0
4810+
<< First->getSourceRange();
4811+
else
4812+
S.Diag(First->getBeginLoc(),
4813+
diag::err_explicit_object_parameter_invalid)
4814+
<< First->getSourceRange();
4815+
4816+
D.setInvalidType();
4817+
AreDeclaratorChunksValid = false;
4818+
}
4819+
47654820
// trailing-return-type is only required if we're declaring a function,
47664821
// and not, for instance, a pointer to a function.
47674822
if (D.getDeclSpec().hasAutoTypeSpec() &&

clang/test/SemaCXX/cxx2b-deducing-this.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,3 +918,44 @@ struct C {
918918
}
919919
};
920920
}
921+
922+
namespace GH85992 {
923+
namespace N {
924+
struct A {
925+
int f(this A);
926+
};
927+
928+
int f(A);
929+
}
930+
931+
struct S {
932+
int (S::*x)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}}
933+
int (*y)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}}
934+
int (***z)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}}
935+
936+
int f(this S);
937+
int ((g))(this S);
938+
friend int h(this S); // expected-error {{an explicit object parameter cannot appear in a non-member function}}
939+
int h(int x, int (*)(this S)); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}}
940+
941+
struct T {
942+
int f(this T);
943+
};
944+
945+
friend int T::f(this T);
946+
friend int N::A::f(this N::A);
947+
friend int N::f(this N::A); // expected-error {{an explicit object parameter cannot appear in a non-member function}}
948+
int friend func(this T); // expected-error {{an explicit object parameter cannot appear in a non-member function}}
949+
};
950+
951+
using T = int (*)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}}
952+
using U = int (S::*)(this int); // expected-error {{an explicit object parameter can only appear as the first parameter of a member function}}
953+
int h(this int); // expected-error {{an explicit object parameter cannot appear in a non-member function}}
954+
955+
int S::f(this S) { return 1; }
956+
957+
namespace a {
958+
void f();
959+
};
960+
void a::f(this auto) {} // expected-error {{an explicit object parameter cannot appear in a non-member function}}
961+
}

0 commit comments

Comments
 (0)