Skip to content

Commit 7563ce7

Browse files
committed
[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).
1 parent d9cf0db commit 7563ce7

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

clang/lib/Parse/Parser.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,6 +2020,27 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(
20202020
CXXScopeSpec &SS, bool IsNewScope,
20212021
ImplicitTypenameContext AllowImplicitTypename) {
20222022
if (Tok.is(tok::identifier)) {
2023+
// In incremental/clang-repl mode, suppress access checks for qualified
2024+
// type lookups when no delayed diagnostic pool is active. This handles
2025+
// the case where we're parsing input like "A::B *A::foo()" where the type
2026+
// "A::B" might be a private member type, but if this turns out to be an
2027+
// out-of-line member function definition, access should be allowed.
2028+
//
2029+
// When the declaration is actually parsed (via ParseDeclarationOrFunctionDefinition),
2030+
// the ParsingDeclSpec will set up proper delayed diagnostics to handle
2031+
// access checking in the correct context.
2032+
//
2033+
// We only do this in incremental mode because this is where the issue
2034+
// manifests - disambiguation happens before ParsingDeclSpec is created.
2035+
// In normal compilation, these access checks would be re-triggered during
2036+
// actual parsing with delayed diagnostics active.
2037+
bool SuppressAccess = getLangOpts().IncrementalExtensions &&
2038+
SS.isNotEmpty() &&
2039+
!Actions.DelayedDiagnostics.shouldDelayDiagnostics();
2040+
std::optional<SuppressAccessChecks> SAC;
2041+
if (SuppressAccess)
2042+
SAC.emplace(*this, true);
2043+
20232044
// Determine whether the identifier is a type name.
20242045
if (ParsedType Ty = Actions.getTypeName(
20252046
*Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS,
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: cat %s | clang-repl | FileCheck %s
2+
3+
extern "C" int printf(const char*, ...);
4+
5+
// Test 1: Private nested struct in return type
6+
class Container { struct Node { int data; }; public: Node* create(); };
7+
Container::Node* Container::create() { return new Node{456}; }
8+
printf("Private nested struct: %d\n", Container().create()->data);
9+
// CHECK: Private nested struct: 456
10+
11+
// Test 2: Private enum in return type
12+
class Status { enum Code { OK = 0, ERROR = 1 }; public: Code get(); };
13+
Status::Code Status::get() { return OK; }
14+
printf("Private enum: %d\n", Status().get());
15+
// CHECK: Private enum: 0
16+
17+
// Test 3: Template with private type alias
18+
template<typename T> class Handler { using ptr = T*; public: ptr get(); };
19+
template<typename T> typename Handler<T>::ptr Handler<T>::get() { return nullptr; }
20+
printf("Template with private type: passed\n");
21+
// CHECK: Template with private type: passed
22+
23+
// Test 4: Protected type alias (not just private)
24+
class ProtectedBase { protected: using value_type = int; public: value_type get(); };
25+
ProtectedBase::value_type ProtectedBase::get() { return 42; }
26+
printf("Protected type alias: %d\n", ProtectedBase().get());
27+
// CHECK: Protected type alias: 42
28+
29+
// Test 5: Deeply nested private type (A::B::C)
30+
class Outer { public: class Middle { struct Inner { int x; }; public: Inner* create(); }; };
31+
Outer::Middle::Inner* Outer::Middle::create() { return new Inner{789}; }
32+
printf("Deeply nested: %d\n", Outer::Middle().create()->x);
33+
// CHECK: Deeply nested: 789
34+
35+
// Test 6: Private typedef (not using declaration)
36+
class WithTypedef { typedef double real_t; public: real_t compute(); };
37+
WithTypedef::real_t WithTypedef::compute() { return 2.718; }
38+
printf("Private typedef: %.3f\n", WithTypedef().compute());
39+
// CHECK: Private typedef: 2.718
40+
41+
// Test 7: Const-qualified return type with private type
42+
class ConstReturn { using data_t = int; public: const data_t& get(); private: data_t val = 100; };
43+
const ConstReturn::data_t& ConstReturn::get() { return val; }
44+
printf("Const return: %d\n", ConstReturn().get());
45+
// CHECK: Const return: 100
46+
47+
// Test 8: Reference return type with private type
48+
class RefReturn { using ref_t = int; ref_t value = 55; public: ref_t& getRef(); };
49+
RefReturn::ref_t& RefReturn::getRef() { return value; }
50+
RefReturn rr; rr.getRef() = 66;
51+
printf("Reference return: %d\n", rr.getRef());
52+
// CHECK: Reference return: 66
53+
54+
// Test 9: Pointer-to-pointer with private type
55+
class PtrPtr { using inner_t = int; public: inner_t** get(); };
56+
PtrPtr::inner_t** PtrPtr::get() { static int* p = nullptr; return &p; }
57+
printf("Pointer to pointer: passed\n");
58+
// CHECK: Pointer to pointer: passed
59+
60+
%quit

0 commit comments

Comments
 (0)