about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2015-02-11 16:37:36 -0500
committerNiko Matsakis <niko@alum.mit.edu>2015-02-16 10:55:36 -0500
commit931a3c4f9df2fbf043bc30c0524bc93e1ffa397e (patch)
treec766d38008da97964668661d37f92adbee59087b /src
parentc5db290bf6df986a6acd5ce993f278c18e55ca37 (diff)
downloadrust-931a3c4f9df2fbf043bc30c0524bc93e1ffa397e.tar.gz
rust-931a3c4f9df2fbf043bc30c0524bc93e1ffa397e.zip
Detect and store object-lifetime-defaults.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/metadata/tydecode.rs20
-rw-r--r--src/librustc/metadata/tyencode.rs15
-rw-r--r--src/librustc/middle/ty.rs25
-rw-r--r--src/librustc/middle/ty_fold.rs13
-rw-r--r--src/librustc_typeck/collect.rs129
-rw-r--r--src/test/compile-fail/object-lifetime-default.rs32
6 files changed, 228 insertions, 6 deletions
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index 9962f49dfcf..94654b84922 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<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")
     }
 }
 
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 640b9649286..e1e9d49dd05 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<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,
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index e276683236f..6be16de9501 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<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),
+        }
+    }
+}
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index 645a7ab9440..5e46ce08e4f 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<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)),
         }
     }
 }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 8a1945c16a6..f26041dbbe1 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::<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
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 00000000000..73f71751ee8
--- /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 <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() { }