@@ -11,9 +11,19 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
11
11
/// Performs type inference fallback, returning true if any fallback
12
12
/// occurs.
13
13
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
+
14
19
// All type checking constraints were added, try to fallback unsolved variables.
15
20
self . select_obligations_where_possible ( false , |_| { } ) ;
16
21
22
+ debug ! (
23
+ "type-inference-fallback post selection obligations: {:#?}" ,
24
+ self . fulfillment_cx. borrow_mut( ) . pending_obligations( )
25
+ ) ;
26
+
17
27
// Check if we have any unsolved varibales. If not, no need for fallback.
18
28
let unsolved_variables = self . unsolved_variables ( ) ;
19
29
if unsolved_variables. is_empty ( ) {
@@ -251,6 +261,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
251
261
) -> FxHashMap < Ty < ' tcx > , Ty < ' tcx > > {
252
262
debug ! ( "calculate_diverging_fallback({:?})" , unsolved_variables) ;
253
263
264
+ let relationships = self . fulfillment_cx . borrow_mut ( ) . relationships ( ) . clone ( ) ;
265
+
254
266
// Construct a coercion graph where an edge `A -> B` indicates
255
267
// a type variable is that is coerced
256
268
let coercion_graph = self . create_coercion_graph ( ) ;
@@ -334,21 +346,63 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
334
346
roots_reachable_from_non_diverging,
335
347
) ;
336
348
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
+
337
353
// For each diverging variable, figure out whether it can
338
354
// reach a member of N. If so, it falls back to `()`. Else
339
355
// `!`.
340
356
let mut diverging_fallback = FxHashMap :: default ( ) ;
357
+ diverging_fallback. reserve ( diverging_vids. len ( ) ) ;
341
358
for & diverging_vid in & diverging_vids {
342
359
let diverging_ty = self . tcx . mk_ty_var ( diverging_vid) ;
343
360
let root_vid = self . infcx . root_var ( diverging_vid) ;
344
361
let can_reach_non_diverging = coercion_graph
345
362
. depth_first_search ( root_vid)
346
363
. 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) ;
349
403
diverging_fallback. insert ( diverging_ty, self . tcx . types . unit ) ;
350
404
} else {
351
- debug ! ( "fallback to !: {:?}" , diverging_vid) ;
405
+ debug ! ( "fallback to ! - all diverging : {:?}" , diverging_vid) ;
352
406
diverging_fallback. insert ( diverging_ty, self . tcx . mk_diverging_default ( ) ) ;
353
407
}
354
408
}
@@ -369,10 +423,23 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
369
423
obligation. predicate . kind ( ) . no_bound_vars ( )
370
424
} )
371
425
. 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)
376
443
} else {
377
444
return None ;
378
445
} ;
0 commit comments