Skip to content

Commit

Permalink
introduce a Coerce predicate
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis authored and Mark-Simulacrum committed Aug 19, 2021
1 parent 5a8edc0 commit 947c0de
Show file tree
Hide file tree
Showing 24 changed files with 153 additions and 2 deletions.
29 changes: 29 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Expand Up @@ -969,6 +969,35 @@ 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>,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_infer/src/infer/outlives/mod.rs
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
4 changes: 4 additions & 0 deletions compiler/rustc_infer/src/traits/util.rs
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
Expand Up @@ -1652,6 +1652,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
ObjectSafe(..) |
ClosureKind(..) |
Subtype(..) |
Coerce(..) |
ConstEvaluatable(..) |
ConstEquate(..) |
TypeWellFormedFromEnv(..) => continue,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/flags.rs
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
28 changes: 28 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Expand Up @@ -485,8 +485,22 @@ pub enum PredicateKind<'tcx> {
ClosureKind(DefId, SubstsRef<'tcx>, ClosureKind),

/// `T1 <: T2`
///
/// This obligation is created most often when we have two
/// unresolved type variables and hence don't have enough
/// information to process the subtyping obligation yet.
Subtype(SubtypePredicate<'tcx>),

/// `T1` coerced to `T2`
///
/// Like a subtyping obligation, this is created most often
/// when we have two unresolved type variables and hence
/// don't have enough information to process the coercion
/// obligation yet. At the moment, we actually process coercions
/// very much like subtyping and don't handle the full coercion
/// logic.
Coerce(CoercePredicate<'tcx>),

/// Constant initializer must evaluate successfully.
ConstEvaluatable(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),

Expand Down Expand Up @@ -655,6 +669,9 @@ pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'t
pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;

/// Encodes that `a` must be a subtype of `b`. The `a_is_expected` flag indicates
/// whether the `a` type is the type that we should label as "expected" when
/// presenting user diagnostics.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable)]
pub struct SubtypePredicate<'tcx> {
Expand All @@ -664,6 +681,15 @@ pub struct SubtypePredicate<'tcx> {
}
pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>;

/// Encodes that we have to coerce *from* the `a` type to the `b` type.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable)]
pub struct CoercePredicate<'tcx> {
pub a: Ty<'tcx>,
pub b: Ty<'tcx>,
}
pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>;

/// This kind of predicate has no *direct* correspondent in the
/// syntax, but it roughly corresponds to the syntactic forms:
///
Expand Down Expand Up @@ -806,6 +832,7 @@ impl<'tcx> Predicate<'tcx> {
}
PredicateKind::Projection(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
| PredicateKind::RegionOutlives(..)
| PredicateKind::WellFormed(..)
| PredicateKind::ObjectSafe(..)
Expand All @@ -824,6 +851,7 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::Trait(..)
| PredicateKind::Projection(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
| PredicateKind::RegionOutlives(..)
| PredicateKind::WellFormed(..)
| PredicateKind::ObjectSafe(..)
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Expand Up @@ -2236,6 +2236,10 @@ define_print_and_forward_display! {
p!(print(self.a), " <: ", print(self.b))
}

ty::CoercePredicate<'tcx> {
p!(print(self.a), " -> ", print(self.b))
}

ty::TraitPredicate<'tcx> {
p!(print(self.trait_ref.self_ty()), ": ",
print(self.trait_ref.print_only_trait_path()))
Expand Down Expand Up @@ -2268,6 +2272,7 @@ define_print_and_forward_display! {
p!(print(data))
}
ty::PredicateKind::Subtype(predicate) => p!(print(predicate)),
ty::PredicateKind::Coerce(predicate) => p!(print(predicate)),
ty::PredicateKind::RegionOutlives(predicate) => p!(print(predicate)),
ty::PredicateKind::TypeOutlives(predicate) => p!(print(predicate)),
ty::PredicateKind::Projection(predicate) => p!(print(predicate)),
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/ty/structural_impls.rs
Expand Up @@ -179,6 +179,7 @@ impl fmt::Debug for ty::PredicateKind<'tcx> {
match *self {
ty::PredicateKind::Trait(ref a) => a.fmt(f),
ty::PredicateKind::Subtype(ref pair) => pair.fmt(f),
ty::PredicateKind::Coerce(ref pair) => pair.fmt(f),
ty::PredicateKind::RegionOutlives(ref pair) => pair.fmt(f),
ty::PredicateKind::TypeOutlives(ref pair) => pair.fmt(f),
ty::PredicateKind::Projection(ref pair) => pair.fmt(f),
Expand Down Expand Up @@ -380,6 +381,13 @@ impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> {
}
}

impl<'a, 'tcx> Lift<'tcx> for ty::CoercePredicate<'a> {
type Lifted = ty::CoercePredicate<'tcx>;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<ty::CoercePredicate<'tcx>> {
tcx.lift((self.a, self.b)).map(|(a, b)| ty::CoercePredicate { a, b })
}
}

impl<'tcx, A: Copy + Lift<'tcx>, B: Copy + Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate<A, B> {
type Lifted = ty::OutlivesPredicate<A::Lifted, B::Lifted>;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
Expand Down Expand Up @@ -420,6 +428,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> {
match self {
ty::PredicateKind::Trait(data) => tcx.lift(data).map(ty::PredicateKind::Trait),
ty::PredicateKind::Subtype(data) => tcx.lift(data).map(ty::PredicateKind::Subtype),
ty::PredicateKind::Coerce(data) => tcx.lift(data).map(ty::PredicateKind::Coerce),
ty::PredicateKind::RegionOutlives(data) => {
tcx.lift(data).map(ty::PredicateKind::RegionOutlives)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir/src/transform/check_consts/check.rs
Expand Up @@ -420,8 +420,8 @@ impl Checker<'mir, 'tcx> {
ty::PredicateKind::ClosureKind(..) => {
bug!("closure kind predicate on function: {:#?}", predicate)
}
ty::PredicateKind::Subtype(_) => {
bug!("subtype predicate on function: {:#?}", predicate)
ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) => {
bug!("subtype/coerce predicate on function: {:#?}", predicate)
}
ty::PredicateKind::Trait(pred) => {
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/opaque_types.rs
Expand Up @@ -1119,6 +1119,7 @@ crate fn required_region_bounds(
ty::PredicateKind::Projection(..)
| ty::PredicateKind::Trait(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
Expand Down
Expand Up @@ -565,6 +565,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate)
}

ty::PredicateKind::Coerce(predicate) => {
// Errors for Coerce predicates show up as
// `FulfillmentErrorCode::CodeSubtypeError`,
// not selection error.
span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate)
}

ty::PredicateKind::RegionOutlives(predicate) => {
let predicate = bound_predicate.rebind(predicate);
let predicate = self.resolve_vars_if_possible(predicate);
Expand Down
26 changes: 26 additions & 0 deletions compiler/rustc_trait_selection/src/traits/fulfill.rs
Expand Up @@ -402,6 +402,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..) => {
let pred = infcx.replace_bound_vars_with_placeholders(binder);
Expand Down Expand Up @@ -517,6 +518,31 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
}
}

ty::PredicateKind::Coerce(coerce) => {
match self.selcx.infcx().coerce_predicate(
&obligation.cause,
obligation.param_env,
Binder::dummy(coerce),
) {
None => {
// None means that both are unresolved.
pending_obligation.stalled_on = vec![
TyOrConstInferVar::maybe_from_ty(coerce.a).unwrap(),
TyOrConstInferVar::maybe_from_ty(coerce.b).unwrap(),
];
ProcessResult::Unchanged
}
Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)),
Some(Err(err)) => {
let expected_found = ExpectedFound::new(false, coerce.a, coerce.b);
ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError(
expected_found,
err,
))
}
}
}

ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
match const_evaluatable::is_const_evaluatable(
self.selcx.infcx(),
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_trait_selection/src/traits/object_safety.rs
Expand Up @@ -308,6 +308,7 @@ fn predicate_references_self(
| ty::PredicateKind::RegionOutlives(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
Expand Down Expand Up @@ -336,6 +337,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
}
ty::PredicateKind::Projection(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::RegionOutlives(..)
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::ObjectSafe(..)
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Expand Up @@ -512,6 +512,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}

ty::PredicateKind::Coerce(p) => {
let p = bound_predicate.rebind(p);
// Does this code ever run?
match self.infcx.coerce_predicate(&obligation.cause, obligation.param_env, p) {
Some(Ok(InferOk { mut obligations, .. })) => {
self.add_depth(obligations.iter_mut(), obligation.recursion_depth);
self.evaluate_predicates_recursively(
previous_stack,
obligations.into_iter(),
)
}
Some(Err(_)) => Ok(EvaluatedToErr),
None => Ok(EvaluatedToAmbig),
}
}

ty::PredicateKind::WellFormed(arg) => match wf::obligations(
self.infcx,
obligation.param_env,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Expand Up @@ -128,6 +128,10 @@ pub fn predicate_obligations<'a, 'tcx>(
wf.compute(a.into());
wf.compute(b.into());
}
ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
wf.compute(a.into());
wf.compute(b.into());
}
ty::PredicateKind::ConstEvaluatable(def, substs) => {
let obligations = wf.nominal_obligations(def.did, substs);
wf.out.extend(obligations);
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_traits/src/chalk/lowering.rs
Expand Up @@ -109,6 +109,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..) => bug!("unexpected predicate {}", predicate),
};
Expand Down Expand Up @@ -193,6 +194,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi
// some of these in terms of chalk operations.
ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..) => {
chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
Expand Down Expand Up @@ -592,6 +594,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'
ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {
Expand Down Expand Up @@ -719,6 +722,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_solve::rust_ir::QuantifiedInlineBound<Ru
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_traits/src/implied_outlives_bounds.rs
Expand Up @@ -99,6 +99,7 @@ fn compute_implied_outlives_bounds<'tcx>(
Some(pred) => match pred {
ty::PredicateKind::Trait(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Projection(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::ObjectSafe(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_traits/src/normalize_erasing_regions.rs
Expand Up @@ -65,6 +65,7 @@ fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool {
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => true,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
Expand Up @@ -812,6 +812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation))
}
ty::PredicateKind::Subtype(..) => None,
ty::PredicateKind::Coerce(..) => None,
ty::PredicateKind::RegionOutlives(..) => None,
ty::PredicateKind::TypeOutlives(..) => None,
ty::PredicateKind::WellFormed(..) => None,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/check/method/probe.rs
Expand Up @@ -841,6 +841,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Projection(..)
| ty::PredicateKind::RegionOutlives(..)
| ty::PredicateKind::WellFormed(..)
Expand Down
Expand Up @@ -407,6 +407,7 @@ fn trait_predicate_kind<'tcx>(
| ty::PredicateKind::Projection(_)
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::ConstEvaluatable(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/outlives/explicit.rs
Expand Up @@ -56,6 +56,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => (),
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/clean/mod.rs
Expand Up @@ -332,6 +332,7 @@ impl<'a> Clean<Option<WherePredicate>> for ty::Predicate<'a> {
ty::PredicateKind::ConstEvaluatable(..) => None,

ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
Expand Down

0 comments on commit 947c0de

Please sign in to comment.