Skip to content

Commit 900cf5e

Browse files
committed
Auto merge of #88804 - Mark-Simulacrum:never-algo-v2, r=nikomatsakis,jackh726
Revise never type fallback algorithm This is a rebase of #84573, but dropping the stabilization of never type (and the accompanying large test diff). Each commit builds & has tests updated alongside it, and could be reviewed in a more or less standalone fashion. But it may make more sense to review the PR as a whole, I'm not sure. It should be noted that tests being updated isn't really a good indicator of final behavior -- never_type_fallback is not enabled by default in this PR, so we can't really see the full effects of the commits here. This combines the work by Niko, which is [documented in this gist](https://gist.github.com/nikomatsakis/7a07b265dc12f5c3b3bd0422018fa660), with some additional rules largely derived to target specific known patterns that regress with the algorithm solely derived by Niko. We build these from an intuition that: * In general, fallback to `()` is *sound* in all cases * But, in general, we *prefer* fallback to `!` as it accepts more code, particularly that written to intentionally use `!` (e.g., Result's with a Infallible/! variant). When evaluating Niko's proposed algorithm, we find that there are certain cases where fallback to `!` leads to compilation failures in real-world code, and fallback to `()` fixes those errors. In order to allow for stabilization, we need to fix a good portion of these patterns. The final rule set this PR proposes is that, by default, we fallback from `?T` to `!`, with the following exceptions: 1. `?T: Foo` and `Bar::Baz = ?T` and `(): Foo`, then fallback to `()` 2. Per [Niko's algorithm](https://gist.github.com/nikomatsakis/7a07b265dc12f5c3b3bd0422018fa660#proposal-fallback-chooses-between--and--based-on-the-coercion-graph), the "live" `?T` also fallback to `()`. The first rule is necessary to address a fairly common pattern which boils down to something like the snippet below. Without rule 1, we do not see the closure's return type as needing a () fallback, which leads to compilation failure. ```rust #![feature(never_type_fallback)] trait Bar { } impl Bar for () { } impl Bar for u32 { } fn foo<R: Bar>(_: impl Fn() -> R) {} fn main() { foo(|| panic!()); } ``` r? `@jackh726`
2 parents 2b862be + c4c5fc8 commit 900cf5e

30 files changed

+733
-174
lines changed

compiler/rustc_infer/src/infer/combine.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222
// is also useful to track which value is the "expected" value in
2323
// terms of error reporting.
2424

25+
use super::equate::Equate;
2526
use super::glb::Glb;
2627
use super::lub::Lub;
2728
use super::sub::Sub;
2829
use super::type_variable::TypeVariableValue;
2930
use super::unify_key::replace_if_possible;
3031
use super::unify_key::{ConstVarValue, ConstVariableValue};
3132
use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
32-
use super::{equate::Equate, type_variable::Diverging};
3333
use super::{InferCtxt, MiscVariable, TypeTrace};
3434

3535
use crate::traits::{Obligation, PredicateObligations};
@@ -645,7 +645,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
645645
.inner
646646
.borrow_mut()
647647
.type_variables()
648-
.new_var(self.for_universe, Diverging::NotDiverging, origin);
648+
.new_var(self.for_universe, origin);
649649
let u = self.tcx().mk_ty_var(new_var_id);
650650

651651
// Record that we replaced `vid` with `new_var_id` as part of a generalization
@@ -885,11 +885,12 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
885885

886886
let origin =
887887
*self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
888-
let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var(
889-
self.for_universe,
890-
Diverging::NotDiverging,
891-
origin,
892-
);
888+
let new_var_id = self
889+
.infcx
890+
.inner
891+
.borrow_mut()
892+
.type_variables()
893+
.new_var(self.for_universe, origin);
893894
let u = self.tcx().mk_ty_var(new_var_id);
894895
debug!(
895896
"ConstInferUnifier: replacing original vid={:?} with new={:?}",

compiler/rustc_infer/src/infer/mod.rs

+10-25
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, Veri
4646
use self::region_constraints::{
4747
RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot,
4848
};
49-
use self::type_variable::{Diverging, TypeVariableOrigin, TypeVariableOriginKind};
49+
use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
5050

5151
pub mod at;
5252
pub mod canonical;
@@ -702,17 +702,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
702702
t.fold_with(&mut self.freshener())
703703
}
704704

705-
/// Returns whether `ty` is a diverging type variable or not.
706-
/// (If `ty` is not a type variable at all, returns not diverging.)
707-
///
708-
/// No attempt is made to resolve `ty`.
709-
pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> Diverging {
710-
match *ty.kind() {
711-
ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid),
712-
_ => Diverging::NotDiverging,
713-
}
714-
}
715-
716705
/// Returns the origin of the type variable identified by `vid`, or `None`
717706
/// if this is not a type variable.
718707
///
@@ -1071,31 +1060,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
10711060
})
10721061
}
10731062

1074-
pub fn next_ty_var_id(&self, diverging: Diverging, origin: TypeVariableOrigin) -> TyVid {
1075-
self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin)
1063+
/// Number of type variables created so far.
1064+
pub fn num_ty_vars(&self) -> usize {
1065+
self.inner.borrow_mut().type_variables().num_vars()
1066+
}
1067+
1068+
pub fn next_ty_var_id(&self, origin: TypeVariableOrigin) -> TyVid {
1069+
self.inner.borrow_mut().type_variables().new_var(self.universe(), origin)
10761070
}
10771071

10781072
pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
1079-
self.tcx.mk_ty_var(self.next_ty_var_id(Diverging::NotDiverging, origin))
1073+
self.tcx.mk_ty_var(self.next_ty_var_id(origin))
10801074
}
10811075

10821076
pub fn next_ty_var_in_universe(
10831077
&self,
10841078
origin: TypeVariableOrigin,
10851079
universe: ty::UniverseIndex,
10861080
) -> Ty<'tcx> {
1087-
let vid = self.inner.borrow_mut().type_variables().new_var(
1088-
universe,
1089-
Diverging::NotDiverging,
1090-
origin,
1091-
);
1081+
let vid = self.inner.borrow_mut().type_variables().new_var(universe, origin);
10921082
self.tcx.mk_ty_var(vid)
10931083
}
10941084

1095-
pub fn next_diverging_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
1096-
self.tcx.mk_ty_var(self.next_ty_var_id(Diverging::Diverges, origin))
1097-
}
1098-
10991085
pub fn next_const_var(
11001086
&self,
11011087
ty: Ty<'tcx>,
@@ -1207,7 +1193,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
12071193
// as the substitutions for the default, `(T, U)`.
12081194
let ty_var_id = self.inner.borrow_mut().type_variables().new_var(
12091195
self.universe(),
1210-
Diverging::NotDiverging,
12111196
TypeVariableOrigin {
12121197
kind: TypeVariableOriginKind::TypeParameterDefinition(
12131198
param.name,

compiler/rustc_infer/src/infer/nll_relate/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
//! constituents)
2323
2424
use crate::infer::combine::ConstEquateRelation;
25-
use crate::infer::type_variable::Diverging;
2625
use crate::infer::InferCtxt;
2726
use crate::infer::{ConstVarValue, ConstVariableValue};
2827
use rustc_data_structures::fx::FxHashMap;
@@ -927,8 +926,7 @@ where
927926
// Replacing with a new variable in the universe `self.universe`,
928927
// it will be unified later with the original type variable in
929928
// the universe `_universe`.
930-
let new_var_id =
931-
variables.new_var(self.universe, Diverging::NotDiverging, origin);
929+
let new_var_id = variables.new_var(self.universe, origin);
932930

933931
let u = self.tcx().mk_ty_var(new_var_id);
934932
debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);

compiler/rustc_infer/src/infer/type_variable.rs

+7-22
Original file line numberDiff line numberDiff line change
@@ -129,19 +129,16 @@ pub enum TypeVariableOriginKind {
129129
SubstitutionPlaceholder,
130130
AutoDeref,
131131
AdjustmentType,
132-
DivergingFn,
132+
133+
/// In type check, when we are type checking a function that
134+
/// returns `-> dyn Foo`, we substitute a type variable for the
135+
/// return type for diagnostic purposes.
136+
DynReturnFn,
133137
LatticeVariable,
134138
}
135139

136140
pub(crate) struct TypeVariableData {
137141
origin: TypeVariableOrigin,
138-
diverging: Diverging,
139-
}
140-
141-
#[derive(Copy, Clone, Debug)]
142-
pub enum Diverging {
143-
NotDiverging,
144-
Diverges,
145142
}
146143

147144
#[derive(Copy, Clone, Debug)]
@@ -191,14 +188,6 @@ impl<'tcx> TypeVariableStorage<'tcx> {
191188
}
192189

193190
impl<'tcx> TypeVariableTable<'_, 'tcx> {
194-
/// Returns the diverges flag given when `vid` was created.
195-
///
196-
/// Note that this function does not return care whether
197-
/// `vid` has been unified with something else or not.
198-
pub fn var_diverges(&self, vid: ty::TyVid) -> Diverging {
199-
self.storage.values.get(vid.index()).diverging
200-
}
201-
202191
/// Returns the origin that was given when `vid` was created.
203192
///
204193
/// Note that this function does not return care whether
@@ -260,21 +249,17 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
260249
pub fn new_var(
261250
&mut self,
262251
universe: ty::UniverseIndex,
263-
diverging: Diverging,
264252
origin: TypeVariableOrigin,
265253
) -> ty::TyVid {
266254
let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
267255

268256
let sub_key = self.sub_relations().new_key(());
269257
assert_eq!(eq_key.vid, sub_key);
270258

271-
let index = self.values().push(TypeVariableData { origin, diverging });
259+
let index = self.values().push(TypeVariableData { origin });
272260
assert_eq!(eq_key.vid.as_u32(), index as u32);
273261

274-
debug!(
275-
"new_var(index={:?}, universe={:?}, diverging={:?}, origin={:?}",
276-
eq_key.vid, universe, diverging, origin,
277-
);
262+
debug!("new_var(index={:?}, universe={:?}, origin={:?}", eq_key.vid, universe, origin,);
278263

279264
eq_key.vid
280265
}

compiler/rustc_infer/src/traits/engine.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::infer::InferCtxt;
22
use crate::traits::Obligation;
3+
use rustc_data_structures::fx::FxHashMap;
34
use rustc_hir as hir;
45
use rustc_hir::def_id::DefId;
56
use rustc_middle::ty::{self, ToPredicate, Ty, WithConstness};
@@ -73,6 +74,8 @@ pub trait TraitEngine<'tcx>: 'tcx {
7374
}
7475

7576
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
77+
78+
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships>;
7679
}
7780

7881
pub trait TraitEngineExt<'tcx> {

compiler/rustc_middle/src/ty/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -2090,3 +2090,16 @@ impl<'tcx> fmt::Debug for SymbolName<'tcx> {
20902090
fmt::Display::fmt(&self.name, fmt)
20912091
}
20922092
}
2093+
2094+
#[derive(Debug, Default, Copy, Clone)]
2095+
pub struct FoundRelationships {
2096+
/// This is true if we identified that this Ty (`?T`) is found in a `?T: Foo`
2097+
/// obligation, where:
2098+
///
2099+
/// * `Foo` is not `Sized`
2100+
/// * `(): Foo` may be satisfied
2101+
pub self_in_trait: bool,
2102+
/// This is true if we identified that this Ty (`?T`) is found in a `<_ as
2103+
/// _>::AssocType = ?T`
2104+
pub output: bool,
2105+
}

compiler/rustc_middle/src/ty/sty.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,14 @@ impl<'tcx> TyS<'tcx> {
16721672
matches!(self.kind(), Infer(TyVar(_)))
16731673
}
16741674

1675+
#[inline]
1676+
pub fn ty_vid(&self) -> Option<ty::TyVid> {
1677+
match self.kind() {
1678+
&Infer(TyVar(vid)) => Some(vid),
1679+
_ => None,
1680+
}
1681+
}
1682+
16751683
#[inline]
16761684
pub fn is_ty_infer(&self) -> bool {
16771685
matches!(self.kind(), Infer(_))

compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@ use crate::traits::{
77
ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, ObligationCause,
88
PredicateObligation, SelectionError, TraitEngine,
99
};
10-
use rustc_data_structures::fx::FxIndexSet;
10+
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
1111
use rustc_middle::ty::{self, Ty};
1212

1313
pub struct FulfillmentContext<'tcx> {
1414
obligations: FxIndexSet<PredicateObligation<'tcx>>,
15+
16+
relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
1517
}
1618

1719
impl FulfillmentContext<'tcx> {
1820
crate fn new() -> Self {
19-
FulfillmentContext { obligations: FxIndexSet::default() }
21+
FulfillmentContext {
22+
obligations: FxIndexSet::default(),
23+
relationships: FxHashMap::default(),
24+
}
2025
}
2126
}
2227

@@ -39,6 +44,8 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
3944
assert!(!infcx.is_in_snapshot());
4045
let obligation = infcx.resolve_vars_if_possible(obligation);
4146

47+
super::relationships::update(self, infcx, &obligation);
48+
4249
self.obligations.insert(obligation);
4350
}
4451

@@ -146,4 +153,8 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
146153
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
147154
self.obligations.iter().cloned().collect()
148155
}
156+
157+
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
158+
&mut self.relationships
159+
}
149160
}

compiler/rustc_trait_selection/src/traits/fulfill.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::infer::{InferCtxt, TyOrConstInferVar};
2+
use rustc_data_structures::fx::FxHashMap;
23
use rustc_data_structures::obligation_forest::ProcessResult;
34
use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
45
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
@@ -53,6 +54,9 @@ pub struct FulfillmentContext<'tcx> {
5354
// A list of all obligations that have been registered with this
5455
// fulfillment context.
5556
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
57+
58+
relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
59+
5660
// Should this fulfillment context register type-lives-for-region
5761
// obligations on its parent infcx? In some cases, region
5862
// obligations are either already known to hold (normalization) or
@@ -97,6 +101,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
97101
pub fn new() -> FulfillmentContext<'tcx> {
98102
FulfillmentContext {
99103
predicates: ObligationForest::new(),
104+
relationships: FxHashMap::default(),
100105
register_region_obligations: true,
101106
usable_in_snapshot: false,
102107
}
@@ -105,6 +110,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
105110
pub fn new_in_snapshot() -> FulfillmentContext<'tcx> {
106111
FulfillmentContext {
107112
predicates: ObligationForest::new(),
113+
relationships: FxHashMap::default(),
108114
register_region_obligations: true,
109115
usable_in_snapshot: true,
110116
}
@@ -113,6 +119,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
113119
pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> {
114120
FulfillmentContext {
115121
predicates: ObligationForest::new(),
122+
relationships: FxHashMap::default(),
116123
register_region_obligations: false,
117124
usable_in_snapshot: false,
118125
}
@@ -210,6 +217,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
210217

211218
assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
212219

220+
super::relationships::update(self, infcx, &obligation);
221+
213222
self.predicates
214223
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
215224
}
@@ -265,6 +274,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
265274
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
266275
self.predicates.map_pending_obligations(|o| o.obligation.clone())
267276
}
277+
278+
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
279+
&mut self.relationships
280+
}
268281
}
269282

270283
struct FulfillProcessor<'a, 'b, 'tcx> {

compiler/rustc_trait_selection/src/traits/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod object_safety;
1515
mod on_unimplemented;
1616
mod project;
1717
pub mod query;
18+
pub(crate) mod relationships;
1819
mod select;
1920
mod specialize;
2021
mod structural_match;

0 commit comments

Comments
 (0)