Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

conditional fallback for the ! type #79366

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 52 additions & 2 deletions compiler/rustc_data_structures/src/graph/iterate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,58 @@ impl<G> DepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
pub fn new(graph: &'graph G, start_node: G::Node) -> Self {
Self { graph, stack: vec![start_node], visited: BitSet::new_empty(graph.num_nodes()) }
pub fn new(graph: &'graph G) -> Self {
Self { graph, stack: vec![], visited: BitSet::new_empty(graph.num_nodes()) }
}

/// Version of `push_start_node` that is convenient for chained
/// use.
pub fn with_start_node(mut self, start_node: G::Node) -> Self {
self.push_start_node(start_node);
self
}

/// Pushes another start node onto the stack. If the node
/// has not already been visited, then you will be able to
/// walk its successors (and so forth) after the current
/// contents of the stack are drained. If multiple start nodes
/// are added into the walk, then their mutual successors
/// will all be walked. You can use this method once the
/// iterator has been completely drained to add additional
/// start nodes.
pub fn push_start_node(&mut self, start_node: G::Node) {
if self.visited.insert(start_node) {
self.stack.push(start_node);
}
}

/// Searches all nodes reachable from the current start nodes.
/// This is equivalent to just invoke `next` repeatedly until
/// you get a `None` result.
pub fn complete_search(&mut self) {
while let Some(_) = self.next() {}
}

/// Returns true if node has been visited thus far.
/// A node is considered "visited" once it is pushed
/// onto the internal stack; it may not yet have been yielded
/// from the iterator. This method is best used after
/// the iterator is completely drained.
pub fn visited(&self, node: G::Node) -> bool {
self.visited.contains(node)
}
}

impl<G> std::fmt::Debug for DepthFirstSearch<'_, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut f = fmt.debug_set();
for n in self.visited.iter() {
f.entry(&n);
}
f.finish()
}
}

Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_data_structures/src/graph/iterate/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,19 @@ fn is_cyclic() {
assert!(!is_cyclic(&diamond_acyclic));
assert!(is_cyclic(&diamond_cyclic));
}

#[test]
fn dfs() {
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3), (3, 0)]);

let result: Vec<usize> = DepthFirstSearch::new(&graph).with_start_node(0).collect();
assert_eq!(result, vec![0, 2, 3, 1]);
}

#[test]
fn dfs_debug() {
let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3), (3, 0)]);
let mut dfs = DepthFirstSearch::new(&graph).with_start_node(0);
while let Some(_) = dfs.next() {}
assert_eq!(format!("{{0, 1, 2, 3}}"), format!("{:?}", dfs));
}
2 changes: 1 addition & 1 deletion compiler/rustc_data_structures/src/graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ where
where
Self: WithNumNodes,
{
iterate::DepthFirstSearch::new(self, from)
iterate::DepthFirstSearch::new(self).with_start_node(from)
}
}

Expand Down
18 changes: 12 additions & 6 deletions compiler/rustc_infer/src/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,8 +639,13 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
.inner
.borrow_mut()
.type_variables()
.new_var(self.for_universe, false, origin);
.new_var(self.for_universe, 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 @@ -856,11 +861,12 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {

let origin =
*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,
origin,
);
let new_var_id = self
.infcx
.inner
.borrow_mut()
.type_variables()
.new_var(self.for_universe, origin);
let u = self.tcx().mk_ty_var(new_var_id);
debug!(
"ConstInferUnifier: replacing original vid={:?} with new={:?}",
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/fudge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> {
if self.type_vars.0.contains(&vid) {
// This variable was created during the fudging.
// Recreate it with a fresh variable here.
let idx = (vid.index - self.type_vars.0.start.index) as usize;
let idx = vid.as_usize() - self.type_vars.0.start.as_usize();
let origin = self.type_vars.1[idx];
self.infcx.next_ty_var(origin)
} else {
Expand Down
119 changes: 67 additions & 52 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
use rustc_middle::mir;
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 Down Expand Up @@ -641,39 +641,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
t.fold_with(&mut self.freshener())
}

pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool {
/// 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)) => self.inner.borrow_mut().type_variables().var_diverges(vid),
_ => false,
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)
}

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 @@ -926,29 +909,61 @@ 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
// least) capture any of the things bound by this binder.
// 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(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 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.
{
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 @@ -977,27 +992,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
})
}

pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin)
/// Number of type variables created so far.
pub fn num_ty_vars(&self) -> usize {
self.inner.borrow_mut().type_variables().num_vars()
}

pub fn next_ty_var_id(&self, origin: TypeVariableOrigin) -> TyVid {
self.inner.borrow_mut().type_variables().new_var(self.universe(), 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(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, 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))
}

pub fn next_const_var(
&self,
ty: Ty<'tcx>,
Expand Down Expand Up @@ -1109,7 +1125,6 @@ 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,
TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeParameterDefinition(
param.name,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/nll_relate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@ 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, 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 @@ -21,6 +21,7 @@ pub fn explicit_outlives_bounds<'tcx>(
ty::PredicateAtom::Projection(..)
| ty::PredicateAtom::Trait(..)
| ty::PredicateAtom::Subtype(..)
| ty::PredicateAtom::Coerce(..)
| ty::PredicateAtom::WellFormed(..)
| ty::PredicateAtom::ObjectSafe(..)
| ty::PredicateAtom::ClosureKind(..)
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_infer/src/infer/sub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ 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.
Expand All @@ -96,7 +96,6 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
// 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);
self.fields.obligations.push(Obligation::new(
self.fields.trace.cause.clone(),
self.fields.param_env,
Expand Down
Loading