From 15f2d26a7be338ac88a5edd11e162f0b1f6a974d Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Thu, 23 Jul 2026 02:54:32 +0000
Subject: [PATCH 19/30] hir_conv: elide output lifetimes through arbitrary self
 types

The output-lifetime elision rule only recognised a receiver whose type
was a direct borrow (`&self`/`&mut self`). For arbitrary self types
like `self: Pin<&mut Self>` no candidate was found, and with multiple
input lifetimes the elided return lifetime then hit "Unspecified
lifetime in outer context".

rustc's self-elision looks through the receiver type for references to
`Self`; mirror that: when the receiver isn't a direct borrow, search it
for `&Self` borrows and use the lifetime if exactly one is found.

Seen with futures-io 0.3.32:
  fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>)
      -> Poll<Result<&[u8]>>;

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wCVW89GjmNyYviEcANPHM
---
 src/hir_conv/lifetime_elision.cpp | 34 +++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/src/hir_conv/lifetime_elision.cpp b/src/hir_conv/lifetime_elision.cpp
index ce8e9f1c..33c72e0d 100644
--- a/src/hir_conv/lifetime_elision.cpp
+++ b/src/hir_conv/lifetime_elision.cpp
@@ -1115,6 +1115,40 @@ namespace
                         elided_output_lifetime = b->lifetime;
                     }
                 }
+                else {
+                    // Arbitrary self types (e.g. `self: Pin<&mut Self>`, futures-io's
+                    // `fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>>`):
+                    // rustc's self-elision rule looks through the receiver type for
+                    // references to `Self` - if there is exactly one, its lifetime is
+                    // assigned to elided output lifetimes.
+                    struct SelfBorrowV: public HIR::Visitor {
+                        const ::HIR::TypeRef*   self_type;
+                        HIR::LifetimeRef out;
+                        unsigned n_found = 0;
+                        bool is_self(const HIR::TypeRef& ty) const {
+                            if( ty.data().is_Generic() && ty.data().as_Generic().is_self() )
+                                return true;
+                            if( self_type && ty == *self_type )
+                                return true;
+                            return false;
+                        }
+                        void visit_type(HIR::TypeRef& ty) override {
+                            if(const auto* tep = ty.data().opt_Borrow()) {
+                                if( is_self(tep->inner) ) {
+                                    n_found += 1;
+                                    out = tep->lifetime;
+                                }
+                            }
+                            HIR::Visitor::visit_type(ty);
+                        }
+                    } v;
+                    v.self_type = m_resolve.m_self_type;
+                    v.visit_type(item.m_args[0].second);
+                    if( v.n_found == 1 ) {
+                        elided_output_lifetime = v.out;
+                        DEBUG("Elided/explicit 'self (through receiver) - " << elided_output_lifetime);
+                    }
+                }
                 if( item.m_receiver == HIR::Function::Receiver::Value ) {
                     m_value_self_type = m_resolve.m_self_type;
                 }
-- 
2.43.0

