Skip to content

Commit 7531f59

Browse files
v2 of fallback algo
1 parent be24f7b commit 7531f59

File tree

4 files changed

+77
-8
lines changed

4 files changed

+77
-8
lines changed

compiler/rustc_typeck/src/check/fallback.rs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use rustc_data_structures::{
55
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
66
stable_set::FxHashSet,
77
};
8-
use rustc_middle::ty::{self, Ty};
8+
use rustc_middle::traits;
9+
use rustc_middle::ty::{self, ToPredicate, Ty, WithConstness};
10+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
911

1012
impl<'tcx> FnCtxt<'_, 'tcx> {
1113
/// Performs type inference fallback, returning true if any fallback
@@ -337,17 +339,68 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
337339
// reach a member of N. If so, it falls back to `()`. Else
338340
// `!`.
339341
let mut diverging_fallback = FxHashMap::default();
340-
for &diverging_vid in &diverging_vids {
342+
diverging_fallback.reserve(diverging_vids.len());
343+
'outer: for &diverging_vid in &diverging_vids {
341344
let diverging_ty = self.tcx.mk_ty_var(diverging_vid);
342345
let root_vid = self.infcx.root_var(diverging_vid);
343346
let can_reach_non_diverging = coercion_graph
344347
.depth_first_search(root_vid)
345348
.any(|n| roots_reachable_from_non_diverging.visited(n));
349+
350+
for obligation in self.fulfillment_cx.borrow_mut().pending_obligations() {
351+
// We need to check if this obligation is a trait bound like
352+
// `root_vid: Foo`, and then we check:
353+
//
354+
// If `(): Foo` may hold, then fallback to (),
355+
// otherwise continue on.
356+
if let ty::PredicateKind::Trait(predicate, constness) =
357+
obligation.predicate.kind().skip_binder()
358+
{
359+
if predicate.trait_ref.def_id
360+
== self.infcx.tcx.require_lang_item(rustc_hir::LangItem::Sized, None)
361+
{
362+
// Skip sized obligations, those are not usually
363+
// 'intentional', satisfied by both ! and () though.
364+
continue;
365+
}
366+
367+
// If this trait bound is on the current root_vid...
368+
if self.root_vid(predicate.self_ty()) == Some(root_vid) {
369+
// fixme: copy of mk_trait_obligation_with_new_self_ty
370+
let new_self_ty = self.infcx.tcx.types.unit;
371+
372+
let trait_ref = ty::TraitRef {
373+
substs: self
374+
.infcx
375+
.tcx
376+
.mk_substs_trait(new_self_ty, &predicate.trait_ref.substs[1..]),
377+
..predicate.trait_ref
378+
};
379+
380+
// Then contstruct a new obligation with Self = () added
381+
// to the ParamEnv, and see if it holds.
382+
let o = rustc_infer::traits::Obligation::new(
383+
traits::ObligationCause::dummy(),
384+
obligation.param_env,
385+
// FIXME: this drops the binder on the floor that
386+
// previously existed?
387+
trait_ref.with_constness(constness).to_predicate(self.infcx.tcx),
388+
);
389+
if self.infcx.predicate_may_hold(&o) {
390+
// If we might hold for (), then fallback to ().
391+
debug!("fallback to () as {:?} may hold: {:?}", o, diverging_vid);
392+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
393+
continue 'outer;
394+
}
395+
}
396+
}
397+
}
398+
346399
if can_reach_non_diverging {
347-
debug!("fallback to (): {:?}", diverging_vid);
400+
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
348401
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
349402
} else {
350-
debug!("fallback to !: {:?}", diverging_vid);
403+
debug!("fallback to ! - all diverging: {:?}", diverging_vid);
351404
diverging_fallback.insert(diverging_ty, self.tcx.mk_diverging_default());
352405
}
353406
}

src/test/ui/never_type/diverging-fallback-no-leak.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#![feature(never_type_fallback)]
2-
31
fn make_unit() {}
42

53
trait Test {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// check-pass
2+
3+
trait Bar { }
4+
5+
impl Bar for () {}
6+
impl Bar for u32 {}
7+
8+
fn takes_closure_ret<F, R>(f: F)
9+
where F: FnOnce() -> R,
10+
R: Bar,
11+
{}
12+
13+
fn main() {
14+
takes_closure_ret(|| ());
15+
// This would normally fallback to ! without v2 fallback algorithm,
16+
// and then fail because !: Bar is not satisfied.
17+
takes_closure_ret(|| panic!("test"));
18+
}

src/test/ui/type-alias-impl-trait/different_defining_uses_never_type.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: concrete type differs from previous defining opaque type use
22
--> $DIR/different_defining_uses_never_type.rs:12:1
33
|
44
LL | fn bar() -> Foo {
5-
| ^^^^^^^^^^^^^^^ expected `&'static str`, got `!`
5+
| ^^^^^^^^^^^^^^^ expected `&'static str`, got `()`
66
|
77
note: previous use here
88
--> $DIR/different_defining_uses_never_type.rs:8:1
@@ -14,7 +14,7 @@ error: concrete type differs from previous defining opaque type use
1414
--> $DIR/different_defining_uses_never_type.rs:17:1
1515
|
1616
LL | fn boo() -> Foo {
17-
| ^^^^^^^^^^^^^^^ expected `&'static str`, got `!`
17+
| ^^^^^^^^^^^^^^^ expected `&'static str`, got `()`
1818
|
1919
note: previous use here
2020
--> $DIR/different_defining_uses_never_type.rs:8:1

0 commit comments

Comments
 (0)