Infer variance of types with respect to the region parameter.

A similar approach could be used for type parameters.

Fixes #2282.
This commit is contained in:
Niko Matsakis 2012-08-09 09:59:50 -07:00
parent 8185ede1fa
commit 511e7626ae
33 changed files with 720 additions and 232 deletions

View File

@ -770,6 +770,7 @@ enum AncestorList = option<unsafe::Exclusive<AncestorNode>>;
fn access_group<U>(x: &TaskGroupArc, blk: fn(TaskGroupInner) -> U) -> U {
unsafe { x.with(blk) }
}
#[inline(always)]
fn access_ancestors<U>(x: &unsafe::Exclusive<AncestorNode>,
blk: fn(x: &mut AncestorNode) -> U) -> U {

View File

@ -299,7 +299,20 @@ fn node_id_to_str(map: map, id: node_id, itr: ident_interner) -> ~str {
fmt!{"unknown node (id=%d)", id}
}
some(node_item(item, path)) => {
fmt!{"item %s (id=%?)", path_ident_to_str(*path, item.ident, itr), id}
let path_str = path_ident_to_str(*path, item.ident, itr);
let item_str = match item.node {
item_const(*) => ~"const",
item_fn(*) => ~"fn",
item_mod(*) => ~"mod",
item_foreign_mod(*) => ~"foreign mod",
item_ty(*) => ~"ty",
item_enum(*) => ~"enum",
item_class(*) => ~"class",
item_trait(*) => ~"trait",
item_impl(*) => ~"impl",
item_mac(*) => ~"macro"
};
fmt!("%s %s (id=%?)", item_str, path_str, id)
}
some(node_foreign_item(item, abi, path)) => {
fmt!{"foreign item %s with abi %? (id=%?)",

View File

@ -127,7 +127,7 @@ fn get_type(tcx: ty::ctxt, def: ast::def_id) -> ty::ty_param_bounds_and_ty {
}
fn get_region_param(cstore: metadata::cstore::cstore,
def: ast::def_id) -> bool {
def: ast::def_id) -> option<ty::region_variance> {
let cdata = cstore::get_crate_data(cstore, def.crate);
return decoder::get_region_param(cdata, def.node);
}
@ -149,7 +149,9 @@ fn get_field_type(tcx: ty::ctxt, class_id: ast::def_id,
class_id, def} );
debug!{"got field data %?", the_field};
let ty = decoder::item_type(def, the_field, tcx, cdata);
return {bounds: @~[], rp: false, ty: ty};
return {bounds: @~[],
region_param: none,
ty: ty};
}
// Given a def_id for an impl or class, return the traits it implements,

View File

@ -237,11 +237,11 @@ fn item_ty_param_bounds(item: ebml::doc, tcx: ty::ctxt, cdata: cmd)
@bounds
}
fn item_ty_region_param(item: ebml::doc) -> bool {
match ebml::maybe_get_doc(item, tag_region_param) {
some(_) => true,
none => false
}
fn item_ty_region_param(item: ebml::doc) -> option<ty::region_variance> {
ebml::maybe_get_doc(item, tag_region_param).map(|doc| {
let d = ebml::ebml_deserializer(doc);
ty::deserialize_region_variance(d)
})
}
fn item_ty_param_count(item: ebml::doc) -> uint {
@ -340,10 +340,14 @@ fn get_type(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
item_ty_param_bounds(item, tcx, cdata)
} else { @~[] };
let rp = item_ty_region_param(item);
return {bounds: tp_bounds, rp: rp, ty: t};
return {bounds: tp_bounds,
region_param: rp,
ty: t};
}
fn get_region_param(cdata: cmd, id: ast::node_id) -> bool {
fn get_region_param(cdata: cmd, id: ast::node_id)
-> option<ty::region_variance> {
let item = lookup_item(id, cdata.data);
return item_ty_region_param(item);
}

View File

@ -81,8 +81,12 @@ fn encode_def_id(ebml_w: ebml::writer, id: def_id) {
fn encode_region_param(ecx: @encode_ctxt, ebml_w: ebml::writer,
it: @ast::item) {
let rp = ecx.tcx.region_paramd_items.contains_key(it.id);
if rp { do ebml_w.wr_tag(tag_region_param) { } }
let opt_rp = ecx.tcx.region_paramd_items.find(it.id);
for opt_rp.each |rp| {
do ebml_w.wr_tag(tag_region_param) {
ty::serialize_region_variance(ebml_w, rp);
}
}
}
fn encode_mutability(ebml_w: ebml::writer, mt: class_mutability) {

View File

@ -601,8 +601,10 @@ impl ebml::writer: ebml_writer_helpers {
self.emit_bounds(ecx, bs);
}
}
do self.emit_rec_field(~"rp", 1u) {
self.emit_bool(tpbt.rp);
do self.emit_rec_field(~"region_param", 1u) {
ty::serialize_opt_region_variance(
self,
tpbt.region_param);
}
do self.emit_rec_field(~"ty", 2u) {
self.emit_ty(ecx, tpbt.ty);
@ -817,8 +819,8 @@ impl ebml::ebml_deserializer: ebml_deserializer_decoder_helpers {
bounds: self.read_rec_field(~"bounds", 0u, || {
@self.read_to_vec(|| self.read_bounds(xcx) )
}),
rp: self.read_rec_field(~"rp", 1u, || {
self.read_bool()
region_param: self.read_rec_field(~"region_param", 1u, || {
ty::deserialize_opt_region_variance(self)
}),
ty: self.read_rec_field(~"ty", 2u, || {
self.read_ty(xcx)

View File

@ -16,6 +16,7 @@ import syntax::ast_util::new_def_hash;
import syntax::ast_map;
import dvec::{DVec, dvec};
import metadata::csearch;
import ty::{region_variance, rv_covariant, rv_invariant, rv_contravariant};
import std::list;
import std::list::list;
@ -365,8 +366,9 @@ fn resolve_crate(sess: session, def_map: resolve3::DefMap,
// a worklist. We can then process the worklist, propagating indirect
// dependencies until a fixed point is reached.
type region_paramd_items = hashmap<ast::node_id, ()>;
type dep_map = hashmap<ast::node_id, @DVec<ast::node_id>>;
type region_paramd_items = hashmap<ast::node_id, region_variance>;
type region_dep = {ambient_variance: region_variance, id: ast::node_id};
type dep_map = hashmap<ast::node_id, @DVec<region_dep>>;
type determine_rp_ctxt_ = {
sess: session,
@ -381,42 +383,98 @@ type determine_rp_ctxt_ = {
// true when we are within an item but not within a method.
// see long discussion on region_is_relevant()
mut anon_implies_rp: bool
mut anon_implies_rp: bool,
// encodes the context of the current type; invariant if
// mutable, covariant otherwise
mut ambient_variance: region_variance,
};
enum determine_rp_ctxt {
determine_rp_ctxt_(@determine_rp_ctxt_)
}
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`.
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 determine_rp_ctxt {
fn add_rp(id: ast::node_id) {
fn add_variance(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.
fn add_rp(id: ast::node_id, variance: region_variance) {
assert id != 0;
if self.region_paramd_items.insert(id, ()) {
debug!{"add region-parameterized item: %d (%s)", id,
ast_map::node_id_to_str(self.ast_map, id,
self.sess.parse_sess.interner)};
let old_variance = self.region_paramd_items.find(id);
let joined_variance = match old_variance {
none => variance,
some(v) => join_variance(v, variance)
};
debug!["add_rp() variance for %s: %? == %? ^ %?",
ast_map::node_id_to_str(self.ast_map, id,
self.sess.parse_sess.interner),
joined_variance, old_variance, variance];
if some(joined_variance) != old_variance {
self.region_paramd_items.insert(id, joined_variance);
self.worklist.push(id);
} else {
debug!{"item %d already region-parameterized", id};
}
}
fn add_dep(from: ast::node_id, to: ast::node_id) {
debug!{"add dependency from %d -> %d (%s -> %s)",
from, to,
/// 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.
fn add_dep(from: ast::node_id) {
debug!["add dependency from %d -> %d (%s -> %s) with variance %?",
from, self.item_id,
ast_map::node_id_to_str(self.ast_map, from,
self.sess.parse_sess.interner),
ast_map::node_id_to_str(self.ast_map, to,
self.sess.parse_sess.interner)};
ast_map::node_id_to_str(self.ast_map, self.item_id,
self.sess.parse_sess.interner),
copy self.ambient_variance];
let vec = match self.dep_map.find(from) {
some(vec) => {vec}
some(vec) => vec,
none => {
let vec = @dvec();
self.dep_map.insert(from, vec);
vec
}
};
if !vec.contains(to) { vec.push(to); }
let dep = {ambient_variance: self.ambient_variance, id: self.item_id};
if !vec.contains(dep) { vec.push(dep); }
}
// Determines whether a reference to a region that appears in the
@ -460,7 +518,9 @@ impl determine_rp_ctxt {
}
}
fn with(item_id: ast::node_id, anon_implies_rp: bool, f: fn()) {
fn with(item_id: ast::node_id,
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;
@ -471,6 +531,13 @@ impl determine_rp_ctxt {
self.item_id = old_item_id;
self.anon_implies_rp = old_anon_implies_rp;
}
fn with_ambient_variance(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(item: @ast::item,
@ -484,12 +551,17 @@ fn determine_rp_in_item(item: @ast::item,
fn determine_rp_in_fn(fk: visit::fn_kind,
decl: ast::fn_decl,
body: ast::blk,
sp: span,
id: ast::node_id,
_sp: span,
_id: ast::node_id,
&&cx: determine_rp_ctxt,
visitor: visit::vt<determine_rp_ctxt>) {
do cx.with(cx.item_id, false) {
visit::visit_fn(fk, decl, body, sp, id, cx, visitor);
do cx.with_ambient_variance(rv_contravariant) {
for decl.inputs.each |a| { visitor.visit_ty(a.ty, cx, visitor); }
}
visitor.visit_ty(decl.output, cx, visitor);
visitor.visit_ty_params(visit::tps_of_fn(fk), cx, visitor);
visitor.visit_block(body, cx, visitor);
}
}
@ -511,16 +583,18 @@ fn determine_rp_in_ty(ty: @ast::ty,
// impl etc. So we can ignore it and its components.
if cx.item_id == 0 { return; }
// if this type directly references a region, either via a
// region pointer like &r.ty or a region-parameterized path
// like path/r, add to the worklist/set
// 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)
match ty.node {
ast::ty_rptr(r, _) |
ast::ty_path(@{rp: some(r), _}, _) => {
debug!{"referenced type with regions %s",
pprust::ty_to_str(ty, cx.sess.intr())};
ast::ty_rptr(r, _) => {
debug!["referenced rptr type %s",
pprust::ty_to_str(ty, cx.sess.intr())];
if cx.region_is_relevant(r) {
cx.add_rp(cx.item_id);
cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant))
}
}
@ -528,7 +602,7 @@ fn determine_rp_in_ty(ty: @ast::ty,
ast::ty_fn(ast::proto_block, _, _) if cx.anon_implies_rp => {
debug!("referenced bare fn type with regions %s",
pprust::ty_to_str(ty, cx.sess.intr()));
cx.add_rp(cx.item_id);
cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant));
}
_ => {}
@ -543,13 +617,16 @@ fn determine_rp_in_ty(ty: @ast::ty,
match cx.def_map.get(id) {
ast::def_ty(did) | ast::def_class(did, _) => {
if did.crate == ast::local_crate {
cx.add_dep(did.node, cx.item_id);
cx.add_dep(did.node);
} else {
let cstore = cx.sess.cstore;
if csearch::get_region_param(cstore, did) {
debug!{"reference to external, rp'd type %s",
pprust::ty_to_str(ty, cx.sess.intr())};
cx.add_rp(cx.item_id);
match csearch::get_region_param(cstore, did) {
none => {}
some(variance) => {
debug!["reference to external, rp'd type %s",
pprust::ty_to_str(ty, cx.sess.intr())];
cx.add_rp(cx.item_id, cx.add_variance(variance))
}
}
}
}
@ -560,15 +637,73 @@ fn determine_rp_in_ty(ty: @ast::ty,
}
match ty.node {
ast::ty_fn(*) => {
do cx.with(cx.item_id, false) {
visit::visit_ty(ty, cx, visitor);
ast::ty_box(mt) | ast::ty_uniq(mt) | ast::ty_vec(mt) |
ast::ty_rptr(_, mt) | ast::ty_ptr(mt) => {
visit_mt(mt, cx, visitor);
}
ast::ty_rec(fields) => {
for fields.each |field| {
visit_mt(field.node.mt, cx, visitor);
}
}
ast::ty_path(path, _) => {
// type parameters are---for now, anyway---always invariant
do cx.with_ambient_variance(rv_invariant) {
for path.types.each |tp| {
visitor.visit_ty(tp, cx, visitor);
}
}
}
ast::ty_fn(_, bounds, 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 decl.inputs.each |a| {
visitor.visit_ty(a.ty, cx, visitor);
}
}
visit::visit_ty_param_bounds(bounds, cx, visitor);
visitor.visit_ty(decl.output, cx, visitor);
}
}
_ => {
visit::visit_ty(ty, cx, visitor);
}
}
fn visit_mt(mt: ast::mt, &&cx: determine_rp_ctxt,
visitor: visit::vt<determine_rp_ctxt>) {
// mutability is invariant
if mt.mutbl == ast::m_mutbl {
do cx.with_ambient_variance(rv_invariant) {
visitor.visit_ty(mt.ty, cx, visitor);
}
} else {
visitor.visit_ty(mt.ty, cx, visitor);
}
}
}
fn determine_rp_in_struct_field(cm: @ast::struct_field,
&&cx: determine_rp_ctxt,
visitor: visit::vt<determine_rp_ctxt>) {
match cm.node.kind {
ast::named_field(_, ast::class_mutable, _) => {
do cx.with_ambient_variance(rv_invariant) {
visit::visit_struct_field(cm, cx, visitor);
}
}
ast::named_field(_, ast::class_immutable, _) |
ast::unnamed_field => {
visit::visit_struct_field(cm, cx, visitor);
}
}
}
fn determine_rp_in_crate(sess: session,
@ -582,32 +717,56 @@ fn determine_rp_in_crate(sess: session,
dep_map: int_hash(),
worklist: dvec(),
mut item_id: 0,
mut anon_implies_rp: false});
mut anon_implies_rp: false,
mut ambient_variance: rv_covariant});
// gather up the base set, worklist and dep_map:
// Gather up the base set, worklist and dep_map
let visitor = visit::mk_vt(@{
visit_fn: determine_rp_in_fn,
visit_item: determine_rp_in_item,
visit_ty: determine_rp_in_ty,
visit_ty_method: determine_rp_in_ty_method,
visit_struct_field: determine_rp_in_struct_field,
with *visit::default_visitor()
});
visit::visit_crate(*crate, cx, visitor);
// propagate indirect dependencies
// 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.
while cx.worklist.len() != 0 {
let id = cx.worklist.pop();
debug!{"popped %d from worklist", id};
match cx.dep_map.find(id) {
let c_id = cx.worklist.pop();
let c_variance = cx.region_paramd_items.get(c_id);
debug!["popped %d from worklist", c_id];
match cx.dep_map.find(c_id) {
none => {}
some(vec) => {
for vec.each |to_id| {
cx.add_rp(to_id);
some(deps) => {
for deps.each |dep| {
let v = add_variance(dep.ambient_variance, c_variance);
cx.add_rp(dep.id, v);
}
}
}
}
debug!("%s", {
debug!("Region variance results:");
for cx.region_paramd_items.each |key, value| {
debug!("item %? (%s) is parameterized with variance %?",
key,
ast_map::node_id_to_str(ast_map, key,
sess.parse_sess.interner),
value);
}
"----"
});
// return final set
return cx.region_paramd_items;
}

View File

@ -2348,7 +2348,7 @@ fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id)
}
csearch::found(ast::ii_method(impl_did, mth)) => {
ccx.external.insert(fn_id, some(mth.id));
let {bounds: impl_bnds, rp: _, ty: impl_ty} =
let {bounds: impl_bnds, region_param: _, ty: impl_ty} =
ty::lookup_item_type(ccx.tcx, impl_did);
if (*impl_bnds).len() + mth.tps.len() == 0u {
let llfn = get_item_val(ccx, mth.id);

View File

@ -17,6 +17,8 @@ import middle::lint::{get_lint_level, allow};
import syntax::ast::*;
import syntax::print::pprust::*;
import util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str};
import std::serialization::{serialize_option,
deserialize_option};
export tv_vid, tvi_vid, region_vid, vid;
export br_hashmap;
@ -181,6 +183,10 @@ export ast_proto_to_proto;
export is_blockish;
export method_call_bounds;
export hash_region;
export region_variance, rv_covariant, rv_invariant, rv_contravariant;
export serialize_region_variance, deserialize_region_variance;
export opt_region_variance;
export serialize_opt_region_variance, deserialize_opt_region_variance;
// Data types
@ -226,6 +232,12 @@ enum ast_ty_to_ty_cache_entry {
atttce_resolved(t) /* resolved to a type, irrespective of region */
}
#[auto_serialize]
type opt_region_variance = option<region_variance>;
#[auto_serialize]
enum region_variance { rv_covariant, rv_invariant, rv_contravariant }
// N.B.: Borrows from inlined content are not accurately deserialized. This
// is because we don't need the details in trans, we only care if there is an
// entry in the table or not.
@ -565,7 +577,7 @@ fn param_bounds_to_kind(bounds: param_bounds) -> kind {
/// - `ty`: the base type. May have reference to the (unsubstituted) bound
/// region `&self` or to (unsubstituted) ty_param types
type ty_param_bounds_and_ty = {bounds: @~[param_bounds],
rp: bool,
region_param: option<region_variance>,
ty: t};
type type_cache = hashmap<ast::def_id, ty_param_bounds_and_ty>;

View File

@ -200,7 +200,7 @@ fn lookup_def_ccx(ccx: @crate_ctxt, sp: span, id: ast::node_id) -> ast::def {
}
fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
{bounds: @~[], rp: false, ty: t}
{bounds: @~[], region_param: none, ty: t}
}
fn require_same_types(

View File

@ -84,20 +84,20 @@ fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy owned>(
path: @ast::path) -> ty_param_substs_and_ty {
let tcx = self.tcx();
let {bounds: decl_bounds, rp: decl_rp, ty: decl_ty} =
let {bounds: decl_bounds, region_param: decl_rp, ty: decl_ty} =
self.get_item_ty(did);
debug!{"ast_path_to_substs_and_ty: did=%? decl_rp=%b",
did, decl_rp};
debug!["ast_path_to_substs_and_ty: did=%? decl_rp=%?",
did, decl_rp];
// If the type is parameterized by the self region, then replace self
// region with the current anon region binding (in other words,
// whatever & would get replaced with).
let self_r = match (decl_rp, path.rp) {
(false, none) => {
(none, none) => {
none
}
(false, some(_)) => {
(none, some(_)) => {
tcx.sess.span_err(
path.span,
fmt!{"no region bound is allowed on `%s`, \
@ -105,12 +105,12 @@ fn ast_path_to_substs_and_ty<AC: ast_conv, RS: region_scope copy owned>(
ty::item_path_str(tcx, did)});
none
}
(true, none) => {
(some(_), none) => {
let res = rscope.anon_region(path.span);
let r = get_region_reporting_err(self.tcx(), path.span, res);
some(r)
}
(true, some(r)) => {
(some(_), some(r)) => {
some(ast_region_to_region(self, rscope, path.span, r))
}
};

View File

@ -71,7 +71,8 @@ import astconv::{ast_region_to_region};
import middle::ty::{tv_vid, vid};
import regionmanip::{replace_bound_regions_in_fn_ty};
import rscope::{anon_rscope, binding_rscope, empty_rscope, in_anon_rscope};
import rscope::{in_binding_rscope, region_scope, type_rscope};
import rscope::{in_binding_rscope, region_scope, type_rscope,
bound_self_region};
import syntax::ast::ty_i;
import typeck::infer::{resolve_type, force_tvar};
@ -84,42 +85,39 @@ type self_info = {
explicit_self: ast::self_ty
};
type fn_ctxt_ =
struct fn_ctxt {
// var_bindings, locals and next_var_id are shared
// with any nested functions that capture the environment
// (and with any functions whose environment is being captured).
{self_impl_def_id: option<ast::def_id>,
ret_ty: ty::t,
// Used by loop bodies that return from the outer function
indirect_ret_ty: option<ty::t>,
purity: ast::purity,
infcx: infer::infer_ctxt,
locals: hashmap<ast::node_id, tv_vid>,
self_impl_def_id: option<ast::def_id>;
ret_ty: ty::t;
// Used by loop bodies that return from the outer function
indirect_ret_ty: option<ty::t>;
purity: ast::purity;
infcx: infer::infer_ctxt;
locals: hashmap<ast::node_id, tv_vid>;
// Sometimes we generate region pointers where the precise region
// to use is not known. For example, an expression like `&x.f`
// where `x` is of type `@T`: in this case, we will be rooting
// `x` onto the stack frame, and we could choose to root it until
// the end of (almost) any enclosing block or expression. We
// want to pick the narrowest block that encompasses all uses.
//
// What we do in such cases is to generate a region variable with
// `region_lb` as a lower bound. The regionck pass then adds
// other constriants based on how the variable is used and region
// inference selects the ultimate value. Finally, borrowck is
// charged with guaranteeing that the value whose address was taken
// can actually be made to live as long as it needs to live.
mut region_lb: ast::node_id,
// Sometimes we generate region pointers where the precise region
// to use is not known. For example, an expression like `&x.f`
// where `x` is of type `@T`: in this case, we will be rooting
// `x` onto the stack frame, and we could choose to root it until
// the end of (almost) any enclosing block or expression. We
// want to pick the narrowest block that encompasses all uses.
//
// What we do in such cases is to generate a region variable with
// `region_lb` as a lower bound. The regionck pass then adds
// other constriants based on how the variable is used and region
// inference selects the ultimate value. Finally, borrowck is
// charged with guaranteeing that the value whose address was taken
// can actually be made to live as long as it needs to live.
mut region_lb: ast::node_id;
in_scope_regions: isr_alist,
in_scope_regions: isr_alist;
node_types: hashmap<ast::node_id, ty::t>,
node_type_substs: hashmap<ast::node_id, ty::substs>,
node_types: hashmap<ast::node_id, ty::t>;
node_type_substs: hashmap<ast::node_id, ty::substs>;
ccx: @crate_ctxt};
enum fn_ctxt {
fn_ctxt_(fn_ctxt_)
ccx: @crate_ctxt;
}
// Used by check_const and check_enum_variants
@ -127,17 +125,19 @@ fn blank_fn_ctxt(ccx: @crate_ctxt, rty: ty::t,
region_bnd: ast::node_id) -> @fn_ctxt {
// It's kind of a kludge to manufacture a fake function context
// and statement context, but we might as well do write the code only once
@fn_ctxt_({self_impl_def_id: none,
ret_ty: rty,
indirect_ret_ty: none,
purity: ast::pure_fn,
infcx: infer::new_infer_ctxt(ccx.tcx),
locals: int_hash(),
mut region_lb: region_bnd,
in_scope_regions: @nil,
node_types: map::int_hash(),
node_type_substs: map::int_hash(),
ccx: ccx})
@fn_ctxt {
self_impl_def_id: none,
ret_ty: rty,
indirect_ret_ty: none,
purity: ast::pure_fn,
infcx: infer::new_infer_ctxt(ccx.tcx),
locals: int_hash(),
mut region_lb: region_bnd,
in_scope_regions: @nil,
node_types: map::int_hash(),
node_type_substs: map::int_hash(),
ccx: ccx
}
}
// a list of mapping from in-scope-region-names ("isr") to the
@ -245,17 +245,19 @@ fn check_fn(ccx: @crate_ctxt,
}
} else { none };
@fn_ctxt_({self_impl_def_id: self_info.map(|info| info.def_id),
ret_ty: ret_ty,
indirect_ret_ty: indirect_ret_ty,
purity: purity,
infcx: infcx,
locals: locals,
mut region_lb: body.node.id,
in_scope_regions: isr,
node_types: node_types,
node_type_substs: node_type_substs,
ccx: ccx})
@fn_ctxt {
self_impl_def_id: self_info.map(|info| info.def_id),
ret_ty: ret_ty,
indirect_ret_ty: indirect_ret_ty,
purity: purity,
infcx: infcx,
locals: locals,
mut region_lb: body.node.id,
in_scope_regions: isr,
node_types: node_types,
node_type_substs: node_type_substs,
ccx: ccx
}
};
// Update the self_info to contain an accurate self type (taking
@ -478,9 +480,9 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
check_bare_fn(ccx, decl, body, it.id, none);
}
ast::item_impl(tps, _, ty, ms) => {
let rp = ccx.tcx.region_paramd_items.contains_key(it.id);
debug!{"item_impl %s with id %d rp %b",
ccx.tcx.sess.str_of(it.ident), it.id, rp};
let rp = ccx.tcx.region_paramd_items.find(it.id);
debug!("item_impl %s with id %d rp %?",
ccx.tcx.sess.str_of(it.ident), it.id, rp);
let self_ty = ccx.to_ty(rscope::type_rscope(rp), ty);
for ms.each |m| {
check_method(ccx, m, self_ty, local_def(it.id));
@ -701,6 +703,15 @@ impl @fn_ctxt {
self.region_lb = old_region_lb;
return v;
}
fn region_var_if_parameterized(rp: option<ty::region_variance>,
span: span)
-> option<ty::region> {
match rp {
some(_) => some(self.infcx.next_region_var_nb(span)),
none => none
}
}
}
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
@ -789,14 +800,14 @@ fn impl_self_ty(fcx: @fn_ctxt,
require_rp: bool) -> ty_param_substs_and_ty {
let tcx = fcx.ccx.tcx;
let {n_tps, rp, raw_ty} = if did.crate == ast::local_crate {
let rp = fcx.tcx().region_paramd_items.contains_key(did.node);
let {n_tps, region_param, raw_ty} = if did.crate == ast::local_crate {
let region_param = fcx.tcx().region_paramd_items.find(did.node);
match check tcx.items.find(did.node) {
some(ast_map::node_item(@{node: ast::item_impl(ts, _, st, _),
_}, _)) => {
{n_tps: ts.len(),
rp: rp,
raw_ty: fcx.ccx.to_ty(rscope::type_rscope(rp), st)}
region_param: region_param,
raw_ty: fcx.ccx.to_ty(rscope::type_rscope(region_param), st)}
}
some(ast_map::node_item(@{node: ast::item_class(_, ts),
id: class_id, _},_)) => {
@ -805,10 +816,9 @@ fn impl_self_ty(fcx: @fn_ctxt,
we substitute in fresh vars for them)
*/
{n_tps: ts.len(),
rp: rp,
region_param: region_param,
raw_ty: ty::mk_class(tcx, local_def(class_id),
{self_r: if rp {some(ty::re_bound(ty::br_self))}
else {none},
{self_r: rscope::bound_self_region(region_param),
self_ty: none,
tps: ty::ty_params_to_tys(tcx, ts)})}
}
@ -818,13 +828,15 @@ fn impl_self_ty(fcx: @fn_ctxt,
} else {
let ity = ty::lookup_item_type(tcx, did);
{n_tps: vec::len(*ity.bounds),
rp: ity.rp,
region_param: ity.region_param,
raw_ty: ity.ty}
};
let rp = rp || require_rp;
let self_r = if rp {some(fcx.infcx.next_region_var(expr.span, expr.id))}
else {none};
let self_r = if region_param.is_some() || require_rp {
some(fcx.infcx.next_region_var(expr.span, expr.id))
} else {
none
};
let tps = fcx.infcx.next_ty_vars(n_tps);
let substs = {self_r: self_r, self_ty: none, tps: tps};
@ -1832,7 +1844,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let type_parameter_count, region_parameterized, raw_type;
if class_id.crate == ast::local_crate {
region_parameterized =
tcx.region_paramd_items.contains_key(class_id.node);
tcx.region_paramd_items.find(class_id.node);
match tcx.items.find(class_id.node) {
some(ast_map::node_item(@{
node: ast::item_class(_, type_parameters),
@ -1841,12 +1853,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
type_parameter_count = type_parameters.len();
let self_region;
if region_parameterized {
self_region = some(ty::re_bound(ty::br_self));
} else {
self_region = none;
}
let self_region =
bound_self_region(region_parameterized);
raw_type = ty::mk_class(tcx, class_id, {
self_r: self_region,
@ -1862,18 +1870,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
} else {
let item_type = ty::lookup_item_type(tcx, class_id);
type_parameter_count = (*item_type.bounds).len();
region_parameterized = item_type.rp;
region_parameterized = item_type.region_param;
raw_type = item_type.ty;
}
// Generate the struct type.
let self_region;
if region_parameterized {
self_region = some(fcx.infcx.next_region_var(expr.span, expr.id));
} else {
self_region = none;
}
let self_region =
fcx.region_var_if_parameterized(region_parameterized,
expr.span);
let type_parameters = fcx.infcx.next_ty_vars(type_parameter_count);
let substitutions = {
self_r: self_region,
@ -2082,8 +2086,8 @@ fn check_block_no_value(fcx: @fn_ctxt, blk: ast::blk) -> bool {
fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool {
let fcx = match blk.node.rules {
ast::unchecked_blk => @fn_ctxt_({purity: ast::impure_fn with **fcx0}),
ast::unsafe_blk => @fn_ctxt_({purity: ast::unsafe_fn with **fcx0}),
ast::unchecked_blk => @fn_ctxt {purity: ast::impure_fn with *fcx0},
ast::unsafe_blk => @fn_ctxt {purity: ast::unsafe_fn with *fcx0},
ast::default_blk => fcx0
};
do fcx.with_region_lb(blk.node.id) {
@ -2294,7 +2298,7 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
// extern functions are just u8 pointers
return {
bounds: @~[],
rp: false,
region_param: none,
ty: ty::mk_ptr(
fcx.ccx.tcx,
{
@ -2358,19 +2362,21 @@ fn instantiate_path(fcx: @fn_ctxt,
// determine the region bound, using the value given by the user
// (if any) and otherwise using a fresh region variable
let self_r = match pth.rp {
some(r) if !tpt.rp => {
fcx.ccx.tcx.sess.span_err
(span, ~"this item is not region-parameterized");
none
}
some(r) => {
some(ast_region_to_region(fcx, fcx, span, r))
}
none if tpt.rp => {
some(fcx.infcx.next_region_var_with_lb(span, region_lb))
match tpt.region_param {
none => {
fcx.ccx.tcx.sess.span_err
(span, ~"this item is not region-parameterized");
none
}
some(_) => {
some(ast_region_to_region(fcx, fcx, span, r))
}
}
}
none => {
none
fcx.region_var_if_parameterized(
tpt.region_param, span)
}
};

View File

@ -402,11 +402,9 @@ struct CoherenceChecker {
fn universally_quantify_polytype(polytype: ty_param_bounds_and_ty) -> t {
// NDM--this span is bogus.
let self_region = if !polytype.rp {
none
} else {
some(self.inference_context.next_region_var_nb(dummy_sp()))
};
let self_region =
polytype.region_param.map(
|_r| self.inference_context.next_region_var_nb(dummy_sp()));
let bounds_count = polytype.bounds.len();
let type_parameters =

View File

@ -112,7 +112,7 @@ fn get_enum_variant_types(ccx: @crate_ctxt,
enum_ty: ty::t,
variants: ~[ast::variant],
ty_params: ~[ast::ty_param],
rp: bool) {
rp: option<ty::region_variance>) {
let tcx = ccx.tcx;
// Create a set of parameter types shared among all the variants.
@ -150,7 +150,7 @@ fn get_enum_variant_types(ccx: @crate_ctxt,
none => {}
some(result_ty) => {
let tpt = {bounds: ty_param_bounds(ccx, ty_params),
rp: rp,
region_param: rp,
ty: result_ty};
tcx.tcache.insert(local_def(variant.node.id), tpt);
write_ty_to_tcx(tcx, variant.node.id, result_ty);
@ -167,7 +167,8 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id, trait_ty: ty::t) {
fn make_static_method_ty(ccx: @crate_ctxt,
am: ast::ty_method,
rp: bool, m: ty::method,
rp: option<ty::region_variance>,
m: ty::method,
// Take this as an argument b/c we may check
// the impl before the trait.
trait_ty: ty::t,
@ -197,20 +198,22 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id, trait_ty: ty::t) {
let bounds = @(*trait_bounds + ~[@~[ty::bound_trait(trait_ty)]]
+ *m.tps);
ccx.tcx.tcache.insert(local_def(am.id),
{bounds: bounds, rp: rp, ty: ty});
{bounds: bounds,
region_param: rp,
ty: ty});
}
let tcx = ccx.tcx;
let rp = tcx.region_paramd_items.contains_key(id);
let region_paramd = tcx.region_paramd_items.find(id);
match tcx.items.get(id) {
ast_map::node_item(@{node: ast::item_trait(params, _, ms), _}, _) => {
store_methods::<ast::trait_method>(ccx, id, ms, |m| {
let trait_bounds = ty_param_bounds(ccx, params);
let ty_m = trait_method_to_ty_method(m);
let method_ty = ty_of_ty_method(ccx, ty_m, rp);
let method_ty = ty_of_ty_method(ccx, ty_m, region_paramd);
if ty_m.self_ty.node == ast::sty_static {
make_static_method_ty(ccx, ty_m, rp,
make_static_method_ty(ccx, ty_m, region_paramd,
method_ty, trait_ty, trait_bounds);
}
method_ty
@ -220,7 +223,7 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id, trait_ty: ty::t) {
// All methods need to be stored, since lookup_method
// relies on the same method cache for self-calls
store_methods::<@ast::method>(ccx, id, struct_def.methods, |m| {
ty_of_method(ccx, m, rp)
ty_of_method(ccx, m, region_paramd)
});
}
_ => { /* Ignore things that aren't traits or classes */ }
@ -316,7 +319,7 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span,
fn check_methods_against_trait(ccx: @crate_ctxt,
tps: ~[ast::ty_param],
rp: bool,
rp: option<ty::region_variance>,
selfty: ty::t,
a_trait_ty: @ast::trait_ref,
impl_ms: ~[converted_method]) {
@ -370,21 +373,23 @@ fn check_methods_against_trait(ccx: @crate_ctxt,
} // fn
fn convert_field(ccx: @crate_ctxt,
rp: bool,
rp: option<ty::region_variance>,
bounds: @~[ty::param_bounds],
v: @ast::struct_field) {
let tt = ccx.to_ty(type_rscope(rp), v.node.ty);
write_ty_to_tcx(ccx.tcx, v.node.id, tt);
/* add the field to the tcache */
ccx.tcx.tcache.insert(local_def(v.node.id),
{bounds: bounds, rp: rp, ty: tt});
{bounds: bounds,
region_param: rp,
ty: tt});
}
type converted_method = {mty: ty::method, id: ast::node_id, span: span};
fn convert_methods(ccx: @crate_ctxt,
ms: ~[@ast::method],
rp: bool,
rp: option<ty::region_variance>,
rcvr_bounds: @~[ty::param_bounds]) -> ~[converted_method] {
let tcx = ccx.tcx;
@ -397,7 +402,9 @@ fn convert_methods(ccx: @crate_ctxt,
// n.b.: the type of a method is parameterized by both
// the tps on the receiver and those on the method itself
{bounds: @(vec::append(*rcvr_bounds, *bounds)), rp: rp, ty: fty});
{bounds: @(vec::append(*rcvr_bounds, *bounds)),
region_param: rp,
ty: fty});
write_ty_to_tcx(tcx, m.id, fty);
{mty: mty, id: m.id, span: m.span}
}
@ -405,9 +412,9 @@ fn convert_methods(ccx: @crate_ctxt,
fn convert(ccx: @crate_ctxt, it: @ast::item) {
let tcx = ccx.tcx;
let rp = tcx.region_paramd_items.contains_key(it.id);
debug!{"convert: item %s with id %d rp %b", tcx.sess.str_of(it.ident),
it.id, rp};
let rp = tcx.region_paramd_items.find(it.id);
#debug["convert: item %s with id %d rp %?",
tcx.sess.str_of(it.ident), it.id, rp];
match it.node {
// These don't define types.
ast::item_foreign_mod(_) | ast::item_mod(_) => {}
@ -423,7 +430,7 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) {
write_ty_to_tcx(tcx, it.id, selfty);
tcx.tcache.insert(local_def(it.id),
{bounds: i_bounds,
rp: rp,
region_param: rp,
ty: selfty});
let cms = convert_methods(ccx, ms, rp, i_bounds);
@ -465,8 +472,11 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) {
}
}
fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def,
tps: ~[ast::ty_param], tpt: ty::ty_param_bounds_and_ty,
fn convert_struct(ccx: @crate_ctxt,
rp: option<ty::region_variance>,
struct_def: @ast::struct_def,
tps: ~[ast::ty_param],
tpt: ty::ty_param_bounds_and_ty,
id: ast::node_id) {
let tcx = ccx.tcx;
do option::iter(struct_def.ctor) |ctor| {
@ -475,7 +485,7 @@ fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def,
|a| ty_of_arg(ccx, type_rscope(rp), a, none) );
let t_res = ty::mk_class(
tcx, local_def(id),
{self_r: if rp {some(ty::re_bound(ty::br_self))} else {none},
{self_r: rscope::bound_self_region(rp),
self_ty: none,
tps: ty::ty_params_to_tys(tcx, tps)});
let t_ctor = ty::mk_fn(
@ -488,7 +498,7 @@ fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def,
write_ty_to_tcx(tcx, ctor.node.id, t_ctor);
tcx.tcache.insert(local_def(ctor.node.id),
{bounds: tpt.bounds,
rp: rp,
region_param: rp,
ty: t_ctor});
}
@ -501,7 +511,7 @@ fn convert_struct(ccx: @crate_ctxt, rp: bool, struct_def: @ast::struct_def,
write_ty_to_tcx(tcx, dtor.node.id, t_dtor);
tcx.tcache.insert(local_def(dtor.node.id),
{bounds: tpt.bounds,
rp: rp,
region_param: rp,
ty: t_dtor});
};
ensure_trait_methods(ccx, id, tpt.ty);
@ -536,8 +546,7 @@ fn convert_foreign(ccx: @crate_ctxt, i: @ast::foreign_item) {
fn ty_of_method(ccx: @crate_ctxt,
m: @ast::method,
rp: bool) -> ty::method {
// XXX: Are the bounds correct here?
rp: option<ty::region_variance>) -> ty::method {
{ident: m.ident,
tps: ty_param_bounds(ccx, m.tps),
fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, @~[],
@ -549,7 +558,7 @@ fn ty_of_method(ccx: @crate_ctxt,
fn ty_of_ty_method(self: @crate_ctxt,
m: ast::ty_method,
rp: bool) -> ty::method {
rp: option<ty::region_variance>) -> ty::method {
{ident: m.ident,
tps: ty_param_bounds(self, m.tps),
fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, @~[], m.decl,
@ -564,7 +573,8 @@ fn ty_of_ty_method(self: @crate_ctxt,
it's bound to a valid trait type. Returns the def_id for the defining
trait. Fails if the type is a type other than an trait type.
*/
fn instantiate_trait_ref(ccx: @crate_ctxt, t: @ast::trait_ref, rp: bool)
fn instantiate_trait_ref(ccx: @crate_ctxt, t: @ast::trait_ref,
rp: option<ty::region_variance>)
-> (ast::def_id, ty_param_substs_and_ty) {
let sp = t.path.span, err = ~"can only implement trait types",
@ -596,7 +606,7 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
some(tpt) => return tpt,
_ => {}
}
let rp = tcx.region_paramd_items.contains_key(it.id);
let rp = tcx.region_paramd_items.find(it.id);
match it.node {
ast::item_const(t, _) => {
let typ = ccx.to_ty(empty_rscope, t);
@ -609,7 +619,7 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare, @~[],
decl, none, it.span);
let tpt = {bounds: bounds,
rp: false, // functions do not have a self
region_param: none,
ty: ty::mk_fn(ccx.tcx, tofd)};
debug!{"type of %s (id %d) is %s",
tcx.sess.str_of(it.ident), it.id, ty_to_str(tcx, tpt.ty)};
@ -622,7 +632,7 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
none => { }
}
let rp = tcx.region_paramd_items.contains_key(it.id);
let rp = tcx.region_paramd_items.find(it.id);
let tpt = {
let ty = {
let t0 = ccx.to_ty(type_rscope(rp), t);
@ -634,7 +644,9 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
ty::mk_with_id(tcx, t0, def_id)
}
};
{bounds: ty_param_bounds(ccx, tps), rp: rp, ty: ty}
{bounds: ty_param_bounds(ccx, tps),
region_param: rp,
ty: ty}
};
tcx.tcache.insert(local_def(it.id), tpt);
@ -644,21 +656,27 @@ fn ty_of_item(ccx: @crate_ctxt, it: @ast::item)
// Create a new generic polytype.
let {bounds, substs} = mk_substs(ccx, tps, rp);
let t = ty::mk_enum(tcx, local_def(it.id), substs);
let tpt = {bounds: bounds, rp: rp, ty: t};
let tpt = {bounds: bounds,
region_param: rp,
ty: t};
tcx.tcache.insert(local_def(it.id), tpt);
return tpt;
}
ast::item_trait(tps, _, ms) => {
let {bounds, substs} = mk_substs(ccx, tps, rp);
let t = ty::mk_trait(tcx, local_def(it.id), substs, ty::vstore_box);
let tpt = {bounds: bounds, rp: rp, ty: t};
let tpt = {bounds: bounds,
region_param: rp,
ty: t};
tcx.tcache.insert(local_def(it.id), tpt);
return tpt;
}
ast::item_class(_, tps) => {
let {bounds,substs} = mk_substs(ccx, tps, rp);
let t = ty::mk_class(tcx, local_def(it.id), substs);
let tpt = {bounds: bounds, rp: rp, ty: t};
let tpt = {bounds: bounds,
region_param: rp,
ty: t};
tcx.tcache.insert(local_def(it.id), tpt);
return tpt;
}
@ -736,7 +754,7 @@ fn ty_of_foreign_fn_decl(ccx: @crate_ctxt,
inputs: input_tys,
output: output_ty,
ret_style: ast::return_val});
let tpt = {bounds: bounds, rp: false, ty: t_fn};
let tpt = {bounds: bounds, region_param: none, ty: t_fn};
ccx.tcx.tcache.insert(def_id, tpt);
return tpt;
}
@ -754,10 +772,11 @@ fn mk_ty_params(ccx: @crate_ctxt, atps: ~[ast::ty_param])
})}
}
fn mk_substs(ccx: @crate_ctxt, atps: ~[ast::ty_param], rp: bool)
fn mk_substs(ccx: @crate_ctxt, atps: ~[ast::ty_param],
rp: option<ty::region_variance>)
-> {bounds: @~[ty::param_bounds], substs: ty::substs} {
let {bounds, params} = mk_ty_params(ccx, atps);
let self_r = if rp {some(ty::re_bound(ty::br_self))} else {none};
let self_r = rscope::bound_self_region(rp);
{bounds: bounds, substs: {self_r: self_r, self_ty: none, tps: params}}
}

View File

@ -58,7 +58,8 @@ trait combine {
fn tys(a: ty::t, b: ty::t) -> cres<ty::t>;
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]>;
fn self_tys(a: option<ty::t>, b: option<ty::t>) -> cres<option<ty::t>>;
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs>;
fn substs(did: ast::def_id, as: &ty::substs,
bs: &ty::substs) -> cres<ty::substs>;
fn fns(a: &ty::fn_ty, b: &ty::fn_ty) -> cres<ty::fn_ty>;
fn flds(a: ty::field, b: ty::field) -> cres<ty::field>;
fn modes(a: ast::mode, b: ast::mode) -> cres<ast::mode>;
@ -148,12 +149,57 @@ fn eq_opt_regions<C:combine>(
}
fn super_substs<C:combine>(
self: &C, a: &ty::substs, b: &ty::substs) -> cres<ty::substs> {
self: &C, did: ast::def_id,
a: &ty::substs, b: &ty::substs) -> cres<ty::substs> {
fn relate_region_param<C:combine>(
self: &C,
did: ast::def_id,
a: option<ty::region>,
b: option<ty::region>)
-> cres<option<ty::region>>
{
let polyty = ty::lookup_item_type(self.infcx().tcx, did);
match (polyty.region_param, a, b) {
(none, none, none) => {
ok(none)
}
(some(ty::rv_invariant), some(a), some(b)) => {
do eq_regions(self, a, b).then {
ok(some(a))
}
}
(some(ty::rv_covariant), some(a), some(b)) => {
do self.regions(a, b).chain |r| {
ok(some(r))
}
}
(some(ty::rv_contravariant), some(a), some(b)) => {
do self.contraregions(a, b).chain |r| {
ok(some(r))
}
}
(_, _, _) => {
// If these two substitutions are for the same type (and
// they should be), then the type should either
// consistently have a region parameter or not have a
// region parameter, and that should match with the
// polytype.
self.infcx().tcx.sess.bug(
fmt!("substitution a had opt_region %s and \
b had opt_region %s with variance %?",
a.to_str(self.infcx()),
b.to_str(self.infcx()),
polyty.region_param));
}
}
}
do self.tps(a.tps, b.tps).chain |tps| {
do self.self_tys(a.self_ty, b.self_ty).chain |self_ty| {
do eq_opt_regions(self, a.self_r, b.self_r).chain
|self_r| {
do relate_region_param(self, did,
a.self_r, b.self_r).chain |self_r|
{
ok({self_r: self_r, self_ty: self_ty, tps: tps})
}
}
@ -348,7 +394,7 @@ fn super_tys<C:combine>(
(ty::ty_enum(a_id, ref a_substs),
ty::ty_enum(b_id, ref b_substs))
if a_id == b_id => {
do self.substs(a_substs, b_substs).chain |substs| {
do self.substs(a_id, a_substs, b_substs).chain |substs| {
ok(ty::mk_enum(tcx, a_id, substs))
}
}
@ -356,7 +402,7 @@ fn super_tys<C:combine>(
(ty::ty_trait(a_id, ref a_substs, a_vstore),
ty::ty_trait(b_id, ref b_substs, b_vstore))
if a_id == b_id => {
do self.substs(a_substs, b_substs).chain |substs| {
do self.substs(a_id, a_substs, b_substs).chain |substs| {
do self.vstores(ty::terr_trait, a_vstore, b_vstore).chain |vs| {
ok(ty::mk_trait(tcx, a_id, substs, vs))
}
@ -365,7 +411,7 @@ fn super_tys<C:combine>(
(ty::ty_class(a_id, ref a_substs), ty::ty_class(b_id, ref b_substs))
if a_id == b_id => {
do self.substs(a_substs, b_substs).chain |substs| {
do self.substs(a_id, a_substs, b_substs).chain |substs| {
ok(ty::mk_class(tcx, a_id, substs))
}
}

View File

@ -151,8 +151,10 @@ impl Glb: combine {
super_fns(&self, a, b)
}
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, as, bs)
fn substs(did: ast::def_id,
as: &ty::substs,
bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, did, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {

View File

@ -130,8 +130,10 @@ impl Lub: combine {
super_fns(&self, a, b)
}
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, as, bs)
fn substs(did: ast::def_id,
as: &ty::substs,
bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, did, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {

View File

@ -184,8 +184,10 @@ impl Sub: combine {
super_args(&self, a, b)
}
fn substs(as: &ty::substs, bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, as, bs)
fn substs(did: ast::def_id,
as: &ty::substs,
bs: &ty::substs) -> cres<ty::substs> {
super_substs(&self, did, as, bs)
}
fn tps(as: &[ty::t], bs: &[ty::t]) -> cres<~[ty::t]> {

View File

@ -17,14 +17,13 @@ impl empty_rscope: region_scope {
}
}
enum type_rscope = bool;
enum type_rscope = option<ty::region_variance>;
impl type_rscope: region_scope {
fn anon_region(_span: span) -> result<ty::region, ~str> {
if *self {
result::ok(ty::re_bound(ty::br_self))
} else {
result::err(~"to use region types here, the containing type \
must be declared with a region bound")
match *self {
some(_) => result::ok(ty::re_bound(ty::br_self)),
none => result::err(~"to use region types here, the containing \
type must be declared with a region bound")
}
}
fn named_region(span: span, id: ast::ident) -> result<ty::region, ~str> {
@ -39,6 +38,13 @@ impl type_rscope: region_scope {
}
}
fn bound_self_region(rp: option<ty::region_variance>) -> option<ty::region> {
match rp {
some(_) => some(ty::re_bound(ty::br_self)),
none => none
}
}
enum anon_rscope = {anon: ty::region, base: region_scope};
fn in_anon_rscope<RS: region_scope copy owned>(self: RS, r: ty::region)
-> @anon_rscope {

View File

@ -5,7 +5,6 @@ enum ast {
fn mk_add_bad1(x: &a/ast, y: &b/ast) -> ast/&a {
add(x, y) //~ ERROR cannot infer an appropriate lifetime
//~^ ERROR cannot infer an appropriate lifetime
}
fn main() {

View File

@ -6,7 +6,6 @@ enum ast {
fn mk_add_bad2(x: &a/ast, y: &a/ast, z: &ast) -> ast {
add(x, y)
//~^ ERROR cannot infer an appropriate lifetime
//~^^ ERROR cannot infer an appropriate lifetime
}
fn main() {

View File

@ -0,0 +1,18 @@
struct contravariant {
f: &int;
}
fn to_same_lifetime(bi: contravariant/&r) {
let bj: contravariant/&r = bi;
}
fn to_shorter_lifetime(bi: contravariant/&r) {
let bj: contravariant/&blk = bi;
}
fn to_longer_lifetime(bi: contravariant/&r) -> contravariant/&static {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

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

View File

@ -0,0 +1,22 @@
// Covariant with respect to a region:
//
// You can upcast to a *larger region* but not a smaller one.
struct covariant {
f: fn@(x: &self/int) -> int;
}
fn to_same_lifetime(bi: covariant/&r) {
let bj: covariant/&r = bi;
}
fn to_shorter_lifetime(bi: covariant/&r) {
let bj: covariant/&blk = bi; //~ ERROR mismatched types
}
fn to_longer_lifetime(bi: covariant/&r) -> covariant/&static {
bi
}
fn main() {
}

View File

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

View File

@ -0,0 +1,18 @@
struct invariant {
f: @mut &int;
}
fn to_same_lifetime(bi: invariant/&r) {
let bj: invariant/&r = bi;
}
fn to_shorter_lifetime(bi: invariant/&r) {
let bj: invariant/&blk = bi; //~ ERROR mismatched types
}
fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -0,0 +1,18 @@
struct invariant {
f: @[mut &int];
}
fn to_same_lifetime(bi: invariant/&r) {
let bj: invariant/&r = bi;
}
fn to_shorter_lifetime(bi: invariant/&r) {
let bj: invariant/&blk = bi; //~ ERROR mismatched types
}
fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -0,0 +1,18 @@
struct invariant {
f: fn@(x: @mut &self/int);
}
fn to_same_lifetime(bi: invariant/&r) {
let bj: invariant/&r = bi;
}
fn to_shorter_lifetime(bi: invariant/&r) {
let bj: invariant/&blk = bi; //~ ERROR mismatched types
}
fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -0,0 +1,18 @@
struct invariant {
f: fn@() -> @mut &self/int;
}
fn to_same_lifetime(bi: invariant/&r) {
let bj: invariant/&r = bi;
}
fn to_shorter_lifetime(bi: invariant/&r) {
let bj: invariant/&blk = bi; //~ ERROR mismatched types
}
fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -0,0 +1,18 @@
struct invariant {
mut f: &int;
}
fn to_same_lifetime(bi: invariant/&r) {
let bj: invariant/&r = bi;
}
fn to_shorter_lifetime(bi: invariant/&r) {
let bj: invariant/&blk = bi; //~ ERROR mismatched types
}
fn to_longer_lifetime(bi: invariant/&r) -> invariant/&static {
bi //~ ERROR mismatched types
}
fn main() {
}

View File

@ -0,0 +1,18 @@
struct boxed_int {
f: &int;
}
fn max(bi: &r/boxed_int, f: &r/int) -> int {
if *bi.f > *f {*bi.f} else {*f}
}
fn with(bi: &boxed_int) -> int {
let i = 22;
max(bi, &i)
}
fn main() {
let g = 21;
let foo = boxed_int { f: &g };
assert with(&foo) == 22;
}

View File

@ -0,0 +1,21 @@
struct boxed_int {
f: &int;
}
fn get(bi: &r/boxed_int) -> &r/int {
bi.f
}
fn with(bi: &r/boxed_int) {
// Here, the upcast is allowed because the `boxed_int` type is
// contravariant with respect to `&r`. See also
// compile-fail/regions-infer-invariance-due-to-mutability.rs
let bi: &blk/boxed_int/&blk = bi;
assert *get(bi) == 22;
}
fn main() {
let g = 22;
let foo = boxed_int { f: &g };
with(&foo);
}

View File

@ -20,7 +20,5 @@ fn main() {
let ctxt = { v: 22u };
let hc = { c: &ctxt };
// This no longer works, interestingly, due to the ownership
// requirement. Perhaps this ownership requirement is too strict.
// assert get_v(hc as get_ctxt) == 22u;
assert get_v(hc as get_ctxt) == 22u;
}