https://github.com/pfeodrippe updated https://github.com/llvm/llvm-project/pull/169272
>From 7563ce7f2f16d1ba4c80947a4dd0d1e611625fbb 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 When parsing qualified type names (e.g., `io_context::impl_type`) at file scope in clang-repl, suppress access checks during type annotation. This allows private member type aliases to be used in return types of out-of-line member function definitions, matching the C++ standard's scoping rules for such declarations. Fixes: Parsing errors in clang-repl when including headers with out-of-line member functions that return private nested types (e.g., ASIO's io_context::impl_type). --- clang/lib/Parse/Parser.cpp | 21 +++++++ .../Interpreter/private-member-access.cpp | 60 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 clang/test/Interpreter/private-member-access.cpp diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index a6fc676f23a51..ac5c90bf7c7ac 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -2020,6 +2020,27 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec( CXXScopeSpec &SS, bool IsNewScope, ImplicitTypenameContext AllowImplicitTypename) { if (Tok.is(tok::identifier)) { + // In incremental/clang-repl mode, suppress access checks for qualified + // type lookups when no delayed diagnostic pool is active. This handles + // the case where we're parsing input like "A::B *A::foo()" where the type + // "A::B" might be a private member type, but if this turns out to be an + // out-of-line member function definition, access should be allowed. + // + // When the declaration is actually parsed (via ParseDeclarationOrFunctionDefinition), + // the ParsingDeclSpec will set up proper delayed diagnostics to handle + // access checking in the correct context. + // + // We only do this in incremental mode because this is where the issue + // manifests - disambiguation happens before ParsingDeclSpec is created. + // In normal compilation, these access checks would be re-triggered during + // actual parsing with delayed diagnostics active. + bool SuppressAccess = getLangOpts().IncrementalExtensions && + SS.isNotEmpty() && + !Actions.DelayedDiagnostics.shouldDelayDiagnostics(); + std::optional<SuppressAccessChecks> SAC; + if (SuppressAccess) + SAC.emplace(*this, true); + // Determine whether the identifier is a type name. if (ParsedType Ty = Actions.getTypeName( *Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS, diff --git a/clang/test/Interpreter/private-member-access.cpp b/clang/test/Interpreter/private-member-access.cpp new file mode 100644 index 0000000000000..a18860e8ab152 --- /dev/null +++ b/clang/test/Interpreter/private-member-access.cpp @@ -0,0 +1,60 @@ +// RUN: cat %s | clang-repl | FileCheck %s + +extern "C" int printf(const char*, ...); + +// Test 1: Private nested struct in return type +class Container { struct Node { int data; }; public: Node* create(); }; +Container::Node* Container::create() { return new Node{456}; } +printf("Private nested struct: %d\n", Container().create()->data); +// CHECK: Private nested struct: 456 + +// Test 2: Private enum in return type +class Status { enum Code { OK = 0, ERROR = 1 }; public: Code get(); }; +Status::Code Status::get() { return OK; } +printf("Private enum: %d\n", Status().get()); +// CHECK: Private enum: 0 + +// Test 3: Template with private type alias +template<typename T> class Handler { using ptr = T*; public: ptr get(); }; +template<typename T> typename Handler<T>::ptr Handler<T>::get() { return nullptr; } +printf("Template with private type: passed\n"); +// CHECK: Template with private type: passed + +// Test 4: Protected type alias (not just private) +class ProtectedBase { protected: using value_type = int; public: value_type get(); }; +ProtectedBase::value_type ProtectedBase::get() { return 42; } +printf("Protected type alias: %d\n", ProtectedBase().get()); +// CHECK: Protected type alias: 42 + +// Test 5: Deeply nested private type (A::B::C) +class Outer { public: class Middle { struct Inner { int x; }; public: Inner* create(); }; }; +Outer::Middle::Inner* Outer::Middle::create() { return new Inner{789}; } +printf("Deeply nested: %d\n", Outer::Middle().create()->x); +// CHECK: Deeply nested: 789 + +// Test 6: Private typedef (not using declaration) +class WithTypedef { typedef double real_t; public: real_t compute(); }; +WithTypedef::real_t WithTypedef::compute() { return 2.718; } +printf("Private typedef: %.3f\n", WithTypedef().compute()); +// CHECK: Private typedef: 2.718 + +// Test 7: Const-qualified return type with private type +class ConstReturn { using data_t = int; public: const data_t& get(); private: data_t val = 100; }; +const ConstReturn::data_t& ConstReturn::get() { return val; } +printf("Const return: %d\n", ConstReturn().get()); +// CHECK: Const return: 100 + +// Test 8: Reference return type with private type +class RefReturn { using ref_t = int; ref_t value = 55; public: ref_t& getRef(); }; +RefReturn::ref_t& RefReturn::getRef() { return value; } +RefReturn rr; rr.getRef() = 66; +printf("Reference return: %d\n", rr.getRef()); +// CHECK: Reference return: 66 + +// Test 9: Pointer-to-pointer with private type +class PtrPtr { using inner_t = int; public: inner_t** get(); }; +PtrPtr::inner_t** PtrPtr::get() { static int* p = nullptr; return &p; } +printf("Pointer to pointer: passed\n"); +// CHECK: Pointer to pointer: passed + +%quit _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
