Skip to content

Commit 58432c2

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 58432c2

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: cat %s | clang-repl | FileCheck %s
2+
3+
extern "C" int printf(const char*, ...);
4+
5+
struct scheduler { };
6+
class io_context { using impl_type = scheduler; public: impl_type *get_impl(); };
7+
io_context::impl_type *io_context::get_impl() { return nullptr; }
8+
printf("Private type alias: passed\n");
9+
// CHECK: Private type alias: passed
10+
11+
class Container { struct Node { int data; }; public: Node* create(); };
12+
Container::Node* Container::create() { return new Node{456}; }
13+
printf("Private nested struct: %d\n", Container().create()->data);
14+
// CHECK: Private nested struct: 456
15+
16+
class Status { enum Code { OK = 0 }; public: Code get(); };
17+
Status::Code Status::get() { return OK; }
18+
printf("Private enum: %d\n", Status().get());
19+
// CHECK: Private enum: 0
20+
21+
template<typename T> class Handler { using ptr = T*; public: ptr get(); };
22+
template<typename T> typename Handler<T>::ptr Handler<T>::get() { return nullptr; }
23+
printf("Template with private type: passed\n");
24+
// CHECK: Template with private type: passed
25+
26+
namespace ns { class C { using val_t = double; public: val_t compute(); }; }
27+
ns::C::val_t ns::C::compute() { return 3.14; }
28+
printf("Namespace qualified: %.2f\n", ns::C().compute());
29+
// CHECK: Namespace qualified: 3.14
30+
31+
%quit

0 commit comments

Comments
 (0)