Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion clang/lib/Parse/ParseTentative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if this comment above should be modified \o

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))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume the c++ scope specifier has been parsed already. In this case we should call a parser function rather than implementing some parsing by ourselves. You can trace what clang does for out-of-line definitions after calling ParseOptionalCXXScopeSpecifier. We should do the same and if that fails we know that this is not a declaration, more parsing will be required otherwise.

ConsumeToken();
if (Tok.is(tok::identifier))
return true;
}
}
break;
}
Expand Down
35 changes: 35 additions & 0 deletions clang/test/Interpreter/private-member-access.cpp
Original file line number Diff line number Diff line change
@@ -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