Detect and store object-lifetime-defaults.
This commit is contained in:
parent
c5db290bf6
commit
931a3c4f9d
6 changed files with 228 additions and 6 deletions
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ¶m.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
|
||||
|
|
|
|||
32
src/test/compile-fail/object-lifetime-default.rs
Normal file
32
src/test/compile-fail/object-lifetime-default.rs
Normal 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() { }
|
||||
Loading…
Add table
Add a link
Reference in a new issue