https://github.com/zwuis updated 
https://github.com/llvm/llvm-project/pull/220463

>From 09fdee4bb5e89de3cf519510c8ca933baa204443 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <[email protected]>
Date: Wed, 2 Sep 2026 10:50:29 +0800
Subject: [PATCH 1/3] Add missing information to AST for calling explicit
 object member functions

---
 clang/lib/Sema/SemaOverload.cpp               | 64 ++++++++++++-----
 clang/test/AST/ast-print-deducing-this.cpp    | 69 +++++++++++++++++++
 .../ast-dump-sycl-kernel-call-stmt.cpp        | 14 ++--
 3 files changed, 121 insertions(+), 26 deletions(-)
 create mode 100644 clang/test/AST/ast-print-deducing-this.cpp

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 106ddb90ed9dc..55c2bd04c2a0c 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -63,9 +63,12 @@ static bool functionHasPassObjectSizeParams(const 
FunctionDecl *FD) {
 
 /// A convenience routine for creating a decayed reference to a function.
 static ExprResult CreateFunctionRefExpr(
-    Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, const Expr *Base,
-    bool HadMultipleCandidates, SourceLocation Loc = SourceLocation(),
-    const DeclarationNameLoc &LocInfo = DeclarationNameLoc()) {
+    Sema &S, NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc,
+    FunctionDecl *Fn, NamedDecl *FoundDecl, const Expr *Base,
+    bool HadMultipleCandidates, const DeclarationNameInfo &NameInfo,
+    const TemplateArgumentListInfo *TemplateArgs) {
+  SourceLocation Loc = NameInfo.getLoc();
+
   if (S.DiagnoseUseOfDecl(FoundDecl, Loc))
     return ExprError();
   // If FoundDecl is different from Fn (such as if one is a template
@@ -76,8 +79,10 @@ static ExprResult CreateFunctionRefExpr(
   // being used.
   if (FoundDecl != Fn && S.DiagnoseUseOfDecl(Fn, Loc))
     return ExprError();
-  DeclRefExpr *DRE = new (S.Context)
-      DeclRefExpr(S.Context, Fn, false, Fn->getType(), VK_LValue, Loc, 
LocInfo);
+  auto *DRE = DeclRefExpr::Create(S.Context, QualifierLoc, TemplateKWLoc, Fn,
+                                  /*RefersToEnclosingVariableOrCapture=*/false,
+                                  NameInfo, Fn->getType(), VK_LValue, 
FoundDecl,
+                                  TemplateArgs);
   if (HadMultipleCandidates)
     DRE->setHadMultipleCandidates(true);
 
@@ -92,6 +97,23 @@ static ExprResult CreateFunctionRefExpr(
                              CK_FunctionToPointerDecay);
 }
 
+static ExprResult CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn,
+                                        NamedDecl *FoundDecl, const Expr *Base,
+                                        bool HadMultipleCandidates,
+                                        const DeclarationNameInfo &NameInfo) {
+  return CreateFunctionRefExpr(S, /*QualifierLoc=*/{}, /*TemplateKWLoc=*/{}, 
Fn,
+                               FoundDecl, Base, HadMultipleCandidates, 
NameInfo,
+                               /*TemplateArgs=*/nullptr);
+}
+
+static ExprResult CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn,
+                                        NamedDecl *FoundDecl, const Expr *Base,
+                                        bool HadMultipleCandidates,
+                                        SourceLocation Loc) {
+  return CreateFunctionRefExpr(S, Fn, FoundDecl, Base, HadMultipleCandidates,
+                               DeclarationNameInfo(Fn->getDeclName(), Loc));
+}
+
 static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
                                  bool InOverloadResolution,
                                  StandardConversionSequence &SCS,
@@ -16225,9 +16247,9 @@ ExprResult 
Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
         // Build the actual expression node.
         DeclarationNameInfo OpLocInfo(OpName, LLoc);
         OpLocInfo.setCXXOperatorNameRange(SourceRange(LLoc, RLoc));
-        ExprResult FnExpr = CreateFunctionRefExpr(
-            *this, FnDecl, Best->FoundDecl, Base, HadMultipleCandidates,
-            OpLocInfo.getLoc(), OpLocInfo.getInfo());
+        ExprResult FnExpr =
+            CreateFunctionRefExpr(*this, FnDecl, Best->FoundDecl, Base,
+                                  HadMultipleCandidates, OpLocInfo);
         if (FnExpr.isInvalid())
           return ExprError();
 
@@ -16561,10 +16583,18 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, 
Expr *MemExprE,
                                       NewArgs))
       return ExprError();
 
+    // FIXME: avoid copy.
+    TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = nullptr;
+    if (MemExpr->hasExplicitTemplateArgs()) {
+      MemExpr->copyTemplateArgumentsInto(TemplateArgsBuffer);
+      TemplateArgs = &TemplateArgsBuffer;
+    }
+
     // Build the actual expression node.
-    ExprResult FnExpr =
-        CreateFunctionRefExpr(*this, Method, FoundDecl, MemExpr,
-                              HadMultipleCandidates, MemExpr->getExprLoc());
+    ExprResult FnExpr = CreateFunctionRefExpr(
+        *this, MemExpr->getQualifierLoc(), MemExpr->getTemplateKeywordLoc(),
+        Method, FoundDecl, MemExpr, HadMultipleCandidates,
+        MemExpr->getMemberNameInfo(), TemplateArgs);
     if (FnExpr.isInvalid())
       return ExprError();
 
@@ -16853,10 +16883,8 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
   DeclarationNameInfo OpLocInfo(
                Context.DeclarationNames.getCXXOperatorName(OO_Call), 
LParenLoc);
   OpLocInfo.setCXXOperatorNameRange(SourceRange(LParenLoc, RParenLoc));
-  ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
-                                           Obj, HadMultipleCandidates,
-                                           OpLocInfo.getLoc(),
-                                           OpLocInfo.getInfo());
+  ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl, Obj,
+                                           HadMultipleCandidates, OpLocInfo);
   if (NewFn.isInvalid())
     return true;
 
@@ -17085,10 +17113,8 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult 
&R,
   }
 
   FunctionDecl *FD = Best->Function;
-  ExprResult Fn = CreateFunctionRefExpr(*this, FD, Best->FoundDecl,
-                                        nullptr, HadMultipleCandidates,
-                                        SuffixInfo.getLoc(),
-                                        SuffixInfo.getInfo());
+  ExprResult Fn = CreateFunctionRefExpr(*this, FD, Best->FoundDecl, nullptr,
+                                        HadMultipleCandidates, SuffixInfo);
   if (Fn.isInvalid())
     return true;
 
diff --git a/clang/test/AST/ast-print-deducing-this.cpp 
b/clang/test/AST/ast-print-deducing-this.cpp
new file mode 100644
index 0000000000000..1fba787288cce
--- /dev/null
+++ b/clang/test/AST/ast-print-deducing-this.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -std=c++23 -ast-print %s | FileCheck %s --match-full-lines
+
+struct S {
+  void f(this const S &);
+  template <typename> void g(this const S &);
+};
+
+struct Ptr1 {
+  const S *operator->() const;
+};
+
+struct Ptr2 {
+  const S *operator->(this const Ptr2 &);
+};
+
+// FIXME: Should output the syntax of calling member functions.
+void h(S s, S *ptr, Ptr1 ptr1, Ptr2 ptr2) {
+  s.f();
+  // CHECK: f(s);
+  s.S::f();
+  // CHECK: S::f(s);
+  s.g<S>();
+  // CHECK: g<S>(s);
+  s.template g<S>();
+  // CHECK: template g<S>(s);
+  s.S::g<S>();
+  // CHECK: S::g<S>(s);
+  s.S::template g<S>();
+  // CHECK: S::template g<S>(s);
+
+  ptr->f();
+  // CHECK: f(*ptr);
+  ptr->S::f();
+  // CHECK: S::f(*ptr);
+  ptr->g<S>();
+  // CHECK: g<S>(*ptr);
+  ptr->template g<S>();
+  // CHECK: template g<S>(*ptr);
+  ptr->S::g<S>();
+  // CHECK: S::g<S>(*ptr);
+  ptr->S::template g<S>();
+  // CHECK: S::template g<S>(*ptr);
+
+  ptr1->f();
+  // CHECK: f(*ptr1);
+  ptr1->S::f();
+  // CHECK: S::f(*ptr1);
+  ptr1->g<S>();
+  // CHECK: g<S>(*ptr1);
+  ptr1->template g<S>();
+  // CHECK: template g<S>(*ptr1);
+  ptr1->S::g<S>();
+  // CHECK: S::g<S>(*ptr1);
+  ptr1->S::template g<S>();
+  // CHECK: S::template g<S>(*ptr1);
+
+  ptr2->f();
+  // CHECK: f(*ptr2);
+  ptr2->S::f();
+  // CHECK: S::f(*ptr2);
+  ptr2->g<S>();
+  // CHECK: g<S>(*ptr2);
+  ptr2->template g<S>();
+  // CHECK: template g<S>(*ptr2);
+  ptr2->S::g<S>();
+  // CHECK: S::g<S>(*ptr2);
+  ptr2->S::template g<S>();
+  // CHECK: S::template g<S>(*ptr2);
+}
diff --git a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp 
b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
index d66d4fdcc9483..fc66b78b85508 100644
--- a/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
+++ b/clang/test/ASTSYCL/ast-dump-sycl-kernel-call-stmt.cpp
@@ -87,7 +87,7 @@ void skep2<KN<2>>(K<2>);
 // CHECK-NEXT: |   | |-CompoundStmt {{.*}}
 // CHECK-NEXT: |   | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
 // CHECK-NEXT: |   | |   |-ImplicitCastExpr {{.*}} 'void (*)() const' 
<FunctionToPointerDecay>
-// CHECK-NEXT: |   | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue 
CXXMethod {{.*}} 'operator()' 'void () const'
+// CHECK-NEXT: |   | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue 
CXXMethod {{.*}} 'operator()' 'void () const' (FunctionTemplate {{.*}} 
'operator()')
 // CHECK-NEXT: |   | |   `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
 // CHECK-NEXT: |   | |     `-DeclRefExpr {{.*}} 'K<2>' lvalue ParmVar {{.*}} 
'k' 'K<2>'
 // CHECK-NEXT: |   | |-CompoundStmt {{.*}}
@@ -104,7 +104,7 @@ void skep2<KN<2>>(K<2>);
 // CHECK-NEXT: |   |   `-CompoundStmt {{.*}}
 // CHECK-NEXT: |   |     `-CXXOperatorCallExpr {{.*}} 'void' '()'
 // CHECK-NEXT: |   |       |-ImplicitCastExpr {{.*}} 'void (*)() const' 
<FunctionToPointerDecay>
-// CHECK-NEXT: |   |       | `-DeclRefExpr {{.*}} 'void () const' lvalue 
CXXMethod {{.*}} 'operator()' 'void () const'
+// CHECK-NEXT: |   |       | `-DeclRefExpr {{.*}} 'void () const' lvalue 
CXXMethod {{.*}} 'operator()' 'void () const' (FunctionTemplate {{.*}} 
'operator()')
 // CHECK-NEXT: |   |       `-ImplicitCastExpr {{.*}} 'const K<2>' lvalue <NoOp>
 // CHECK-NEXT: |   |         `-DeclRefExpr {{.*}} 'K<2>' lvalue ImplicitParam 
{{.*}} 'k' 'K<2>'
 // CHECK-NEXT: |   `-SYCLKernelEntryPointAttr {{.*}} KN<2>
@@ -147,7 +147,7 @@ void skep3<KN<3>>(K<3> k) {
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
 // CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)() const' 
<FunctionToPointerDecay>
-// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod 
{{.*}} 'operator()' 'void () const'
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void () const' lvalue CXXMethod 
{{.*}} 'operator()' 'void () const' (FunctionTemplate {{.*}} 'operator()')
 // CHECK-NEXT: | | |   `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
 // CHECK-NEXT: | | |     `-DeclRefExpr {{.*}} 'K<3>' lvalue ParmVar {{.*}} 'k' 
'K<3>'
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
@@ -164,7 +164,7 @@ void skep3<KN<3>>(K<3> k) {
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
 // CHECK-NEXT: | |     `-CXXOperatorCallExpr {{.*}} 'void' '()'
 // CHECK-NEXT: | |       |-ImplicitCastExpr {{.*}} 'void (*)() const' 
<FunctionToPointerDecay>
-// CHECK-NEXT: | |       | `-DeclRefExpr {{.*}} 'void () const' lvalue 
CXXMethod {{.*}} 'operator()' 'void () const'
+// CHECK-NEXT: | |       | `-DeclRefExpr {{.*}} 'void () const' lvalue 
CXXMethod {{.*}} 'operator()' 'void () const' (FunctionTemplate {{.*}} 
'operator()')
 // CHECK-NEXT: | |       `-ImplicitCastExpr {{.*}} 'const K<3>' lvalue <NoOp>
 // CHECK-NEXT: | |         `-DeclRefExpr {{.*}} 'K<3>' lvalue ImplicitParam 
{{.*}} 'k' 'K<3>'
 // CHECK-NEXT: | `-SYCLKernelEntryPointAttr {{.*}} KN<3>
@@ -181,7 +181,7 @@ void skep4(K<4> k, int p1, int p2) {
 // CHECK-NEXT: | | |-CompoundStmt {{.*}}
 // CHECK-NEXT: | | | `-CXXOperatorCallExpr {{.*}} 'void' '()'
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'void (*)(int, int) const' 
<FunctionToPointerDecay>
-// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue 
CXXMethod {{.*}} 'operator()' 'void (int, int) const'
+// CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue 
CXXMethod {{.*}} 'operator()' 'void (int, int) const' (FunctionTemplate {{.*}} 
'operator()')
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
 // CHECK-NEXT: | | |   | `-DeclRefExpr {{.*}} 'K<4>' lvalue ParmVar {{.*}} 'k' 
'K<4>'
 // CHECK-NEXT: | | |   |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
@@ -210,7 +210,7 @@ void skep4(K<4> k, int p1, int p2) {
 // CHECK-NEXT: | |   `-CompoundStmt {{.*}}
 // CHECK-NEXT: | |     `-CXXOperatorCallExpr {{.*}} 'void' '()'
 // CHECK-NEXT: | |       |-ImplicitCastExpr {{.*}} 'void (*)(int, int) const' 
<FunctionToPointerDecay>
-// CHECK-NEXT: | |       | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue 
CXXMethod {{.*}} 'operator()' 'void (int, int) const'
+// CHECK-NEXT: | |       | `-DeclRefExpr {{.*}} 'void (int, int) const' lvalue 
CXXMethod {{.*}} 'operator()' 'void (int, int) const' (FunctionTemplate {{.*}} 
'operator()')
 // CHECK-NEXT: | |       |-ImplicitCastExpr {{.*}} 'const K<4>' lvalue <NoOp>
 // CHECK-NEXT: | |       | `-DeclRefExpr {{.*}} 'K<4>' lvalue ImplicitParam 
{{.*}} 'k' 'K<4>'
 // CHECK-NEXT: | |       |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
@@ -269,7 +269,7 @@ void skep5(int unused1, K<5> k, int unused2, int p, int 
unused3) {
 // CHECK-NEXT: | |     |   `-IntegerLiteral {{.*}} 'int' 4
 // CHECK-NEXT: | |     `-CXXOperatorCallExpr {{.*}} 'void' '()'
 // CHECK-NEXT: | |       |-ImplicitCastExpr {{.*}} 'void (*)(int, int, int, 
int, int, int, (lambda {{.*}}) const' <FunctionToPointerDecay>
-// CHECK-NEXT: | |       | `-DeclRefExpr {{.*}} 'void (int, int, int, int, 
int, int, (lambda {{.*}})) const' lvalue CXXMethod {{.*}} 'operator()' 'void 
(int, int, int, int, int, int, (lambda {{.*}})) const'
+// CHECK-NEXT: | |       | `-DeclRefExpr {{.*}} 'void (int, int, int, int, 
int, int, (lambda {{.*}})) const' lvalue CXXMethod {{.*}} 'operator()' 'void 
(int, int, int, int, int, int, (lambda {{.*}})) const' (FunctionTemplate {{.*}} 
'operator()')
 // CHECK-NEXT: | |       |-ImplicitCastExpr {{.*}} 'const K<5>' lvalue <NoOp>
 // CHECK-NEXT: | |       | `-DeclRefExpr {{.*}} 'K<5>' lvalue ImplicitParam 
{{.*}} 'k' 'K<5>'
 // CHECK-NEXT: | |       |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>

>From 1c3ab7881f99a4df917063acde31db067c8cea2b Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <[email protected]>
Date: Fri, 4 Sep 2026 00:11:39 +0800
Subject: [PATCH 2/3] Add clang-include-cleaner test

---
 .../include-cleaner/unittests/WalkASTTest.cpp         | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp 
b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index 1e2ff594ef87a..fe0d342b4be0e 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -1428,5 +1428,16 @@ TEST(WalkAST, ObjCEncodeExpr) {
            {"-x", "objective-c"});
 }
 
+TEST(WalkAST, GH218829) {
+  testWalk("struct $explicit^S1 {};",
+           R"cpp(
+struct S2 {
+  template <typename T> void f(this const S2 &);
+};
+void h(S2 s) { s.f<^S1>(); }
+)cpp",
+           {"-std=c++23"});
+}
+
 } // namespace
 } // namespace clang::include_cleaner

>From 7d9842e5e08dd58eba4d88fb2948b6a1cd92120c Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <[email protected]>
Date: Fri, 4 Sep 2026 12:23:14 +0800
Subject: [PATCH 3/3] Add release note

---
 clang/docs/ReleaseNotes.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 82772714b5d46..a647f8984cd89 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -594,6 +594,9 @@ features cannot lower the translation-unit ABI level;
 - `FunctionDecl::getReturnTypeSourceRange()` now returns correct source
   location of a trailing return type. (#GH162649)
 
+- Added missed information to the AST node representing the member function
+  when calling a explicit object member function. (#GH218829)
+
 #### Miscellaneous Bug Fixes
 
 #### Miscellaneous Clang Crashes Fixed

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to