Introduce new variance inference pass that replaces (and generalizes) old

region-parameterization/variance inference. We now compute variance for
type parameters but do not make use of it (most of the way towards #3598).
This commit is contained in:
Niko Matsakis 2013-10-29 06:08:34 -04:00
parent 1f4faaee40
commit 9d3f57ef08
23 changed files with 1276 additions and 805 deletions

View File

@ -89,7 +89,7 @@ pub static tag_path_elt_name: uint = 0x43u;
pub static tag_item_field: uint = 0x44u;
pub static tag_struct_mut: uint = 0x45u;
pub static tag_region_param: uint = 0x46u;
pub static tag_item_variances: uint = 0x46;
pub static tag_mod_impl_trait: uint = 0x47u;
/*
trait items contain tag_item_trait_method elements,

View File

@ -14,7 +14,6 @@
use metadata::common::*;
use metadata::cstore;
use metadata::decoder;
use metadata;
use middle::ty;
use middle::typeck;
@ -144,6 +143,12 @@ pub fn get_trait_method_def_ids(cstore: @mut cstore::CStore,
decoder::get_trait_method_def_ids(cdata, def.node)
}
pub fn get_item_variances(cstore: @mut cstore::CStore,
def: ast::DefId) -> ty::ItemVariances {
let cdata = cstore::get_crate_data(cstore, def.crate);
decoder::get_item_variances(cdata, def.node)
}
pub fn get_provided_trait_methods(tcx: ty::ctxt,
def: ast::DefId)
-> ~[@ty::Method] {

View File

@ -1088,6 +1088,14 @@ pub fn get_trait_method_def_ids(cdata: Cmd,
result
}
pub fn get_item_variances(cdata: Cmd, id: ast::NodeId) -> ty::ItemVariances {
let data = cdata.data;
let item_doc = lookup_item(id, data);
let variance_doc = reader::get_doc(item_doc, tag_item_variances);
let mut decoder = reader::Decoder(variance_doc);
Decodable::decode(&mut decoder)
}
pub fn get_provided_trait_methods(intr: @ident_interner, cdata: Cmd,
id: ast::NodeId, tcx: ty::ctxt) ->
~[@ty::Method] {

View File

@ -211,6 +211,15 @@ fn encode_region_param_defs(ebml_w: &mut writer::Encoder,
}
}
fn encode_item_variances(ebml_w: &mut writer::Encoder,
ecx: &EncodeContext,
id: ast::NodeId) {
let v = ty::item_variances(ecx.tcx, ast_util::local_def(id));
ebml_w.start_tag(tag_item_variances);
v.encode(ebml_w);
ebml_w.end_tag();
}
fn encode_bounds_and_type(ebml_w: &mut writer::Encoder,
ecx: &EncodeContext,
tpt: &ty::ty_param_bounds_and_ty) {
@ -992,6 +1001,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
ebml_w.start_tag(tag_items_data_item);
encode_def_id(ebml_w, def_id);
encode_family(ebml_w, 't');
encode_item_variances(ebml_w, ecx, item.id);
encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id));
encode_name(ecx, ebml_w, item.ident);
encode_attributes(ebml_w, item.attrs);
@ -1032,6 +1042,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_family(ebml_w, 'S');
encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id));
encode_item_variances(ebml_w, ecx, item.id);
encode_name(ecx, ebml_w, item.ident);
encode_attributes(ebml_w, item.attrs);
encode_path(ecx, ebml_w, path, ast_map::path_name(item.ident));
@ -1138,6 +1149,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
ebml_w.start_tag(tag_items_data_item);
encode_def_id(ebml_w, def_id);
encode_family(ebml_w, 'I');
encode_item_variances(ebml_w, ecx, item.id);
let trait_def = ty::lookup_trait_def(tcx, def_id);
encode_ty_type_param_defs(ebml_w, ecx,
trait_def.generics.type_param_defs,

View File

@ -22,22 +22,14 @@ Most of the documentation on regions can be found in
use driver::session::Session;
use metadata::csearch;
use middle::resolve;
use middle::ty::{region_variance, rv_covariant, rv_invariant};
use middle::ty::{rv_contravariant, FreeRegion};
use middle::ty::{FreeRegion};
use middle::ty;
use std::hashmap::{HashMap, HashSet};
use syntax::ast_map;
use syntax::codemap::Span;
use syntax::print::pprust;
use syntax::parse::token;
use syntax::parse::token::special_idents;
use syntax::{ast, visit};
use syntax::visit::{Visitor,fn_kind};
use syntax::ast::{Block,item,fn_decl,NodeId,Arm,Pat,Stmt,Expr,Local};
use syntax::ast::{Ty,TypeMethod,struct_field};
/**
The region maps encode information about region relationships.
@ -74,7 +66,6 @@ pub struct Context {
struct RegionResolutionVisitor {
sess: Session,
def_map: resolve::DefMap,
// Generated maps:
region_maps: @mut RegionMaps,
@ -504,7 +495,6 @@ impl Visitor<Context> for RegionResolutionVisitor {
}
pub fn resolve_crate(sess: Session,
def_map: resolve::DefMap,
crate: &ast::Crate) -> @mut RegionMaps
{
let region_maps = @mut RegionMaps {
@ -516,483 +506,9 @@ pub fn resolve_crate(sess: Session,
var_parent: None};
let mut visitor = RegionResolutionVisitor {
sess: sess,
def_map: def_map,
region_maps: region_maps,
};
visit::walk_crate(&mut visitor, crate, cx);
return region_maps;
}
// ___________________________________________________________________________
// Determining region parameterization
//
// Infers which type defns must be region parameterized---this is done
// by scanning their contents to see whether they reference a region
// type, directly or indirectly. This is a fixed-point computation.
//
// We do it in two passes. First we walk the AST and construct a map
// from each type defn T1 to other defns which make use of it. For example,
// if we have a type like:
//
// type S = *int;
// type T = S;
//
// Then there would be a map entry from S to T. During the same walk,
// we also construct add any types that reference regions to a set and
// a worklist. We can then process the worklist, propagating indirect
// dependencies until a fixed point is reached.
pub type region_paramd_items = @mut HashMap<ast::NodeId, region_variance>;
#[deriving(Eq)]
pub struct region_dep {
ambient_variance: region_variance,
id: ast::NodeId
}
pub struct DetermineRpCtxt {
sess: Session,
ast_map: ast_map::map,
def_map: resolve::DefMap,
region_paramd_items: region_paramd_items,
dep_map: @mut HashMap<ast::NodeId, @mut ~[region_dep]>,
worklist: ~[ast::NodeId],
// the innermost enclosing item id
item_id: ast::NodeId,
// true when we are within an item but not within a method.
// see long discussion on region_is_relevant().
anon_implies_rp: bool,
// encodes the context of the current type; invariant if
// mutable, covariant otherwise
ambient_variance: region_variance,
}
pub fn join_variance(variance1: region_variance,
variance2: region_variance)
-> region_variance {
match (variance1, variance2) {
(rv_invariant, _) => {rv_invariant}
(_, rv_invariant) => {rv_invariant}
(rv_covariant, rv_contravariant) => {rv_invariant}
(rv_contravariant, rv_covariant) => {rv_invariant}
(rv_covariant, rv_covariant) => {rv_covariant}
(rv_contravariant, rv_contravariant) => {rv_contravariant}
}
}
/// Combines the ambient variance with the variance of a
/// particular site to yield the final variance of the reference.
///
/// Example: if we are checking function arguments then the ambient
/// variance is contravariant. If we then find a `&'r T` pointer, `r`
/// appears in a co-variant position. This implies that this
/// occurrence of `r` is contra-variant with respect to the current
/// item, and hence the function returns `rv_contravariant`.
pub fn add_variance(ambient_variance: region_variance,
variance: region_variance)
-> region_variance {
match (ambient_variance, variance) {
(rv_invariant, _) => rv_invariant,
(_, rv_invariant) => rv_invariant,
(rv_covariant, c) => c,
(c, rv_covariant) => c,
(rv_contravariant, rv_contravariant) => rv_covariant
}
}
impl DetermineRpCtxt {
pub fn add_variance(&self, variance: region_variance) -> region_variance {
add_variance(self.ambient_variance, variance)
}
/// Records that item `id` is region-parameterized with the
/// variance `variance`. If `id` was already parameterized, then
/// the new variance is joined with the old variance.
pub fn add_rp(&mut self, id: ast::NodeId, variance: region_variance) {
assert!(id != 0);
let old_variance = self.region_paramd_items.find(&id).map(|x| *x);
let joined_variance = match old_variance {
None => variance,
Some(v) => join_variance(v, variance)
};
debug!("add_rp() variance for {}: {:?} == {:?} ^ {:?}",
ast_map::node_id_to_str(self.ast_map, id,
token::get_ident_interner()),
joined_variance, old_variance, variance);
if Some(joined_variance) != old_variance {
let region_paramd_items = self.region_paramd_items;
region_paramd_items.insert(id, joined_variance);
self.worklist.push(id);
}
}
/// Indicates that the region-parameterization of the current item
/// is dependent on the region-parameterization of the item
/// `from`. Put another way, it indicates that the current item
/// contains a value of type `from`, so if `from` is
/// region-parameterized, so is the current item.
pub fn add_dep(&mut self, from: ast::NodeId) {
debug!("add dependency from {} -> {} ({} -> {}) with variance {:?}",
from, self.item_id,
ast_map::node_id_to_str(self.ast_map, from,
token::get_ident_interner()),
ast_map::node_id_to_str(self.ast_map, self.item_id,
token::get_ident_interner()),
self.ambient_variance);
let vec = do self.dep_map.find_or_insert_with(from) |_| {
@mut ~[]
};
let dep = region_dep {
ambient_variance: self.ambient_variance,
id: self.item_id
};
if !vec.iter().any(|x| x == &dep) { vec.push(dep); }
}
// Determines whether a reference to a region that appears in the
// AST implies that the enclosing type is region-parameterized (RP).
// This point is subtle. Here are some examples to make it more
// concrete.
//
// 1. impl foo for &int { ... }
// 2. impl foo for &'self int { ... }
// 3. impl foo for bar { fn m(@self) -> &'self int { ... } }
// 4. impl foo for bar { fn m(&self) -> &'self int { ... } }
// 5. impl foo for bar { fn m(&self) -> &int { ... } }
//
// In case 1, the anonymous region is being referenced,
// but it appears in a context where the anonymous region
// resolves to self, so the impl foo is RP.
//
// In case 2, the self parameter is written explicitly.
//
// In case 3, the method refers to the region `self`, so that
// implies that the impl must be region parameterized. (If the
// type bar is not region parameterized, that is an error, because
// the self region is effectively unconstrained, but that is
// detected elsewhere).
//
// In case 4, the method refers to the region `self`, but the
// `self` region is bound by the `&self` receiver, and so this
// does not require that `bar` be RP.
//
// In case 5, the anonymous region is referenced, but it
// bound by the method, so it does not refer to self. This impl
// need not be region parameterized.
//
// Normally, & or &self implies that the enclosing item is RP.
// However, within a function, & is always bound. Within a method
// with &self type, &self is also bound. We detect those last two
// cases via flags (anon_implies_rp and self_implies_rp) that are
// true when the anon or self region implies RP.
pub fn region_is_relevant(&self, r: &Option<ast::Lifetime>) -> bool {
match r {
&None => {
self.anon_implies_rp
}
&Some(ref l) if l.ident == special_idents::statik => {
false
}
&Some(ref l) if l.ident == special_idents::self_ => {
true
}
&Some(_) => {
false
}
}
}
pub fn with(@mut self,
item_id: ast::NodeId,
anon_implies_rp: bool,
f: &fn()) {
let old_item_id = self.item_id;
let old_anon_implies_rp = self.anon_implies_rp;
self.item_id = item_id;
self.anon_implies_rp = anon_implies_rp;
debug!("with_item_id({}, {})",
item_id,
anon_implies_rp);
let _i = ::util::common::indenter();
f();
self.item_id = old_item_id;
self.anon_implies_rp = old_anon_implies_rp;
}
pub fn with_ambient_variance(@mut self,
variance: region_variance,
f: &fn()) {
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.add_variance(variance);
f();
self.ambient_variance = old_ambient_variance;
}
}
fn determine_rp_in_item(visitor: &mut DetermineRpVisitor,
item: @ast::item) {
do visitor.cx.with(item.id, true) {
visit::walk_item(visitor, item, ());
}
}
fn determine_rp_in_fn(visitor: &mut DetermineRpVisitor,
fk: &visit::fn_kind,
decl: &ast::fn_decl,
body: &ast::Block,
_: Span,
_: ast::NodeId) {
let cx = visitor.cx;
do cx.with(cx.item_id, false) {
do cx.with_ambient_variance(rv_contravariant) {
for a in decl.inputs.iter() {
visitor.visit_ty(&a.ty, ());
}
}
visitor.visit_ty(&decl.output, ());
let generics = visit::generics_of_fn(fk);
visitor.visit_generics(&generics, ());
visitor.visit_block(body, ());
}
}
fn determine_rp_in_ty_method(visitor: &mut DetermineRpVisitor,
ty_m: &ast::TypeMethod) {
let cx = visitor.cx;
do cx.with(cx.item_id, false) {
visit::walk_ty_method(visitor, ty_m, ());
}
}
fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor,
ty: &ast::Ty) {
let cx = visitor.cx;
// we are only interested in types that will require an item to
// be region-parameterized. if cx.item_id is zero, then this type
// is not a member of a type defn nor is it a constitutent of an
// impl etc. So we can ignore it and its components.
if cx.item_id == 0 { return; }
// if this type directly references a region pointer like &'r ty,
// add to the worklist/set. Note that &'r ty is contravariant with
// respect to &r, because &'r ty can be used whereever a *smaller*
// region is expected (and hence is a supertype of those
// locations)
let sess = cx.sess;
match ty.node {
ast::ty_rptr(ref r, _) => {
debug!("referenced rptr type {}",
pprust::ty_to_str(ty, sess.intr()));
if cx.region_is_relevant(r) {
let rv = cx.add_variance(rv_contravariant);
cx.add_rp(cx.item_id, rv)
}
}
ast::ty_closure(ref f) => {
debug!("referenced fn type: {}",
pprust::ty_to_str(ty, sess.intr()));
match f.region {
Some(_) => {
if cx.region_is_relevant(&f.region) {
let rv = cx.add_variance(rv_contravariant);
cx.add_rp(cx.item_id, rv)
}
}
None => {
if f.sigil == ast::BorrowedSigil && cx.anon_implies_rp {
let rv = cx.add_variance(rv_contravariant);
cx.add_rp(cx.item_id, rv)
}
}
}
}
_ => {}
}
// if this references another named type, add the dependency
// to the dep_map. If the type is not defined in this crate,
// then check whether it is region-parameterized and consider
// that as a direct dependency.
match ty.node {
ast::ty_path(ref path, _, id) => {
match cx.def_map.find(&id) {
Some(&ast::DefTy(did)) |
Some(&ast::DefTrait(did)) |
Some(&ast::DefStruct(did)) => {
if did.crate == ast::LOCAL_CRATE {
if cx.region_is_relevant(&path.segments.last().lifetime) {
cx.add_dep(did.node);
}
} else {
let cstore = sess.cstore;
match csearch::get_region_param(cstore, did) {
None => {}
Some(variance) => {
debug!("reference to external, rp'd type {}",
pprust::ty_to_str(ty, sess.intr()));
if cx.region_is_relevant(&path.segments.last().lifetime) {
let rv = cx.add_variance(variance);
cx.add_rp(cx.item_id, rv)
}
}
}
}
}
_ => {}
}
}
_ => {}
}
match ty.node {
ast::ty_box(ref mt) | ast::ty_uniq(ref mt) | ast::ty_vec(ref mt) |
ast::ty_rptr(_, ref mt) | ast::ty_ptr(ref mt) => {
visit_mt(visitor, mt);
}
ast::ty_path(ref path, _, _) => {
// type parameters are---for now, anyway---always invariant
do cx.with_ambient_variance(rv_invariant) {
for tp in path.segments.iter().flat_map(|s| s.types.iter()) {
visitor.visit_ty(tp, ());
}
}
}
ast::ty_closure(@ast::TyClosure {decl: ref decl, _}) |
ast::ty_bare_fn(@ast::TyBareFn {decl: ref decl, _}) => {
// fn() binds the & region, so do not consider &T types that
// appear *inside* a fn() type to affect the enclosing item:
do cx.with(cx.item_id, false) {
// parameters are contravariant
do cx.with_ambient_variance(rv_contravariant) {
for a in decl.inputs.iter() {
visitor.visit_ty(&a.ty, ());
}
}
visitor.visit_ty(&decl.output, ());
}
}
_ => {
visit::walk_ty(visitor, ty, ());
}
}
fn visit_mt(visitor: &mut DetermineRpVisitor,
mt: &ast::mt) {
let cx = visitor.cx;
// mutability is invariant
if mt.mutbl == ast::MutMutable {
do cx.with_ambient_variance(rv_invariant) {
visitor.visit_ty(mt.ty, ());
}
} else {
visitor.visit_ty(mt.ty, ());
}
}
}
fn determine_rp_in_struct_field(visitor: &mut DetermineRpVisitor,
cm: @ast::struct_field) {
visit::walk_struct_field(visitor, cm, ());
}
struct DetermineRpVisitor {
cx: @mut DetermineRpCtxt
}
impl Visitor<()> for DetermineRpVisitor {
fn visit_fn(&mut self, fk:&fn_kind, fd:&fn_decl,
b:&Block, s:Span, n:NodeId, _:()) {
determine_rp_in_fn(self, fk, fd, b, s, n);
}
fn visit_item(&mut self, i:@item, _:()) {
determine_rp_in_item(self, i);
}
fn visit_ty(&mut self, t:&Ty, _:()) {
determine_rp_in_ty(self, t);
}
fn visit_ty_method(&mut self, t:&TypeMethod, _:()) {
determine_rp_in_ty_method(self, t);
}
fn visit_struct_field(&mut self, s:@struct_field, _:()) {
determine_rp_in_struct_field(self, s);
}
}
pub fn determine_rp_in_crate(sess: Session,
ast_map: ast_map::map,
def_map: resolve::DefMap,
crate: &ast::Crate)
-> region_paramd_items {
let cx = @mut DetermineRpCtxt {
sess: sess,
ast_map: ast_map,
def_map: def_map,
region_paramd_items: @mut HashMap::new(),
dep_map: @mut HashMap::new(),
worklist: ~[],
item_id: 0,
anon_implies_rp: false,
ambient_variance: rv_covariant
};
// Gather up the base set, worklist and dep_map
let mut visitor = DetermineRpVisitor { cx: cx };
visit::walk_crate(&mut visitor, crate, ());
// Propagate indirect dependencies
//
// Each entry in the worklist is the id of an item C whose region
// parameterization has been updated. So we pull ids off of the
// worklist, find the current variance, and then iterate through
// all of the dependent items (that is, those items that reference
// C). For each dependent item D, we combine the variance of C
// with the ambient variance where the reference occurred and then
// update the region-parameterization of D to reflect the result.
{
let cx = &mut *cx;
while cx.worklist.len() != 0 {
let c_id = cx.worklist.pop();
let c_variance = cx.region_paramd_items.get_copy(&c_id);
debug!("popped {} from worklist", c_id);
match cx.dep_map.find(&c_id) {
None => {}
Some(deps) => {
for dep in deps.iter() {
let v = add_variance(dep.ambient_variance, c_variance);
cx.add_rp(dep.id, v);
}
}
}
}
}
debug!("{}", {
debug!("Region variance results:");
let region_paramd_items = cx.region_paramd_items;
for (&key, &value) in region_paramd_items.iter() {
debug!("item {:?} ({}) is parameterized with variance {:?}",
key,
ast_map::node_id_to_str(ast_map, key,
token::get_ident_interner()),
value);
}
"----"
});
// return final set
return cx.region_paramd_items;
}

View File

@ -209,13 +209,19 @@ pub enum ast_ty_to_ty_cache_entry {
atttce_resolved(t) /* resolved to a type, irrespective of region */
}
pub type opt_region_variance = Option<region_variance>;
#[deriving(Clone, Eq, Decodable, Encodable)]
pub struct ItemVariances {
self_param: Option<Variance>,
type_params: OptVec<Variance>,
region_params: OptVec<Variance>
}
#[deriving(Clone, Eq, Decodable, Encodable)]
pub enum region_variance {
rv_covariant,
rv_invariant,
rv_contravariant,
pub enum Variance {
Covariant,
Invariant,
Contravariant,
Bivariant,
}
#[deriving(Decodable, Encodable)]
@ -264,7 +270,6 @@ struct ctxt_ {
named_region_map: @mut resolve_lifetime::NamedRegionMap,
region_maps: @mut middle::region::RegionMaps,
region_paramd_items: middle::region::region_paramd_items,
// Stores the types for various nodes in the AST. Note that this table
// is not guaranteed to be populated until after typeck. See
@ -309,6 +314,10 @@ struct ctxt_ {
provided_method_sources: @mut HashMap<ast::DefId, ast::DefId>,
supertraits: @mut HashMap<ast::DefId, @~[@TraitRef]>,
// Maps from def-id of a type or region parameter to its
// (inferred) variance.
item_variance_map: @mut HashMap<ast::DefId, @ItemVariances>,
// A mapping from the def ID of an enum or struct type to the def ID
// of the method that implements its destructor. If the type is not
// present in this map, it does not have a destructor. This map is
@ -954,11 +963,11 @@ pub fn mk_ctxt(s: session::Session,
amap: ast_map::map,
freevars: freevars::freevar_map,
region_maps: @mut middle::region::RegionMaps,
region_paramd_items: middle::region::region_paramd_items,
lang_items: middle::lang_items::LanguageItems)
-> ctxt {
@ctxt_ {
named_region_map: named_region_map,
item_variance_map: @mut HashMap::new(),
diag: s.diagnostic(),
interner: @mut HashMap::new(),
next_id: @mut primitives::LAST_PRIMITIVE_ID,
@ -966,7 +975,6 @@ pub fn mk_ctxt(s: session::Session,
sess: s,
def_map: dm,
region_maps: region_maps,
region_paramd_items: region_paramd_items,
node_types: @mut HashMap::new(),
node_type_substs: @mut HashMap::new(),
trait_refs: @mut HashMap::new(),
@ -4410,6 +4418,12 @@ pub fn visitor_object_ty(tcx: ctxt,
EmptyBuiltinBounds())))
}
pub fn item_variances(tcx: ctxt, item_id: ast::DefId) -> @ItemVariances {
lookup_locally_or_in_crate_store(
"item_variance_map", item_id, tcx.item_variance_map,
|| @csearch::get_item_variances(tcx.cstore, item_id))
}
/// Records a trait-to-implementation mapping.
fn record_trait_implementation(tcx: ctxt,
trait_def_id: DefId,
@ -4692,6 +4706,17 @@ pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: @str) -> u64 {
hash.result_u64()
}
impl Variance {
pub fn to_str(self) -> &'static str {
match self {
Covariant => "+",
Contravariant => "-",
Invariant => "o",
Bivariant => "*",
}
}
}
pub fn construct_parameter_environment(
tcx: ctxt,
self_bound: Option<@TraitRef>,

View File

@ -123,76 +123,70 @@ pub trait Combine {
}
}
fn substs(&self, generics: &ty::Generics, as_: &ty::substs,
fn substs(&self,
item_def_id: ast::DefId,
as_: &ty::substs,
bs: &ty::substs) -> cres<ty::substs> {
fn relate_region_params<C:Combine>(
this: &C,
generics: &ty::Generics,
fn relate_region_params<C:Combine>(this: &C,
item_def_id: ast::DefId,
a: &ty::RegionSubsts,
b: &ty::RegionSubsts)
-> cres<ty::RegionSubsts>
{
-> cres<ty::RegionSubsts> {
let tcx = this.infcx().tcx;
match (a, b) {
(&ty::ErasedRegions, _) |
(_, &ty::ErasedRegions) => {
(&ty::ErasedRegions, _) | (_, &ty::ErasedRegions) => {
Ok(ty::ErasedRegions)
}
(&ty::NonerasedRegions(ref a_rs),
&ty::NonerasedRegions(ref b_rs)) => {
match generics.region_param {
None => {
assert!(a_rs.is_empty());
assert!(b_rs.is_empty());
Ok(ty::NonerasedRegions(opt_vec::Empty))
}
let variances = ty::item_variances(tcx, item_def_id);
let region_params = &variances.region_params;
let num_region_params = region_params.len();
Some(variance) => {
assert_eq!(a_rs.len(), 1);
assert_eq!(b_rs.len(), 1);
let a_r = *a_rs.get(0);
let b_r = *b_rs.get(0);
debug!("relate_region_params(\
item_def_id={}, \
a_rs={}, \
b_rs={},
region_params={})",
item_def_id.repr(tcx),
a_rs.repr(tcx),
b_rs.repr(tcx),
region_params.repr(tcx));
match variance {
ty::rv_invariant => {
do eq_regions(this, a_r, b_r).then {
Ok(ty::NonerasedRegions(opt_vec::with(a_r)))
}
}
ty::rv_covariant => {
do this.regions(a_r, b_r).and_then |r| {
Ok(ty::NonerasedRegions(opt_vec::with(r)))
}
}
ty::rv_contravariant => {
do this.contraregions(a_r, b_r).and_then |r| {
Ok(ty::NonerasedRegions(opt_vec::with(r)))
}
}
assert_eq!(num_region_params, a_rs.len());
assert_eq!(num_region_params, b_rs.len());
let mut rs = opt_vec::Empty;
for i in range(0, num_region_params) {
let a_r = *a_rs.get(i);
let b_r = *b_rs.get(i);
let variance = *region_params.get(i);
let r = match variance {
ty::Invariant => {
eq_regions(this, a_r, b_r)
.and_then(|()| Ok(a_r))
}
}
ty::Covariant => this.regions(a_r, b_r),
ty::Contravariant => this.contraregions(a_r, b_r),
ty::Bivariant => Ok(a_r),
};
rs.push(if_ok!(r));
}
Ok(ty::NonerasedRegions(rs))
}
}
}
do self.tps(as_.tps, bs.tps).and_then |tps| {
do self.self_tys(as_.self_ty, bs.self_ty).and_then |self_ty| {
do relate_region_params(self,
generics,
&as_.regions,
&bs.regions).and_then |regions| {
Ok(substs {
regions: regions,
self_ty: self_ty,
tps: tps.clone()
})
}
}
}
let tps = if_ok!(self.tps(as_.tps, bs.tps));
let self_ty = if_ok!(self.self_tys(as_.self_ty, bs.self_ty));
let regions = if_ok!(relate_region_params(self,
item_def_id,
&as_.regions,
&bs.regions));
Ok(substs { regions: regions,
self_ty: self_ty,
tps: tps.clone() })
}
fn bare_fn_tys(&self, a: &ty::BareFnTy,
@ -267,9 +261,11 @@ pub trait Combine {
-> cres<ty::Region>;
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region>;
fn vstores(&self, vk: ty::terr_vstore_kind,
a: ty::vstore, b: ty::vstore) -> cres<ty::vstore> {
fn vstores(&self,
vk: ty::terr_vstore_kind,
a: ty::vstore,
b: ty::vstore)
-> cres<ty::vstore> {
debug!("{}.vstores(a={:?}, b={:?})", self.tag(), a, b);
match (a, b) {
@ -293,8 +289,7 @@ pub trait Combine {
vk: ty::terr_vstore_kind,
a: ty::TraitStore,
b: ty::TraitStore)
-> cres<ty::TraitStore> {
-> cres<ty::TraitStore> {
debug!("{}.trait_stores(a={:?}, b={:?})", self.tag(), a, b);
match (a, b) {
@ -317,7 +312,8 @@ pub trait Combine {
fn trait_refs(&self,
a: &ty::TraitRef,
b: &ty::TraitRef) -> cres<ty::TraitRef> {
b: &ty::TraitRef)
-> cres<ty::TraitRef> {
// Different traits cannot be related
// - NOTE in the future, expand out subtraits!
@ -326,15 +322,9 @@ pub trait Combine {
Err(ty::terr_traits(
expected_found(self, a.def_id, b.def_id)))
} else {
let tcx = self.infcx().tcx;
let trait_def = ty::lookup_trait_def(tcx, a.def_id);
let substs = if_ok!(self.substs(&trait_def.generics,
&a.substs,
&b.substs));
Ok(ty::TraitRef {
def_id: a.def_id,
substs: substs
})
let substs = if_ok!(self.substs(a.def_id, &a.substs, &b.substs));
Ok(ty::TraitRef { def_id: a.def_id,
substs: substs })
}
}
}
@ -366,8 +356,8 @@ pub fn eq_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> ures {
pub fn eq_regions<C:Combine>(this: &C, a: ty::Region, b: ty::Region)
-> ures {
debug!("eq_regions({}, {})",
a.inf_str(this.infcx()),
b.inf_str(this.infcx()));
a.repr(this.infcx().tcx),
b.repr(this.infcx().tcx));
let sub = this.sub();
do indent {
this.infcx().try(|| {
@ -511,36 +501,30 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
(&ty::ty_enum(a_id, ref a_substs),
&ty::ty_enum(b_id, ref b_substs))
if a_id == b_id => {
let type_def = ty::lookup_item_type(tcx, a_id);
do this.substs(&type_def.generics, a_substs, b_substs).and_then |substs| {
Ok(ty::mk_enum(tcx, a_id, substs))
}
let substs = if_ok!(this.substs(a_id,
a_substs,
b_substs));
Ok(ty::mk_enum(tcx, a_id, substs))
}
(&ty::ty_trait(a_id, ref a_substs, a_store, a_mutbl, a_bounds),
&ty::ty_trait(b_id, ref b_substs, b_store, b_mutbl, b_bounds))
if a_id == b_id && a_mutbl == b_mutbl => {
let trait_def = ty::lookup_trait_def(tcx, a_id);
do this.substs(&trait_def.generics, a_substs, b_substs).and_then |substs| {
do this.trait_stores(ty::terr_trait, a_store, b_store).and_then |s| {
do this.bounds(a_bounds, b_bounds).and_then |bounds| {
Ok(ty::mk_trait(tcx,
a_id,
substs.clone(),
s,
a_mutbl,
bounds))
}
}
}
let substs = if_ok!(this.substs(a_id, a_substs, b_substs));
let s = if_ok!(this.trait_stores(ty::terr_trait, a_store, b_store));
let bounds = if_ok!(this.bounds(a_bounds, b_bounds));
Ok(ty::mk_trait(tcx,
a_id,
substs.clone(),
s,
a_mutbl,
bounds))
}
(&ty::ty_struct(a_id, ref a_substs), &ty::ty_struct(b_id, ref b_substs))
if a_id == b_id => {
let type_def = ty::lookup_item_type(tcx, a_id);
do this.substs(&type_def.generics, a_substs, b_substs).and_then |substs| {
Ok(ty::mk_struct(tcx, a_id, substs))
}
let substs = if_ok!(this.substs(a_id, a_substs, b_substs));
Ok(ty::mk_struct(tcx, a_id, substs))
}
(&ty::ty_box(ref a_mt), &ty::ty_box(ref b_mt)) => {
@ -576,9 +560,8 @@ pub fn super_tys<C:Combine>(this: &C, a: ty::t, b: ty::t) -> cres<ty::t> {
}
(&ty::ty_estr(vs_a), &ty::ty_estr(vs_b)) => {
do this.vstores(ty::terr_str, vs_a, vs_b).and_then |vs| {
Ok(ty::mk_estr(tcx,vs))
}
let vs = if_ok!(this.vstores(ty::terr_str, vs_a, vs_b));
Ok(ty::mk_estr(tcx,vs))
}
(&ty::ty_tup(ref as_), &ty::ty_tup(ref bs)) => {

View File

@ -19,16 +19,23 @@ The type checker is responsible for:
3. Guaranteeing that most type rules are met ("most?", you say, "why most?"
Well, dear reader, read on)
The main entry point is `check_crate()`. Type checking operates in two major
phases: collect and check. The collect phase passes over all items and
determines their type, without examining their "innards". The check phase
then checks function bodies and so forth.
The main entry point is `check_crate()`. Type checking operates in
several major phases:
Within the check phase, we check each function body one at a time (bodies of
function expressions are checked as part of the containing function).
Inference is used to supply types wherever they are unknown. The actual
checking of a function itself has several phases (check, regionck, writeback),
as discussed in the documentation for the `check` module.
1. The collect phase first passes over all items and determines their
type, without examining their "innards".
2. Variance inference then runs to compute the variance of each parameter
3. Coherence checks for overlapping or orphaned impls
4. Finally, the check phase then checks function bodies and so forth.
Within the check phase, we check each function body one at a time
(bodies of function expressions are checked as part of the
containing function). Inference is used to supply types wherever
they are unknown. The actual checking of a function itself has
several phases (check, regionck, writeback), as discussed in the
documentation for the `check` module.
The type checker is defined into various submodules which are documented
independently:
@ -39,6 +46,10 @@ independently:
- collect: computes the types of each top-level item and enters them into
the `cx.tcache` table for later use
- coherence: enforces coherence rules, builds some tables
- variance: variance inference
- check: walks over function bodies and type checks them, inferring types for
local variables, type parameters, etc as necessary.
@ -71,6 +82,7 @@ pub mod astconv;
pub mod infer;
pub mod collect;
pub mod coherence;
pub mod variance;
#[deriving(Clone, Encodable, Decodable, Eq, Ord)]
pub enum param_index {
@ -455,6 +467,9 @@ pub fn check_crate(tcx: ty::ctxt,
// have valid types and not error
tcx.sess.abort_if_errors();
time(time_passes, "variance inference", (), |_|
variance::infer_variance(tcx, crate));
time(time_passes, "coherence checking", (), |_|
coherence::check_coherence(ccx, crate));

View File

@ -0,0 +1,841 @@
// Copyright 2012 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.
/*!
This file infers the variance of type and lifetime parameters. The
algorithm is taken from Section 4 of the paper "Taming the Wildcards:
Combining Definition- and Use-Site Variance" published in PLDI'11 and
written by Altidor et al., and hereafter referred to as The Paper.
The basic idea is quite straightforward. We iterate over the types
defined and, for each use of a type parameter X, accumulate a
constraint indicating that the variance of X must be valid for the
variance of that use site. We then iteratively refine the variance of
X until all constraints are met. There is *always* a sol'n, because at
the limit we can declare all type parameters to be invariant and all
constriants will be satisfied.
As a simple example, consider:
enum Option<A> { Some(A), None }
enum OptionalFn<B> { Some(&fn(B)), None }
enum OptionalMap<C> { Some(&fn(C) -> C), None }
Here, we will generate the constraints:
1. V(A) <= +
2. V(B) <= -
3. V(C) <= +
4. V(C) <= -
These indicate that (1) the variance of A must be at most covariant;
(2) the variance of B must be at most contravariant; and (3, 4) the
variance of C must be at most covariant *and* contravariant. All of these
results are based on a variance lattice defined as follows:
* Top (bivariant)
- +
o Bottom (invariant)
Based on this lattice, the solution V(A)=+, V(B)=-, V(C)=o is the
minimal solution (which is what we are looking for; the maximal
solution is just that all variables are invariant. Not so exciting.).
You may be wondering why fixed-point iteration is required. The reason
is that the variance of a use site may itself be a function of the
variance of other type parameters. In full generality, our constraints
take the form:
V(X) <= Term
Term := + | - | * | o | V(X) | Term x Term
Here the notation V(X) indicates the variance of a type/region
parameter `X` with respect to its defining class. `Term x Term`
represents the "variance transform" as defined in the paper -- `V1 x
V2` is the resulting variance when a use site with variance V2 appears
inside a use site with variance V1.
*/
use std::hashmap::HashMap;
use extra::arena;
use extra::arena::Arena;
use middle::ty;
use std::vec;
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util;
use syntax::parse::token;
use syntax::opt_vec;
use syntax::visit;
use syntax::visit::Visitor;
pub fn infer_variance(tcx: ty::ctxt,
crate: &ast::Crate) {
let mut arena = arena::Arena::new();
let terms_cx = determine_parameters_to_be_inferred(tcx, &mut arena, crate);
let constraints_cx = add_constraints_from_crate(terms_cx, crate);
solve_constraints(constraints_cx);
}
/**************************************************************************
* Representing terms
*
* Terms are structured as a straightforward tree. Rather than rely on
* GC, we allocate terms out of a bounded arena (the lifetime of this
* arena is the lifetime 'self that is threaded around).
*
* We assign a unique index to each type/region parameter whose variance
* is to be inferred. We refer to such variables as "inferreds". An
* `InferredIndex` is a newtype'd int representing the index of such
* a variable.
*/
type VarianceTermPtr<'self> = &'self VarianceTerm<'self>;
struct InferredIndex(uint);
enum VarianceTerm<'self> {
ConstantTerm(ty::Variance),
TransformTerm(VarianceTermPtr<'self>, VarianceTermPtr<'self>),
InferredTerm(InferredIndex),
}
impl<'self> ToStr for VarianceTerm<'self> {
fn to_str(&self) -> ~str {
match *self {
ConstantTerm(c1) => format!("{}", c1.to_str()),
TransformTerm(v1, v2) => format!("({} \u00D7 {})",
v1.to_str(), v2.to_str()),
InferredTerm(id) => format!("[{}]", *id)
}
}
}
/**************************************************************************
* The first pass over the crate simply builds up the set of inferreds.
*/
struct TermsContext<'self> {
tcx: ty::ctxt,
arena: &'self Arena,
// Maps from the node id of a type/generic parameter to the
// corresponding inferred index.
inferred_map: HashMap<ast::NodeId, InferredIndex>,
inferred_infos: ~[InferredInfo<'self>],
}
enum ParamKind { TypeParam, RegionParam, SelfParam }
struct InferredInfo<'self> {
item_id: ast::NodeId,
kind: ParamKind,
index: uint,
param_id: ast::NodeId,
term: VarianceTermPtr<'self>,
}
fn determine_parameters_to_be_inferred<'a>(tcx: ty::ctxt,
arena: &'a mut Arena,
crate: &ast::Crate)
-> TermsContext<'a> {
let mut terms_cx = TermsContext {
tcx: tcx,
arena: arena,
inferred_map: HashMap::new(),
inferred_infos: ~[],
};
visit::walk_crate(&mut terms_cx, crate, ());
terms_cx
}
impl<'self> TermsContext<'self> {
fn add_inferred(&mut self,
item_id: ast::NodeId,
kind: ParamKind,
index: uint,
param_id: ast::NodeId) {
let inf_index = InferredIndex(self.inferred_infos.len());
let term = self.arena.alloc(|| InferredTerm(inf_index));
self.inferred_infos.push(InferredInfo { item_id: item_id,
kind: kind,
index: index,
param_id: param_id,
term: term });
let newly_added = self.inferred_map.insert(param_id, inf_index);
assert!(newly_added);
debug!("add_inferred(item_id={}, \
kind={:?}, \
index={}, \
param_id={},
inf_index={:?})",
item_id, kind, index, param_id, inf_index);
}
fn num_inferred(&self) -> uint {
self.inferred_infos.len()
}
}
impl<'self> Visitor<()> for TermsContext<'self> {
fn visit_item(&mut self,
item: @ast::item,
(): ()) {
debug!("add_inferreds for item {}", item.repr(self.tcx));
let inferreds_on_entry = self.num_inferred();
// NB: In the code below for writing the results back into the
// tcx, we rely on the fact that all inferreds for a particular
// item are assigned continuous indices.
match item.node {
ast::item_trait(*) => {
self.add_inferred(item.id, SelfParam, 0, item.id);
}
_ => { }
}
match item.node {
ast::item_enum(_, ref generics) |
ast::item_struct(_, ref generics) |
ast::item_trait(ref generics, _, _) => {
for (i, p) in generics.lifetimes.iter().enumerate() {
self.add_inferred(item.id, RegionParam, i, p.id);
}
for (i, p) in generics.ty_params.iter().enumerate() {
self.add_inferred(item.id, TypeParam, i, p.id);
}
// If this item has no type or lifetime parameters,
// then there are no variances to infer, so just
// insert an empty entry into the variance map.
// Arguably we could just leave the map empty in this
// case but it seems cleaner to be able to distinguish
// "invalid item id" from "item id with no
// parameters".
if self.num_inferred() == inferreds_on_entry {
let newly_added = self.tcx.item_variance_map.insert(
ast_util::local_def(item.id),
@ty::ItemVariances {
self_param: None,
type_params: opt_vec::Empty,
region_params: opt_vec::Empty
});
assert!(newly_added);
}
visit::walk_item(self, item, ());
}
ast::item_impl(*) |
ast::item_static(*) |
ast::item_fn(*) |
ast::item_mod(*) |
ast::item_foreign_mod(*) |
ast::item_ty(*) |
ast::item_mac(*) => {
visit::walk_item(self, item, ());
}
}
}
}
/**************************************************************************
* Constraint construction and representation
*
* The second pass over the AST determines the set of constraints.
* We walk the set of items and, for each member, generate new constraints.
*/
struct ConstraintContext<'self> {
terms_cx: TermsContext<'self>,
covariant: VarianceTermPtr<'self>,
contravariant: VarianceTermPtr<'self>,
invariant: VarianceTermPtr<'self>,
bivariant: VarianceTermPtr<'self>,
constraints: ~[Constraint<'self>],
}
/// Declares that the variable `decl_id` appears in a location with
/// variance `variance`.
struct Constraint<'self> {
inferred: InferredIndex,
variance: &'self VarianceTerm<'self>,
}
fn add_constraints_from_crate<'a>(terms_cx: TermsContext<'a>,
crate: &ast::Crate)
-> ConstraintContext<'a> {
let covariant = terms_cx.arena.alloc(|| ConstantTerm(ty::Covariant));
let contravariant = terms_cx.arena.alloc(|| ConstantTerm(ty::Contravariant));
let invariant = terms_cx.arena.alloc(|| ConstantTerm(ty::Invariant));
let bivariant = terms_cx.arena.alloc(|| ConstantTerm(ty::Bivariant));
let mut constraint_cx = ConstraintContext {
terms_cx: terms_cx,
covariant: covariant,
contravariant: contravariant,
invariant: invariant,
bivariant: bivariant,
constraints: ~[],
};
visit::walk_crate(&mut constraint_cx, crate, ());
constraint_cx
}
impl<'self> Visitor<()> for ConstraintContext<'self> {
fn visit_item(&mut self,
item: @ast::item,
(): ()) {
let did = ast_util::local_def(item.id);
let tcx = self.terms_cx.tcx;
match item.node {
ast::item_enum(ref enum_definition, _) => {
// Hack: If we directly call `ty::enum_variants`, it
// annoyingly takes it upon itself to run off and
// evaluate the discriminants eagerly (*grumpy* that's
// not the typical pattern). This results in double
// error messagees because typeck goes off and does
// this at a later time. All we really care about is
// the types of the variant arguments, so we just call
// `ty::VariantInfo::from_ast_variant()` ourselves
// here, mainly so as to mask the differences between
// struct-like enums and so forth.
for ast_variant in enum_definition.variants.iter() {
let variant =
ty::VariantInfo::from_ast_variant(tcx,
ast_variant,
/*discrimant*/ 0);
for &arg_ty in variant.args.iter() {
self.add_constraints_from_ty(arg_ty, self.covariant);
}
}
}
ast::item_struct(*) => {
let struct_fields = ty::lookup_struct_fields(tcx, did);
for field_info in struct_fields.iter() {
assert_eq!(field_info.id.crate, ast::LOCAL_CRATE);
let field_ty = ty::node_id_to_type(tcx, field_info.id.node);
self.add_constraints_from_ty(field_ty, self.covariant);
}
}
ast::item_trait(*) => {
let methods = ty::trait_methods(tcx, did);
for method in methods.iter() {
match method.transformed_self_ty {
Some(self_ty) => {
// The self type is a parameter, so its type
// should be considered contravariant:
self.add_constraints_from_ty(
self_ty, self.contravariant);
}
None => {}
}
self.add_constraints_from_sig(
&method.fty.sig, self.covariant);
}
}
ast::item_static(*) |
ast::item_fn(*) |
ast::item_mod(*) |
ast::item_foreign_mod(*) |
ast::item_ty(*) |
ast::item_impl(*) |
ast::item_mac(*) => {
visit::walk_item(self, item, ());
}
}
}
}
impl<'self> ConstraintContext<'self> {
fn tcx(&self) -> ty::ctxt {
self.terms_cx.tcx
}
fn inferred_index(&self, param_id: ast::NodeId) -> InferredIndex {
match self.terms_cx.inferred_map.find(&param_id) {
Some(&index) => index,
None => {
self.tcx().sess.bug(format!(
"No inferred index entry for {}",
ast_map::node_id_to_str(self.tcx().items,
param_id,
token::get_ident_interner())));
}
}
}
fn declared_variance(&self,
param_def_id: ast::DefId,
item_def_id: ast::DefId,
kind: ParamKind,
index: uint)
-> VarianceTermPtr<'self> {
/*!
* Returns a variance term representing the declared variance of
* the type/region parameter with the given id.
*/
assert_eq!(param_def_id.crate, item_def_id.crate);
if param_def_id.crate == ast::LOCAL_CRATE {
// Parameter on an item defined within current crate:
// variance not yet inferred, so return a symbolic
// variance.
let index = self.inferred_index(param_def_id.node);
self.terms_cx.inferred_infos[*index].term
} else {
// Parameter on an item defined within another crate:
// variance already inferred, just look it up.
let variances = ty::item_variances(self.tcx(), item_def_id);
let variance = match kind {
SelfParam => variances.self_param.unwrap(),
TypeParam => *variances.type_params.get(index),
RegionParam => *variances.region_params.get(index),
};
self.constant_term(variance)
}
}
fn add_constraint(&mut self,
index: InferredIndex,
variance: VarianceTermPtr<'self>) {
debug!("add_constraint(index={}, variance={})",
*index, variance.to_str());
self.constraints.push(Constraint { inferred: index,
variance: variance });
}
fn contravariant(&mut self,
variance: VarianceTermPtr<'self>)
-> VarianceTermPtr<'self> {
self.xform(variance, self.contravariant)
}
fn invariant(&mut self,
variance: VarianceTermPtr<'self>)
-> VarianceTermPtr<'self> {
self.xform(variance, self.invariant)
}
fn constant_term(&self, v: ty::Variance) -> VarianceTermPtr<'self> {
match v {
ty::Covariant => self.covariant,
ty::Invariant => self.invariant,
ty::Contravariant => self.contravariant,
ty::Bivariant => self.bivariant,
}
}
fn xform(&mut self,
v1: VarianceTermPtr<'self>,
v2: VarianceTermPtr<'self>)
-> VarianceTermPtr<'self> {
match (*v1, *v2) {
(_, ConstantTerm(ty::Covariant)) => {
// Applying a "covariant" transform is always a no-op
v1
}
(ConstantTerm(c1), ConstantTerm(c2)) => {
self.constant_term(c1.xform(c2))
}
_ => {
self.terms_cx.arena.alloc(|| TransformTerm(v1, v2))
}
}
}
fn add_constraints_from_ty(&mut self,
ty: ty::t,
variance: VarianceTermPtr<'self>) {
debug!("add_constraints_from_ty(ty={})", ty.repr(self.tcx()));
match ty::get(ty).sty {
ty::ty_nil | ty::ty_bot | ty::ty_bool |
ty::ty_char | ty::ty_int(_) | ty::ty_uint(_) |
ty::ty_float(_) => {
/* leaf type -- noop */
}
ty::ty_rptr(region, ref mt) => {
let contra = self.contravariant(variance);
self.add_constraints_from_region(region, contra);
self.add_constraints_from_mt(mt, variance);
}
ty::ty_estr(vstore) => {
self.add_constraints_from_vstore(vstore, variance);
}
ty::ty_evec(ref mt, vstore) => {
self.add_constraints_from_vstore(vstore, variance);
self.add_constraints_from_mt(mt, variance);
}
ty::ty_box(ref mt) |
ty::ty_uniq(ref mt) |
ty::ty_ptr(ref mt) => {
self.add_constraints_from_mt(mt, variance);
}
ty::ty_tup(ref subtys) => {
for &subty in subtys.iter() {
self.add_constraints_from_ty(subty, variance);
}
}
ty::ty_enum(def_id, ref substs) |
ty::ty_struct(def_id, ref substs) => {
let item_type = ty::lookup_item_type(self.tcx(), def_id);
self.add_constraints_from_substs(def_id, &item_type.generics,
substs, variance);
}
ty::ty_trait(def_id, ref substs, _, _, _) => {
let trait_def = ty::lookup_trait_def(self.tcx(), def_id);
self.add_constraints_from_substs(def_id, &trait_def.generics,
substs, variance);
}
ty::ty_param(ty::param_ty { def_id: ref def_id, _ }) => {
assert_eq!(def_id.crate, ast::LOCAL_CRATE);
match self.terms_cx.inferred_map.find(&def_id.node) {
Some(&index) => {
self.add_constraint(index, variance);
}
None => {
// We do not infer variance for type parameters
// declared on methods. They will not be present
// in the inferred_map.
}
}
}
ty::ty_self(ref def_id) => {
assert_eq!(def_id.crate, ast::LOCAL_CRATE);
let index = self.inferred_index(def_id.node);
self.add_constraint(index, variance);
}
ty::ty_bare_fn(ty::BareFnTy { sig: ref sig, _ }) => {
self.add_constraints_from_sig(sig, variance);
}
ty::ty_closure(ty::ClosureTy { sig: ref sig, region, _ }) => {
let contra = self.contravariant(variance);
self.add_constraints_from_region(region, contra);
self.add_constraints_from_sig(sig, variance);
}
ty::ty_infer(*) | ty::ty_err | ty::ty_type |
ty::ty_opaque_box | ty::ty_opaque_closure_ptr(*) |
ty::ty_unboxed_vec(*) => {
self.tcx().sess.bug(
format!("Unexpected type encountered in \
variance inference: {}",
ty.repr(self.tcx())));
}
}
}
fn add_constraints_from_vstore(&mut self,
vstore: ty::vstore,
variance: VarianceTermPtr<'self>) {
match vstore {
ty::vstore_slice(r) => {
let contra = self.contravariant(variance);
self.add_constraints_from_region(r, contra);
}
ty::vstore_fixed(_) | ty::vstore_uniq | ty::vstore_box => {
}
}
}
fn add_constraints_from_substs(&mut self,
def_id: ast::DefId,
generics: &ty::Generics,
substs: &ty::substs,
variance: VarianceTermPtr<'self>) {
debug!("add_constraints_from_substs(def_id={:?})", def_id);
for (i, p) in generics.type_param_defs.iter().enumerate() {
let variance_decl =
self.declared_variance(p.def_id, def_id, TypeParam, i);
let variance_i = self.xform(variance, variance_decl);
self.add_constraints_from_ty(substs.tps[i], variance_i);
}
match substs.regions {
ty::ErasedRegions => {}
ty::NonerasedRegions(ref rps) => {
for (i, p) in generics.region_param_defs.iter().enumerate() {
let variance_decl =
self.declared_variance(p.def_id, def_id, RegionParam, i);
let variance_i = self.xform(variance, variance_decl);
self.add_constraints_from_region(*rps.get(i), variance_i);
}
}
}
}
fn add_constraints_from_sig(&mut self,
sig: &ty::FnSig,
variance: VarianceTermPtr<'self>) {
let contra = self.contravariant(variance);
for &input in sig.inputs.iter() {
self.add_constraints_from_ty(input, contra);
}
self.add_constraints_from_ty(sig.output, variance);
}
fn add_constraints_from_region(&mut self,
region: ty::Region,
variance: VarianceTermPtr<'self>) {
match region {
ty::re_type_bound(param_id, _, _) => {
let index = self.inferred_index(param_id);
self.add_constraint(index, variance);
}
ty::re_static => { }
ty::re_fn_bound(*) => {
// We do not infer variance for region parameters on
// methods or in fn types.
}
ty::re_free(*) | ty::re_scope(*) | ty::re_infer(*) |
ty::re_empty => {
// We don't expect to see anything but 'static or bound
// regions when visiting member types or method types.
self.tcx().sess.bug(format!("Unexpected region encountered in \
variance inference: {}",
region.repr(self.tcx())));
}
}
}
fn add_constraints_from_mt(&mut self,
mt: &ty::mt,
variance: VarianceTermPtr<'self>) {
match mt.mutbl {
ast::MutMutable => {
let invar = self.invariant(variance);
self.add_constraints_from_ty(mt.ty, invar);
}
ast::MutImmutable => {
self.add_constraints_from_ty(mt.ty, variance);
}
}
}
}
/**************************************************************************
* Constraint solving
*
* The final phase iterates over the constraints, refining the variance
* for each inferred until a fixed point is reached. This will be the
* maximal solution to the constraints. The final variance for each
* inferred is then written into the `variance_map` in the tcx.
*/
struct SolveContext<'self> {
terms_cx: TermsContext<'self>,
constraints: ~[Constraint<'self>],
solutions: ~[ty::Variance]
}
fn solve_constraints(constraints_cx: ConstraintContext) {
let ConstraintContext { terms_cx, constraints, _ } = constraints_cx;
let solutions = vec::from_elem(terms_cx.num_inferred(), ty::Bivariant);
let mut solutions_cx = SolveContext {
terms_cx: terms_cx,
constraints: constraints,
solutions: solutions
};
solutions_cx.solve();
solutions_cx.write();
}
impl<'self> SolveContext<'self> {
fn solve(&mut self) {
// Propagate constraints until a fixed point is reached. Note
// that the maximum number of iterations is 2C where C is the
// number of constraints (each variable can change values at most
// twice). Since number of constraints is linear in size of the
// input, so is the inference process.
let mut changed = true;
while changed {
changed = false;
for constraint in self.constraints.iter() {
let Constraint { inferred, variance: term } = *constraint;
let variance = self.evaluate(term);
let old_value = self.solutions[*inferred];
let new_value = glb(variance, old_value);
if old_value != new_value {
debug!("Updating inferred {} (node {}) \
from {:?} to {:?} due to {}",
*inferred,
self.terms_cx.inferred_infos[*inferred].param_id,
old_value,
new_value,
term.to_str());
self.solutions[*inferred] = new_value;
changed = true;
}
}
}
}
fn write(&self) {
// Collect all the variances for a particular item and stick
// them into the variance map. We rely on the fact that we
// generate all the inferreds for a particular item
// consecutively.
let tcx = self.terms_cx.tcx;
let item_variance_map = tcx.item_variance_map;
let solutions = &self.solutions;
let inferred_infos = &self.terms_cx.inferred_infos;
let mut index = 0;
let num_inferred = self.terms_cx.num_inferred();
while index < num_inferred {
let item_id = inferred_infos[index].item_id;
let mut item_variances = ty::ItemVariances {
self_param: None,
type_params: opt_vec::Empty,
region_params: opt_vec::Empty
};
while (index < num_inferred &&
inferred_infos[index].item_id == item_id) {
let info = &inferred_infos[index];
match info.kind {
SelfParam => {
assert!(item_variances.self_param.is_none());
item_variances.self_param = Some(solutions[index]);
}
TypeParam => {
item_variances.type_params.push(solutions[index]);
}
RegionParam => {
item_variances.region_params.push(solutions[index]);
}
}
index += 1;
}
debug!("item_id={} item_variances={}",
item_id,
item_variances.repr(tcx));
let item_def_id = ast_util::local_def(item_id);
// For unit testing: check for a special "rustc_variance"
// attribute and report an error with various results if found.
if ty::has_attr(tcx, item_def_id, "rustc_variance") {
let found = item_variances.repr(tcx);
tcx.sess.span_err(ast_map::item_span(tcx.items, item_id), found);
}
let newly_added = item_variance_map.insert(item_def_id,
@item_variances);
assert!(newly_added);
}
}
fn evaluate(&self, term: VarianceTermPtr<'self>) -> ty::Variance {
match *term {
ConstantTerm(v) => {
v
}
TransformTerm(t1, t2) => {
let v1 = self.evaluate(t1);
let v2 = self.evaluate(t2);
v1.xform(v2)
}
InferredTerm(index) => {
self.solutions[*index]
}
}
}
}
/**************************************************************************
* Miscellany transformations on variance
*/
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,
}
}
}
fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance {
// Greatest lower bound of the variance lattice as
// defined in The Paper:
//
// *
// - +
// o
match (v1, v2) {
(ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant,
(ty::Covariant, ty::Contravariant) => ty::Invariant,
(ty::Contravariant, ty::Covariant) => ty::Invariant,
(ty::Covariant, ty::Covariant) => ty::Covariant,
(ty::Contravariant, ty::Contravariant) => ty::Contravariant,
(x, ty::Bivariant) | (ty::Bivariant, x) => x,
}
}

View File

@ -1,28 +0,0 @@
// Copyright 2012 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.
struct contravariant<'self> {
f: &'self int
}
fn to_same_lifetime<'r>(bi: contravariant<'r>) {
let bj: contravariant<'r> = bi;
}
fn to_shorter_lifetime<'r>(bi: contravariant<'r>) {
let bj: contravariant<'blk> = bi;
}
fn to_longer_lifetime<'r>(bi: contravariant<'r>) -> contravariant<'static> {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -1,33 +0,0 @@
// Copyright 2012 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.
// Contravariant with respect to a region:
//
// You can upcast to a *smaller region* but not a larger one. This is
// the normal case.
struct contravariant<'self> {
f: &'static fn() -> &'self int
}
fn to_same_lifetime<'r>(bi: contravariant<'r>) {
let bj: contravariant<'r> = bi;
}
fn to_shorter_lifetime<'r>(bi: contravariant<'r>) {
let bj: contravariant<'blk> = bi;
}
fn to_longer_lifetime<'r>(bi: contravariant<'r>) -> contravariant<'static> {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -1,33 +0,0 @@
// Copyright 2012 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.
// Covariant with respect to a region:
//
// You can upcast to a *larger region* but not a smaller one.
struct covariant<'self> {
f: &'static fn(x: &'self int) -> int
}
fn to_same_lifetime<'r>(bi: covariant<'r>) {
let bj: covariant<'r> = bi;
}
fn to_shorter_lifetime<'r>(bi: covariant<'r>) {
let bj: covariant<'blk> = bi; //~ ERROR mismatched types
//~^ ERROR cannot infer an appropriate lifetime
}
fn to_longer_lifetime<'r>(bi: covariant<'r>) -> covariant<'static> {
bi
}
fn main() {
}

View File

@ -1,32 +0,0 @@
// Copyright 2012 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.
// Invariance with respect to a region:
//
// You cannot convert between regions.
struct invariant<'self> {
f: &'self fn(x: &'self int) -> &'self int
}
fn to_same_lifetime<'r>(bi: invariant<'r>) {
let bj: invariant<'r> = bi;
}
fn to_shorter_lifetime<'r>(bi: invariant<'r>) {
let bj: invariant<'blk> = bi; //~ ERROR mismatched types
}
fn to_longer_lifetime<'r>(bi: invariant<'r>) -> invariant<'static> {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -1,26 +0,0 @@
// Copyright 2012 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.
#[feature(managed_boxes)];
struct invariant<'self> {
f: @mut &'self int
}
fn to_same_lifetime<'r>(bi: invariant<'r>) {
let bj: invariant<'r> = bi;
}
fn to_longer_lifetime<'r>(bi: invariant<'r>) -> invariant<'static> {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -1,26 +0,0 @@
// Copyright 2012 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.
#[feature(managed_boxes)];
struct invariant<'self> {
f: @mut [&'self int]
}
fn to_same_lifetime<'r>(bi: invariant<'r>) {
let bj: invariant<'r> = bi;
}
fn to_longer_lifetime<'r>(bi: invariant<'r>) -> invariant<'static> {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -1,30 +0,0 @@
// Copyright 2012 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.
#[feature(managed_boxes)];
struct invariant<'self> {
f: @mut &'self int
}
fn to_same_lifetime<'r>(bi: invariant<'r>) {
let bj: invariant<'r> = bi;
}
fn to_shorter_lifetime<'r>(bi: invariant<'r>) {
let bj: invariant<'blk> = bi; //~ ERROR mismatched types
}
fn to_longer_lifetime<'r>(bi: invariant<'r>) -> invariant<'static> {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -0,0 +1,38 @@
// Copyright 2012 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.
// Test that a type which is contravariant with respect to its region
// parameter yields an error when used in a covariant way.
//
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
// This is covariant with respect to 'a, meaning that
// Covariant<'foo> <: Covariant<'static> because
// 'foo <= 'static
struct Covariant<'a> {
f: extern "Rust" fn(&'a int)
}
fn use_<'a>(c: Covariant<'a>) {
let x = 3;
// 'b winds up being inferred to 'a because
// Covariant<'a> <: Covariant<'b> => 'a <= 'b
//
// Borrow checker then reports an error because `x` does not
// have the lifetime 'a.
collapse(&x, c); //~ ERROR borrowed value does not live long enough
fn collapse<'b>(x: &'b int, c: Covariant<'b>) { }
}
fn main() {}

View File

@ -0,0 +1,33 @@
// Copyright 2012 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.
// Test that a covariant region parameter used in a covariant position
// yields an error.
//
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
struct Invariant<'a> {
f: &'static mut &'a int
}
fn use_<'a>(c: Invariant<'a>) {
let x = 3;
// 'b winds up being inferred to 'a, because that is the
// only way that Invariant<'a> <: Invariant<'b>, and hence
// we get an error in the borrow checker because &x cannot
// live that long
collapse(&x, c); //~ ERROR borrowed value does not live long enough
fn collapse<'b>(x: &'b int, c: Invariant<'b>) { }
}
fn main() { }

View File

@ -0,0 +1,27 @@
// Copyright 2012 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.
// Test that a type which is invariant with respect to its region
// parameter used in a covariant way yields an error.
//
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
struct Invariant<'a> {
f: &'static mut &'a int
}
fn use_<'a>(c: Invariant<'a>) {
// For this assignment to be legal, Invariant<'a> <: Invariant<'static>,
// which (if Invariant were covariant) would require 'a <= 'static.
let _: Invariant<'static> = c; //~ ERROR mismatched types
}
fn main() { }

View File

@ -0,0 +1,73 @@
// Copyright 2012 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.
// Test that we correctly infer variance for region parameters in
// various self-contained types.
// Regions that just appear in normal spots are contravariant:
#[rustc_variance]
struct Test2<'a, 'b, 'c> { //~ ERROR region_params=[-, -, -]
x: &'a int,
y: &'b [int],
c: &'c str
}
// Those same annotations in function arguments become covariant:
#[rustc_variance]
struct Test3<'a, 'b, 'c> { //~ ERROR region_params=[+, +, +]
x: extern "Rust" fn(&'a int),
y: extern "Rust" fn(&'b [int]),
c: extern "Rust" fn(&'c str),
}
// Mutability induces invariance:
#[rustc_variance]
struct Test4<'a, 'b> { //~ ERROR region_params=[-, o]
x: &'a mut &'b int,
}
// Mutability induces invariance, even when in a
// contravariant context:
#[rustc_variance]
struct Test5<'a, 'b> { //~ ERROR region_params=[+, o]
x: extern "Rust" fn(&'a mut &'b int),
}
// Invariance is a trap from which NO ONE CAN ESCAPE.
// In other words, even though the `&'b int` occurs in
// a argument list (which is contravariant), that
// argument list occurs in an invariant context.
#[rustc_variance]
struct Test6<'a, 'b> { //~ ERROR region_params=[-, o]
x: &'a mut extern "Rust" fn(&'b int),
}
// No uses at all is bivariant:
#[rustc_variance]
struct Test7<'a> { //~ ERROR region_params=[*]
x: int
}
// Try enums too.
#[rustc_variance]
enum Test8<'a, 'b, 'c> { //~ ERROR region_params=[+, -, o]
Test8A(extern "Rust" fn(&'a int)),
Test8B(&'b [int]),
Test8C(&'b mut &'c str),
}
fn main() {}

View File

@ -0,0 +1,42 @@
// Copyright 2012 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.
// Test that we correctly infer variance for region parameters in
// case that involve multiple intracrate types.
// Try enums too.
#[rustc_variance]
enum Base<'a, 'b, 'c, 'd> { //~ ERROR region_params=[+, -, o, *]
Test8A(extern "Rust" fn(&'a int)),
Test8B(&'b [int]),
Test8C(&'b mut &'c str),
}
#[rustc_variance]
struct Derived1<'w, 'x, 'y, 'z> { //~ ERROR region_params=[*, o, -, +]
f: Base<'z, 'y, 'x, 'w>
}
#[rustc_variance] // Combine - and + to yield o
struct Derived2<'a, 'b, 'c> { //~ ERROR region_params=[o, o, *]
f: Base<'a, 'a, 'b, 'c>
}
#[rustc_variance] // Combine + and o to yield o (just pay attention to 'a here)
struct Derived3<'a, 'b, 'c> { //~ ERROR region_params=[o, -, *]
f: Base<'a, 'b, 'a, 'c>
}
#[rustc_variance] // Combine + and * to yield + (just pay attention to 'a here)
struct Derived4<'a, 'b, 'c> { //~ ERROR region_params=[+, -, o]
f: Base<'a, 'b, 'c, 'a>
}
fn main() {}

View File

@ -0,0 +1,32 @@
// Copyright 2012 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.
// Test that a type which is contravariant with respect to its region
// parameter compiles successfully when used in a contravariant way.
//
// Note: see compile-fail/variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
struct Contravariant<'a> {
f: &'a int
}
fn use_<'a>(c: Contravariant<'a>) {
let x = 3;
// 'b winds up being inferred to this call.
// Contravariant<'a> <: Contravariant<'call> is true
// if 'call <= 'a, which is true, so no error.
collapse(&x, c);
fn collapse<'b>(x: &'b int, c: Contravariant<'b>) { }
}
fn main() {}

View File

@ -0,0 +1,29 @@
// Copyright 2012 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.
// Test that a type which is covariant with respect to its region
// parameter is successful when used in a covariant way.
//
// Note: see compile-fail/variance-regions-*.rs for the tests that
// check that the variance inference works in the first place.
// This is covariant with respect to 'a, meaning that
// Covariant<'foo> <: Covariant<'static> because
// 'foo <= 'static
struct Covariant<'a> {
f: extern "Rust" fn(&'a int)
}
fn use_<'a>(c: Covariant<'a>) {
// OK Because Covariant<'a> <: Covariant<'static> iff 'a <= 'static
let _: Covariant<'static> = c;
}
fn main() {}