https://github.com/pfeodrippe updated 
https://github.com/llvm/llvm-project/pull/169272

>From e1ca2f5dbcf676b77a2296f81e7a5277c1c76898 Mon Sep 17 00:00:00 2001
From: Paulo Feodrippe <[email protected]>
Date: Sun, 23 Nov 2025 22:34:43 -0500
Subject: [PATCH] [clang][Parser] Allow private type aliases in out-of-line
 member function return types

Bug: In clang-repl (incremental parsing mode), out-of-line member
function definitions that use private/protected type aliases in their
return type incorrectly fail with access errors (e.g.,
io_context::impl_type *io_context::foo() errors with "'impl_type' is a
private member of 'io_context'"). This happens because the
disambiguation logic in isCXXDeclarationStatement calls
isCXXSimpleDeclaration, which triggers access checking before the
parser establishes the correct declaration context.

Fix: Extend the heuristic in ParseTentative.cpp that skips
isCXXSimpleDeclaration to also recognize pointer/reference
patterns (A::B *id, A::B &id, A::B &&id, A::B **id) as likely
declarations, avoiding the premature access check.
---
 clang/lib/Parse/ParseTentative.cpp            | 20 ++++++++++-
 .../Interpreter/private-member-access.cpp     | 35 +++++++++++++++++++
 2 files changed, 54 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/Interpreter/private-member-access.cpp

diff --git a/clang/lib/Parse/ParseTentative.cpp 
b/clang/lib/Parse/ParseTentative.cpp
index 82f2294ff5bb7..ac16066460e13 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -61,8 +61,26 @@ bool Parser::isCXXDeclarationStatement(
           // token is also an identifier and assume a declaration.
           // We cannot check if the scopes match because the declarations could
           // involve namespaces and friend declarations.
-          if (NextToken().is(tok::identifier))
+          //
+          // Also handle cases like "A::B *foo" or "A::B &foo" where the type
+          // is followed by ptr/ref declarator operators. These patterns are
+          // very likely to be declarations (e.g., out-of-line member function
+          // definitions with pointer/reference return types).
+          Token Next = NextToken();
+          if (Next.is(tok::identifier))
             return true;
+          // Check for pointer/reference patterns: A::B *id, A::B &id, A::B 
&&id
+          // Also handles multiple indirections like A::B **id
+          if (Next.isOneOf(tok::star, tok::amp, tok::ampamp)) {
+            // Look ahead to see if there's an identifier after ptr/ref ops
+            RevertingTentativeParsingAction PA(*this);
+            ConsumeToken(); // consume the identifier (type name)
+            // Skip all consecutive *, &, && tokens (for cases like A::B **)
+            while (Tok.isOneOf(tok::star, tok::amp, tok::ampamp))
+              ConsumeToken();
+            if (Tok.is(tok::identifier))
+              return true;
+          }
         }
         break;
       }
diff --git a/clang/test/Interpreter/private-member-access.cpp 
b/clang/test/Interpreter/private-member-access.cpp
new file mode 100644
index 0000000000000..101e2beed4889
--- /dev/null
+++ b/clang/test/Interpreter/private-member-access.cpp
@@ -0,0 +1,35 @@
+// RUN: cat %s | clang-repl | FileCheck %s
+
+extern "C" int printf(const char*, ...);
+
+// Test 1: Pointer to private type alias (the original bug report)
+class io_context { using impl_type = int; public: impl_type* get(); };
+io_context::impl_type* io_context::get() { return nullptr; }
+printf("Pointer to private type: %s\n", io_context().get() == nullptr ? 
"passed" : "failed");
+// CHECK: Pointer to private type: passed
+
+// Test 2: Reference to private type
+class RefReturn { using ref_t = int; ref_t value = 42; public: ref_t& 
getRef(); };
+RefReturn::ref_t& RefReturn::getRef() { return value; }
+printf("Reference to private type: %d\n", RefReturn().getRef());
+// CHECK: Reference to private type: 42
+
+// Test 3: Double pointer to private type
+class PtrPtr { using inner_t = int; public: inner_t** get(); };
+PtrPtr::inner_t** PtrPtr::get() { static int* p = nullptr; return &p; }
+printf("Double pointer to private type: %s\n", PtrPtr().get() != nullptr ? 
"passed" : "failed");
+// CHECK: Double pointer to private type: passed
+
+// Test 4: Const reference to private type
+class ConstRef { using data_t = int; data_t val = 100; public: const data_t& 
get(); };
+const ConstRef::data_t& ConstRef::get() { return val; }
+printf("Const reference to private type: %d\n", ConstRef().get());
+// CHECK: Const reference to private type: 100
+
+// Test 5: Pointer to private nested struct
+class Container { struct Node { int x; }; public: Node* create(); };
+Container::Node* Container::create() { return new Node{789}; }
+printf("Pointer to private nested struct: %d\n", Container().create()->x);
+// CHECK: Pointer to private nested struct: 789
+
+%quit

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

Reply via email to