mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 06:51:58 +00:00
move fallback_if_possible
and friends to fallback.rs
Along the way, simplify and document the logic more clearly.
This commit is contained in:
parent
faf84263f2
commit
60cc00f540
@ -23,7 +23,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
|
||||
use rustc_middle::mir::interpret::EvalToConstValueResult;
|
||||
use rustc_middle::traits::select;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
|
||||
use rustc_middle::ty::relate::RelateResult;
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef};
|
||||
@ -679,6 +679,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
t.fold_with(&mut self.freshener())
|
||||
}
|
||||
|
||||
/// Returns whether `ty` is a diverging type variable or not.
|
||||
/// (If `ty` is not a type variable at all, returns not diverging.)
|
||||
///
|
||||
/// No attempt is made to resolve `ty`.
|
||||
pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> Diverging {
|
||||
match *ty.kind() {
|
||||
ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid),
|
||||
@ -686,6 +690,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the origin of the type variable identified by `vid`, or `None`
|
||||
/// if this is not a type variable.
|
||||
///
|
||||
/// No attempt is made to resolve `ty`.
|
||||
pub fn type_var_origin(&'a self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> {
|
||||
match *ty.kind() {
|
||||
ty::Infer(ty::TyVar(vid)) => {
|
||||
Some(*self.inner.borrow_mut().type_variables().var_origin(vid))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn freshener<'b>(&'b self) -> TypeFreshener<'b, 'tcx> {
|
||||
freshen::TypeFreshener::new(self, false)
|
||||
}
|
||||
@ -695,28 +712,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
freshen::TypeFreshener::new(self, true)
|
||||
}
|
||||
|
||||
pub fn type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric {
|
||||
use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
|
||||
use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
|
||||
match *ty.kind() {
|
||||
ty::Infer(ty::IntVar(vid)) => {
|
||||
if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() {
|
||||
Neither
|
||||
} else {
|
||||
UnconstrainedInt
|
||||
}
|
||||
}
|
||||
ty::Infer(ty::FloatVar(vid)) => {
|
||||
if self.inner.borrow_mut().float_unification_table().probe_value(vid).is_some() {
|
||||
Neither
|
||||
} else {
|
||||
UnconstrainedFloat
|
||||
}
|
||||
}
|
||||
_ => Neither,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let mut vars: Vec<Ty<'_>> = inner
|
||||
|
@ -71,12 +71,6 @@ pub enum TypeError<'tcx> {
|
||||
TargetFeatureCast(DefId),
|
||||
}
|
||||
|
||||
pub enum UnconstrainedNumeric {
|
||||
UnconstrainedFloat,
|
||||
UnconstrainedInt,
|
||||
Neither,
|
||||
}
|
||||
|
||||
/// Explains the source of a type err in a short, human readable way. This is meant to be placed
|
||||
/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()`
|
||||
/// afterwards to present additional details, particularly when it comes to lifetime-related
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::check::FallbackMode;
|
||||
use crate::check::FnCtxt;
|
||||
use rustc_infer::infer::type_variable::Diverging;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
pub(super) fn type_inference_fallback(&self) {
|
||||
@ -12,8 +13,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
// The first time, we do *not* replace opaque types.
|
||||
for ty in &self.unsolved_variables() {
|
||||
debug!("unsolved_variable = {:?}", ty);
|
||||
fallback_has_occurred |= self.fallback_if_possible(ty, FallbackMode::NoOpaque);
|
||||
fallback_has_occurred |= self.fallback_if_possible(ty);
|
||||
}
|
||||
|
||||
// We now see if we can make progress. This might
|
||||
// cause us to unify inference variables for opaque types,
|
||||
// since we may have unified some other type variables
|
||||
@ -43,10 +45,113 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
// unconstrained opaque type variables, in addition to performing
|
||||
// other kinds of fallback.
|
||||
for ty in &self.unsolved_variables() {
|
||||
fallback_has_occurred |= self.fallback_if_possible(ty, FallbackMode::All);
|
||||
fallback_has_occurred |= self.fallback_opaque_type_vars(ty);
|
||||
}
|
||||
|
||||
// See if we can make any more progress.
|
||||
self.select_obligations_where_possible(fallback_has_occurred, |_| {});
|
||||
}
|
||||
|
||||
// Tries to apply a fallback to `ty` if it is an unsolved variable.
|
||||
//
|
||||
// - Unconstrained ints are replaced with `i32`.
|
||||
//
|
||||
// - Unconstrained floats are replaced with with `f64`.
|
||||
//
|
||||
// - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
|
||||
// is enabled. Otherwise, they are replaced with `()`.
|
||||
//
|
||||
// Fallback becomes very dubious if we have encountered type-checking errors.
|
||||
// In that case, fallback to Error.
|
||||
// The return value indicates whether fallback has occurred.
|
||||
fn fallback_if_possible(&self, ty: Ty<'tcx>) -> bool {
|
||||
// Careful: we do NOT shallow-resolve `ty`. We know that `ty`
|
||||
// is an unsolved variable, and we determine its fallback based
|
||||
// solely on how it was created, not what other type variables
|
||||
// it may have been unified with since then.
|
||||
//
|
||||
// The reason this matters is that other attempts at fallback may
|
||||
// (in principle) conflict with this fallback, and we wish to generate
|
||||
// a type error in that case. (However, this actually isn't true right now,
|
||||
// because we're only using the builtin fallback rules. This would be
|
||||
// true if we were using user-supplied fallbacks. But it's still useful
|
||||
// to write the code to detect bugs.)
|
||||
//
|
||||
// (Note though that if we have a general type variable `?T` that is then unified
|
||||
// with an integer type variable `?I` that ultimately never gets
|
||||
// resolved to a special integral type, `?T` is not considered unsolved,
|
||||
// but `?I` is. The same is true for float variables.)
|
||||
let fallback = match ty.kind() {
|
||||
_ if self.is_tainted_by_errors() => self.tcx.ty_error(),
|
||||
ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
|
||||
ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
|
||||
_ => match self.type_var_diverges(ty) {
|
||||
Diverging::Diverges => self.tcx.mk_diverging_default(),
|
||||
Diverging::NotDiverging => return false,
|
||||
},
|
||||
};
|
||||
debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
|
||||
|
||||
let span = self
|
||||
.infcx
|
||||
.type_var_origin(ty)
|
||||
.map(|origin| origin.span)
|
||||
.unwrap_or(rustc_span::DUMMY_SP);
|
||||
self.demand_eqtype(span, ty, fallback);
|
||||
true
|
||||
}
|
||||
|
||||
/// Second round of fallback: Unconstrained type variables
|
||||
/// created from the instantiation of an opaque
|
||||
/// type fall back to the opaque type itself. This is a
|
||||
/// somewhat incomplete attempt to manage "identity passthrough"
|
||||
/// for `impl Trait` types.
|
||||
///
|
||||
/// For example, in this code:
|
||||
///
|
||||
///```
|
||||
/// type MyType = impl Copy;
|
||||
/// fn defining_use() -> MyType { true }
|
||||
/// fn other_use() -> MyType { defining_use() }
|
||||
/// ```
|
||||
///
|
||||
/// `defining_use` will constrain the instantiated inference
|
||||
/// variable to `bool`, while `other_use` will constrain
|
||||
/// the instantiated inference variable to `MyType`.
|
||||
///
|
||||
/// When we process opaque types during writeback, we
|
||||
/// will handle cases like `other_use`, and not count
|
||||
/// them as defining usages
|
||||
///
|
||||
/// However, we also need to handle cases like this:
|
||||
///
|
||||
/// ```rust
|
||||
/// pub type Foo = impl Copy;
|
||||
/// fn produce() -> Option<Foo> {
|
||||
/// None
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// In the above snippet, the inference variable created by
|
||||
/// instantiating `Option<Foo>` will be completely unconstrained.
|
||||
/// We treat this as a non-defining use by making the inference
|
||||
/// variable fall back to the opaque type itself.
|
||||
fn fallback_opaque_type_vars(&self, ty: Ty<'tcx>) -> bool {
|
||||
let span = self
|
||||
.infcx
|
||||
.type_var_origin(ty)
|
||||
.map(|origin| origin.span)
|
||||
.unwrap_or(rustc_span::DUMMY_SP);
|
||||
let oty = self.inner.borrow().opaque_types_vars.get(ty).map(|v| *v);
|
||||
if let Some(opaque_ty) = oty {
|
||||
debug!(
|
||||
"fallback_opaque_type_vars(ty={:?}): falling back to opaque type {:?}",
|
||||
ty, opaque_ty
|
||||
);
|
||||
self.demand_eqtype(span, ty, opaque_ty);
|
||||
true
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use crate::astconv::{
|
||||
};
|
||||
use crate::check::callee::{self, DeferredCallResolution};
|
||||
use crate::check::method::{self, MethodCallee, SelfSource};
|
||||
use crate::check::{BreakableCtxt, Diverges, Expectation, FallbackMode, FnCtxt, LocalTy};
|
||||
use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
|
||||
|
||||
use rustc_ast::TraitObjectSyntax;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
@ -17,7 +17,6 @@ use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, GenericArg, Node, QPath, TyKind};
|
||||
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
|
||||
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
|
||||
use rustc_infer::infer::type_variable::Diverging;
|
||||
use rustc_infer::infer::{InferOk, InferResult};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
@ -636,87 +635,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// Tries to apply a fallback to `ty` if it is an unsolved variable.
|
||||
//
|
||||
// - Unconstrained ints are replaced with `i32`.
|
||||
//
|
||||
// - Unconstrained floats are replaced with with `f64`.
|
||||
//
|
||||
// - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
|
||||
// is enabled. Otherwise, they are replaced with `()`.
|
||||
//
|
||||
// Fallback becomes very dubious if we have encountered type-checking errors.
|
||||
// In that case, fallback to Error.
|
||||
// The return value indicates whether fallback has occurred.
|
||||
pub(in super::super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool {
|
||||
use rustc_middle::ty::error::UnconstrainedNumeric::Neither;
|
||||
use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
|
||||
|
||||
assert!(ty.is_ty_infer());
|
||||
let fallback = match self.type_is_unconstrained_numeric(ty) {
|
||||
_ if self.is_tainted_by_errors() => self.tcx().ty_error(),
|
||||
UnconstrainedInt => self.tcx.types.i32,
|
||||
UnconstrainedFloat => self.tcx.types.f64,
|
||||
Neither => match self.type_var_diverges(ty) {
|
||||
Diverging::Diverges => self.tcx.mk_diverging_default(),
|
||||
|
||||
Diverging::NotDiverging => {
|
||||
// This type variable was created from the instantiation of an opaque
|
||||
// type. The fact that we're attempting to perform fallback for it
|
||||
// means that the function neither constrained it to a concrete
|
||||
// type, nor to the opaque type itself.
|
||||
//
|
||||
// For example, in this code:
|
||||
//
|
||||
//```
|
||||
// type MyType = impl Copy;
|
||||
// fn defining_use() -> MyType { true }
|
||||
// fn other_use() -> MyType { defining_use() }
|
||||
// ```
|
||||
//
|
||||
// `defining_use` will constrain the instantiated inference
|
||||
// variable to `bool`, while `other_use` will constrain
|
||||
// the instantiated inference variable to `MyType`.
|
||||
//
|
||||
// When we process opaque types during writeback, we
|
||||
// will handle cases like `other_use`, and not count
|
||||
// them as defining usages
|
||||
//
|
||||
// However, we also need to handle cases like this:
|
||||
//
|
||||
// ```rust
|
||||
// pub type Foo = impl Copy;
|
||||
// fn produce() -> Option<Foo> {
|
||||
// None
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// In the above snippet, the inference variable created by
|
||||
// instantiating `Option<Foo>` will be completely unconstrained.
|
||||
// We treat this as a non-defining use by making the inference
|
||||
// variable fall back to the opaque type itself.
|
||||
if let FallbackMode::All = mode {
|
||||
if let Some(opaque_ty) = self.infcx.inner.borrow().opaque_types_vars.get(ty)
|
||||
{
|
||||
debug!(
|
||||
"fallback_if_possible: falling back opaque type var {:?} to {:?}",
|
||||
ty, opaque_ty
|
||||
);
|
||||
*opaque_ty
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback);
|
||||
self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback);
|
||||
true
|
||||
}
|
||||
|
||||
pub(in super::super) fn select_all_obligations_or_error(&self) {
|
||||
debug!("select_all_obligations_or_error");
|
||||
if let Err(errors) = self
|
||||
|
@ -872,16 +872,6 @@ enum TupleArgumentsFlag {
|
||||
TupleArguments,
|
||||
}
|
||||
|
||||
/// Controls how we perform fallback for unconstrained
|
||||
/// type variables.
|
||||
enum FallbackMode {
|
||||
/// Do not fallback type variables to opaque types.
|
||||
NoOpaque,
|
||||
/// Perform all possible kinds of fallback, including
|
||||
/// turning type variables to opaque types.
|
||||
All,
|
||||
}
|
||||
|
||||
/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field.
|
||||
#[derive(Copy, Clone)]
|
||||
struct MaybeInProgressTables<'a, 'tcx> {
|
||||
|
Loading…
Reference in New Issue
Block a user