From 0ef65429992b3d55394b7cc409a5665e495df9ab Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 22 Jul 2026 20:04:36 +0000 Subject: [PATCH 10/25] 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 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