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&gt; #include "asio-repro.hpp"
In file included from &lt;&lt;&lt; inputs &gt;&gt;&gt;: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

Reply via email to