[clang] 864478c - [Clang] Disallow explicit object parameters in more contexts (#89078)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 15 16:55:14 PDT 2024
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}}
+}
More information about the cfe-commits
mailing list