llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Paulo Rafael Feodrippe (pfeodrippe) <details> <summary>Changes</summary> Fixes https://github.com/llvm/llvm-project/issues/164885. Used Copilot. Run all clang tests locally with `ninja check-clang` (everything that was gree in main is also green here.) >From https://github.com/llvm/llvm-project/issues/164885, we can see the issue >below in clang-repl that works just fine in normal clang. The issue happens >when we are 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. The fix, only applied in incremental mode (clang-repl / Interpreter) seems to be to suppress access checks for qualified type lookups when no delayed diagnostic pool is active. ```c /***** somecode.c *****/ struct scheduler { }; class io_context { using impl_type = scheduler; public: impl_type *foo(); }; /* The error doesn't occur if `impl_type` is a parameter type. Only for the return type. */ io_context::impl_type *io_context::foo() { return nullptr; } ``` ```c /***** in clang-repl *****/ ❯ clang-repl clang-repl> #include "asio-repro.hpp" In file included from <<< inputs >>>:1: In file included from input_line_1:1: ./asio-repro.hpp:13:13: error: 'impl_type' is a private member of 'io_context' 13 | io_context::impl_type *io_context::add_impl() | ^ ./asio-repro.hpp:7:9: note: implicitly declared private here 7 | using impl_type = scheduler; | ^ error: Parsing failed. ``` --- Full diff: https://github.com/llvm/llvm-project/pull/169272.diff 2 Files Affected: - (modified) clang/lib/Parse/Parser.cpp (+21) - (added) clang/test/Interpreter/private-member-access.cpp (+60) ``````````diff 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 `````````` </details> https://github.com/llvm/llvm-project/pull/169272 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
