[clang] [Clang] Disallow explicit object parameters in more contexts (PR #89078)

via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 23 10:10:54 PDT 2024


https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/89078

>From c611122688657287e8285edd9a2875e4975d26dd Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 17 Apr 2024 16:15:39 +0200
Subject: [PATCH 1/6] [Clang] Disallow explicit object parameters in more
 contexts

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 ++
 clang/lib/Sema/SemaDeclCXX.cpp                |  6 ++--
 clang/lib/Sema/SemaType.cpp                   | 30 +++++++++++++++++++
 clang/test/SemaCXX/cxx2b-deducing-this.cpp    | 22 ++++++++++++--
 4 files changed, 54 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5ec0218aedfe86..680638d2e59c6a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7494,6 +7494,8 @@ def err_explicit_object_parameter_mutable: Error<
 def err_invalid_explicit_object_type_in_lambda: Error<
   "invalid explicit object parameter type %0 in lambda with capture; "
   "the type must be the same as, or derived from, the lambda">;
+def err_explicit_object_parameter_invalid: Error<
+  "an explicit object parameter is not allowed here">;
 
 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 7669171fea56ff..ea634df48e5deb 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11469,10 +11469,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 404c4e8e31b558..406065741bf2d9 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5287,6 +5287,36 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
       // Check for auto functions and trailing return type and adjust the
       // return type accordingly.
       if (!D.isInvalidType()) {
+        // [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 either:
+        //
+        //   - a member-declarator that declares a member function [class.mem], 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;
+        if (First && First->isExplicitObjectParameter() &&
+            C != DeclaratorContext::LambdaExpr &&
+
+            // Either not a member or nested declarator in a member.
+            (C != DeclaratorContext::Member ||
+             D.getInnermostNonParenChunk() != &DeclType) &&
+
+            // Allow out-of-line definitions if we have a scope spec.
+            D.getCXXScopeSpec().isEmpty()) {
+          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 5f29a955e053c3..ddb770a92db0f9 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -5,7 +5,7 @@
 void f(this); // expected-error{{variable has incomplete type 'void'}} \
               // expected-error{{invalid use of 'this' outside of a non-static member function}}
 
-void g(this auto); // expected-error{{an explicit object parameter cannot appear in a non-member function}}
+void g(this auto); // expected-error{{an explicit object parameter is not allowed here}}
 
 auto l1 = [] (this auto) static {}; // expected-error{{an explicit object parameter cannot appear in a static lambda}}
 auto l2 = [] (this auto) mutable {}; // expected-error{{a lambda with an explicit object parameter cannot be mutable}}
@@ -685,7 +685,7 @@ struct S {
   static void j(this S s); // expected-error {{an explicit object parameter cannot appear in a static function}}
 };
 
-void nonmember(this S s); // expected-error {{an explicit object parameter cannot appear in a non-member function}}
+void nonmember(this S s); // expected-error {{an explicit object parameter is not allowed here}}
 
 int test() {
   S s;
@@ -838,3 +838,21 @@ int h() {
   }();
 }
 }
+
+namespace GH85992 {
+struct S {
+  int (S::*x)(this int); // expected-error {{an explicit object parameter is not allowed here}}
+  int (*y)(this int); // expected-error {{an explicit object parameter is not allowed here}}
+  int (***z)(this int); // expected-error {{an explicit object parameter is not allowed here}}
+
+  int f(this S);
+  int ((g))(this S);
+  int h(int x, int (*)(this S)); // expected-error {{an explicit object parameter is not allowed here}}
+};
+
+using T = int (*)(this int); // expected-error {{an explicit object parameter is not allowed here}}
+using U = int (S::*)(this int); // expected-error {{an explicit object parameter is not allowed here}}
+int h(this int); // expected-error {{an explicit object parameter is not allowed here}}
+
+int S::f(this S) { return 1; }
+}

>From c27a3ff93474bc890de2b66ab7f486bb59e3b6d8 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 17 Apr 2024 16:16:47 +0200
Subject: [PATCH 2/6] [NFC] clang-format

---
 clang/lib/Sema/SemaType.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 406065741bf2d9..23f2b9d74931bd 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5294,7 +5294,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
         // appear only as the first parameter-declaration of a
         // parameter-declaration-list of either:
         //
-        //   - a member-declarator that declares a member function [class.mem], or
+        //   - a member-declarator that declares a member function [class.mem],
+        //   or
         //   - a lambda-declarator [expr.prim.lambda].
         DeclaratorContext C = D.getContext();
         ParmVarDecl *First =

>From 0c1239a85159aaa4958bb574a326e85f50719b1b Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 17 Apr 2024 16:23:15 +0200
Subject: [PATCH 3/6] [NFC] Reformat comment

---
 clang/lib/Sema/SemaType.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 23f2b9d74931bd..95257b801c8a6c 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5294,9 +5294,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
         // appear only as the first parameter-declaration of a
         // parameter-declaration-list of either:
         //
-        //   - a member-declarator that declares a member function [class.mem],
-        //   or
-        //   - a lambda-declarator [expr.prim.lambda].
+        // - a member-declarator that declares a member function [class.mem], or
+        // - a lambda-declarator [expr.prim.lambda].
         DeclaratorContext C = D.getContext();
         ParmVarDecl *First =
             FTI.NumParams

>From 8edd57fd41c5c62de628b8ea9cf6b0ff1e4bc535 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 23 Apr 2024 18:25:04 +0200
Subject: [PATCH 4/6] [Clang] Check that the NNS is a class type

---
 clang/lib/Sema/SemaType.cpp                | 9 ++++++++-
 clang/test/SemaCXX/cxx2b-deducing-this.cpp | 5 +++++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 95257b801c8a6c..d8b74b640af40a 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5287,6 +5287,13 @@ 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));
+        };
+
         // [dcl.fct]p6:
         //
         // An explicit-object-parameter-declaration is a parameter-declaration
@@ -5309,7 +5316,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
              D.getInnermostNonParenChunk() != &DeclType) &&
 
             // Allow out-of-line definitions if we have a scope spec.
-            D.getCXXScopeSpec().isEmpty()) {
+            !isClassType(D.getCXXScopeSpec())) {
           S.Diag(First->getBeginLoc(),
                  diag::err_explicit_object_parameter_invalid)
               << First->getSourceRange();
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index ddb770a92db0f9..d2c47b77365cd8 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -855,4 +855,9 @@ using U = int (S::*)(this int); // expected-error {{an explicit object parameter
 int h(this int); // expected-error {{an explicit object parameter is not allowed here}}
 
 int S::f(this S) { return 1; }
+
+namespace a {
+void f();
+};
+void a::f(this auto) {} // expected-error {{an explicit object parameter is not allowed here}}
 }

>From 9c0609cf787cf00685d05ecabf707a885a77cc73 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 23 Apr 2024 19:09:45 +0200
Subject: [PATCH 5/6] [Clang] Handle friends and restore better diagnostic

---
 clang/lib/Sema/SemaDeclCXX.cpp             | 17 +++++++++++++++++
 clang/lib/Sema/SemaType.cpp                | 22 +++++++++++++++++-----
 clang/test/SemaCXX/cxx2b-deducing-this.cpp | 15 +++++++++++----
 3 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index ea634df48e5deb..1333f8492b40d4 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11459,6 +11459,23 @@ void Sema::CheckExplicitObjectMemberFunction(Declarator &D,
     D.setInvalidType();
   }
 
+  // Handle the following case:
+  //
+  // struct S {
+  //   struct T {
+  //     int f(this T);
+  //   };
+  //
+  //   friend int T::f(this T); // Allow this.
+  //   friend int f(this S);    // But disallow this.
+  // };
+  if (D.getDeclSpec().isFriendSpecified() && D.getCXXScopeSpec().isEmpty()) {
+    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)
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d8b74b640af40a..2c2bdb503a34ca 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5308,18 +5308,30 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
             FTI.NumParams
                 ? dyn_cast_if_present<ParmVarDecl>(FTI.Params[0].Param)
                 : nullptr;
+
+        auto IsFunctionDecl = D.getInnermostNonParenChunk() == &DeclType;
         if (First && First->isExplicitObjectParameter() &&
             C != DeclaratorContext::LambdaExpr &&
 
             // Either not a member or nested declarator in a member.
-            (C != DeclaratorContext::Member ||
-             D.getInnermostNonParenChunk() != &DeclType) &&
+            //
+            // 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 if we have a scope spec.
             !isClassType(D.getCXXScopeSpec())) {
-          S.Diag(First->getBeginLoc(),
-                 diag::err_explicit_object_parameter_invalid)
-              << First->getSourceRange();
+          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;
         }
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index d2c47b77365cd8..48f963acc27e61 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -5,7 +5,7 @@
 void f(this); // expected-error{{variable has incomplete type 'void'}} \
               // expected-error{{invalid use of 'this' outside of a non-static member function}}
 
-void g(this auto); // expected-error{{an explicit object parameter is not allowed here}}
+void g(this auto); // expected-error{{an explicit object parameter cannot appear in a non-member function}}
 
 auto l1 = [] (this auto) static {}; // expected-error{{an explicit object parameter cannot appear in a static lambda}}
 auto l2 = [] (this auto) mutable {}; // expected-error{{a lambda with an explicit object parameter cannot be mutable}}
@@ -685,7 +685,7 @@ struct S {
   static void j(this S s); // expected-error {{an explicit object parameter cannot appear in a static function}}
 };
 
-void nonmember(this S s); // expected-error {{an explicit object parameter is not allowed here}}
+void nonmember(this S s); // expected-error{{an explicit object parameter cannot appear in a non-member function}}
 
 int test() {
   S s;
@@ -847,17 +847,24 @@ struct S {
 
   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 is not allowed here}}
+
+  struct T {
+    int f(this T);
+  };
+
+  friend int T::f(this T);
 };
 
 using T = int (*)(this int); // expected-error {{an explicit object parameter is not allowed here}}
 using U = int (S::*)(this int); // expected-error {{an explicit object parameter is not allowed here}}
-int h(this int); // expected-error {{an explicit object parameter is not allowed here}}
+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 is not allowed here}}
+void a::f(this auto) {} // expected-error {{an explicit object parameter cannot appear in a non-member function}}
 }

>From 853da04fe387f56fa69a468342bb665381db2b79 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 23 Apr 2024 19:10:11 +0200
Subject: [PATCH 6/6] [NFC] clang-format

---
 clang/lib/Sema/SemaType.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 2c2bdb503a34ca..6a7754dce96d76 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -5287,7 +5287,7 @@ 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) {
+        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() ||



More information about the cfe-commits mailing list