Skip to content

Commit 01decf3

Browse files
nikomatsakisMark-Simulacrum
authored andcommitted
move fallback_if_possible and friends to fallback.rs
Along the way, simplify and document the logic more clearly.
1 parent 3861583 commit 01decf3

File tree

5 files changed

+127
-125
lines changed

5 files changed

+127
-125
lines changed

compiler/rustc_infer/src/infer/mod.rs

+18-23
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
2323
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
2424
use rustc_middle::mir::interpret::EvalToConstValueResult;
2525
use rustc_middle::traits::select;
26-
use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
26+
use rustc_middle::ty::error::{ExpectedFound, TypeError};
2727
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
2828
use rustc_middle::ty::relate::RelateResult;
2929
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef};
@@ -679,13 +679,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
679679
t.fold_with(&mut self.freshener())
680680
}
681681

682+
/// Returns whether `ty` is a diverging type variable or not.
683+
/// (If `ty` is not a type variable at all, returns not diverging.)
684+
///
685+
/// No attempt is made to resolve `ty`.
682686
pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> Diverging {
683687
match *ty.kind() {
684688
ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid),
685689
_ => Diverging::NotDiverging,
686690
}
687691
}
688692

693+
/// Returns the origin of the type variable identified by `vid`, or `None`
694+
/// if this is not a type variable.
695+
///
696+
/// No attempt is made to resolve `ty`.
697+
pub fn type_var_origin(&'a self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> {
698+
match *ty.kind() {
699+
ty::Infer(ty::TyVar(vid)) => {
700+
Some(*self.inner.borrow_mut().type_variables().var_origin(vid))
701+
}
702+
_ => None,
703+
}
704+
}
705+
689706
pub fn freshener<'b>(&'b self) -> TypeFreshener<'b, 'tcx> {
690707
freshen::TypeFreshener::new(self, false)
691708
}
@@ -695,28 +712,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
695712
freshen::TypeFreshener::new(self, true)
696713
}
697714

698-
pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric {
699-
use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
700-
use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
701-
match *ty.kind() {
702-
ty::Infer(ty::IntVar(vid)) => {
703-
if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() {
704-
Neither
705-
} else {
706-
UnconstrainedInt
707-
}
708-
}
709-
ty::Infer(ty::FloatVar(vid)) => {
710-
if self.inner.borrow_mut().float_unification_table().probe_value(vid).is_some() {
711-
Neither
712-
} else {
713-
UnconstrainedFloat
714-
}
715-
}
716-
_ => Neither,
717-
}
718-
}
719-
720715
pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> {
721716
let mut inner = self.inner.borrow_mut();
722717
let mut vars: Vec<Ty<'_>> = inner

compiler/rustc_middle/src/ty/error.rs

-6
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,6 @@ pub enum TypeError<'tcx> {
7171
TargetFeatureCast(DefId),
7272
}
7373

74-
pub enum UnconstrainedNumeric {
75-
UnconstrainedFloat,
76-
UnconstrainedInt,
77-
Neither,
78-
}
79-
8074
/// Explains the source of a type err in a short, human readable way. This is meant to be placed
8175
/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()`
8276
/// afterwards to present additional details, particularly when it comes to lifetime-related

compiler/rustc_typeck/src/check/fallback.rs

+108-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use crate::check::FallbackMode;
21
use crate::check::FnCtxt;
2+
use rustc_infer::infer::type_variable::Diverging;
3+
use rustc_middle::ty::{self, Ty};
34

45
impl<'tcx> FnCtxt<'_, 'tcx> {
56
pub(super) fn type_inference_fallback(&self) {
@@ -12,8 +13,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
1213
// The first time, we do *not* replace opaque types.
1314
for ty in &self.unsolved_variables() {
1415
debug!("unsolved_variable = {:?}", ty);
15-
fallback_has_occurred |= self.fallback_if_possible(ty, FallbackMode::NoOpaque);
16+
fallback_has_occurred |= self.fallback_if_possible(ty);
1617
}
18+
1719
// We now see if we can make progress. This might
1820
// cause us to unify inference variables for opaque types,
1921
// since we may have unified some other type variables
@@ -43,10 +45,113 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
4345
// unconstrained opaque type variables, in addition to performing
4446
// other kinds of fallback.
4547
for ty in &self.unsolved_variables() {
46-
fallback_has_occurred |= self.fallback_if_possible(ty, FallbackMode::All);
48+
fallback_has_occurred |= self.fallback_opaque_type_vars(ty);
4749
}
4850

4951
// See if we can make any more progress.
5052
self.select_obligations_where_possible(fallback_has_occurred, |_| {});
5153
}
54+
55+
// Tries to apply a fallback to `ty` if it is an unsolved variable.
56+
//
57+
// - Unconstrained ints are replaced with `i32`.
58+
//
59+
// - Unconstrained floats are replaced with with `f64`.
60+
//
61+
// - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
62+
// is enabled. Otherwise, they are replaced with `()`.
63+
//
64+
// Fallback becomes very dubious if we have encountered type-checking errors.
65+
// In that case, fallback to Error.
66+
// The return value indicates whether fallback has occurred.
67+
fn fallback_if_possible(&self, ty: Ty<'tcx>) -> bool {
68+
// Careful: we do NOT shallow-resolve `ty`. We know that `ty`
69+
// is an unsolved variable, and we determine its fallback based
70+
// solely on how it was created, not what other type variables
71+
// it may have been unified with since then.
72+
//
73+
// The reason this matters is that other attempts at fallback may
74+
// (in principle) conflict with this fallback, and we wish to generate
75+
// a type error in that case. (However, this actually isn't true right now,
76+
// because we're only using the builtin fallback rules. This would be
77+
// true if we were using user-supplied fallbacks. But it's still useful
78+
// to write the code to detect bugs.)
79+
//
80+
// (Note though that if we have a general type variable `?T` that is then unified
81+
// with an integer type variable `?I` that ultimately never gets
82+
// resolved to a special integral type, `?T` is not considered unsolved,
83+
// but `?I` is. The same is true for float variables.)
84+
let fallback = match ty.kind() {
85+
_ if self.is_tainted_by_errors() => self.tcx.ty_error(),
86+
ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
87+
ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
88+
_ => match self.type_var_diverges(ty) {
89+
Diverging::Diverges => self.tcx.mk_diverging_default(),
90+
Diverging::NotDiverging => return false,
91+
},
92+
};
93+
debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
94+
95+
let span = self
96+
.infcx
97+
.type_var_origin(ty)
98+
.map(|origin| origin.span)
99+
.unwrap_or(rustc_span::DUMMY_SP);
100+
self.demand_eqtype(span, ty, fallback);
101+
true
102+
}
103+
104+
/// Second round of fallback: Unconstrained type variables
105+
/// created from the instantiation of an opaque
106+
/// type fall back to the opaque type itself. This is a
107+
/// somewhat incomplete attempt to manage "identity passthrough"
108+
/// for `impl Trait` types.
109+
///
110+
/// For example, in this code:
111+
///
112+
///```
113+
/// type MyType = impl Copy;
114+
/// fn defining_use() -> MyType { true }
115+
/// fn other_use() -> MyType { defining_use() }
116+
/// ```
117+
///
118+
/// `defining_use` will constrain the instantiated inference
119+
/// variable to `bool`, while `other_use` will constrain
120+
/// the instantiated inference variable to `MyType`.
121+
///
122+
/// When we process opaque types during writeback, we
123+
/// will handle cases like `other_use`, and not count
124+
/// them as defining usages
125+
///
126+
/// However, we also need to handle cases like this:
127+
///
128+
/// ```rust
129+
/// pub type Foo = impl Copy;
130+
/// fn produce() -> Option<Foo> {
131+
/// None
132+
/// }
133+
/// ```
134+
///
135+
/// In the above snippet, the inference variable created by
136+
/// instantiating `Option<Foo>` will be completely unconstrained.
137+
/// We treat this as a non-defining use by making the inference
138+
/// variable fall back to the opaque type itself.
139+
fn fallback_opaque_type_vars(&self, ty: Ty<'tcx>) -> bool {
140+
let span = self
141+
.infcx
142+
.type_var_origin(ty)
143+
.map(|origin| origin.span)
144+
.unwrap_or(rustc_span::DUMMY_SP);
145+
let oty = self.inner.borrow().opaque_types_vars.get(ty).map(|v| *v);
146+
if let Some(opaque_ty) = oty {
147+
debug!(
148+
"fallback_opaque_type_vars(ty={:?}): falling back to opaque type {:?}",
149+
ty, opaque_ty
150+
);
151+
self.demand_eqtype(span, ty, opaque_ty);
152+
true
153+
} else {
154+
return false;
155+
}
156+
}
52157
}

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

+1-83
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::astconv::{
44
};
55
use crate::check::callee::{self, DeferredCallResolution};
66
use crate::check::method::{self, MethodCallee, SelfSource};
7-
use crate::check::{BreakableCtxt, Diverges, Expectation, FallbackMode, FnCtxt, LocalTy};
7+
use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
88

99
use rustc_ast::TraitObjectSyntax;
1010
use rustc_data_structures::captures::Captures;
@@ -17,7 +17,6 @@ use rustc_hir::lang_items::LangItem;
1717
use rustc_hir::{ExprKind, GenericArg, Node, QPath, TyKind};
1818
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
1919
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
20-
use rustc_infer::infer::type_variable::Diverging;
2120
use rustc_infer::infer::{InferOk, InferResult};
2221
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
2322
use rustc_middle::ty::fold::TypeFoldable;
@@ -636,87 +635,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
636635
}
637636
}
638637

639-
// Tries to apply a fallback to `ty` if it is an unsolved variable.
640-
//
641-
// - Unconstrained ints are replaced with `i32`.
642-
//
643-
// - Unconstrained floats are replaced with with `f64`.
644-
//
645-
// - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
646-
// is enabled. Otherwise, they are replaced with `()`.
647-
//
648-
// Fallback becomes very dubious if we have encountered type-checking errors.
649-
// In that case, fallback to Error.
650-
// The return value indicates whether fallback has occurred.
651-
pub(in super::super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
652-
use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
653-
use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
654-
655-
assert!(ty.is_ty_infer());
656-
let fallback = match self.type_is_unconstrained_numeric(ty) {
657-
_ if self.is_tainted_by_errors() => self.tcx().ty_error(),
658-
UnconstrainedInt => self.tcx.types.i32,
659-
UnconstrainedFloat => self.tcx.types.f64,
660-
Neither => match self.type_var_diverges(ty) {
661-
Diverging::Diverges => self.tcx.mk_diverging_default(),
662-
663-
Diverging::NotDiverging => {
664-
// This type variable was created from the instantiation of an opaque
665-
// type. The fact that we're attempting to perform fallback for it
666-
// means that the function neither constrained it to a concrete
667-
// type, nor to the opaque type itself.
668-
//
669-
// For example, in this code:
670-
//
671-
//```
672-
// type MyType = impl Copy;
673-
// fn defining_use() -> MyType { true }
674-
// fn other_use() -> MyType { defining_use() }
675-
// ```
676-
//
677-
// `defining_use` will constrain the instantiated inference
678-
// variable to `bool`, while `other_use` will constrain
679-
// the instantiated inference variable to `MyType`.
680-
//
681-
// When we process opaque types during writeback, we
682-
// will handle cases like `other_use`, and not count
683-
// them as defining usages
684-
//
685-
// However, we also need to handle cases like this:
686-
//
687-
// ```rust
688-
// pub type Foo = impl Copy;
689-
// fn produce() -> Option<Foo> {
690-
// None
691-
// }
692-
// ```
693-
//
694-
// In the above snippet, the inference variable created by
695-
// instantiating `Option<Foo>` will be completely unconstrained.
696-
// We treat this as a non-defining use by making the inference
697-
// variable fall back to the opaque type itself.
698-
if let FallbackMode::All = mode {
699-
if let Some(opaque_ty) = self.infcx.inner.borrow().opaque_types_vars.get(ty)
700-
{
701-
debug!(
702-
"fallback_if_possible: falling back opaque type var {:?} to {:?}",
703-
ty, opaque_ty
704-
);
705-
*opaque_ty
706-
} else {
707-
return false;
708-
}
709-
} else {
710-
return false;
711-
}
712-
}
713-
},
714-
};
715-
debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
716-
self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
717-
true
718-
}
719-
720638
pub(in super::super) fn select_all_obligations_or_error(&self) {
721639
debug!("select_all_obligations_or_error");
722640
if let Err(errors) = self

compiler/rustc_typeck/src/check/mod.rs

-10
Original file line numberDiff line numberDiff line change
@@ -872,16 +872,6 @@ enum TupleArgumentsFlag {
872872
TupleArguments,
873873
}
874874

875-
/// Controls how we perform fallback for unconstrained
876-
/// type variables.
877-
enum FallbackMode {
878-
/// Do not fallback type variables to opaque types.
879-
NoOpaque,
880-
/// Perform all possible kinds of fallback, including
881-
/// turning type variables to opaque types.
882-
All,
883-
}
884-
885875
/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field.
886876
#[derive(Copy, Clone)]
887877
struct MaybeInProgressTables<'a, 'tcx> {

0 commit comments

Comments
 (0)