Skip to content

Commit

Permalink
Rollup merge of rust-lang#41716 - nikomatsakis:issue-41677, r=arielb1
Browse files Browse the repository at this point in the history
enforce WF conditions after generalizing

Add a `WF(T')` obligation after generalizing `T` to `T'`, if `T'` contains an unconstrained type variable in a bivariant context.

Fixes rust-lang#41677.

Beta nominating -- regression.

r? @arielb1
  • Loading branch information
Mark-Simulacrum authored May 12, 2017
2 parents bae7079 + 2490ee5 commit 5b5c045
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 81 deletions.
184 changes: 143 additions & 41 deletions src/librustc/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ use super::{MiscVariable, TypeTrace};
use ty::{IntType, UintType};
use ty::{self, Ty, TyCtxt};
use ty::error::TypeError;
use ty::fold::TypeFoldable;
use ty::relate::{RelateResult, TypeRelation};
use traits::PredicateObligations;
use ty::relate::{self, Relate, RelateResult, TypeRelation};
use traits::{Obligation, PredicateObligations};

use syntax::ast;
use syntax_pos::Span;
Expand Down Expand Up @@ -207,11 +206,16 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
// `'?2` and `?3` are fresh region/type inference
// variables. (Down below, we will relate `a_ty <: b_ty`,
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
let b_ty = self.generalize(a_ty, b_vid, dir == EqTo)?;
let Generalization { ty: b_ty, needs_wf } = self.generalize(a_ty, b_vid, dir)?;
debug!("instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})",
a_ty, dir, b_vid, b_ty);
self.infcx.type_variables.borrow_mut().instantiate(b_vid, b_ty);

if needs_wf {
self.obligations.push(Obligation::new(self.trace.cause.clone(),
ty::Predicate::WellFormed(b_ty)));
}

// Finally, relate `b_ty` to `a_ty`, as described in previous comment.
//
// FIXME(#16847): This code is non-ideal because all these subtype
Expand All @@ -230,50 +234,125 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {

/// Attempts to generalize `ty` for the type variable `for_vid`.
/// This checks for cycle -- that is, whether the type `ty`
/// references `for_vid`. If `is_eq_relation` is false, it will
/// also replace all regions/unbound-type-variables with fresh
/// variables. Returns `TyError` in the case of a cycle, `Ok`
/// otherwise.
/// references `for_vid`. The `dir` is the "direction" for which we
/// a performing the generalization (i.e., are we producing a type
/// that can be used as a supertype etc).
///
/// Preconditions:
///
/// - `for_vid` is a "root vid"
fn generalize(&self,
ty: Ty<'tcx>,
for_vid: ty::TyVid,
is_eq_relation: bool)
-> RelateResult<'tcx, Ty<'tcx>>
dir: RelationDir)
-> RelateResult<'tcx, Generalization<'tcx>>
{
// Determine the ambient variance within which `ty` appears.
// The surrounding equation is:
//
// ty [op] ty2
//
// where `op` is either `==`, `<:`, or `:>`. This maps quite
// naturally.
let ambient_variance = match dir {
RelationDir::EqTo => ty::Invariant,
RelationDir::SubtypeOf => ty::Covariant,
RelationDir::SupertypeOf => ty::Contravariant,
};

let mut generalize = Generalizer {
infcx: self.infcx,
span: self.trace.cause.span,
for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
is_eq_relation: is_eq_relation,
cycle_detected: false
ambient_variance: ambient_variance,
needs_wf: false,
};
let u = ty.fold_with(&mut generalize);
if generalize.cycle_detected {
Err(TypeError::CyclicTy)
} else {
Ok(u)
}

let ty = generalize.relate(&ty, &ty)?;
let needs_wf = generalize.needs_wf;
Ok(Generalization { ty, needs_wf })
}
}

struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
span: Span,
for_vid_sub_root: ty::TyVid,
is_eq_relation: bool,
cycle_detected: bool,
ambient_variance: ty::Variance,
needs_wf: bool, // see the field `needs_wf` in `Generalization`
}

impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx, 'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx> {
/// Result from a generalization operation. This includes
/// not only the generalized type, but also a bool flag
/// indicating whether further WF checks are needed.q
struct Generalization<'tcx> {
ty: Ty<'tcx>,

/// If true, then the generalized type may not be well-formed,
/// even if the source type is well-formed, so we should add an
/// additional check to enforce that it is. This arises in
/// particular around 'bivariant' type parameters that are only
/// constrained by a where-clause. As an example, imagine a type:
///
/// struct Foo<A, B> where A: Iterator<Item=B> {
/// data: A
/// }
///
/// here, `A` will be covariant, but `B` is
/// unconstrained. However, whatever it is, for `Foo` to be WF, it
/// must be equal to `A::Item`. If we have an input `Foo<?A, ?B>`,
/// then after generalization we will wind up with a type like
/// `Foo<?C, ?D>`. When we enforce that `Foo<?A, ?B> <: Foo<?C,
/// ?D>` (or `>:`), we will wind up with the requirement that `?A
/// <: ?C`, but no particular relationship between `?B` and `?D`
/// (after all, we do not know the variance of the normalized form
/// of `A::Item` with respect to `A`). If we do nothing else, this
/// may mean that `?D` goes unconstrained (as in #41677). So, in
/// this scenario where we create a new type variable in a
/// bivariant context, we set the `needs_wf` flag to true. This
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
/// holds, which in turn implies that `?C::Item == ?D`. So once
/// `?C` is constrained, that should suffice to restrict `?D`.
needs_wf: bool,
}

impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, 'tcx> {
fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
self.infcx.tcx
}

fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
fn tag(&self) -> &'static str {
"Generalizer"
}

fn a_is_expected(&self) -> bool {
true
}

fn binders<T>(&mut self, a: &ty::Binder<T>, b: &ty::Binder<T>)
-> RelateResult<'tcx, ty::Binder<T>>
where T: Relate<'tcx>
{
Ok(ty::Binder(self.relate(a.skip_binder(), b.skip_binder())?))
}

fn relate_with_variance<T: Relate<'tcx>>(&mut self,
variance: ty::Variance,
a: &T,
b: &T)
-> RelateResult<'tcx, T>
{
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);

let result = self.relate(a, b);
self.ambient_variance = old_ambient_variance;
result
}

fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==

// Check to see whether the type we are genealizing references
// any other type variable related to `vid` via
// subtyping. This is basically our "occurs check", preventing
Expand All @@ -286,41 +365,63 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx
if sub_vid == self.for_vid_sub_root {
// If sub-roots are equal, then `for_vid` and
// `vid` are related via subtyping.
self.cycle_detected = true;
self.tcx().types.err
return Err(TypeError::CyclicTy);
} else {
match variables.probe_root(vid) {
Some(u) => {
drop(variables);
self.fold_ty(u)
self.relate(&u, &u)
}
None => {
if !self.is_eq_relation {
let origin = variables.origin(vid);
let new_var_id = variables.new_var(false, origin, None);
let u = self.tcx().mk_var(new_var_id);
debug!("generalize: replacing original vid={:?} with new={:?}",
vid, u);
u
} else {
t
match self.ambient_variance {
// Invariant: no need to make a fresh type variable.
ty::Invariant => return Ok(t),

// Bivariant: make a fresh var, but we
// may need a WF predicate. See
// comment on `needs_wf` field for
// more info.
ty::Bivariant => self.needs_wf = true,

// Co/contravariant: this will be
// sufficiently constrained later on.
ty::Covariant | ty::Contravariant => (),
}

let origin = variables.origin(vid);
let new_var_id = variables.new_var(false, origin, None);
let u = self.tcx().mk_var(new_var_id);
debug!("generalize: replacing original vid={:?} with new={:?}",
vid, u);
return Ok(u);
}
}
}
}
ty::TyInfer(ty::IntVar(_)) |
ty::TyInfer(ty::FloatVar(_)) => {
// No matter what mode we are in,
// integer/floating-point types must be equal to be
// relatable.
Ok(t)
}
_ => {
t.super_fold_with(self)
relate::super_relate_tys(self, t, t)
}
}
}

fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
fn regions(&mut self, r: ty::Region<'tcx>, r2: ty::Region<'tcx>)
-> RelateResult<'tcx, ty::Region<'tcx>> {
assert_eq!(r, r2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==

match *r {
// Never make variables for regions bound within the type itself,
// nor for erased regions.
ty::ReLateBound(..) |
ty::ReErased => { return r; }
ty::ReErased => {
return Ok(r);
}

// Early-bound regions should really have been substituted away before
// we get to this point.
Expand All @@ -342,15 +443,16 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx
ty::ReScope(..) |
ty::ReVar(..) |
ty::ReFree(..) => {
if self.is_eq_relation {
return r;
match self.ambient_variance {
ty::Invariant => return Ok(r),
ty::Bivariant | ty::Covariant | ty::Contravariant => (),
}
}
}

// FIXME: This is non-ideal because we don't give a
// very descriptive origin for this region variable.
self.infcx.next_region_var(MiscVariable(self.span))
Ok(self.infcx.next_region_var(MiscVariable(self.span)))
}
}

Expand Down
60 changes: 60 additions & 0 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,66 @@ pub struct CrateVariancesMap {
pub empty_variance: Rc<Vec<ty::Variance>>,
}

impl Variance {
/// `a.xform(b)` combines the variance of a context with the
/// variance of a type with the following meaning. If we are in a
/// context with variance `a`, and we encounter a type argument in
/// a position with variance `b`, then `a.xform(b)` is the new
/// variance with which the argument appears.
///
/// Example 1:
///
/// *mut Vec<i32>
///
/// Here, the "ambient" variance starts as covariant. `*mut T` is
/// invariant with respect to `T`, so the variance in which the
/// `Vec<i32>` appears is `Covariant.xform(Invariant)`, which
/// yields `Invariant`. Now, the type `Vec<T>` is covariant with
/// respect to its type argument `T`, and hence the variance of
/// the `i32` here is `Invariant.xform(Covariant)`, which results
/// (again) in `Invariant`.
///
/// Example 2:
///
/// fn(*const Vec<i32>, *mut Vec<i32)
///
/// The ambient variance is covariant. A `fn` type is
/// contravariant with respect to its parameters, so the variance
/// within which both pointer types appear is
/// `Covariant.xform(Contravariant)`, or `Contravariant`. `*const
/// T` is covariant with respect to `T`, so the variance within
/// which the first `Vec<i32>` appears is
/// `Contravariant.xform(Covariant)` or `Contravariant`. The same
/// is true for its `i32` argument. In the `*mut T` case, the
/// variance of `Vec<i32>` is `Contravariant.xform(Invariant)`,
/// and hence the outermost type is `Invariant` with respect to
/// `Vec<i32>` (and its `i32` argument).
///
/// Source: Figure 1 of "Taming the Wildcards:
/// Combining Definition- and Use-Site Variance" published in PLDI'11.
pub fn xform(self, v: ty::Variance) -> ty::Variance {
match (self, v) {
// Figure 1, column 1.
(ty::Covariant, ty::Covariant) => ty::Covariant,
(ty::Covariant, ty::Contravariant) => ty::Contravariant,
(ty::Covariant, ty::Invariant) => ty::Invariant,
(ty::Covariant, ty::Bivariant) => ty::Bivariant,

// Figure 1, column 2.
(ty::Contravariant, ty::Covariant) => ty::Contravariant,
(ty::Contravariant, ty::Contravariant) => ty::Covariant,
(ty::Contravariant, ty::Invariant) => ty::Invariant,
(ty::Contravariant, ty::Bivariant) => ty::Bivariant,

// Figure 1, column 3.
(ty::Invariant, _) => ty::Invariant,

// Figure 1, column 4.
(ty::Bivariant, _) => ty::Bivariant,
}
}
}

#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
pub struct MethodCallee<'tcx> {
/// Impl method ID, for inherent methods, or trait method ID, otherwise.
Expand Down
1 change: 0 additions & 1 deletion src/librustc_typeck/variance/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use rustc_data_structures::transitive_relation::TransitiveRelation;

use super::terms::*;
use super::terms::VarianceTerm::*;
use super::xform::*;

pub struct ConstraintContext<'a, 'tcx: 'a> {
pub terms_cx: TermsContext<'a, 'tcx>,
Expand Down
29 changes: 0 additions & 29 deletions src/librustc_typeck/variance/xform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,6 @@

use rustc::ty;

pub trait Xform {
fn xform(self, v: Self) -> Self;
}

impl Xform for ty::Variance {
fn xform(self, v: ty::Variance) -> ty::Variance {
// "Variance transformation", Figure 1 of The Paper
match (self, v) {
// Figure 1, column 1.
(ty::Covariant, ty::Covariant) => ty::Covariant,
(ty::Covariant, ty::Contravariant) => ty::Contravariant,
(ty::Covariant, ty::Invariant) => ty::Invariant,
(ty::Covariant, ty::Bivariant) => ty::Bivariant,

// Figure 1, column 2.
(ty::Contravariant, ty::Covariant) => ty::Contravariant,
(ty::Contravariant, ty::Contravariant) => ty::Covariant,
(ty::Contravariant, ty::Invariant) => ty::Invariant,
(ty::Contravariant, ty::Bivariant) => ty::Bivariant,

// Figure 1, column 3.
(ty::Invariant, _) => ty::Invariant,

// Figure 1, column 4.
(ty::Bivariant, _) => ty::Bivariant,
}
}
}

pub fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance {
// Greatest lower bound of the variance lattice as
// defined in The Paper:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
// over time, but this test used to exhibit some pretty bogus messages
// that were not remotely helpful.

// error-pattern:cannot infer
// error-pattern:cannot outlive the lifetime 'a
// error-pattern:must be valid for the static lifetime
// error-pattern:the lifetime 'a
// error-pattern:the static lifetime

struct Invariant<'a>(Option<&'a mut &'a mut ()>);

Expand Down
Loading

0 comments on commit 5b5c045

Please sign in to comment.