Skip to content

Commit

Permalink
Auto merge of #55323 - nikomatsakis:nll-ICE-55219-and-55241, r=pnkfelix
Browse files Browse the repository at this point in the history
introduce type-op for user-type ascription in NLL

Handle user-type ascription in a type operator, which gives us a lot more flexibility around the order in which we resolve things.

r? @pnkfelix

Fixes #55219
Fixes #55241
  • Loading branch information
bors committed Oct 25, 2018
2 parents 365b900 + 62f0fc5 commit 3476ac0
Show file tree
Hide file tree
Showing 15 changed files with 367 additions and 126 deletions.
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 @@ -330,7 +330,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

0 comments on commit 3476ac0

Please sign in to comment.