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

introduce type-op for user-type ascription in NLL #55323

Merged
merged 2 commits into from
Oct 25, 2018
Merged
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
6 changes: 4 additions & 2 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ use std::hash::Hash;
use syntax_pos::symbol::InternedString;
use traits;
use traits::query::{
CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal,
CanonicalPredicateGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal,
CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal,
CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal, CanonicalPredicateGoal,
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal,
};
use ty::{TyCtxt, FnSig, Instance, InstanceDef,
ParamEnv, ParamEnvAnd, Predicate, PolyFnSig, PolyTraitRef, Ty};
Expand Down Expand Up @@ -654,6 +655,7 @@ define_dep_nodes!( <'tcx>
[] ImpliedOutlivesBounds(CanonicalTyGoal<'tcx>),
[] DropckOutlives(CanonicalTyGoal<'tcx>),
[] EvaluateObligation(CanonicalPredicateGoal<'tcx>),
[] TypeOpAscribeUserType(CanonicalTypeOpAscribeUserTypeGoal<'tcx>),
[] TypeOpEq(CanonicalTypeOpEqGoal<'tcx>),
[] TypeOpSubtype(CanonicalTypeOpSubtypeGoal<'tcx>),
[] TypeOpProvePredicate(CanonicalTypeOpProvePredicateGoal<'tcx>),
Expand Down
22 changes: 22 additions & 0 deletions src/librustc/infer/at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,28 @@ impl<'a, 'gcx, 'tcx> At<'a, 'gcx, 'tcx> {
self.trace(expected, actual).eq(&expected, &actual)
}

pub fn relate<T>(
self,
expected: T,
variance: ty::Variance,
actual: T,
) -> InferResult<'tcx, ()>
where T: ToTrace<'tcx>
{
match variance {
ty::Variance::Covariant => self.sub(expected, actual),
ty::Variance::Invariant => self.eq(expected, actual),
ty::Variance::Contravariant => self.sup(expected, actual),

// We could make this make sense but it's not readily
// exposed and I don't feel like dealing with it. Note
// that bivariance in general does a bit more than just
// *nothing*, it checks that the types are the same
// "modulo variance" basically.
ty::Variance::Bivariant => panic!("Bivariant given to `relate()`"),
}
}

/// Compute the least-upper-bound, or mutual supertype, of two
/// values. The order of the arguments doesn't matter, but since
/// this can result in an error (e.g., if asked to compute LUB of
Expand Down
8 changes: 8 additions & 0 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2438,6 +2438,14 @@ EnumTypeFoldableImpl! {
}
}

EnumLiftImpl! {
impl<'a, 'tcx> Lift<'tcx> for UserTypeAnnotation<'a> {
type Lifted = UserTypeAnnotation<'tcx>;
(UserTypeAnnotation::Ty)(ty),
(UserTypeAnnotation::TypeOf)(def, substs),
}
}

newtype_index! {
pub struct Promoted {
DEBUG_FORMAT = "promoted[{}]"
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/traits/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>
pub type CanonicalPredicateGoal<'tcx> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>;

pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ascribe_user_type::AscribeUserType<'tcx>>>;

pub type CanonicalTypeOpEqGoal<'tcx> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::eq::Eq<'tcx>>>;

Expand Down
77 changes: 77 additions & 0 deletions src/librustc/traits/query/type_op/ascribe_user_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse};
use traits::query::Fallible;
use hir::def_id::DefId;
use ty::{self, ParamEnvAnd, Ty, TyCtxt};
use ty::subst::UserSubsts;

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct AscribeUserType<'tcx> {
pub mir_ty: Ty<'tcx>,
pub variance: ty::Variance,
pub def_id: DefId,
pub user_substs: UserSubsts<'tcx>,
}

impl<'tcx> AscribeUserType<'tcx> {
pub fn new(
mir_ty: Ty<'tcx>,
variance: ty::Variance,
def_id: DefId,
user_substs: UserSubsts<'tcx>,
) -> Self {
AscribeUserType { mir_ty, variance, def_id, user_substs }
}
}

impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for AscribeUserType<'tcx> {
type QueryResponse = ();

fn try_fast_path(
_tcx: TyCtxt<'_, 'gcx, 'tcx>,
_key: &ParamEnvAnd<'tcx, Self>,
) -> Option<Self::QueryResponse> {
None
}

fn perform_query(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
canonicalized: Canonicalized<'gcx, ParamEnvAnd<'tcx, Self>>,
) -> Fallible<CanonicalizedQueryResponse<'gcx, ()>> {
tcx.type_op_ascribe_user_type(canonicalized)
}

fn shrink_to_tcx_lifetime(
v: &'a CanonicalizedQueryResponse<'gcx, ()>,
) -> &'a Canonical<'tcx, QueryResponse<'tcx, ()>> {
v
}
}

BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for AscribeUserType<'tcx> {
mir_ty, variance, def_id, user_substs
}
}

BraceStructLiftImpl! {
impl<'a, 'tcx> Lift<'tcx> for AscribeUserType<'a> {
type Lifted = AscribeUserType<'tcx>;
mir_ty, variance, def_id, user_substs
}
}

impl_stable_hash_for! {
struct AscribeUserType<'tcx> {
mir_ty, variance, def_id, user_substs
}
}
1 change: 1 addition & 0 deletions src/librustc/traits/query/type_op/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use traits::ObligationCause;
use ty::fold::TypeFoldable;
use ty::{Lift, ParamEnvAnd, TyCtxt};

pub mod ascribe_user_type;
pub mod custom;
pub mod eq;
pub mod implied_outlives_bounds;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ impl Visibility {
}
}

#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable, Copy)]
#[derive(Copy, Clone, PartialEq, Eq, RustcDecodable, RustcEncodable, Hash)]
pub enum Variance {
Covariant, // T<A> <: T<B> iff A <: B -- e.g., function return type
Invariant, // T<A> <: T<B> iff B == A -- e.g., type of mutable cell
Expand Down
14 changes: 12 additions & 2 deletions src/librustc/ty/query/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ use hir::def_id::{CrateNum, DefId, DefIndex};
use mir::interpret::GlobalId;
use traits;
use traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, CanonicalTypeOpEqGoal,
CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal,
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal,
};
use ty::{self, ParamEnvAnd, Ty, TyCtxt};
use ty::subst::Substs;
Expand Down Expand Up @@ -115,6 +116,15 @@ impl<'tcx> QueryDescription<'tcx> for queries::evaluate_obligation<'tcx> {
}
}

impl<'tcx> QueryDescription<'tcx> for queries::type_op_ascribe_user_type<'tcx> {
fn describe(
_tcx: TyCtxt<'_, '_, '_>,
goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx>,
) -> Cow<'static, str> {
format!("evaluating `type_op_ascribe_user_type` `{:?}`", goal).into()
}
}

impl<'tcx> QueryDescription<'tcx> for queries::type_op_eq<'tcx> {
fn describe(_tcx: TyCtxt<'_, '_, '_>, goal: CanonicalTypeOpEqGoal<'tcx>) -> Cow<'static, str> {
format!("evaluating `type_op_eq` `{:?}`", goal).into()
Expand Down
17 changes: 14 additions & 3 deletions src/librustc/ty/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ use mir::interpret::GlobalId;
use session::{CompileResult, CrateDisambiguator};
use session::config::OutputFilenames;
use traits::{self, Vtable};
use traits::query::{CanonicalPredicateGoal, CanonicalProjectionGoal,
CanonicalTyGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpSubtypeGoal,
CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpNormalizeGoal, NoSolution};
use traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal,
CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal,
CanonicalTypeOpSubtypeGoal, CanonicalTypeOpProvePredicateGoal,
CanonicalTypeOpNormalizeGoal, NoSolution,
};
use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult};
use traits::query::normalize::NormalizationResult;
use traits::query::outlives_bounds::OutlivesBound;
Expand Down Expand Up @@ -589,6 +592,14 @@ define_queries! { <'tcx>
CanonicalPredicateGoal<'tcx>
) -> Result<traits::EvaluationResult, traits::OverflowError>,

/// Do not call this query directly: part of the `Eq` type-op
[] fn type_op_ascribe_user_type: TypeOpAscribeUserType(
CanonicalTypeOpAscribeUserTypeGoal<'tcx>
) -> Result<
Lrc<Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>>,
NoSolution,
>,

/// Do not call this query directly: part of the `Eq` type-op
[] fn type_op_eq: TypeOpEq(
CanonicalTypeOpEqGoal<'tcx>
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/query/plumbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
DepKind::ImpliedOutlivesBounds |
DepKind::DropckOutlives |
DepKind::EvaluateObligation |
DepKind::TypeOpAscribeUserType |
DepKind::TypeOpEq |
DepKind::TypeOpSubtype |
DepKind::TypeOpProvePredicate |
Expand Down
113 changes: 15 additions & 98 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use rustc::traits::query::type_op::custom::CustomTypeOp;
use rustc::traits::query::{Fallible, NoSolution};
use rustc::traits::{ObligationCause, PredicateObligations};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::{Subst, Substs, UnpackedKind, UserSelfTy, UserSubsts};
use rustc::ty::subst::{Subst, Substs, UnpackedKind};
use rustc::ty::{self, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind};
use std::rc::Rc;
use std::{fmt, iter};
Expand Down Expand Up @@ -975,126 +975,43 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
locations: Locations,
category: ConstraintCategory,
) -> Fallible<()> {
let tcx = self.tcx();

debug!(
"relate_type_and_user_type(a={:?}, v={:?}, b={:?}, locations={:?})",
a, v, user_ty, locations
"relate_type_and_user_type(a={:?}, v={:?}, user_ty={:?}, locations={:?})",
a, v, user_ty, locations,
);

// The `TypeRelating` code assumes that "unresolved inference
// variables" appear in the "a" side, so flip `Contravariant`
// ambient variance to get the right relationship.
let v1 = ty::Contravariant.xform(v);

match user_ty {
UserTypeAnnotation::Ty(canonical_ty) => {
let (ty, _) = self.infcx
.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonical_ty);

self.relate_types(ty, v1, a, locations, category)?;
// The `TypeRelating` code assumes that "unresolved inference
// variables" appear in the "a" side, so flip `Contravariant`
// ambient variance to get the right relationship.
let v1 = ty::Contravariant.xform(v);

self.prove_predicate(ty::Predicate::WellFormed(ty), locations, category);
self.relate_types(ty, v1, a, locations, category)?;
}
UserTypeAnnotation::TypeOf(def_id, canonical_substs) => {
let (
UserSubsts {
substs,
user_self_ty,
},
user_substs,
_,
) = self.infcx
.instantiate_canonical_with_fresh_inference_vars(DUMMY_SP, &canonical_substs);

let ty = self.tcx().type_of(def_id);
let ty = ty.subst(tcx, substs);
let ty = self.normalize(ty, locations);

self.relate_types(ty, v1, a, locations, category)?;

if let Some(UserSelfTy {
impl_def_id,
self_ty,
}) = user_self_ty
{
let impl_self_ty = tcx.type_of(impl_def_id);
let impl_self_ty = impl_self_ty.subst(tcx, &substs);
let impl_self_ty = self.normalize(impl_self_ty, locations);

// There may be type variables in `substs` and hence
// in `impl_self_ty`, but they should all have been
// resolved to some fixed value during the first call
// to `relate`, above. Therefore, if we use
// `resolve_type_vars_if_possible` we should get to
// something without type variables. This is important
// because the `b` type in `relate_with_variance`
// below is not permitted to have inference variables.
let impl_self_ty = self.infcx.resolve_type_vars_if_possible(&impl_self_ty);
assert!(!impl_self_ty.has_infer_types());

self.eq_types(self_ty, impl_self_ty, locations, category)?;

self.prove_predicate(
ty::Predicate::WellFormed(impl_self_ty),
locations,
category,
);
}

// Prove the predicates coming along with `def_id`.
//
// Also, normalize the `instantiated_predicates`
// because otherwise we wind up with duplicate "type
// outlives" error messages.
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
let instantiated_predicates = self.fold_to_region_vid(instantiated_predicates);
self.normalize_and_prove_instantiated_predicates(
instantiated_predicates,
self.fully_perform_op(
locations,
);

// In addition to proving the predicates, we have to
// prove that `ty` is well-formed -- this is because
// the WF of `ty` is predicated on the substs being
// well-formed, and we haven't proven *that*. We don't
// want to prove the WF of types from `substs` directly because they
// haven't been normalized.
//
// FIXME(nmatsakis): Well, perhaps we should normalize
// them? This would only be relevant if some input
// type were ill-formed but did not appear in `ty`,
// which...could happen with normalization...
self.prove_predicate(ty::Predicate::WellFormed(ty), locations, category);
category,
self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
a, v, def_id, user_substs,
)),
)?;
}
}

Ok(())
}

/// Replace all free regions in `value` with their NLL `RegionVid`
/// equivalents; if not in NLL, does nothing. This is never
/// particularly necessary -- we'll do it lazilly as we process
/// the value anyway -- but in some specific cases it is useful to
/// normalize so we can suppress duplicate error messages.
fn fold_to_region_vid<T>(&self, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
if let Some(borrowck_context) = &self.borrowck_context {
self.tcx().fold_regions(&value, &mut false, |r, _debruijn| {
if r.has_free_regions() {
self.tcx().mk_region(ty::RegionKind::ReVar(
borrowck_context.universal_regions.to_region_vid(r),
))
} else {
r
}
})
} else {
value
}
}

fn eq_opaque_type_and_type(
&mut self,
revealed_ty: Ty<'tcx>,
Expand Down
Loading