diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 9962f49dfcfd..94654b849229 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -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 + 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") } } diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 640b9649286c..e1e9d49dd058 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -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) +{ + 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, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index e276683236f8..6be16de95015 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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>, + pub object_lifetime_default: Option, } #[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), + } + } +} diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 645a7ab9440b..5e46ce08e4f7 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -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>(&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)), } } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 8a1945c16a6a..f26041dbbe1e 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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::>() + .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 +{ + 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 + { + 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 + { + 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 diff --git a/src/test/compile-fail/object-lifetime-default.rs b/src/test/compile-fail/object-lifetime-default.rs new file mode 100644 index 000000000000..73f71751ee80 --- /dev/null +++ b/src/test/compile-fail/object-lifetime-default.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[rustc_object_lifetime_default] +struct A(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() { }