Skip to content
11 changes: 8 additions & 3 deletions compiler/rustc_infer/src/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
// is also useful to track which value is the "expected" value in
// terms of error reporting.

use super::equate::Equate;
use super::glb::Glb;
use super::lub::Lub;
use super::sub::Sub;
use super::type_variable::TypeVariableValue;
use super::unify_key::replace_if_possible;
use super::unify_key::{ConstVarValue, ConstVariableValue};
use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use super::{equate::Equate, type_variable::Diverging};
use super::{InferCtxt, MiscVariable, TypeTrace};

use crate::traits::{Obligation, PredicateObligations};
Expand Down Expand Up @@ -643,8 +643,13 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
.inner
.borrow_mut()
.type_variables()
.new_var(self.for_universe, false, origin);
.new_var(self.for_universe, Diverging::NotDiverging, origin);
let u = self.tcx().mk_ty_var(new_var_id);

// Record that we replaced `vid` with `new_var_id` as part of a generalization
// operation. This is needed to detect cyclic types. To see why, see the
// docs in the `type_variables` module.
self.infcx.inner.borrow_mut().type_variables().sub(vid, new_var_id);
debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);
Ok(u)
}
Expand Down Expand Up @@ -881,7 +886,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
*self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var(
self.for_universe,
false,
Diverging::NotDiverging,
origin,
);
let u = self.tcx().mk_ty_var(new_var_id);
Expand Down
124 changes: 78 additions & 46 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -46,7 +46,7 @@ use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, Veri
use self::region_constraints::{
RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot,
};
use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use self::type_variable::{Diverging, TypeVariableOrigin, TypeVariableOriginKind};

pub mod at;
pub mod canonical;
Expand Down Expand Up @@ -679,10 +679,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
t.fold_with(&mut self.freshener())
}

pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool {
/// 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),
_ => false,
_ => Diverging::NotDiverging,
}
}

/// 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,
}
}

Expand All @@ -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
Expand Down Expand Up @@ -969,29 +964,62 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
);
}

/// Processes a `Coerce` predicate from the fulfillment context.
/// This is NOT the preferred way to handle coercion, which is to
/// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`).
///
/// This method here is actually a fallback that winds up being
/// invoked when `FnCtxt::coerce` encounters unresolved type variables
/// and records a coercion predicate. Presently, this method is equivalent
/// to `subtype_predicate` -- that is, "coercing" `a` to `b` winds up
/// actually requiring `a <: b`. This is of course a valid coercion,
/// but it's not as flexible as `FnCtxt::coerce` would be.
///
/// (We may refactor this in the future, but there are a number of
/// practical obstacles. Among other things, `FnCtxt::coerce` presently
/// records adjustments that are required on the HIR in order to perform
/// the coercion, and we don't currently have a way to manage that.)
pub fn coerce_predicate(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
predicate: ty::PolyCoercePredicate<'tcx>,
) -> Option<InferResult<'tcx, ()>> {
let subtype_predicate = predicate.map_bound(|p| ty::SubtypePredicate {
a_is_expected: false, // when coercing from `a` to `b`, `b` is expected
a: p.a,
b: p.b,
});
self.subtype_predicate(cause, param_env, subtype_predicate)
}

pub fn subtype_predicate(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
predicate: ty::PolySubtypePredicate<'tcx>,
) -> Option<InferResult<'tcx, ()>> {
// Subtle: it's ok to skip the binder here and resolve because
// `shallow_resolve` just ignores anything that is not a type
// variable, and because type variable's can't (at present, at
// Check for two unresolved inference variables, in which case we can
// make no progress. This is partly a micro-optimization, but it's
// also an opportunity to "sub-unify" the variables. This isn't
// *necessary* to prevent cycles, because they would eventually be sub-unified
// anyhow during generalization, but it helps with diagnostics (we can detect
// earlier that they are sub-unified).
//
// Note that we can just skip the binders here because
// type variables can't (at present, at
// least) capture any of the things bound by this binder.
//
// NOTE(nmatsakis): really, there is no *particular* reason to do this
// `shallow_resolve` here except as a micro-optimization.
// Naturally I could not resist.
let two_unbound_type_vars = {
let a = self.shallow_resolve(predicate.skip_binder().a);
let b = self.shallow_resolve(predicate.skip_binder().b);
a.is_ty_var() && b.is_ty_var()
};

if two_unbound_type_vars {
// Two unbound type variables? Can't make progress.
return None;
// Note that this sub here is not just for diagnostics - it has semantic
// effects as well.
let r_a = self.shallow_resolve(predicate.skip_binder().a);
let r_b = self.shallow_resolve(predicate.skip_binder().b);
match (r_a.kind(), r_b.kind()) {
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
self.inner.borrow_mut().type_variables().sub(a_vid, b_vid);
return None;
}
_ => {}
}

Some(self.commit_if_ok(|_snapshot| {
Expand Down Expand Up @@ -1020,25 +1048,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
})
}

pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
pub fn next_ty_var_id(&self, diverging: Diverging, origin: TypeVariableOrigin) -> TyVid {
self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin)
}

pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
self.tcx.mk_ty_var(self.next_ty_var_id(false, origin))
self.tcx.mk_ty_var(self.next_ty_var_id(Diverging::NotDiverging, origin))
}

pub fn next_ty_var_in_universe(
&self,
origin: TypeVariableOrigin,
universe: ty::UniverseIndex,
) -> Ty<'tcx> {
let vid = self.inner.borrow_mut().type_variables().new_var(universe, false, origin);
let vid = self.inner.borrow_mut().type_variables().new_var(
universe,
Diverging::NotDiverging,
origin,
);
self.tcx.mk_ty_var(vid)
}

pub fn next_diverging_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
self.tcx.mk_ty_var(self.next_ty_var_id(true, origin))
self.tcx.mk_ty_var(self.next_ty_var_id(Diverging::Diverges, origin))
}

pub fn next_const_var(
Expand Down Expand Up @@ -1152,7 +1184,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// as the substitutions for the default, `(T, U)`.
let ty_var_id = self.inner.borrow_mut().type_variables().new_var(
self.universe(),
false,
Diverging::NotDiverging,
TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeParameterDefinition(
param.name,
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_infer/src/infer/nll_relate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
//! constituents)

use crate::infer::combine::ConstEquateRelation;
use crate::infer::type_variable::Diverging;
use crate::infer::InferCtxt;
use crate::infer::{ConstVarValue, ConstVariableValue};
use rustc_data_structures::fx::FxHashMap;
Expand Down Expand Up @@ -920,7 +921,8 @@ where
// Replacing with a new variable in the universe `self.universe`,
// it will be unified later with the original type variable in
// the universe `_universe`.
let new_var_id = variables.new_var(self.universe, false, origin);
let new_var_id =
variables.new_var(self.universe, Diverging::NotDiverging, origin);

let u = self.tcx().mk_ty_var(new_var_id);
debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_infer/src/infer/outlives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub fn explicit_outlives_bounds<'tcx>(
.filter_map(move |kind| match kind {
ty::PredicateKind::Projection(..)
| ty::PredicateKind::Trait(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::ObjectSafe(..)
Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_infer/src/infer/sub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,15 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
match (a.kind(), b.kind()) {
(&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => {
(&ty::Infer(TyVar(_)), &ty::Infer(TyVar(_))) => {
// Shouldn't have any LBR here, so we can safely put
// this under a binder below without fear of accidental
// capture.
assert!(!a.has_escaping_bound_vars());
assert!(!b.has_escaping_bound_vars());

// can't make progress on `A <: B` if both A and B are
// type variables, so record an obligation. We also
// have to record in the `type_variables` tracker that
// the two variables are equal modulo subtyping, which
// is important to the occurs check later on.
infcx.inner.borrow_mut().type_variables().sub(a_vid, b_vid);
// type variables, so record an obligation.
self.fields.obligations.push(Obligation::new(
self.fields.trace.cause.clone(),
self.fields.param_env,
Expand Down
36 changes: 29 additions & 7 deletions compiler/rustc_infer/src/infer/type_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,30 @@ pub struct TypeVariableStorage<'tcx> {
/// ?1 <: ?3
/// Box<?3> <: ?1
///
/// This works because `?1` and `?3` are unified in the
/// `sub_relations` relation (not in `eq_relations`). Then when we
/// process the `Box<?3> <: ?1` constraint, we do an occurs check
/// on `Box<?3>` and find a potential cycle.
/// Without this second table, what would happen in a case like
/// this is that we would instantiate `?1` with a generalized
/// type like `Box<?6>`. We would then relate `Box<?3> <: Box<?6>`
/// and infer that `?3 <: ?6`. Next, since `?1` was instantiated,
/// we would process `?1 <: ?3`, generalize `?1 = Box<?6>` to `Box<?9>`,
/// and instantiate `?3` with `Box<?9>`. Finally, we would relate
/// `?6 <: ?9`. But now that we instantiated `?3`, we can process
/// `?3 <: ?6`, which gives us `Box<?9> <: ?6`... and the cycle
/// continues. (This is `occurs-check-2.rs`.)
///
/// What prevents this cycle is that when we generalize
/// `Box<?3>` to `Box<?6>`, we also sub-unify `?3` and `?6`
/// (in the generalizer). When we then process `Box<?6> <: ?3`,
/// the occurs check then fails because `?6` and `?3` are sub-unified,
/// and hence generalization fails.
///
/// This is reasonable because, in Rust, subtypes have the same
/// "skeleton" and hence there is no possible type such that
/// (e.g.) `Box<?3> <: ?3` for any `?3`.
///
/// In practice, we sometimes sub-unify variables in other spots, such
/// as when processing subtype predicates. This is not necessary but is
/// done to aid diagnostics, as it allows us to be more effective when
/// we guide the user towards where they should insert type hints.
sub_relations: ut::UnificationTableStorage<ty::TyVid>,
}

Expand Down Expand Up @@ -119,7 +135,13 @@ pub enum TypeVariableOriginKind {

pub(crate) struct TypeVariableData {
origin: TypeVariableOrigin,
diverging: bool,
diverging: Diverging,
}

#[derive(Copy, Clone, Debug)]
pub enum Diverging {
NotDiverging,
Diverges,
}

#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -173,7 +195,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
///
/// Note that this function does not return care whether
/// `vid` has been unified with something else or not.
pub fn var_diverges(&self, vid: ty::TyVid) -> bool {
pub fn var_diverges(&self, vid: ty::TyVid) -> Diverging {
self.storage.values.get(vid.index as usize).diverging
}

Expand Down Expand Up @@ -238,7 +260,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
pub fn new_var(
&mut self,
universe: ty::UniverseIndex,
diverging: bool,
diverging: Diverging,
origin: TypeVariableOrigin,
) -> ty::TyVid {
let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ impl Elaborator<'tcx> {
// Currently, we do not "elaborate" predicates like `X <: Y`,
// though conceivably we might.
}
ty::PredicateKind::Coerce(..) => {
// Currently, we do not "elaborate" predicates like `X -> Y`,
// though conceivably we might.
}
ty::PredicateKind::Projection(..) => {
// Nothing to elaborate in a projection predicate.
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1652,6 +1652,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
ObjectSafe(..) |
ClosureKind(..) |
Subtype(..) |
Coerce(..) |
ConstEvaluatable(..) |
ConstEquate(..) |
TypeWellFormedFromEnv(..) => continue,
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_middle/src/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ impl FlagComputation {
self.add_ty(a);
self.add_ty(b);
}
ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
self.add_ty(a);
self.add_ty(b);
}
ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, ty }) => {
self.add_projection_ty(projection_ty);
self.add_ty(ty);
Expand Down
Loading