Detect and store object-lifetime-defaults.

This commit is contained in:
Niko Matsakis 2015-02-11 16:37:36 -05:00
parent c5db290bf6
commit 931a3c4f9d
6 changed files with 228 additions and 6 deletions

View file

@ -824,6 +824,7 @@ fn parse_type_param_def_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F)
assert_eq!(next(st), '|');
let bounds = parse_bounds_(st, conv);
let default = parse_opt(st, |st| parse_ty_(st, conv));
let object_lifetime_default = parse_object_lifetime_default(st, conv);
ty::TypeParameterDef {
name: name,
@ -831,7 +832,24 @@ fn parse_type_param_def_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F)
space: space,
index: index,
bounds: bounds,
default: default
default: default,
object_lifetime_default: object_lifetime_default,
}
}
fn parse_object_lifetime_default<'a,'tcx, F>(st: &mut PState<'a,'tcx>,
conv: &mut F)
-> Option<ty::ObjectLifetimeDefault>
where F: FnMut(DefIdSource, ast::DefId) -> ast::DefId,
{
match next(st) {
'n' => None,
'a' => Some(ty::ObjectLifetimeDefault::Ambiguous),
's' => {
let region = parse_region_(st, conv);
Some(ty::ObjectLifetimeDefault::Specific(region))
}
_ => panic!("parse_object_lifetime_default: bad input")
}
}

View file

@ -414,6 +414,21 @@ pub fn enc_type_param_def<'a, 'tcx>(w: &mut SeekableMemWriter, cx: &ctxt<'a, 'tc
v.space.to_uint(), v.index);
enc_bounds(w, cx, &v.bounds);
enc_opt(w, v.default, |w, t| enc_ty(w, cx, t));
enc_object_lifetime_default(w, cx, v.object_lifetime_default);
}
fn enc_object_lifetime_default<'a, 'tcx>(w: &mut SeekableMemWriter,
cx: &ctxt<'a, 'tcx>,
default: Option<ty::ObjectLifetimeDefault>)
{
match default {
None => mywrite!(w, "n"),
Some(ty::ObjectLifetimeDefault::Ambiguous) => mywrite!(w, "a"),
Some(ty::ObjectLifetimeDefault::Specific(r)) => {
mywrite!(w, "s");
enc_region(w, cx, r);
}
}
}
pub fn enc_predicate<'a, 'tcx>(w: &mut SeekableMemWriter,

View file

@ -1758,6 +1758,21 @@ impl fmt::Debug for IntVarValue {
}
}
/// Default region to use for the bound of objects that are
/// supplied as the value for this type parameter. This is derived
/// from `T:'a` annotations appearing in the type definition. If
/// this is `None`, then the default is inherited from the
/// surrounding context. See RFC #599 for details.
#[derive(Copy, Clone, Debug)]
pub enum ObjectLifetimeDefault {
/// Require an explicit annotation. Occurs when multiple
/// `T:'a` constraints are found.
Ambiguous,
/// Use the given region as the default.
Specific(Region),
}
#[derive(Clone, Debug)]
pub struct TypeParameterDef<'tcx> {
pub name: ast::Name,
@ -1766,6 +1781,7 @@ pub struct TypeParameterDef<'tcx> {
pub index: u32,
pub bounds: ParamBounds<'tcx>,
pub default: Option<Ty<'tcx>>,
pub object_lifetime_default: Option<ObjectLifetimeDefault>,
}
#[derive(RustcEncodable, RustcDecodable, Clone, Debug)]
@ -7386,3 +7402,12 @@ impl<'a, 'tcx> Repr<'tcx> for ParameterEnvironment<'a, 'tcx> {
self.caller_bounds.repr(tcx))
}
}
impl<'tcx> Repr<'tcx> for ObjectLifetimeDefault {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
match *self {
ObjectLifetimeDefault::Ambiguous => format!("Ambiguous"),
ObjectLifetimeDefault::Specific(ref r) => r.repr(tcx),
}
}
}

View file

@ -379,6 +379,19 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> {
index: self.index,
bounds: self.bounds.fold_with(folder),
default: self.default.fold_with(folder),
object_lifetime_default: self.object_lifetime_default.fold_with(folder),
}
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault {
fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ObjectLifetimeDefault {
match *self {
ty::ObjectLifetimeDefault::Ambiguous =>
ty::ObjectLifetimeDefault::Ambiguous,
ty::ObjectLifetimeDefault::Specific(r) =>
ty::ObjectLifetimeDefault::Specific(r.fold_with(folder)),
}
}
}

View file

@ -86,6 +86,7 @@ There are some shortcomings in this design:
*/
use astconv::{self, AstConv, ty_of_arg, ast_ty_to_ty, ast_region_to_region};
use middle::def;
use middle::lang_items::SizedTraitLangItem;
use middle::region;
use middle::resolve_lifetime;
@ -1199,8 +1200,23 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CollectCtxt<'a, 'tcx>,
predicates.clone());
assert!(prev_predicates.is_none());
return (scheme, predicates);
// Debugging aid.
if ty::has_attr(tcx, local_def(it.id), "rustc_object_lifetime_default") {
let object_lifetime_default_reprs: String =
scheme.generics.types.iter()
.map(|t| match t.object_lifetime_default {
Some(ty::ObjectLifetimeDefault::Specific(r)) =>
r.user_string(tcx),
d =>
d.repr(ccx.tcx()),
})
.collect::<Vec<String>>()
.connect(",");
tcx.sess.span_err(it.span, &object_lifetime_default_reprs);
}
return (scheme, predicates);
}
fn type_scheme_of_foreign_item<'a, 'tcx>(
@ -1269,6 +1285,7 @@ fn ty_generics_for_type_or_impl<'a, 'tcx>(ccx: &CollectCtxt<'a, 'tcx>,
subst::TypeSpace,
&generics.lifetimes[],
&generics.ty_params[],
&generics.where_clause,
ty::Generics::empty())
}
@ -1298,6 +1315,7 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CollectCtxt<'a, 'tcx>,
subst::TypeSpace,
&ast_generics.lifetimes[],
&ast_generics.ty_params[],
&ast_generics.where_clause,
ty::Generics::empty());
// Add in the self type parameter.
@ -1321,7 +1339,8 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CollectCtxt<'a, 'tcx>,
trait_bounds: vec!(ty::Binder(self_trait_ref.clone())),
projection_bounds: vec!(),
},
default: None
default: None,
object_lifetime_default: None,
};
ccx.tcx.ty_param_defs.borrow_mut().insert(param_id, def.clone());
@ -1341,6 +1360,7 @@ fn ty_generics_for_fn_or_method<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
subst::FnSpace,
&early_lifetimes[],
&generics.ty_params[],
&generics.where_clause,
base_generics)
}
@ -1487,6 +1507,7 @@ fn ty_generics<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
space: subst::ParamSpace,
lifetime_defs: &[ast::LifetimeDef],
types: &[ast::TyParam],
where_clause: &ast::WhereClause,
base_generics: ty::Generics<'tcx>)
-> ty::Generics<'tcx>
{
@ -1511,7 +1532,7 @@ fn ty_generics<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
// Now create the real type parameters.
for (i, param) in types.iter().enumerate() {
let def = get_or_create_type_parameter_def(ccx, space, param, i as u32);
let def = get_or_create_type_parameter_def(ccx, space, param, i as u32, where_clause);
debug!("ty_generics: def for type param: {:?}, {:?}", def, space);
result.types.push(space, def);
}
@ -1522,7 +1543,8 @@ fn ty_generics<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
space: subst::ParamSpace,
param: &ast::TyParam,
index: u32)
index: u32,
where_clause: &ast::WhereClause)
-> ty::TypeParameterDef<'tcx>
{
let tcx = ccx.tcx;
@ -1558,13 +1580,17 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
}
};
let object_lifetime_default =
compute_object_lifetime_default(ccx, space, index, &param.bounds, where_clause);
let def = ty::TypeParameterDef {
space: space,
index: index,
name: param.ident.name,
def_id: local_def(param.id),
bounds: bounds,
default: default
default: default,
object_lifetime_default: object_lifetime_default,
};
tcx.ty_param_defs.borrow_mut().insert(param.id, def.clone());
@ -1572,6 +1598,99 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
def
}
/// Scan the bounds and where-clauses on a parameter to extract bounds
/// of the form `T:'a` so as to determine the `ObjectLifetimeDefault`.
/// This runs as part of computing the minimal type scheme, so we
/// intentionally avoid just asking astconv to convert all the where
/// clauses into a `ty::Predicate`. This is because that could induce
/// artificial cycles.
fn compute_object_lifetime_default<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
space: subst::ParamSpace,
index: u32,
param_bounds: &[ast::TyParamBound],
where_clause: &ast::WhereClause)
-> Option<ty::ObjectLifetimeDefault>
{
let inline_bounds = from_bounds(ccx, param_bounds);
let where_bounds = from_predicates(ccx, space, index, &where_clause.predicates);
let all_bounds: HashSet<_> = inline_bounds.into_iter()
.chain(where_bounds.into_iter())
.collect();
return if all_bounds.len() > 1 {
Some(ty::ObjectLifetimeDefault::Ambiguous)
} else {
all_bounds.into_iter()
.next()
.map(ty::ObjectLifetimeDefault::Specific)
};
fn from_bounds<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
bounds: &[ast::TyParamBound])
-> Vec<ty::Region>
{
bounds.iter()
.filter_map(|bound| {
match *bound {
ast::TraitTyParamBound(..) =>
None,
ast::RegionTyParamBound(ref lifetime) =>
Some(astconv::ast_region_to_region(ccx.tcx(), lifetime)),
}
})
.collect()
}
fn from_predicates<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
space: subst::ParamSpace,
index: u32,
predicates: &[ast::WherePredicate])
-> Vec<ty::Region>
{
predicates.iter()
.flat_map(|predicate| {
match *predicate {
ast::WherePredicate::BoundPredicate(ref data) => {
if data.bound_lifetimes.len() == 0 &&
is_param(ccx, &data.bounded_ty, space, index)
{
from_bounds(ccx, &data.bounds).into_iter()
} else {
Vec::new().into_iter()
}
}
ast::WherePredicate::RegionPredicate(..) |
ast::WherePredicate::EqPredicate(..) => {
Vec::new().into_iter()
}
}
})
.collect()
}
fn is_param(ccx: &CollectCtxt,
ast_ty: &ast::Ty,
space: subst::ParamSpace,
index: u32)
-> bool
{
match ast_ty.node {
ast::TyPath(_, id) => {
match ccx.tcx.def_map.borrow()[id] {
def::DefTyParam(s, i, _, _) => {
space == s && index == i
}
_ => {
false
}
}
}
_ => {
false
}
}
}
}
enum SizedByDefault { Yes, No }
/// Translate the AST's notion of ty param bounds (which are an enum consisting of a newtyped Ty or

View file

@ -0,0 +1,32 @@
// Copyright 2015 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.
#[rustc_object_lifetime_default]
struct A<T>(T); //~ ERROR None
#[rustc_object_lifetime_default]
struct B<'a,T>(&'a (), T); //~ ERROR None
#[rustc_object_lifetime_default]
struct C<'a,T:'a>(&'a T); //~ ERROR 'a
#[rustc_object_lifetime_default]
struct D<'a,'b,T:'a+'b>(&'a T, &'b T); //~ ERROR Ambiguous
#[rustc_object_lifetime_default]
struct E<'a,'b:'a,T:'b>(&'a T, &'b T); //~ ERROR 'b
#[rustc_object_lifetime_default]
struct F<'a,'b,T:'a,U:'b>(&'a T, &'b U); //~ ERROR 'a,'b
#[rustc_object_lifetime_default]
struct G<'a,'b,T:'a,U:'a+'b>(&'a T, &'b U); //~ ERROR 'a,Ambiguous
fn main() { }