@@ -11,9 +11,19 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
1111 /// Performs type inference fallback, returning true if any fallback
1212 /// occurs.
1313 pub ( super ) fn type_inference_fallback ( & self ) -> bool {
14+ debug ! (
15+ "type-inference-fallback start obligations: {:#?}" ,
16+ self . fulfillment_cx. borrow_mut( ) . pending_obligations( )
17+ ) ;
18+
1419 // All type checking constraints were added, try to fallback unsolved variables.
1520 self . select_obligations_where_possible ( false , |_| { } ) ;
1621
22+ debug ! (
23+ "type-inference-fallback post selection obligations: {:#?}" ,
24+ self . fulfillment_cx. borrow_mut( ) . pending_obligations( )
25+ ) ;
26+
1727 // Check if we have any unsolved varibales. If not, no need for fallback.
1828 let unsolved_variables = self . unsolved_variables ( ) ;
1929 if unsolved_variables. is_empty ( ) {
@@ -251,6 +261,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
251261 ) -> FxHashMap < Ty < ' tcx > , Ty < ' tcx > > {
252262 debug ! ( "calculate_diverging_fallback({:?})" , unsolved_variables) ;
253263
264+ let relationships = self . fulfillment_cx . borrow_mut ( ) . relationships ( ) . clone ( ) ;
265+
254266 // Construct a coercion graph where an edge `A -> B` indicates
255267 // a type variable is that is coerced
256268 let coercion_graph = self . create_coercion_graph ( ) ;
@@ -334,21 +346,63 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
334346 roots_reachable_from_non_diverging,
335347 ) ;
336348
349+ debug ! ( "inherited: {:#?}" , self . inh. fulfillment_cx. borrow_mut( ) . pending_obligations( ) ) ;
350+ debug ! ( "obligations: {:#?}" , self . fulfillment_cx. borrow_mut( ) . pending_obligations( ) ) ;
351+ debug ! ( "relationships: {:#?}" , relationships) ;
352+
337353 // For each diverging variable, figure out whether it can
338354 // reach a member of N. If so, it falls back to `()`. Else
339355 // `!`.
340356 let mut diverging_fallback = FxHashMap :: default ( ) ;
357+ diverging_fallback. reserve ( diverging_vids. len ( ) ) ;
341358 for & diverging_vid in & diverging_vids {
342359 let diverging_ty = self . tcx . mk_ty_var ( diverging_vid) ;
343360 let root_vid = self . infcx . root_var ( diverging_vid) ;
344361 let can_reach_non_diverging = coercion_graph
345362 . depth_first_search ( root_vid)
346363 . any ( |n| roots_reachable_from_non_diverging. visited ( n) ) ;
347- if can_reach_non_diverging {
348- debug ! ( "fallback to (): {:?}" , diverging_vid) ;
364+
365+ let mut relationship = ty:: FoundRelationships { self_in_trait : false , output : false } ;
366+
367+ for ( vid, rel) in relationships. iter ( ) {
368+ if self . infcx . root_var ( * vid) == root_vid {
369+ relationship. self_in_trait |= rel. self_in_trait ;
370+ relationship. output |= rel. output ;
371+ }
372+ }
373+
374+ if relationship. self_in_trait && relationship. output {
375+ // This case falls back to () to ensure that the code pattern in
376+ // src/test/ui/never_type/fallback-closure-ret.rs continues to
377+ // compile when never_type_fallback is enabled.
378+ //
379+ // This rule is not readily explainable from first principles,
380+ // but is rather intended as a patchwork fix to ensure code
381+ // which compiles before the stabilization of never type
382+ // fallback continues to work.
383+ //
384+ // Typically this pattern is encountered in a function taking a
385+ // closure as a parameter, where the return type of that closure
386+ // (checked by `relationship.output`) is expected to implement
387+ // some trait (checked by `relationship.self_in_trait`). This
388+ // can come up in non-closure cases too, so we do not limit this
389+ // rule to specifically `FnOnce`.
390+ //
391+ // When the closure's body is something like `panic!()`, the
392+ // return type would normally be inferred to `!`. However, it
393+ // needs to fall back to `()` in order to still compile, as the
394+ // trait is specifically implemented for `()` but not `!`.
395+ //
396+ // For details on the requirements for these relationships to be
397+ // set, see the relationship finding module in
398+ // compiler/rustc_trait_selection/src/traits/relationships.rs.
399+ debug ! ( "fallback to () - found trait and projection: {:?}" , diverging_vid) ;
400+ diverging_fallback. insert ( diverging_ty, self . tcx . types . unit ) ;
401+ } else if can_reach_non_diverging {
402+ debug ! ( "fallback to () - reached non-diverging: {:?}" , diverging_vid) ;
349403 diverging_fallback. insert ( diverging_ty, self . tcx . types . unit ) ;
350404 } else {
351- debug ! ( "fallback to !: {:?}" , diverging_vid) ;
405+ debug ! ( "fallback to ! - all diverging : {:?}" , diverging_vid) ;
352406 diverging_fallback. insert ( diverging_ty, self . tcx . mk_diverging_default ( ) ) ;
353407 }
354408 }
@@ -369,10 +423,23 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
369423 obligation. predicate . kind ( ) . no_bound_vars ( )
370424 } )
371425 . filter_map ( |atom| {
372- if let ty:: PredicateKind :: Coerce ( ty:: CoercePredicate { a, b } ) = atom {
373- let a_vid = self . root_vid ( a) ?;
374- let b_vid = self . root_vid ( b) ?;
375- Some ( ( a_vid, b_vid) )
426+ // We consider both subtyping and coercion to imply 'flow' from
427+ // some position in the code `a` to a different position `b`.
428+ // This is then used to determine which variables interact with
429+ // live code, and as such must fall back to `()` to preserve
430+ // soundness.
431+ //
432+ // In practice currently the two ways that this happens is
433+ // coercion and subtyping.
434+ let ( a, b) = if let ty:: PredicateKind :: Coerce ( ty:: CoercePredicate { a, b } ) = atom {
435+ ( a, b)
436+ } else if let ty:: PredicateKind :: Subtype ( ty:: SubtypePredicate {
437+ a_is_expected : _,
438+ a,
439+ b,
440+ } ) = atom
441+ {
442+ ( a, b)
376443 } else {
377444 return None ;
378445 } ;
0 commit comments