@@ -24,7 +24,7 @@ use stable_mir::ty::{FnDef, RigidTy, Ty, TyKind};
2424use std:: collections:: HashSet ;
2525use std:: fmt;
2626use std:: iter:: Peekable ;
27- use syn:: { PathSegment , QSelf , TypePath } ;
27+ use syn:: { PathArguments , PathSegment , QSelf , TypePath } ;
2828use tracing:: { debug, debug_span} ;
2929
3030mod type_resolution;
@@ -153,7 +153,7 @@ fn resolve_path<'tcx>(
153153 let next_item = match def_kind {
154154 DefKind :: ForeignMod | DefKind :: Mod => resolve_in_module ( tcx, base, & name) ,
155155 DefKind :: Struct | DefKind :: Enum | DefKind :: Union => {
156- resolve_in_type_def ( tcx, base, & name)
156+ resolve_in_type_def ( tcx, base, & path . base_path_args , & name)
157157 }
158158 DefKind :: Trait => resolve_in_trait ( tcx, base, & name) ,
159159 kind => {
@@ -246,12 +246,13 @@ impl fmt::Display for ResolveError<'_> {
246246/// The segments of a path.
247247type Segments = Vec < PathSegment > ;
248248
249- /// A path consisting of a starting point and a bunch of segments. If `base`
249+ /// A path consisting of a starting point, any PathArguments for that starting point, and a bunch of segments. If `base`
250250/// matches `Base::LocalModule { id: _, may_be_external_path : true }`, then
251251/// `segments` cannot be empty.
252252#[ derive( Debug , Hash ) ]
253253struct Path {
254254 pub base : DefId ,
255+ pub base_path_args : PathArguments ,
255256 pub segments : Segments ,
256257}
257258
@@ -285,7 +286,11 @@ fn resolve_prefix<'tcx>(
285286 let next_name = segment. ident . to_string ( ) ;
286287 let result = resolve_external ( tcx, & next_name) ;
287288 if let Some ( def_id) = result {
288- Ok ( Path { base : def_id, segments : segments. cloned ( ) . collect ( ) } )
289+ Ok ( Path {
290+ base : def_id,
291+ base_path_args : segment. arguments . clone ( ) ,
292+ segments : segments. cloned ( ) . collect ( ) ,
293+ } )
289294 } else {
290295 Err ( ResolveError :: MissingItem {
291296 tcx,
@@ -306,7 +311,11 @@ fn resolve_prefix<'tcx>(
306311 None => current_module,
307312 Some ( ( hir_id, _) ) => hir_id. owner . def_id ,
308313 } ;
309- Ok ( Path { base : crate_root. to_def_id ( ) , segments : segments. cloned ( ) . collect ( ) } )
314+ Ok ( Path {
315+ base : crate_root. to_def_id ( ) ,
316+ base_path_args : segment. arguments . clone ( ) ,
317+ segments : segments. cloned ( ) . collect ( ) ,
318+ } )
310319 }
311320 // Path starting with "self::"
312321 ( None , Some ( segment) ) if segment. ident == SELF => {
@@ -334,7 +343,11 @@ fn resolve_prefix<'tcx>(
334343 Err ( err)
335344 }
336345 } ) ?;
337- Ok ( Path { base : def_id, segments : segments. cloned ( ) . collect ( ) } )
346+ Ok ( Path {
347+ base : def_id,
348+ base_path_args : segment. arguments . clone ( ) ,
349+ segments : segments. cloned ( ) . collect ( ) ,
350+ } )
338351 }
339352 _ => {
340353 unreachable ! ( "Empty path: `{path:?}`" )
@@ -364,7 +377,11 @@ where
364377 }
365378 }
366379 debug ! ( "base: {base_module:?}" ) ;
367- Ok ( Path { base : base_module. to_def_id ( ) , segments : segments. cloned ( ) . collect ( ) } )
380+ Ok ( Path {
381+ base : base_module. to_def_id ( ) ,
382+ base_path_args : PathArguments :: None ,
383+ segments : segments. cloned ( ) . collect ( ) ,
384+ } )
368385}
369386
370387/// Resolves an external crate name.
@@ -543,14 +560,19 @@ where
543560 if segments. next ( ) . is_some ( ) {
544561 Err ( ResolveError :: UnexpectedType { tcx, item : def_id, expected : "module" } )
545562 } else {
546- resolve_in_type_def ( tcx, def_id, & name. ident . to_string ( ) )
563+ resolve_in_type_def ( tcx, def_id, & PathArguments :: None , & name. ident . to_string ( ) )
547564 }
548565}
549566
567+ fn generic_args_to_string < T : ToTokens > ( args : & T ) -> String {
568+ args. to_token_stream ( ) . to_string ( ) . chars ( ) . filter ( |c| !c. is_whitespace ( ) ) . collect :: < String > ( )
569+ }
570+
550571/// Resolves a function in a type given its `def_id`.
551572fn resolve_in_type_def < ' tcx > (
552573 tcx : TyCtxt < ' tcx > ,
553574 type_id : DefId ,
575+ base_path_args : & PathArguments ,
554576 name : & str ,
555577) -> Result < DefId , ResolveError < ' tcx > > {
556578 debug ! ( ?name, ?type_id, "resolve_in_type" ) ;
@@ -566,12 +588,57 @@ fn resolve_in_type_def<'tcx>(
566588 match candidates. len ( ) {
567589 0 => Err ( ResolveError :: MissingItem { tcx, base : type_id, unresolved : name. to_string ( ) } ) ,
568590 1 => Ok ( candidates[ 0 ] ) ,
569- _ => Err ( ResolveError :: AmbiguousPartialPath {
570- tcx,
571- name : name. into ( ) ,
572- base : type_id,
573- candidates,
574- } ) ,
591+ _ => {
592+ let invalid_path_err = |generic_args, candidates : Vec < DefId > | -> ResolveError {
593+ ResolveError :: InvalidPath {
594+ msg : format ! (
595+ "the generic arguments {} are invalid. The available implementations are: \n {}" ,
596+ & generic_args,
597+ & candidates
598+ . iter( )
599+ . map( |def_id| tcx. def_path_str( def_id) )
600+ . intersperse( "\n " . to_string( ) )
601+ . collect:: <String >( )
602+ ) ,
603+ }
604+ } ;
605+ // If there are multiple implementations, we need generic arguments on the base type to refine our options.
606+ match base_path_args {
607+ // If there aren't such arguments, report the ambiguity.
608+ PathArguments :: None => Err ( ResolveError :: AmbiguousPartialPath {
609+ tcx,
610+ name : name. into ( ) ,
611+ base : type_id,
612+ candidates,
613+ } ) ,
614+ // Otherwise, use the provided generic arguments to refine our options.
615+ PathArguments :: AngleBracketed ( args) => {
616+ let generic_args = generic_args_to_string ( & args) ;
617+ let refined_candidates: Vec < DefId > = candidates
618+ . iter ( )
619+ . cloned ( )
620+ . filter ( |item| {
621+ is_item_name_with_generic_args ( tcx, * item, & generic_args, name)
622+ } )
623+ . collect ( ) ;
624+ match refined_candidates. len ( ) {
625+ 0 => Err ( invalid_path_err ( & generic_args, candidates) ) ,
626+ 1 => Ok ( refined_candidates[ 0 ] ) ,
627+ // since is_item_name_with_generic_args looks at the entire item path after the base type, it shouldn't be possible to have more than one match
628+ _ => unreachable ! (
629+ "Got multiple refined candidates {:?}" ,
630+ refined_candidates
631+ . iter( )
632+ . map( |def_id| tcx. def_path_str( def_id) )
633+ . collect:: <Vec <String >>( )
634+ ) ,
635+ }
636+ }
637+ PathArguments :: Parenthesized ( args) => {
638+ Err ( invalid_path_err ( & generic_args_to_string ( args) , candidates) )
639+ }
640+ }
641+ }
575642 }
576643}
577644
@@ -625,7 +692,11 @@ where
625692 base : ty,
626693 unresolved : name. to_string ( ) ,
627694 } ) ?;
628- Ok ( Path { base : item, segments : segments. cloned ( ) . collect ( ) } )
695+ Ok ( Path {
696+ base : item,
697+ base_path_args : PathArguments :: None ,
698+ segments : segments. cloned ( ) . collect ( ) ,
699+ } )
629700 } else {
630701 Err ( ResolveError :: InvalidPath { msg : format ! ( "Unexpected primitive type `{ty}`" ) } )
631702 }
@@ -636,3 +707,14 @@ fn is_item_name(tcx: TyCtxt, item: DefId, name: &str) -> bool {
636707 let last = item_path. split ( "::" ) . last ( ) . unwrap ( ) ;
637708 last == name
638709}
710+
711+ fn is_item_name_with_generic_args (
712+ tcx : TyCtxt ,
713+ item : DefId ,
714+ generic_args : & str ,
715+ name : & str ,
716+ ) -> bool {
717+ let item_path = tcx. def_path_str ( item) ;
718+ let all_but_base_type = item_path. find ( "::" ) . map_or ( "" , |idx| & item_path[ idx..] ) ;
719+ all_but_base_type == format ! ( "{}::{}" , generic_args, name)
720+ }
0 commit comments