Skip to content

Commit 26403a2

Browse files
v2 algorithm, with the custom if in output obligations rule
1 parent 7531f59 commit 26403a2

13 files changed

+130
-104
lines changed

compiler/rustc_typeck/src/check/fallback.rs

Lines changed: 97 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,89 @@ use rustc_data_structures::{
66
stable_set::FxHashSet,
77
};
88
use rustc_middle::traits;
9-
use rustc_middle::ty::{self, ToPredicate, Ty, WithConstness};
9+
use rustc_middle::ty::{self, ToPredicate, Ty};
1010
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
1111

12+
#[derive(Default, Copy, Clone)]
13+
struct FoundRelationships {
14+
/// This is true if we identified that this Ty (`?T`) is found in a `?T: Foo`
15+
/// obligation, where:
16+
///
17+
/// * `Foo` is not `Sized`
18+
/// * `(): Foo` may be satisfied
19+
self_in_trait: bool,
20+
/// This is true if we identified that this Ty (`?T`) is found in a `<_ as
21+
/// _>::AssocType = ?T`
22+
output: bool,
23+
}
1224
impl<'tcx> FnCtxt<'_, 'tcx> {
1325
/// Performs type inference fallback, returning true if any fallback
1426
/// occurs.
1527
pub(super) fn type_inference_fallback(&self) -> bool {
28+
debug!(
29+
"type-inference-fallback start obligations: {:#?}",
30+
self.fulfillment_cx.borrow_mut().pending_obligations()
31+
);
32+
33+
let mut relationships: FxHashMap<ty::TyVid, FoundRelationships> = FxHashMap::default();
34+
for obligation in self.fulfillment_cx.borrow_mut().pending_obligations() {
35+
if let ty::PredicateKind::Trait(predicate, constness) =
36+
obligation.predicate.kind().skip_binder()
37+
{
38+
if predicate.trait_ref.def_id
39+
!= self.infcx.tcx.require_lang_item(rustc_hir::LangItem::Sized, None)
40+
{
41+
// fixme: copy of mk_trait_obligation_with_new_self_ty
42+
let new_self_ty = self.infcx.tcx.types.unit;
43+
44+
let trait_ref = ty::TraitRef {
45+
substs: self
46+
.infcx
47+
.tcx
48+
.mk_substs_trait(new_self_ty, &predicate.trait_ref.substs[1..]),
49+
..predicate.trait_ref
50+
};
51+
52+
// Then contstruct a new obligation with Self = () added
53+
// to the ParamEnv, and see if it holds.
54+
let o = rustc_infer::traits::Obligation::new(
55+
traits::ObligationCause::dummy(),
56+
obligation.param_env,
57+
obligation
58+
.predicate
59+
.kind()
60+
.map_bound(|_| {
61+
ty::PredicateKind::Trait(
62+
ty::TraitPredicate { trait_ref },
63+
constness,
64+
)
65+
})
66+
.to_predicate(self.infcx.tcx),
67+
);
68+
if self.infcx.predicate_may_hold(&o) {
69+
if let Some(ty) = self.root_vid(predicate.self_ty()) {
70+
relationships.entry(ty).or_default().self_in_trait = true;
71+
}
72+
}
73+
}
74+
}
75+
if let ty::PredicateKind::Projection(predicate) =
76+
obligation.predicate.kind().skip_binder()
77+
{
78+
if let Some(ty) = self.root_vid(predicate.ty) {
79+
relationships.entry(ty).or_default().output = true;
80+
}
81+
}
82+
}
83+
1684
// All type checking constraints were added, try to fallback unsolved variables.
1785
self.select_obligations_where_possible(false, |_| {});
86+
87+
debug!(
88+
"type-inference-fallback post selection obligations: {:#?}",
89+
self.fulfillment_cx.borrow_mut().pending_obligations()
90+
);
91+
1892
let mut fallback_has_occurred = false;
1993

2094
// Check if we have any unsolved varibales. If not, no need for fallback.
@@ -23,7 +97,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
2397
return;
2498
}
2599

26-
let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
100+
let diverging_fallback =
101+
self.calculate_diverging_fallback(&unsolved_variables, &relationships);
27102

28103
// We do fallback in two passes, to try to generate
29104
// better error messages.
@@ -249,6 +324,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
249324
fn calculate_diverging_fallback(
250325
&self,
251326
unsolved_variables: &[Ty<'tcx>],
327+
relationships: &FxHashMap<ty::TyVid, FoundRelationships>,
252328
) -> FxHashMap<Ty<'tcx>, Ty<'tcx>> {
253329
debug!("calculate_diverging_fallback({:?})", unsolved_variables);
254330

@@ -335,68 +411,27 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
335411
roots_reachable_from_non_diverging,
336412
);
337413

414+
debug!("inherited: {:#?}", self.inh.fulfillment_cx.borrow_mut().pending_obligations());
415+
debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations());
416+
338417
// For each diverging variable, figure out whether it can
339418
// reach a member of N. If so, it falls back to `()`. Else
340419
// `!`.
341420
let mut diverging_fallback = FxHashMap::default();
342421
diverging_fallback.reserve(diverging_vids.len());
343-
'outer: for &diverging_vid in &diverging_vids {
422+
for &diverging_vid in &diverging_vids {
344423
let diverging_ty = self.tcx.mk_ty_var(diverging_vid);
345424
let root_vid = self.infcx.root_var(diverging_vid);
346425
let can_reach_non_diverging = coercion_graph
347426
.depth_first_search(root_vid)
348427
.any(|n| roots_reachable_from_non_diverging.visited(n));
349428

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-
}
429+
let relationship = relationships.get(&root_vid).copied().unwrap_or_default();
398430

399-
if can_reach_non_diverging {
431+
if relationship.self_in_trait && relationship.output {
432+
debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
433+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
434+
} else if can_reach_non_diverging {
400435
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
401436
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
402437
} else {
@@ -425,6 +460,15 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
425460
let a_vid = self.root_vid(a)?;
426461
let b_vid = self.root_vid(b)?;
427462
Some((a_vid, b_vid))
463+
} else if let ty::PredicateKind::Subtype(ty::SubtypePredicate {
464+
a_is_expected: _,
465+
a,
466+
b,
467+
}) = atom
468+
{
469+
let a_vid = self.root_vid(a)?;
470+
let b_vid = self.root_vid(b)?;
471+
Some((a_vid, b_vid))
428472
} else {
429473
None
430474
}
@@ -436,7 +480,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
436480
}
437481

438482
/// If `ty` is an unresolved type variable, returns its root vid.
439-
fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
483+
pub fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
440484
Some(self.infcx.root_var(self.infcx.shallow_resolve(ty).ty_vid()?))
441485
}
442486
}

src/test/ui/binding/empty-types-in-patterns.stderr

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
//~^ NOTE: the lint level is defined here
55
// ignore-wasm32-bare compiled with panic=abort by default
66
#![feature(fn_traits)]
7-
#![feature(never_type)]
87

98
use std::panic;
109

src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
//~^ NOTE: the lint level is defined here
55
// ignore-wasm32-bare compiled with panic=abort by default
66
#![feature(fn_traits)]
7-
#![feature(never_type)]
87

98
use std::panic;
109

src/test/ui/mir/mir_calls_to_shims.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#![feature(fn_traits)]
55

6-
76
use std::panic;
87

98
fn foo(x: u32, y: u32) -> u32 { x/y }

src/test/ui/never_type/diverging-fallback-control-flow.stderr

Lines changed: 0 additions & 10 deletions
This file was deleted.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0277]: the trait bound `!: Test` is not satisfied
2-
--> $DIR/diverging-fallback-no-leak.rs:14:5
2+
--> $DIR/diverging-fallback-no-leak.rs:12:5
33
|
44
LL | unconstrained_arg(return);
55
| ^^^^^^^^^^^^^^^^^ the trait `Test` is not implemented for `!`
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// check-pass
2+
// edition:2018
3+
4+
use std::future::Future;
5+
6+
fn foo() {
7+
let ticker = loopify(async move { loop {} });
8+
9+
match ticker {
10+
Ok(v) => v,
11+
Err(()) => return,
12+
};
13+
}
14+
15+
fn loopify<F>(_: F) -> Result<F::Output, ()>
16+
where
17+
F: Future,
18+
{
19+
loop {}
20+
}
21+
22+
fn main() {}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
// check-pass
22

3-
trait Bar { }
3+
trait Bar {}
44

55
impl Bar for () {}
66
impl Bar for u32 {}
77

88
fn takes_closure_ret<F, R>(f: F)
9-
where F: FnOnce() -> R,
10-
R: Bar,
11-
{}
9+
where
10+
F: FnOnce() -> R,
11+
R: Bar,
12+
{
13+
}
1214

1315
fn main() {
1416
takes_closure_ret(|| ());
1517
// This would normally fallback to ! without v2 fallback algorithm,
1618
// and then fail because !: Bar is not satisfied.
17-
takes_closure_ret(|| panic!("test"));
19+
takes_closure_ret(|| panic!());
1820
}

src/test/ui/never_type/never-value-fallback-issue-66757.stderr

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/test/ui/pattern/usefulness/uninhabited.stderr

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

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)