From e6b8f3ec2aa7d208e26952b28020c9d8f1b4255e Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Wed, 22 Jul 2026 20:04:36 +0000
Subject: [PATCH 12/30] resolve: don't hard-error in speculative import
 resolution

When searching a module's imports for a name, sibling glob and named
imports are speculatively resolved. Such a resolution can legitimately
fail mid-way: an intermediate path component may yield empty bindings
purely because the module is already being searched higher up the stack
(the recursion guard returns empty). That failure aborted compilation
with "Cannot find component N of ..." even when another glob in the
same module had already provided the binding.

Seen with libc 0.2.178's src/new/mod.rs, where cfg_if-expanded sibling
globs (`pub use pthread_::spawn::*;` etc) resolve through a module
brought in by `pub(crate) use apple::*` that is itself a reexport.
Minimal repro: three+ sibling `pub use m_::X::*` globs where `m_`
arrives via a glob of a module containing `pub(crate) use b::m_;`,
with the uses expanded from a macro.

A new soft_fail flag on Resolve_Use_GetBinding makes these speculative
lookups return empty bindings instead of erroring; genuine failures
still error when the import is resolved directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011M9MvswSD3mEeJcBm6S8z7
---
 src/resolve/use.cpp | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/src/resolve/use.cpp b/src/resolve/use.cpp
index 894a6489..9b6b9094 100644
--- a/src/resolve/use.cpp
+++ b/src/resolve/use.cpp
@@ -30,7 +30,7 @@ void Resolve_Use_Mod(const ::AST::Crate& crate, ::AST::Module& mod, ::AST::Path
 ::AST::Path::Bindings Resolve_Use_GetBinding(
     const Span& span, const ::AST::Crate& crate, const ::AST::AbsolutePath& source_mod_path,
     const ::AST::Path& path, ::std::span< const ::AST::Module* > parent_modules,
-    bool types_only=false
+    bool types_only=false, bool soft_fail=false
     );
 
 ::AST::Path::Bindings Resolve_Use_GetBinding_Mod(
@@ -589,7 +589,7 @@ void Resolve_Use_Mod(const ::AST::Crate& crate, ::AST::Module& mod, ::AST::Path
                     if( ::std::find(s_mods.begin(), s_mods.end(), &imp_e.path) == s_mods.end() )
                     {
                         s_mods.push_back(&imp_e.path);
-                        rv.merge_from( Resolve_Use_GetBinding(sp2, crate, mod.path(), Resolve_Use_AbsolutisePath(sp2, crate, mod.path(), imp_e.path), parent_modules) );
+                        rv.merge_from( Resolve_Use_GetBinding(sp2, crate, mod.path(), Resolve_Use_AbsolutisePath(sp2, crate, mod.path(), imp_e.path), parent_modules, /*types_only=*/false, /*soft_fail=*/true) );
                         s_mods.pop_back();
                     }
                     else
@@ -631,7 +631,7 @@ void Resolve_Use_Mod(const ::AST::Crate& crate, ::AST::Module& mod, ::AST::Path
                     if( ::std::find(resolve_stack_ptrs.begin(), resolve_stack_ptrs.end(), &imp_data) == resolve_stack_ptrs.end() )
                     {
                         resolve_stack_ptrs.push_back( &imp_data );
-                        bindings_ = Resolve_Use_GetBinding(sp2, crate, mod.path(), Resolve_Use_AbsolutisePath(sp2, crate, mod.path(), imp_e.path), parent_modules, /*type_only=*/true);
+                        bindings_ = Resolve_Use_GetBinding(sp2, crate, mod.path(), Resolve_Use_AbsolutisePath(sp2, crate, mod.path(), imp_e.path), parent_modules, /*type_only=*/true, /*soft_fail=*/true);
                         if( bindings_.type.is_Unbound() ) {
                             DEBUG("Recursion detected, skipping " << imp_e.path);
                             resolve_stack_ptrs.pop_back();
@@ -1100,7 +1100,7 @@ namespace {
 ::AST::Path::Bindings Resolve_Use_GetBinding(
     const Span& span, const ::AST::Crate& crate, const ::AST::AbsolutePath& source_mod_path,
     const ::AST::Path& path, ::std::span< const ::AST::Module* > parent_modules,
-    bool types_only/*=false*/
+    bool types_only/*=false*/, bool soft_fail/*=false*/
     )
 {
     TRACE_FUNCTION_F(path);
@@ -1149,8 +1149,19 @@ namespace {
         auto b = Resolve_Use_GetBinding_Mod(span, crate, source_mod_path, *mod, nodes.at(i).name(), inner_parent_modules, /*types_only=*/true);
         TU_MATCH_HDRA( (b.type.binding), {)
         default:
+            if( soft_fail ) {
+                DEBUG("Unexpected item type " << b.type.binding.tag_str() << " in import of " << path << " - soft failure");
+                return ::AST::Path::Bindings();
+            }
             ERROR(span, E0000, "Unexpected item type " << b.type.binding.tag_str() << " in import of " << path);
         TU_ARMA(Unbound, e) {
+            // NOTE: `soft_fail` is set when this is a speculative resolution (e.g. searching the
+            // globs/imports of a module mid-resolve); an intermediate component can be
+            // unresolvable simply because the recursion guard returned empty bindings.
+            if( soft_fail ) {
+                DEBUG("Cannot find component " << i << " of " << path << " - soft failure");
+                return ::AST::Path::Bindings();
+            }
             ERROR(span, E0000, "Cannot find component " << i << " of " << path << " (" << b.type.binding << ")");
             }
         TU_ARMA(Crate, e) {
-- 
2.43.0

